mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-30 17:42:06 -04:00
logger: add a dedicated QML logging Singleton
- adds log.info/error/debug/warn/fatal - adds ability to write logs to any file - add CLI options in addition to env to set log levels
This commit is contained in:
@@ -7,6 +7,7 @@ import "../utils/layout.js" as LayoutUtils
|
||||
|
||||
Column {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("DragDropGrid")
|
||||
|
||||
property bool editMode: false
|
||||
property string expandedSection: ""
|
||||
@@ -988,7 +989,7 @@ Column {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("DragDropGrid: stale plugin component for", pluginId, "- reloading");
|
||||
log.warn("stale plugin component for", pluginId, "- reloading");
|
||||
PluginService.reloadPlugin(pluginId);
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -6,6 +6,7 @@ import "../utils/widgets.js" as WidgetUtils
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("WidgetModel")
|
||||
|
||||
property var vpnBuiltinInstance: null
|
||||
property var cupsBuiltinInstance: null
|
||||
@@ -26,7 +27,7 @@ QtObject {
|
||||
const widgets = SettingsData.controlCenterWidgets || [];
|
||||
const hasVpnWidget = widgets.some(w => w.id === "builtin_vpn");
|
||||
if (!hasVpnWidget && vpnLoader.active) {
|
||||
console.log("VpnWidget: No VPN widget in control center, deactivating loader");
|
||||
log.debug("VpnWidget: No VPN widget in control center, deactivating loader");
|
||||
vpnLoader.active = false;
|
||||
}
|
||||
}
|
||||
@@ -55,7 +56,7 @@ QtObject {
|
||||
const widgets = SettingsData.controlCenterWidgets || [];
|
||||
const hasCupsWidget = widgets.some(w => w.id === "builtin_cups");
|
||||
if (!hasCupsWidget && cupsLoader.active) {
|
||||
console.log("CupsWidget: No CUPS widget in control center, deactivating loader");
|
||||
log.debug("CupsWidget: No CUPS widget in control center, deactivating loader");
|
||||
cupsLoader.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
import qs.Services
|
||||
|
||||
CompoundPill {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("ColorPickerPill")
|
||||
|
||||
property var colorPickerModal: null
|
||||
|
||||
@@ -14,14 +16,14 @@ CompoundPill {
|
||||
secondaryText: I18n.tr("Choose a color")
|
||||
|
||||
onToggled: {
|
||||
console.log("ColorPickerPill toggled, modal:", colorPickerModal);
|
||||
log.debug("ColorPickerPill toggled, modal:", colorPickerModal);
|
||||
if (colorPickerModal) {
|
||||
colorPickerModal.show();
|
||||
}
|
||||
}
|
||||
|
||||
onExpandClicked: {
|
||||
console.log("ColorPickerPill expandClicked, modal:", colorPickerModal);
|
||||
log.debug("ColorPickerPill expandClicked, modal:", colorPickerModal);
|
||||
if (colorPickerModal) {
|
||||
colorPickerModal.show();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import qs.Services
|
||||
|
||||
PanelWindow {
|
||||
id: barWindow
|
||||
readonly property var log: Log.scoped("DankBarWindow")
|
||||
|
||||
required property var rootWindow
|
||||
required property var barConfig
|
||||
@@ -164,7 +165,7 @@ PanelWindow {
|
||||
barWindow.BackgroundEffect.blurRegion = region;
|
||||
barWindow.blurRegion = region;
|
||||
} catch (e) {
|
||||
console.warn("BarBlur: Failed to create blur region:", e);
|
||||
log.warn("BarBlur: Failed to create blur region:", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,11 +535,11 @@ PanelWindow {
|
||||
Connections {
|
||||
target: PluginService
|
||||
function onPluginLoaded(pluginId) {
|
||||
console.info("DankBar: Plugin loaded:", pluginId);
|
||||
log.info("DankBar: Plugin loaded:", pluginId);
|
||||
SettingsData.widgetDataChanged();
|
||||
}
|
||||
function onPluginUnloaded(pluginId) {
|
||||
console.info("DankBar: Plugin unloaded:", pluginId);
|
||||
log.info("DankBar: Plugin unloaded:", pluginId);
|
||||
SettingsData.widgetDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import qs.Widgets
|
||||
|
||||
BasePill {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("AppsDock")
|
||||
|
||||
enableBackgroundHover: false
|
||||
enableCursor: false
|
||||
@@ -550,9 +551,9 @@ BasePill {
|
||||
showBadge: root.showOverflowBadge
|
||||
z: 10
|
||||
onClicked: {
|
||||
console.log("Overflow button clicked! Current state:", root.overflowExpanded);
|
||||
log.debug("Overflow button clicked! Current state:", root.overflowExpanded);
|
||||
root.overflowExpanded = !root.overflowExpanded;
|
||||
console.log("New state:", root.overflowExpanded);
|
||||
log.debug("New state:", root.overflowExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import qs.Widgets
|
||||
|
||||
BasePill {
|
||||
id: battery
|
||||
readonly property var log: Log.scoped("Battery")
|
||||
|
||||
property bool batteryPopupVisible: false
|
||||
property var popoutTarget: null
|
||||
@@ -130,13 +131,13 @@ BasePill {
|
||||
// Check if this is a touchpad
|
||||
if (delta !== 120 && delta !== -120) {
|
||||
touchpadAccumulator += delta;
|
||||
console.info("Acc: "+touchpadAccumulator);
|
||||
log.info("Acc: " + touchpadAccumulator);
|
||||
if (Math.abs(touchpadAccumulator) < 500)
|
||||
return;
|
||||
delta = touchpadAccumulator;
|
||||
touchpadAccumulator = 0;
|
||||
}
|
||||
console.info("Trigger! Delta: "+delta)
|
||||
log.info("Trigger! Delta: " + delta);
|
||||
|
||||
// This is after the other delta checks so it only shows on valid Y scroll
|
||||
if (typeof PowerProfiles === "undefined") {
|
||||
@@ -149,11 +150,14 @@ BasePill {
|
||||
var index = profiles.findIndex(profile => PowerProfiles.profile === profile);
|
||||
|
||||
// Step once based on mouse wheel direction
|
||||
if (delta > 0) index += 1;
|
||||
else index -= 1;
|
||||
if (delta > 0)
|
||||
index += 1;
|
||||
else
|
||||
index -= 1;
|
||||
|
||||
// Already at end of list, can't go further
|
||||
if (index < 0 || index >= profiles.length) return;
|
||||
if (index < 0 || index >= profiles.length)
|
||||
return;
|
||||
|
||||
// Set new profile
|
||||
PowerProfiles.profile = profiles[index];
|
||||
|
||||
@@ -6,6 +6,7 @@ import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("CalendarOverviewCard")
|
||||
|
||||
implicitWidth: SettingsData.showWeekNumber ? 736 : 700
|
||||
|
||||
@@ -521,7 +522,7 @@ Rectangle {
|
||||
onClicked: {
|
||||
if (modelData.url && modelData.url !== "") {
|
||||
if (Qt.openUrlExternally(modelData.url) === false) {
|
||||
console.warn("Failed to open URL: " + modelData.url);
|
||||
log.warn("Failed to open URL: " + modelData.url);
|
||||
} else {
|
||||
root.closeDash();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("WeatherTab")
|
||||
|
||||
LayoutMirroring.enabled: I18n.isRtl
|
||||
LayoutMirroring.childrenInherit: true
|
||||
@@ -45,7 +46,7 @@ Item {
|
||||
hourlyList.currentIndex = Math.max(0, Math.min((WeatherService.weather.hourlyForecast?.length ?? 1) - 1, WeatherService.calendarHourDifference((new Date()), date) + (new Date()).getHours()));
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Weather Date Sync Error:", e);
|
||||
log.warn("Weather Date Sync Error:", e);
|
||||
}
|
||||
|
||||
syncing = false;
|
||||
|
||||
@@ -5,9 +5,11 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import "GreetdEnv.js" as GreetdEnv
|
||||
import qs.Services
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("GreetdMemory")
|
||||
|
||||
readonly property string greetCfgDir: Quickshell.env("DMS_GREET_CFG_DIR") || "/var/cache/dms-greeter"
|
||||
readonly property string sessionConfigPath: greetCfgDir + "/session.json"
|
||||
@@ -42,7 +44,7 @@ Singleton {
|
||||
nightModeEnabled = config.nightModeEnabled !== undefined ? config.nightModeEnabled : false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse greeter session config:", e);
|
||||
log.warn("Failed to parse greeter session config:", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ Singleton {
|
||||
if (!rememberLastSession || !rememberLastUser)
|
||||
saveMemory();
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse greetd memory:", e);
|
||||
log.warn("Failed to parse greetd memory:", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +124,7 @@ Singleton {
|
||||
parseSessionConfig(sessionConfigFileView.text());
|
||||
}
|
||||
onLoadFailed: error => {
|
||||
console.warn("Could not load greeter session config from", root.sessionConfigPath, "error:", error);
|
||||
log.warn("Could not load greeter session config from", root.sessionConfigPath, "error:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import "GreetdEnv.js" as GreetdEnv
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("GreetdSettings")
|
||||
|
||||
readonly property string configPath: {
|
||||
const greetCfgDir = Quickshell.env("DMS_GREET_CFG_DIR") || "/var/cache/dms-greeter";
|
||||
@@ -81,8 +83,7 @@ Singleton {
|
||||
|
||||
currentThemeName = settings.currentThemeName !== undefined ? settings.currentThemeName : "purple";
|
||||
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : "";
|
||||
registryThemeVariants = settings.registryThemeVariants !== undefined ?
|
||||
settings.registryThemeVariants : ({});
|
||||
registryThemeVariants = settings.registryThemeVariants !== undefined ? settings.registryThemeVariants : ({});
|
||||
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot";
|
||||
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true;
|
||||
showSeconds = settings.showSeconds !== undefined ? settings.showSeconds : false;
|
||||
@@ -142,7 +143,7 @@ Singleton {
|
||||
Theme.applyGreeterTheme(currentThemeName);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Failed to parse greetd settings:", e);
|
||||
log.warn("Failed to parse greetd settings:", e);
|
||||
} finally {
|
||||
settingsLoaded = true;
|
||||
}
|
||||
@@ -192,7 +193,7 @@ Singleton {
|
||||
parseSettings(settingsFile.text());
|
||||
}
|
||||
onLoadFailed: error => {
|
||||
console.warn("Failed to load greetd settings:", error);
|
||||
log.warn("Failed to load greetd settings:", error);
|
||||
root.parseSettings("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: keyboard_controller
|
||||
readonly property var log: Log.scoped("KeyboardController")
|
||||
|
||||
// reference on the TextInput
|
||||
property Item target
|
||||
@@ -14,21 +16,20 @@ Item {
|
||||
|
||||
function show() {
|
||||
if (!isKeyboardActive && keyboard === null) {
|
||||
keyboard = keyboardComponent.createObject(
|
||||
keyboard_controller.rootObject)
|
||||
keyboard.target = keyboard_controller.target
|
||||
keyboard.dismissed.connect(hide)
|
||||
isKeyboardActive = true
|
||||
keyboard = keyboardComponent.createObject(keyboard_controller.rootObject);
|
||||
keyboard.target = keyboard_controller.target;
|
||||
keyboard.dismissed.connect(hide);
|
||||
isKeyboardActive = true;
|
||||
} else
|
||||
console.log("The keyboard is already shown")
|
||||
log.debug("The keyboard is already shown");
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (isKeyboardActive && keyboard !== null) {
|
||||
keyboard.destroy()
|
||||
isKeyboardActive = false
|
||||
keyboard.destroy();
|
||||
isKeyboardActive = false;
|
||||
} else
|
||||
console.log("The keyboard is already hidden")
|
||||
log.debug("The keyboard is already hidden");
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
@@ -14,6 +14,7 @@ import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("LockScreenContent")
|
||||
|
||||
function encodeFileUrl(path) {
|
||||
if (!path)
|
||||
@@ -95,9 +96,9 @@ Item {
|
||||
if (SessionService.loginctlAvailable && DMSService.apiVersion >= 2) {
|
||||
DMSService.sendRequest("loginctl.lockerReady", null, resp => {
|
||||
if (resp?.error)
|
||||
console.warn("lockerReady failed:", resp.error);
|
||||
log.warn("lockerReady failed:", resp.error);
|
||||
else
|
||||
console.log("lockerReady sent (afterAnimating/afterRendering)");
|
||||
log.debug("lockerReady sent (afterAnimating/afterRendering)");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -803,7 +804,7 @@ Item {
|
||||
}
|
||||
|
||||
if (pam.passwd.active) {
|
||||
console.log("PAM is active, ignoring input");
|
||||
log.debug("PAM is active, ignoring input");
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
@@ -1622,7 +1623,7 @@ Item {
|
||||
buttonSize: 40
|
||||
onClicked: {
|
||||
if (demoMode) {
|
||||
console.log("Demo: Power Menu");
|
||||
log.debug("Demo: Power Menu");
|
||||
} else {
|
||||
powerMenu.show();
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Services
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("LockScreenDemo")
|
||||
|
||||
property bool demoActive: false
|
||||
|
||||
@@ -25,12 +27,12 @@ PanelWindow {
|
||||
color: "transparent"
|
||||
|
||||
function showDemo(): void {
|
||||
console.log("Showing lock screen demo");
|
||||
log.debug("Showing lock screen demo");
|
||||
demoActive = true;
|
||||
}
|
||||
|
||||
function hideDemo(): void {
|
||||
console.log("Hiding lock screen demo");
|
||||
log.debug("Hiding lock screen demo");
|
||||
demoActive = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("VideoScreensaver")
|
||||
|
||||
required property string screenName
|
||||
property bool active: false
|
||||
@@ -53,7 +54,7 @@ Item {
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode !== 0 || !videoPicker.result) {
|
||||
console.warn("VideoScreensaver: no video found in folder");
|
||||
log.warn("no video found in folder");
|
||||
ToastService.showError(I18n.tr("Video Screensaver"), I18n.tr("No video found in folder"));
|
||||
root.dismiss();
|
||||
}
|
||||
@@ -98,14 +99,14 @@ Item {
|
||||
`, background, "VideoScreensaver.VideoPlayer");
|
||||
|
||||
videoPlayer.errorOccurred.connect((error, errorString) => {
|
||||
console.warn("VideoScreensaver: playback error:", errorString);
|
||||
log.warn("playback error:", errorString);
|
||||
ToastService.showError(I18n.tr("Video Screensaver"), I18n.tr("Playback error: ") + errorString);
|
||||
root.dismiss();
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.warn("VideoScreensaver: Failed to create video player:", e);
|
||||
log.warn("Failed to create video player:", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("PluginSettings")
|
||||
|
||||
required property string pluginId
|
||||
property var pluginService: null
|
||||
@@ -131,7 +133,7 @@ Item {
|
||||
return;
|
||||
}
|
||||
if (!hasPermission) {
|
||||
console.warn("PluginSettings: Plugin", pluginId, "does not have settings_write permission");
|
||||
log.warn("Plugin", pluginId, "does not have settings_write permission");
|
||||
return;
|
||||
}
|
||||
if (pluginService.savePluginData) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import qs.Services
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("DisplayConfigState")
|
||||
|
||||
readonly property bool hasOutputBackend: WlrOutputService.wlrOutputAvailable
|
||||
readonly property var wlrOutputs: WlrOutputService.outputs
|
||||
@@ -106,7 +107,7 @@ Singleton {
|
||||
function findMatchingProfile() {
|
||||
const profiles = validatedProfiles;
|
||||
|
||||
console.log("[Profile Match] Current outputs:", JSON.stringify(currentOutputSet));
|
||||
log.debug("[Profile Match] Current outputs:", JSON.stringify(currentOutputSet));
|
||||
|
||||
let bestMatch = "";
|
||||
let bestScore = -1;
|
||||
@@ -116,25 +117,25 @@ Singleton {
|
||||
const profile = profiles[profileId];
|
||||
const profileSet = new Set(profile.outputSet);
|
||||
|
||||
console.log("[Profile Match] Checking", profile.name, "outputSet:", JSON.stringify(profile.outputSet));
|
||||
log.debug("[Profile Match] Checking", profile.name, "outputSet:", JSON.stringify(profile.outputSet));
|
||||
|
||||
let allCurrentPresent = true;
|
||||
for (const output of currentOutputSet) {
|
||||
if (!profileSet.has(output)) {
|
||||
console.log("[Profile Match] - Missing output:", output);
|
||||
log.debug("[Profile Match] - Missing output:", output);
|
||||
allCurrentPresent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allCurrentPresent) {
|
||||
console.log("[Profile Match] - SKIP: not all current outputs present");
|
||||
log.debug("[Profile Match] - SKIP: not all current outputs present");
|
||||
continue;
|
||||
}
|
||||
|
||||
const disconnectedCount = profile.outputSet.length - currentOutputSet.length;
|
||||
const score = currentOutputSet.length * 100 - disconnectedCount;
|
||||
const updatedAt = profile.updatedAt || profile.createdAt || 0;
|
||||
console.log("[Profile Match] - MATCH score:", score, "(disconnected:", disconnectedCount, "updatedAt:", updatedAt + ")");
|
||||
log.debug("[Profile Match] - MATCH score:", score, "(disconnected:", disconnectedCount, "updatedAt:", updatedAt + ")");
|
||||
|
||||
if (score > bestScore || (score === bestScore && updatedAt > bestUpdatedAt)) {
|
||||
bestScore = score;
|
||||
@@ -142,7 +143,7 @@ Singleton {
|
||||
bestUpdatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
console.log("[Profile Match] Best match:", bestMatch, "score:", bestScore);
|
||||
log.debug("[Profile Match] Best match:", bestMatch, "score:", bestScore);
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import qs.Services
|
||||
|
||||
Column {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("WidgetsTabSection")
|
||||
|
||||
property var items: []
|
||||
property var allWidgets: []
|
||||
@@ -1726,11 +1727,11 @@ Column {
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
onOpened: {
|
||||
console.log("Privacy context menu opened");
|
||||
log.debug("Privacy context menu opened");
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
console.log("Privacy Center context menu closed");
|
||||
log.debug("Privacy Center context menu closed");
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
|
||||
@@ -7,6 +7,7 @@ import qs.Widgets
|
||||
import qs.Services
|
||||
|
||||
Variants {
|
||||
readonly property var log: Log.scoped("WallpaperBackground")
|
||||
model: {
|
||||
if (SessionData.isGreeterMode) {
|
||||
return Quickshell.screens;
|
||||
@@ -103,7 +104,7 @@ Variants {
|
||||
function _recheckScreenScale() {
|
||||
const newScale = CompositorService.getScreenScale(modelData);
|
||||
if (newScale !== root.screenScale) {
|
||||
console.info("WallpaperBackground: screen scale corrected for", modelData.name + ":", root.screenScale, "->", newScale);
|
||||
log.info("screen scale corrected for", modelData.name + ":", root.screenScale, "->", newScale);
|
||||
root.screenScale = newScale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
readonly property var log: Log.scoped("OverviewWidget")
|
||||
required property var panelWindow
|
||||
required property bool overviewOpen
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
|
||||
@@ -276,7 +277,7 @@ Item {
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
console.error("OverviewWidget filter error:", e);
|
||||
log.error("OverviewWidget filter error:", e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user