status-desktop/ui/imports/utils/UndoStackManager.qml
Alex Jbanca fcd9567677 feat: Add settings card to control link previews settings in chat input
This commit adds the link preview settings card in the chat input area and connects the settings to the controller.

Not included in this commit: Backend for the preserving the settings, syncing the settings and enforcing the settings on the backend side.

Whenever an url is detected in the chat input area, the link preview settings card is presented. This card enables the user to choose one of the following options:

1. `Show for this message` - All the link previews in the current message will be loaded without asking again. The current message can be defined as the message currently typed/pasted in the chat input. Deleting or sending the current content is resetting this setting and the link preview settings card will be presented again when a new url is detected.
2. `Always show previews` - All the link previews will be loaded automatically. The link preview settings card will not be presented again (in the current state, this settings is enabled for the lifetime of the controller. This will change once the settings are preserved and synced)
3. `Never show previews` - No link preview will be loaded. Same as the `Always show previews` option, this will be preserved for the lifetime of the controller for now.
4. Dismiss (x button) - The link preview settings card will be dismissed. It will be loaded again when a new link preview is detected

The same options can be loaded as a context menu on the link preview card.

Changes:
1. Adding `LinkPreviewSettingsCard`
2. Adding the settings context menu to `LinkPreviewSettingsCard` and `LinkPreviewMiniCard`
3. Connect settings events to the nim controller
4. Adding the controller logic for settings change
5. Adding the link preview dismiss settings flag to the preserverd properties and use it as a condition to load the settings.
6. Adding/Updating corresponding storybook pages
2023-10-12 15:40:01 +03:00

157 lines
4.6 KiB
QML

import QtQuick 2.15
/*
Custom stack-based undo/redo implementation for TextEdit that works with formatted text.
Usage:
TextEdit {
id: textEdit
text: "Hello world"
onCustomEvent: undoStack.clear()
}
UndoRedoStack {
id: undoStack
textEdit: textEdit
enabled: true
maxStackSize: 100
}
*/
Item {
id: root
/*
The TextEdit to apply undo/redo to. The stack manager will be installed automatically on this textEdit
*/
required property TextEdit textEdit
/*
The maximum stack size
Once the maximum stack size is reached, the stack will be reduced to half its size by removing every second item
As a result the undo/redo will be less precise, jumping back/forward by 2 steps instead of 1
The first item in the stack will always be kept
*/
property int maxStackSize: 100
/*
Function used to clear the stack
This function will be called automatically when the TextEdit component changes or when the enabled property changes
*/
function clear() {
d.undoStack = []
d.redoStack = []
d.previousFormattedText = ""
d.previousText = ""
}
/*
Undo the last action
count: The number of actions to redo
*/
function undo(count = 1) {
if(d.undoStack.length == 0 || count <= 0) {
return
}
for (var i = 0; i < count; i++) {
if(d.undoStack.length == 0) {
return
}
const lastAction = d.undoStack.pop()
d.redoStack.push(lastAction)
lastAction.undo()
}
}
/*
Redo the last action
count: The number of actions to redo
*/
function redo(count = 1) {
if(d.redoStack.length == 0 || count <= 0) {
return
}
for (var i = 0; i < count; i++) {
if(d.redoStack.length == 0) {
return
}
const lastAction = d.redoStack.pop()
d.undoStack.push(lastAction)
lastAction.redo()
}
}
onTextEditChanged: {
clear()
textEdit.Keys.forwardTo.push(root)
}
onEnabledChanged: clear()
Keys.enabled: root.enabled
Keys.onPressed: {
if(event.matches(StandardKey.Undo)) {
undo(event.isAutoRepeat ? 2 : 1)
event.accepted = true
return
}
if(event.matches(StandardKey.Redo)) {
redo(event.isAutoRepeat ? 2 : 1)
event.accepted = true
return
}
}
readonly property QtObject d: QtObject {
property var undoStack: []
property var redoStack: []
property string previousFormattedText: ""
property string previousText: ""
property bool aboutToChangeText: false
function reduceUndoStack() {
if(d.undoStack.length <= root.maxStackSize) {
return
}
const newStackSize = Math.ceil(root.maxStackSize / 2)
for(var i = 1; i <= newStackSize; i++) {
d.undoStack.splice(i, Math.ceil(root.maxStackSize / newStackSize))
}
}
readonly property Connections textChangedConnection: Connections {
target: root.textEdit
enabled: root.enabled && !d.aboutToChangeText
function onTextChanged() {
const unformattedText = root.textEdit.getText(0, root.textEdit.length)
if(d.previousText !== unformattedText) {
const newFormattedText = root.textEdit.text
const previousFormattedTextCopy = d.previousFormattedText
d.undoStack.push({
undo: function() {
d.aboutToChangeText = true
root.textEdit.text = previousFormattedTextCopy
root.textEdit.cursorPosition = root.textEdit.length
d.aboutToChangeText = false
},
redo: function() {
d.aboutToChangeText = true
root.textEdit.text = newFormattedText
root.textEdit.cursorPosition = root.textEdit.length
d.aboutToChangeText = false
}
})
d.reduceUndoStack()
d.previousText = unformattedText
d.previousFormattedText = newFormattedText
}
}
}
}
}