forked from Shinonome/dots-hyprland
sidebar: night light dialog
This commit is contained in:
@@ -7,6 +7,7 @@ import QtQuick.Controls
|
|||||||
RippleButton {
|
RippleButton {
|
||||||
id: root
|
id: root
|
||||||
property string buttonIcon
|
property string buttonIcon
|
||||||
|
property alias iconSize: iconWidget.iconSize
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: contentItem.implicitHeight + 8 * 2
|
implicitHeight: contentItem.implicitHeight + 8 * 2
|
||||||
@@ -17,6 +18,7 @@ RippleButton {
|
|||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
spacing: 10
|
spacing: 10
|
||||||
OptionalMaterialSymbol {
|
OptionalMaterialSymbol {
|
||||||
|
id: iconWidget
|
||||||
icon: root.buttonIcon
|
icon: root.buttonIcon
|
||||||
opacity: root.enabled ? 1 : 0.4
|
opacity: root.enabled ? 1 : 0.4
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.services
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Widgets
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias text: sliderName.text
|
||||||
|
property alias from: sliderWidget.from
|
||||||
|
property alias to: sliderWidget.to
|
||||||
|
property alias value: sliderWidget.value
|
||||||
|
property alias tooltipContent: sliderWidget.tooltipContent
|
||||||
|
property alias stopIndicatorValues: sliderWidget.stopIndicatorValues
|
||||||
|
|
||||||
|
signal moved()
|
||||||
|
|
||||||
|
spacing: -2
|
||||||
|
ContentSubsectionLabel {
|
||||||
|
id: sliderName
|
||||||
|
visible: text?.length > 0
|
||||||
|
text: ""
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StyledSlider {
|
||||||
|
id: sliderWidget
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 4
|
||||||
|
rightMargin: leftMargin
|
||||||
|
}
|
||||||
|
configuration: StyledSlider.Configuration.S
|
||||||
|
onMoved: root.moved()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,19 +11,22 @@ import Quickshell.Hyprland
|
|||||||
|
|
||||||
import qs.modules.sidebarRight.quickToggles
|
import qs.modules.sidebarRight.quickToggles
|
||||||
import qs.modules.sidebarRight.quickToggles.classicStyle
|
import qs.modules.sidebarRight.quickToggles.classicStyle
|
||||||
import qs.modules.sidebarRight.wifiNetworks
|
|
||||||
import qs.modules.sidebarRight.bluetoothDevices
|
import qs.modules.sidebarRight.bluetoothDevices
|
||||||
|
import qs.modules.sidebarRight.nightLight
|
||||||
import qs.modules.sidebarRight.volumeMixer
|
import qs.modules.sidebarRight.volumeMixer
|
||||||
|
import qs.modules.sidebarRight.wifiNetworks
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
property int sidebarWidth: Appearance.sizes.sidebarWidth
|
property int sidebarWidth: Appearance.sizes.sidebarWidth
|
||||||
property int sidebarPadding: 10
|
property int sidebarPadding: 10
|
||||||
property string settingsQmlPath: Quickshell.shellPath("settings.qml")
|
property string settingsQmlPath: Quickshell.shellPath("settings.qml")
|
||||||
property bool showWifiDialog: false
|
|
||||||
property bool showBluetoothDialog: false
|
|
||||||
property bool showAudioOutputDialog: false
|
property bool showAudioOutputDialog: false
|
||||||
property bool showAudioInputDialog: false
|
property bool showAudioInputDialog: false
|
||||||
|
property bool showBluetoothDialog: false
|
||||||
|
property bool showNightLightDialog: false
|
||||||
|
property bool showWifiDialog: false
|
||||||
property bool editMode: false
|
property bool editMode: false
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -109,18 +112,20 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ToggleDialog {
|
ToggleDialog {
|
||||||
id: wifiDialogLoader
|
shownPropertyString: "showAudioOutputDialog"
|
||||||
shownPropertyString: "showWifiDialog"
|
dialog: VolumeDialog {
|
||||||
dialog: WifiDialog {}
|
isSink: true
|
||||||
onShownChanged: {
|
}
|
||||||
if (!shown) return;
|
}
|
||||||
Network.enableWifi();
|
|
||||||
Network.rescanWifi();
|
ToggleDialog {
|
||||||
|
shownPropertyString: "showAudioInputDialog"
|
||||||
|
dialog: VolumeDialog {
|
||||||
|
isSink: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleDialog {
|
ToggleDialog {
|
||||||
id: bluetoothDialogLoader
|
|
||||||
shownPropertyString: "showBluetoothDialog"
|
shownPropertyString: "showBluetoothDialog"
|
||||||
dialog: BluetoothDialog {}
|
dialog: BluetoothDialog {}
|
||||||
onShownChanged: {
|
onShownChanged: {
|
||||||
@@ -130,23 +135,21 @@ Item {
|
|||||||
Bluetooth.defaultAdapter.enabled = true;
|
Bluetooth.defaultAdapter.enabled = true;
|
||||||
Bluetooth.defaultAdapter.discovering = true;
|
Bluetooth.defaultAdapter.discovering = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleDialog {
|
ToggleDialog {
|
||||||
id: audioOutputDialogLoader
|
shownPropertyString: "showNightLightDialog"
|
||||||
shownPropertyString: "showAudioOutputDialog"
|
dialog: NightLightDialog {}
|
||||||
dialog: VolumeDialog {
|
|
||||||
isSink: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleDialog {
|
ToggleDialog {
|
||||||
id: audioInputDialogLoader
|
shownPropertyString: "showWifiDialog"
|
||||||
shownPropertyString: "showAudioInputDialog"
|
dialog: WifiDialog {}
|
||||||
dialog: VolumeDialog {
|
onShownChanged: {
|
||||||
isSink: false
|
if (!shown) return;
|
||||||
|
Network.enableWifi();
|
||||||
|
Network.rescanWifi();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,18 +189,21 @@ Item {
|
|||||||
active: Config.options.sidebar.quickToggles.style === styleName
|
active: Config.options.sidebar.quickToggles.style === styleName
|
||||||
Connections {
|
Connections {
|
||||||
target: quickPanelImplLoader.item
|
target: quickPanelImplLoader.item
|
||||||
function onOpenWifiDialog() {
|
|
||||||
root.showWifiDialog = true;
|
|
||||||
}
|
|
||||||
function onOpenBluetoothDialog() {
|
|
||||||
root.showBluetoothDialog = true;
|
|
||||||
}
|
|
||||||
function onOpenAudioOutputDialog() {
|
function onOpenAudioOutputDialog() {
|
||||||
root.showAudioOutputDialog = true;
|
root.showAudioOutputDialog = true;
|
||||||
}
|
}
|
||||||
function onOpenAudioInputDialog() {
|
function onOpenAudioInputDialog() {
|
||||||
root.showAudioInputDialog = true;
|
root.showAudioInputDialog = true;
|
||||||
}
|
}
|
||||||
|
function onOpenBluetoothDialog() {
|
||||||
|
root.showBluetoothDialog = true;
|
||||||
|
}
|
||||||
|
function onOpenNightLightDialog() {
|
||||||
|
root.showNightLightDialog = true;
|
||||||
|
}
|
||||||
|
function onOpenWifiDialog() {
|
||||||
|
root.showWifiDialog = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.common.functions
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
|
WindowDialog {
|
||||||
|
id: root
|
||||||
|
property var screen: root.QsWindow.window?.screen
|
||||||
|
property var brightnessMonitor: Brightness.getMonitorForScreen(screen)
|
||||||
|
|
||||||
|
WindowDialogTitle {
|
||||||
|
text: Translation.tr("Eye protection")
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDialogSectionHeader {
|
||||||
|
text: Translation.tr("Night Light")
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDialogSeparator {
|
||||||
|
Layout.topMargin: -22
|
||||||
|
Layout.leftMargin: 0
|
||||||
|
Layout.rightMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: nightLightColumn
|
||||||
|
Layout.topMargin: -16
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
|
buttonIcon: "lightbulb"
|
||||||
|
text: Translation.tr("Enable now")
|
||||||
|
checked: Hyprsunset.active
|
||||||
|
onCheckedChanged: {
|
||||||
|
Hyprsunset.toggle(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
|
buttonIcon: "night_sight_auto"
|
||||||
|
text: Translation.tr("Automatic")
|
||||||
|
checked: Config.options.light.night.automatic
|
||||||
|
onCheckedChanged: {
|
||||||
|
Config.options.light.night.automatic = checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDialogSlider {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 4
|
||||||
|
rightMargin: 4
|
||||||
|
}
|
||||||
|
text: Translation.tr("Color temperature")
|
||||||
|
from: 1000
|
||||||
|
to: 20000
|
||||||
|
stopIndicatorValues: [6000, to]
|
||||||
|
value: Config.options.light.night.colorTemperature
|
||||||
|
onMoved: Config.options.light.night.colorTemperature = value
|
||||||
|
tooltipContent: `${Math.round(value)}K`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDialogSectionHeader {
|
||||||
|
text: Translation.tr("Brightness")
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDialogSeparator {
|
||||||
|
Layout.topMargin: -22
|
||||||
|
Layout.leftMargin: 0
|
||||||
|
Layout.rightMargin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: brightnessColumn
|
||||||
|
Layout.topMargin: -16
|
||||||
|
Layout.fillWidth: true
|
||||||
|
// Layout.fillHeight: true
|
||||||
|
|
||||||
|
WindowDialogSlider {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 4
|
||||||
|
rightMargin: 4
|
||||||
|
}
|
||||||
|
// text: Translation.tr("Brightness")
|
||||||
|
value: root.brightnessMonitor.brightness
|
||||||
|
onMoved: root.brightnessMonitor.setBrightness(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowDialogButtonRow {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogButton {
|
||||||
|
buttonText: Translation.tr("Done")
|
||||||
|
onClicked: root.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,9 @@ Rectangle {
|
|||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
color: Appearance.colors.colLayer1
|
color: Appearance.colors.colLayer1
|
||||||
|
|
||||||
signal openWifiDialog()
|
|
||||||
signal openBluetoothDialog()
|
|
||||||
signal openAudioOutputDialog()
|
signal openAudioOutputDialog()
|
||||||
signal openAudioInputDialog()
|
signal openAudioInputDialog()
|
||||||
|
signal openBluetoothDialog()
|
||||||
|
signal openNightLightDialog()
|
||||||
|
signal openWifiDialog()
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-12
@@ -12,15 +12,23 @@ AbstractQuickPanel {
|
|||||||
id: root
|
id: root
|
||||||
property bool editMode: false
|
property bool editMode: false
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: (editMode ? contentItem.implicitHeight : usedRows.implicitHeight) + root.padding * 2
|
|
||||||
|
|
||||||
|
// Sizes
|
||||||
|
implicitHeight: (editMode ? contentItem.implicitHeight : usedRows.implicitHeight) + root.padding * 2
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
property real spacing: 6
|
property real spacing: 6
|
||||||
property real padding: 6
|
property real padding: 6
|
||||||
|
readonly property real baseCellWidth: {
|
||||||
|
// This is the wrong calculation, but it looks correct in reality???
|
||||||
|
// (theoretically spacing should be multiplied by 1 column less)
|
||||||
|
const availableWidth = root.width - (root.padding * 2) - (root.spacing * (root.columns))
|
||||||
|
return availableWidth / root.columns
|
||||||
|
}
|
||||||
|
readonly property real baseCellHeight: 56
|
||||||
|
|
||||||
|
// Toggles
|
||||||
readonly property list<string> availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile"]
|
readonly property list<string> availableToggleTypes: ["network", "bluetooth", "idleInhibitor", "easyEffects", "nightLight", "darkMode", "cloudflareWarp", "gameMode", "screenSnip", "colorPicker", "onScreenKeyboard", "mic", "audio", "notifications", "powerProfile"]
|
||||||
readonly property int columns: Config.options.sidebar.quickToggles.android.columns
|
readonly property int columns: Config.options.sidebar.quickToggles.android.columns
|
||||||
readonly property list<var> toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : []
|
readonly property list<var> toggles: Config.ready ? Config.options.sidebar.quickToggles.android.toggles : []
|
||||||
@@ -30,13 +38,6 @@ AbstractQuickPanel {
|
|||||||
return types.map(type => { return { type: type, size: 1 } })
|
return types.map(type => { return { type: type, size: 1 } })
|
||||||
}
|
}
|
||||||
readonly property list<var> unusedToggleRows: toggleRowsForList(unusedToggles)
|
readonly property list<var> unusedToggleRows: toggleRowsForList(unusedToggles)
|
||||||
readonly property real baseCellWidth: {
|
|
||||||
// This is the wrong calculation, but it looks correct in reality???
|
|
||||||
// (theoretically spacing should be multiplied by 1 column less)
|
|
||||||
const availableWidth = root.width - (root.padding * 2) - (root.spacing * (root.columns))
|
|
||||||
return availableWidth / root.columns
|
|
||||||
}
|
|
||||||
readonly property real baseCellHeight: 56
|
|
||||||
|
|
||||||
function toggleRowsForList(togglesList) {
|
function toggleRowsForList(togglesList) {
|
||||||
var rows = [];
|
var rows = [];
|
||||||
@@ -78,7 +79,10 @@ AbstractQuickPanel {
|
|||||||
delegate: ButtonGroup {
|
delegate: ButtonGroup {
|
||||||
id: toggleRow
|
id: toggleRow
|
||||||
required property int index
|
required property int index
|
||||||
property var modelData: root.toggleRows[index]
|
property var modelData: {
|
||||||
|
print(JSON.stringify(root.toggleRows[index]))
|
||||||
|
return root.toggleRows[index]
|
||||||
|
}
|
||||||
property int startingIndex: {
|
property int startingIndex: {
|
||||||
const rows = root.toggleRows;
|
const rows = root.toggleRows;
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
@@ -100,10 +104,11 @@ AbstractQuickPanel {
|
|||||||
baseCellWidth: root.baseCellWidth
|
baseCellWidth: root.baseCellWidth
|
||||||
baseCellHeight: root.baseCellHeight
|
baseCellHeight: root.baseCellHeight
|
||||||
spacing: root.spacing
|
spacing: root.spacing
|
||||||
onOpenWifiDialog: root.openWifiDialog()
|
|
||||||
onOpenBluetoothDialog: root.openBluetoothDialog()
|
|
||||||
onOpenAudioOutputDialog: root.openAudioOutputDialog()
|
onOpenAudioOutputDialog: root.openAudioOutputDialog()
|
||||||
onOpenAudioInputDialog: root.openAudioInputDialog()
|
onOpenAudioInputDialog: root.openAudioInputDialog()
|
||||||
|
onOpenBluetoothDialog: root.openBluetoothDialog()
|
||||||
|
onOpenNightLightDialog: root.openNightLightDialog()
|
||||||
|
onOpenWifiDialog: root.openWifiDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -19,7 +19,7 @@ AndroidQuickToggleButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
altAction: () => {
|
altAction: () => {
|
||||||
Config.options.light.night.automatic = !Config.options.light.night.automatic
|
root.openMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
@@ -27,7 +27,7 @@ AndroidQuickToggleButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
text: Translation.tr("Night Light | Right-click to toggle Auto mode")
|
text: Translation.tr("Night Light | Right-click to configure")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -14,10 +14,11 @@ DelegateChooser {
|
|||||||
required property real baseCellHeight
|
required property real baseCellHeight
|
||||||
required property real spacing
|
required property real spacing
|
||||||
required property int startingIndex
|
required property int startingIndex
|
||||||
signal openWifiDialog()
|
|
||||||
signal openBluetoothDialog()
|
|
||||||
signal openAudioOutputDialog()
|
signal openAudioOutputDialog()
|
||||||
signal openAudioInputDialog()
|
signal openAudioInputDialog()
|
||||||
|
signal openBluetoothDialog()
|
||||||
|
signal openNightLightDialog()
|
||||||
|
signal openWifiDialog()
|
||||||
|
|
||||||
role: "type"
|
role: "type"
|
||||||
|
|
||||||
@@ -90,6 +91,9 @@ DelegateChooser {
|
|||||||
baseCellHeight: root.baseCellHeight
|
baseCellHeight: root.baseCellHeight
|
||||||
cellSpacing: root.spacing
|
cellSpacing: root.spacing
|
||||||
cellSize: modelData.size
|
cellSize: modelData.size
|
||||||
|
onOpenMenu: {
|
||||||
|
root.openNightLightDialog()
|
||||||
|
}
|
||||||
} }
|
} }
|
||||||
|
|
||||||
DelegateChoice { roleValue: "darkMode"; AndroidDarkModeToggle {
|
DelegateChoice { roleValue: "darkMode"; AndroidDarkModeToggle {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import QtQuick
|
|||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple hyprsunset service with automatic mode.
|
* Simple hyprsunset service with automatic mode.
|
||||||
@@ -111,18 +112,28 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle(active = undefined) {
|
||||||
if (root.manualActive === undefined) {
|
if (root.manualActive === undefined) {
|
||||||
root.manualActive = root.active;
|
root.manualActive = root.active;
|
||||||
root.manualActiveHour = root.clockHour;
|
root.manualActiveHour = root.clockHour;
|
||||||
root.manualActiveMinute = root.clockMinute;
|
root.manualActiveMinute = root.clockMinute;
|
||||||
}
|
}
|
||||||
|
|
||||||
root.manualActive = !root.manualActive;
|
root.manualActive = active !== undefined ? active : !root.manualActive;
|
||||||
if (root.manualActive) {
|
if (root.manualActive) {
|
||||||
root.enable();
|
root.enable();
|
||||||
} else {
|
} else {
|
||||||
root.disable();
|
root.disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Change temp
|
||||||
|
Connections {
|
||||||
|
target: Config.options.light.night
|
||||||
|
onColorTemperatureChanged: {
|
||||||
|
if (!root.active) return;
|
||||||
|
Hyprland.dispatch(`hyprctl hyprsunset temperature ${Config.options.light.night.colorTemperature}`);
|
||||||
|
Quickshell.execDetached(["hyprctl", "hyprsunset", "temperature", `${Config.options.light.night.colorTemperature}`]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user