No forgetting, deleting or clearing on a single press, anywhere
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -41,11 +41,36 @@ Item {
|
||||
return list;
|
||||
}
|
||||
|
||||
// Forgetting is armed in place rather than through ConfirmAction: a 44px
|
||||
// row inside a 300px popup has no width for a Keep/Forget it pair beside
|
||||
// the device name, so the trash glyph arms itself and the row's own
|
||||
// sublabel becomes the question. The token is ShellState's, the same one
|
||||
// ConfirmAction uses, so arming here disarms whatever was armed elsewhere
|
||||
// and only one confirm is ever live app-wide.
|
||||
//
|
||||
// The token is keyed by device address rather than held on the delegate,
|
||||
// because discovery rebuilds this list -- and every delegate in it -- every
|
||||
// time BlueZ reports something new. An arm parked on the row object would
|
||||
// be dropped by the next scan result; keyed by address it survives the
|
||||
// rebuild and is only ever cleared by a press or by the section closing.
|
||||
readonly property string forgetPrefix: "bluetooth-forget-quick:"
|
||||
|
||||
function forgetId(device): string {
|
||||
return root.forgetPrefix + (device.address || device.name || "");
|
||||
}
|
||||
|
||||
function disarmForget(): void {
|
||||
if (ShellState.armedConfirm.indexOf(root.forgetPrefix) === 0)
|
||||
ShellState.armedConfirm = "";
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
if (root.active)
|
||||
if (root.active) {
|
||||
Connectivity.acquireDiscovery(root.scanHold);
|
||||
else
|
||||
} else {
|
||||
Connectivity.releaseDiscovery(root.scanHold);
|
||||
root.disarmForget();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onDestruction: Connectivity.releaseDiscovery(root.scanHold)
|
||||
@@ -106,18 +131,36 @@ Item {
|
||||
|
||||
required property var modelData
|
||||
|
||||
readonly property bool forgetArmed:
|
||||
ShellState.armedConfirm === root.forgetId(deviceRow.modelData)
|
||||
|
||||
width: parent.width
|
||||
// BlueZ reports a plain freedesktop name ("audio-headphones");
|
||||
// the symbolic variant is the one that can be recolored.
|
||||
icon: deviceRow.modelData.icon !== "" ? deviceRow.modelData.icon + "-symbolic" : "bluetooth-symbolic"
|
||||
iconFallback: "bluetooth-symbolic"
|
||||
label: deviceRow.modelData.name || deviceRow.modelData.address
|
||||
sublabel: root.stateText(deviceRow.modelData)
|
||||
// Armed, the row states the question and what it costs, since
|
||||
// the glyph alone is too small to carry either.
|
||||
sublabel: deviceRow.forgetArmed
|
||||
? "Press again to drop the pairing"
|
||||
: root.stateText(deviceRow.modelData)
|
||||
selected: deviceRow.modelData.connected
|
||||
onClicked: root.activate(deviceRow.modelData)
|
||||
// While armed the row body is the way out: a press anywhere
|
||||
// else on it takes the arming back instead of connecting, so
|
||||
// the escape is the largest target on screen.
|
||||
onClicked: {
|
||||
if (deviceRow.forgetArmed) {
|
||||
root.disarmForget();
|
||||
return;
|
||||
}
|
||||
root.activate(deviceRow.modelData);
|
||||
}
|
||||
|
||||
// Forgetting is destructive enough that it gets its own
|
||||
// control rather than sharing the row click.
|
||||
// control rather than sharing the row click -- and its own
|
||||
// two presses. See root.forgetPrefix for why the arming
|
||||
// happens in place here rather than through ConfirmAction.
|
||||
IconButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: deviceRow.modelData.paired
|
||||
@@ -125,7 +168,28 @@ Item {
|
||||
iconSize: 13
|
||||
icon: "user-trash-symbolic"
|
||||
iconFallback: "window-close-symbolic"
|
||||
onClicked: deviceRow.modelData.forget()
|
||||
tint: deviceRow.forgetArmed ? Theme.danger : Theme.fg
|
||||
onClicked: {
|
||||
if (!deviceRow.forgetArmed) {
|
||||
ShellState.armedConfirm = root.forgetId(deviceRow.modelData);
|
||||
return;
|
||||
}
|
||||
root.disarmForget();
|
||||
deviceRow.modelData.forget();
|
||||
}
|
||||
|
||||
// Behind the parent's own fill, so the hover tint still
|
||||
// reads on top of it. Danger appears only once armed --
|
||||
// the first press must not look like the last one.
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
radius: parent.radius
|
||||
visible: deviceRow.forgetArmed
|
||||
color: Theme.alpha(Theme.danger, 0.3)
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.danger, 0.6)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,10 +66,16 @@ Column {
|
||||
|
||||
width: parent.width
|
||||
label: entry.modelData.name || entry.modelData.address || "Unknown device"
|
||||
detail: root.stateLabel(entry.modelData)
|
||||
// Armed, the detail names what Forget costs. "Forget" reads like
|
||||
// "hide from this list"; it is the pairing itself that goes, and
|
||||
// the device has to be put back into pairing mode to return.
|
||||
detail: forgetConfirm.armed
|
||||
? "Drops the pairing — the device must be put back into pairing mode to return"
|
||||
: root.stateLabel(entry.modelData)
|
||||
divider: entry.index < root.shown.length - 1
|
||||
|| root.hiddenCount > 0 || root.showAll
|
||||
controlWidth: 200
|
||||
// Room for Keep and the confirming press while the row is armed.
|
||||
controlWidth: forgetConfirm.armed ? 290 : 200
|
||||
activatable: !entry.modelData.pairing
|
||||
onActivated: root.primaryAction(entry.modelData)
|
||||
|
||||
@@ -87,11 +93,17 @@ Column {
|
||||
onClicked: root.primaryAction(entry.modelData)
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
ConfirmAction {
|
||||
id: forgetConfirm
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: entry.modelData.paired
|
||||
text: "Forget"
|
||||
onClicked: entry.modelData.forget()
|
||||
actionId: "bluetooth-forget:"
|
||||
+ (entry.modelData.address || entry.modelData.name || "")
|
||||
armText: "Forget…"
|
||||
confirmText: "Forget it"
|
||||
enabled: !entry.modelData.pairing
|
||||
onConfirmed: entry.modelData.forget()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,16 +529,29 @@ Column {
|
||||
SettingRow {
|
||||
width: parent.width
|
||||
label: "Delete this mode"
|
||||
detail: "Removes it and its exceptions for good"
|
||||
controlWidth: 90
|
||||
// The permanence is said on the armed press, where it is the
|
||||
// question being answered, rather than under a button that had
|
||||
// already done it by the time the sentence was read.
|
||||
detail: deleteConfirm.armed
|
||||
? "Removes " + (root.modeName === "" ? "this mode" : root.modeName)
|
||||
+ (root.allow.length === 0
|
||||
? ""
|
||||
: " and its " + root.allow.length
|
||||
+ (root.allow.length === 1 ? " exception" : " exceptions"))
|
||||
+ " for good"
|
||||
: "Takes it out of the list"
|
||||
controlWidth: deleteConfirm.armed ? 180 : 90
|
||||
divider: false
|
||||
|
||||
SettingsButton {
|
||||
ConfirmAction {
|
||||
id: deleteConfirm
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Delete"
|
||||
tone: "danger"
|
||||
onClicked: FocusModes.removeMode(root.modeId)
|
||||
actionId: "focus-mode-delete:" + root.modeId
|
||||
armText: "Delete…"
|
||||
confirmText: "Delete it"
|
||||
onConfirmed: FocusModes.removeMode(root.modeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ Rectangle {
|
||||
id: aliasFrame
|
||||
anchors.left: reorderControls.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.right: removeButton.left
|
||||
anchors.right: removeConfirm.left
|
||||
anchors.rightMargin: 12
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 12
|
||||
@@ -107,17 +107,23 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// The line under the field says which light this is; armed, it says what
|
||||
// pressing again costs instead, because the alias only lives in this
|
||||
// favourite and there is nowhere else to read it back from.
|
||||
Text {
|
||||
anchors.left: aliasFrame.left
|
||||
anchors.right: removeButton.left
|
||||
anchors.right: removeConfirm.left
|
||||
anchors.rightMargin: 12
|
||||
anchors.top: aliasFrame.bottom
|
||||
anchors.topMargin: 7
|
||||
text: root.sourceName
|
||||
color: Theme.fgDim
|
||||
text: removeConfirm.armed
|
||||
? "Drops it from My Home — the name you gave it goes too"
|
||||
: root.sourceName
|
||||
color: removeConfirm.armed ? Theme.danger : Theme.fgDim
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
elide: Text.ElideRight
|
||||
wrapMode: removeConfirm.armed ? Text.WordWrap : Text.NoWrap
|
||||
elide: removeConfirm.armed ? Text.ElideNone : Text.ElideRight
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -143,17 +149,14 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
id: removeButton
|
||||
ConfirmAction {
|
||||
id: removeConfirm
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 11
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Remove"
|
||||
activeFocusOnTab: true
|
||||
border.width: activeFocus ? 2 : 1
|
||||
border.color: activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.08)
|
||||
onClicked: root.removeRequested(root.favorite.id)
|
||||
Keys.onReturnPressed: root.removeRequested(root.favorite.id)
|
||||
Keys.onSpacePressed: root.removeRequested(root.favorite.id)
|
||||
actionId: "home-favorite-remove:" + root.favorite.id
|
||||
armText: "Remove…"
|
||||
confirmText: "Remove it"
|
||||
onConfirmed: root.removeRequested(root.favorite.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,15 +246,33 @@ SettingsPage {
|
||||
SliderRow { setting: "maxVisibleToasts" }
|
||||
SliderRow { setting: "notificationHistoryLimit" }
|
||||
|
||||
ActionRow {
|
||||
// A SettingRow with its own ConfirmAction rather than an ActionRow:
|
||||
// clearing history is not undoable and it takes the inline replies and
|
||||
// actions with it, so it takes two presses and the armed detail names
|
||||
// both the count and what stops being answerable.
|
||||
SettingRow {
|
||||
label: "Notification history"
|
||||
detail: Notifs.history.length === 1
|
||||
? "1 kept now · the oldest are dropped past the limit"
|
||||
: `${Notifs.history.length} kept now · the oldest are dropped past the limit`
|
||||
action: "Clear"
|
||||
enabled: Notifs.history.length > 0
|
||||
detail: clearConfirm.armed
|
||||
? (Notifs.history.length === 1
|
||||
? "Drops the 1 kept notification — its inline reply and actions go with it"
|
||||
: `Drops all ${Notifs.history.length} kept notifications — their inline replies and actions go with them`)
|
||||
: (Notifs.history.length === 1
|
||||
? "1 kept now · the oldest are dropped past the limit"
|
||||
: `${Notifs.history.length} kept now · the oldest are dropped past the limit`)
|
||||
controlWidth: clearConfirm.armed ? 200 : 110
|
||||
divider: false
|
||||
onTriggered: Notifs.dismissAll()
|
||||
|
||||
ConfirmAction {
|
||||
id: clearConfirm
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
actionId: "notification-history-clear"
|
||||
armText: "Clear…"
|
||||
confirmText: "Clear them"
|
||||
enabled: Notifs.history.length > 0
|
||||
onConfirmed: Notifs.dismissAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -408,17 +408,35 @@ SettingsPage {
|
||||
|
||||
// Rebinding stores only the new chord; what a shortcut does always
|
||||
// comes from the desktop's configuration.
|
||||
ActionRow {
|
||||
//
|
||||
// A SettingRow with its own ConfirmAction rather than an ActionRow:
|
||||
// this one press drops every override at once and reloads the live
|
||||
// compositor, so it takes two, and the armed detail says both.
|
||||
SettingRow {
|
||||
label: "Restore every shipped shortcut"
|
||||
detail: root.overrideCount === 0
|
||||
? "Every shortcut is where it shipped"
|
||||
: root.overrideCount + (root.overrideCount === 1
|
||||
? " shortcut differs from the shipped keymap"
|
||||
: " shortcuts differ from the shipped keymap")
|
||||
action: "Restore all"
|
||||
detail: restoreConfirm.armed
|
||||
? "Puts " + root.overrideCount + (root.overrideCount === 1
|
||||
? " changed shortcut" : " changed shortcuts")
|
||||
+ " back where they shipped and reloads the compositor now"
|
||||
: (root.overrideCount === 0
|
||||
? "Every shortcut is where it shipped"
|
||||
: root.overrideCount + (root.overrideCount === 1
|
||||
? " shortcut differs from the shipped keymap"
|
||||
: " shortcuts differ from the shipped keymap"))
|
||||
controlWidth: restoreConfirm.armed ? 220 : 130
|
||||
divider: false
|
||||
enabled: root.overrideCount > 0 && !Keybinds.reloading
|
||||
onTriggered: Keybinds.resetAll()
|
||||
|
||||
ConfirmAction {
|
||||
id: restoreConfirm
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
actionId: "keybinds-restore-all"
|
||||
armText: "Restore all…"
|
||||
confirmText: "Restore them"
|
||||
enabled: root.overrideCount > 0 && !Keybinds.reloading
|
||||
onConfirmed: Keybinds.resetAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,13 +30,54 @@ Rectangle {
|
||||
readonly property bool isDefault: root.theme.id === (root.theme.scheme === "light"
|
||||
? ThemeCatalog.defaultLight : ThemeCatalog.defaultDark)
|
||||
|
||||
// Delete arms in place rather than through ConfirmAction. The ✕ badge is a
|
||||
// 22px corner of a card whose whole body is the apply target roughly ten
|
||||
// pixels away -- there is nowhere in that caption to put a Keep beside a
|
||||
// Delete it without the pair becoming the card. So the badge itself is the
|
||||
// two presses: the first turns it into a labelled danger pill and reddens
|
||||
// the card's border, the second deletes. The token is ShellState's, the
|
||||
// same one ConfirmAction uses, so one confirm is armed app-wide.
|
||||
readonly property string removeActionId: "theme-card-delete:" + root.theme.id
|
||||
readonly property bool removeArmed: ShellState.armedConfirm === root.removeActionId
|
||||
|
||||
function pressRemove(): void {
|
||||
if (!root.removeArmed) {
|
||||
ShellState.armedConfirm = root.removeActionId;
|
||||
return;
|
||||
}
|
||||
root.disarmRemove();
|
||||
root.removed();
|
||||
}
|
||||
|
||||
function disarmRemove(): void {
|
||||
if (root.removeArmed)
|
||||
ShellState.armedConfirm = "";
|
||||
}
|
||||
|
||||
// Armed, the card body is the way out: a press anywhere on it takes the
|
||||
// arming back instead of applying the theme, because the miss this guards
|
||||
// against is a press landing next to the badge rather than on it.
|
||||
function press(): void {
|
||||
if (root.removeArmed) {
|
||||
root.disarmRemove();
|
||||
return;
|
||||
}
|
||||
root.chosen();
|
||||
}
|
||||
|
||||
// A card that scrolls away, or a gallery that rebuilds, must not leave the
|
||||
// token claiming a theme nothing is showing.
|
||||
Component.onDestruction: root.disarmRemove()
|
||||
|
||||
implicitHeight: preview.height + caption.height + 4
|
||||
radius: Theme.cardRadius + 2
|
||||
color: Theme.alpha(Theme.fg, 0.04)
|
||||
border.width: 2
|
||||
border.color: root.activeFocus
|
||||
? Theme.accentSecondary
|
||||
: (root.active ? Theme.accent : Theme.alpha(Theme.fg, 0.12))
|
||||
border.color: root.removeArmed
|
||||
? Theme.danger
|
||||
: (root.activeFocus
|
||||
? Theme.accentSecondary
|
||||
: (root.active ? Theme.accent : Theme.alpha(Theme.fg, 0.12)))
|
||||
clip: true
|
||||
activeFocusOnTab: true
|
||||
|
||||
@@ -44,11 +85,13 @@ Rectangle {
|
||||
Accessible.name: root.theme.name
|
||||
Accessible.description: (root.theme.scheme === "light" ? "Light theme" : "Dark theme")
|
||||
+ (root.active ? ", selected" : "")
|
||||
+ (root.removeArmed ? ", delete is armed — press this card to keep it" : "")
|
||||
Accessible.focusable: true
|
||||
Accessible.focused: root.activeFocus
|
||||
|
||||
Keys.onReturnPressed: root.chosen()
|
||||
Keys.onSpacePressed: root.chosen()
|
||||
Keys.onReturnPressed: root.press()
|
||||
Keys.onSpacePressed: root.press()
|
||||
Keys.onEscapePressed: root.disarmRemove()
|
||||
|
||||
Rectangle {
|
||||
id: preview
|
||||
@@ -148,36 +191,52 @@ Rectangle {
|
||||
// Only saved themes can be removed, and the affordance is a
|
||||
// separate focus stop so Tab never lands on "delete" when the
|
||||
// person meant "select".
|
||||
//
|
||||
// At rest the badge is neutral: danger marks the press that
|
||||
// confirms, never the one that initiates, which is SettingsButton's
|
||||
// contract and holds here too. Armed, it stops being a glyph and
|
||||
// becomes a labelled pill wide enough to read.
|
||||
Rectangle {
|
||||
id: remove
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !root.shipped
|
||||
width: 22
|
||||
width: root.removeArmed ? removeLabel.implicitWidth + 16 : 22
|
||||
height: 22
|
||||
radius: 7
|
||||
color: removeHover.hovered
|
||||
? Theme.alpha(Theme.danger, 0.22)
|
||||
: Theme.alpha(Theme.fg, 0.07)
|
||||
border.width: remove.activeFocus ? 2 : 1
|
||||
border.color: remove.activeFocus
|
||||
color: root.removeArmed
|
||||
? Theme.alpha(Theme.danger, 0.3)
|
||||
: (removeHover.hovered
|
||||
? Theme.alpha(Theme.fg, 0.14)
|
||||
: Theme.alpha(Theme.fg, 0.07))
|
||||
border.width: root.removeArmed || remove.activeFocus ? 2 : 1
|
||||
border.color: root.removeArmed || remove.activeFocus
|
||||
? Theme.danger : Theme.alpha(Theme.fg, 0.08)
|
||||
activeFocusOnTab: visible
|
||||
|
||||
Accessible.role: Accessible.Button
|
||||
Accessible.name: "Delete " + root.theme.name
|
||||
Accessible.name: root.removeArmed
|
||||
? "Delete " + root.theme.name + ", press again to confirm"
|
||||
: "Delete " + root.theme.name
|
||||
Accessible.description: root.removeArmed
|
||||
? "This theme's palette, terminal colours and effects go with it"
|
||||
: ""
|
||||
Accessible.focusable: true
|
||||
Accessible.focused: remove.activeFocus
|
||||
|
||||
Keys.onReturnPressed: root.removed()
|
||||
Keys.onSpacePressed: root.removed()
|
||||
Keys.onReturnPressed: root.pressRemove()
|
||||
Keys.onSpacePressed: root.pressRemove()
|
||||
Keys.onEscapePressed: root.disarmRemove()
|
||||
|
||||
Text {
|
||||
id: removeLabel
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "✕"
|
||||
color: Theme.danger
|
||||
text: root.removeArmed ? "Delete it" : "✕"
|
||||
color: root.removeArmed ? Theme.danger : Theme.fgDim
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: root.removeArmed ? Font.DemiBold : Font.Normal
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
@@ -190,7 +249,7 @@ Rectangle {
|
||||
// on its way out.
|
||||
TapHandler {
|
||||
gesturePolicy: TapHandler.ReleaseWithinBounds
|
||||
onTapped: root.removed()
|
||||
onTapped: root.pressRemove()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,6 +261,6 @@ Rectangle {
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: root.chosen()
|
||||
onTapped: root.press()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,16 @@ SettingRow {
|
||||
readonly property bool custom: ThemeProfiles.activeProfile.shipped !== true
|
||||
|
||||
label: "Save as"
|
||||
detail: root.custom
|
||||
? "Saving again under a new name keeps both"
|
||||
: "Editing a shipped theme forks it; give the fork a name to keep it"
|
||||
// Armed, the row says what the delete actually costs: a saved theme is the
|
||||
// only copy of its palette, terminal colours and effect values.
|
||||
detail: deleteConfirm.armed
|
||||
? "Deletes “" + String(ThemeProfiles.activeProfile.name ?? "this theme")
|
||||
+ "” — its palette, terminal colours and effects go with it"
|
||||
: (root.custom
|
||||
? "Saving again under a new name keeps both"
|
||||
: "Editing a shipped theme forks it; give the fork a name to keep it")
|
||||
divider: false
|
||||
controlWidth: root.custom ? 380 : 292
|
||||
controlWidth: root.custom ? (deleteConfirm.armed ? 470 : 380) : 292
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
@@ -83,23 +88,14 @@ SettingRow {
|
||||
Keys.onSpacePressed: saveButton.save()
|
||||
}
|
||||
|
||||
SettingsButton {
|
||||
id: deleteButton
|
||||
ConfirmAction {
|
||||
id: deleteConfirm
|
||||
|
||||
visible: root.custom
|
||||
text: "Delete"
|
||||
tone: "danger"
|
||||
activeFocusOnTab: visible
|
||||
border.width: activeFocus ? 2 : 1
|
||||
border.color: activeFocus ? Theme.danger : Theme.alpha(Theme.danger, 0.45)
|
||||
|
||||
function remove(): void {
|
||||
ThemeProfiles.deleteProfile(ThemeProfiles.activeId);
|
||||
}
|
||||
|
||||
onClicked: deleteButton.remove()
|
||||
Keys.onReturnPressed: deleteButton.remove()
|
||||
Keys.onSpacePressed: deleteButton.remove()
|
||||
actionId: "theme-profile-delete:" + ThemeProfiles.activeId
|
||||
armText: "Delete…"
|
||||
confirmText: "Delete it"
|
||||
onConfirmed: ThemeProfiles.deleteProfile(ThemeProfiles.activeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user