Rework HBox-based pages and account settings
- Refactor everything about HBox, and adapt all the pages and popups that used it - Replace HTabContainer by HTabbedBox - Make boxes swippable - Make esc presses in boxes click the cancel button - Make all boxes and popups scrollable when needed - Replace generic apply button icons in popups - Fix tab focus for error and invite popups - Rework (still WIP) the account settings page: - Use the standard tabbed design of other pages - Ditch the horizontal profile layout, hacky and impossible to extend - Add real-time coloring for the display name field - Implement a device list in account settings (Sessions, still WIP)
This commit is contained in:
@@ -1,79 +0,0 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../Base"
|
||||
|
||||
HPopup {
|
||||
id: popup
|
||||
onAboutToShow: okClicked = false
|
||||
|
||||
|
||||
signal ok()
|
||||
signal cancel()
|
||||
|
||||
|
||||
default property alias boxData: box.body
|
||||
property alias box: box
|
||||
property bool fillAvailableHeight: false
|
||||
|
||||
property alias summary: summary
|
||||
property alias details: details
|
||||
|
||||
property string okText: qsTr("OK")
|
||||
property string okIcon: "ok"
|
||||
property bool okEnabled: true
|
||||
property bool okClicked: false
|
||||
property string cancelText: qsTr("Cancel")
|
||||
|
||||
|
||||
Binding on height {
|
||||
value: popup.maximumPreferredHeight
|
||||
when: popup.fillAvailableHeight
|
||||
}
|
||||
|
||||
HBox {
|
||||
id: box
|
||||
implicitWidth: Math.min(
|
||||
window.width - popup.leftMargin - popup.rightMargin,
|
||||
theme.controls.popup.defaultWidth,
|
||||
)
|
||||
fillAvailableHeight: popup.fillAvailableHeight
|
||||
clickButtonOnEnter: "ok"
|
||||
|
||||
buttonModel: [
|
||||
{ name: "ok", text: okText, iconName: okIcon, enabled: okEnabled},
|
||||
{ name: "cancel", text: cancelText, iconName: "cancel" },
|
||||
]
|
||||
|
||||
buttonCallbacks: ({
|
||||
ok: button => { okClicked = true; popup.ok(); popup.close() },
|
||||
cancel: button => {
|
||||
okClicked = false; popup.cancel(); popup.close()
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Binding on height {
|
||||
value: popup.maximumPreferredHeight
|
||||
when: popup.fillAvailableHeight
|
||||
}
|
||||
|
||||
HLabel {
|
||||
id: summary
|
||||
wrapMode: Text.Wrap
|
||||
font.bold: true
|
||||
visible: Boolean(text)
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
HLabel {
|
||||
id: details
|
||||
wrapMode: Text.Wrap
|
||||
visible: Boolean(text)
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,19 +1,42 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import "../Base/ButtonLayout"
|
||||
|
||||
BoxPopup {
|
||||
summary.text: qsTr("Clear this room's messages?")
|
||||
details.text: qsTr(
|
||||
"The messages will only be removed on your side. " +
|
||||
"They will be available again after you restart the application."
|
||||
)
|
||||
okText: qsTr("Clear")
|
||||
box.focusButton: "ok"
|
||||
|
||||
onOk: py.callClientCoro(userId, "clear_events", [roomId])
|
||||
HFlickableColumnPopup {
|
||||
id: popup
|
||||
|
||||
|
||||
property string userId: ""
|
||||
property string roomId: ""
|
||||
|
||||
|
||||
page.footer: ButtonLayout {
|
||||
ApplyButton {
|
||||
id: clearButton
|
||||
text: qsTr("Clear")
|
||||
icon.name: "clear-messages"
|
||||
onClicked: {
|
||||
py.callClientCoro(userId, "clear_events", [roomId])
|
||||
popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
CancelButton {
|
||||
onClicked: popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
SummaryLabel {
|
||||
text: qsTr("Clear this room's messages?")
|
||||
}
|
||||
|
||||
DetailsLabel {
|
||||
text: qsTr(
|
||||
"The messages will only be removed on your side. " +
|
||||
"They will be available again after you restart the application."
|
||||
)
|
||||
}
|
||||
|
||||
onOpened: clearButton.forceActiveFocus()
|
||||
}
|
||||
|
12
src/gui/Popups/DetailsLabel.qml
Normal file
12
src/gui/Popups/DetailsLabel.qml
Normal file
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../Base"
|
||||
|
||||
HLabel {
|
||||
wrapMode: Text.Wrap
|
||||
visible: Boolean(text)
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
@@ -1,35 +1,10 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import "../Base/ButtonLayout"
|
||||
|
||||
BoxPopup {
|
||||
HFlickableColumnPopup {
|
||||
id: popup
|
||||
summary.text: qsTr("Leave <i>%1</i> and lose the history?").arg(roomName)
|
||||
summary.textFormat: Text.StyledText
|
||||
details.text: qsTr(
|
||||
"You will not be able to see the messages you received in " +
|
||||
"this room anymore.\n\n" +
|
||||
|
||||
"If all members forget the room, it will be removed from the servers."
|
||||
)
|
||||
|
||||
okText: qsTr("Forget")
|
||||
box.focusButton: "ok"
|
||||
|
||||
onOk: py.callClientCoro(userId, "room_forget", [roomId], () => {
|
||||
if (window.uiState.page === "Pages/Chat/Chat.qml" &&
|
||||
window.uiState.pageProperties.userId === userId &&
|
||||
window.uiState.pageProperties.roomId === roomId)
|
||||
{
|
||||
window.mainUI.pageLoader.showPrevious() ||
|
||||
window.mainUI.pageLoader.showPage("Default")
|
||||
|
||||
Qt.callLater(popup.destroy)
|
||||
}
|
||||
})
|
||||
|
||||
onCancel: canDestroy = true
|
||||
onClosed: if (canDestroy) Qt.callLater(popup.destroy)
|
||||
|
||||
|
||||
property string userId: ""
|
||||
@@ -37,4 +12,55 @@ BoxPopup {
|
||||
property string roomName: ""
|
||||
|
||||
property bool canDestroy: false
|
||||
|
||||
|
||||
function forget() {
|
||||
py.callClientCoro(userId, "room_forget", [roomId], () => {
|
||||
if (window.uiState.page === "Pages/Chat/Chat.qml" &&
|
||||
window.uiState.pageProperties.userId === userId &&
|
||||
window.uiState.pageProperties.roomId === roomId)
|
||||
{
|
||||
window.mainUI.pageLoader.showPrevious() ||
|
||||
window.mainUI.pageLoader.showPage("Default")
|
||||
|
||||
Qt.callLater(popup.destroy)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
page.footer: ButtonLayout {
|
||||
ApplyButton {
|
||||
id: forgetButton
|
||||
text: qsTr("Forget")
|
||||
icon.name: "room-forget"
|
||||
onClicked: forget()
|
||||
}
|
||||
|
||||
CancelButton {
|
||||
onClicked: {
|
||||
canDestroy = true
|
||||
popup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: forgetButton.forceActiveFocus()
|
||||
onClosed: if (canDestroy) Qt.callLater(popup.destroy)
|
||||
|
||||
|
||||
SummaryLabel {
|
||||
text: qsTr("Leave <i>%1</i> and lose the history?").arg(roomName)
|
||||
textFormat: Text.StyledText
|
||||
}
|
||||
|
||||
DetailsLabel {
|
||||
text: qsTr(
|
||||
"You will not be able to see the messages you received in " +
|
||||
"this room anymore.\n\n" +
|
||||
|
||||
"If all members forget the room, it will be removed from the " +
|
||||
"servers."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
27
src/gui/Popups/HColumnPopup.qml
Normal file
27
src/gui/Popups/HColumnPopup.qml
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import "../Base"
|
||||
|
||||
HPopup {
|
||||
id: popup
|
||||
|
||||
|
||||
default property alias pageData: page.columnData
|
||||
readonly property alias page: page
|
||||
|
||||
|
||||
HColumnPage {
|
||||
id: page
|
||||
implicitWidth: Math.min(
|
||||
popup.maximumPreferredWidth,
|
||||
theme.controls.popup.defaultWidth,
|
||||
)
|
||||
implicitHeight: Math.min(
|
||||
popup.maximumPreferredHeight,
|
||||
implicitHeaderHeight + implicitFooterHeight +
|
||||
topPadding + bottomPadding + implicitContentHeight,
|
||||
)
|
||||
useVariableSpacing: false
|
||||
}
|
||||
}
|
25
src/gui/Popups/HFlickableColumnPopup.qml
Normal file
25
src/gui/Popups/HFlickableColumnPopup.qml
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import "../Base"
|
||||
|
||||
HPopup {
|
||||
id: popup
|
||||
|
||||
|
||||
default property alias pageData: page.columnData
|
||||
readonly property alias page: page
|
||||
|
||||
|
||||
HFlickableColumnPage {
|
||||
id: page
|
||||
implicitWidth: Math.min(
|
||||
popup.maximumPreferredWidth,
|
||||
theme.controls.popup.defaultWidth,
|
||||
)
|
||||
implicitHeight: Math.min(
|
||||
popup.maximumPreferredHeight,
|
||||
implicitHeaderHeight + implicitFooterHeight + contentHeight,
|
||||
)
|
||||
}
|
||||
}
|
@@ -4,51 +4,10 @@ import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../Base"
|
||||
import "../Base/ButtonLayout"
|
||||
|
||||
BoxPopup {
|
||||
HColumnPopup {
|
||||
id: popup
|
||||
// fillAvailableHeight: true
|
||||
summary.text: qsTr("Invite members to <i>%1</i>").arg(roomName)
|
||||
summary.textFormat: Text.StyledText
|
||||
okText: qsTr("Invite")
|
||||
okEnabled: invitingAllowed && Boolean(inviteArea.text.trim())
|
||||
|
||||
onOpened: inviteArea.forceActiveFocus()
|
||||
|
||||
onInvitingAllowedChanged:
|
||||
if (! invitingAllowed && inviteFuture) inviteFuture.cancel()
|
||||
|
||||
box.buttonCallbacks: ({
|
||||
ok: button => {
|
||||
button.loading = true
|
||||
|
||||
const inviteesLeft = inviteArea.text.trim().split(/\s+/).filter(
|
||||
user => ! successfulInvites.includes(user)
|
||||
)
|
||||
|
||||
inviteFuture = py.callClientCoro(
|
||||
userId,
|
||||
"room_mass_invite",
|
||||
[roomId, ...inviteesLeft],
|
||||
|
||||
([successes, errors]) => {
|
||||
if (errors.length < 1) {
|
||||
popup.close()
|
||||
return
|
||||
}
|
||||
|
||||
successfulInvites = successes
|
||||
failedInvites = errors
|
||||
button.loading = false
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
cancel: button => {
|
||||
if (inviteFuture) inviteFuture.cancel()
|
||||
popup.close()
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
property string userId
|
||||
@@ -61,13 +20,68 @@ BoxPopup {
|
||||
property var failedInvites: []
|
||||
|
||||
|
||||
function invite() {
|
||||
inviteButton.loading = true
|
||||
|
||||
const inviteesLeft = inviteArea.text.trim().split(/\s+/).filter(
|
||||
user => ! successfulInvites.includes(user)
|
||||
)
|
||||
|
||||
inviteFuture = py.callClientCoro(
|
||||
userId,
|
||||
"room_mass_invite",
|
||||
[roomId, ...inviteesLeft],
|
||||
|
||||
([successes, errors]) => {
|
||||
if (errors.length < 1) {
|
||||
popup.close()
|
||||
return
|
||||
}
|
||||
|
||||
successfulInvites = successes
|
||||
failedInvites = errors
|
||||
inviteButton.loading = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
page.footer: ButtonLayout {
|
||||
ApplyButton {
|
||||
id: inviteButton
|
||||
text: qsTr("Invite")
|
||||
icon.name: "room-send-invite"
|
||||
enabled: invitingAllowed && Boolean(inviteArea.text.trim())
|
||||
onClicked: invite()
|
||||
}
|
||||
|
||||
CancelButton {
|
||||
id: cancelButton
|
||||
onClicked: popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: inviteArea.forceActiveFocus()
|
||||
onClosed: if (inviteFuture) inviteFuture.cancel()
|
||||
|
||||
onInvitingAllowedChanged:
|
||||
if (! invitingAllowed && inviteFuture) inviteFuture.cancel()
|
||||
|
||||
|
||||
SummaryLabel {
|
||||
text: qsTr("Invite members to <i>%1</i>").arg(roomName)
|
||||
textFormat: Text.StyledText
|
||||
}
|
||||
|
||||
HScrollView {
|
||||
clip: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
HTextArea {
|
||||
id: inviteArea
|
||||
focusItemOnTab: box.firstButton
|
||||
focusItemOnTab: inviteButton.enabled ? inviteButton : cancelButton
|
||||
placeholderText:
|
||||
qsTr("User IDs (e.g. @bob:matrix.org @alice:localhost)")
|
||||
}
|
||||
|
@@ -1,21 +1,46 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import "../Base/ButtonLayout"
|
||||
|
||||
BoxPopup {
|
||||
summary.text: qsTr("Leave <i>%1</i>?").arg(roomName)
|
||||
summary.textFormat: Text.StyledText
|
||||
details.text: qsTr(
|
||||
"If this room is private, you will not be able to rejoin it."
|
||||
)
|
||||
okText: qsTr("Leave")
|
||||
box.focusButton: "ok"
|
||||
|
||||
onOk: py.callClientCoro(userId, "room_leave", [roomId], leftCallback)
|
||||
HFlickableColumnPopup {
|
||||
id: popup
|
||||
|
||||
|
||||
property string userId: ""
|
||||
property string roomId: ""
|
||||
property string roomName: ""
|
||||
property var leftCallback: null
|
||||
|
||||
|
||||
page.footer: ButtonLayout {
|
||||
ApplyButton {
|
||||
id: leaveButton
|
||||
icon.name: "room-leave"
|
||||
text: qsTr("Leave")
|
||||
|
||||
onClicked: {
|
||||
py.callClientCoro(userId, "room_leave", [roomId], leftCallback)
|
||||
popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
CancelButton {
|
||||
onClicked: popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: leaveButton.forceActiveFocus()
|
||||
|
||||
|
||||
SummaryLabel {
|
||||
text: qsTr("Leave <i>%1</i>?").arg(roomName)
|
||||
textFormat: Text.StyledText
|
||||
}
|
||||
|
||||
DetailsLabel {
|
||||
text: qsTr(
|
||||
"If this room is private, you will not be able to rejoin it."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -3,29 +3,22 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../Base"
|
||||
import "../Base/ButtonLayout"
|
||||
|
||||
BoxPopup {
|
||||
HFlickableColumnPopup {
|
||||
id: popup
|
||||
okEnabled: Boolean(passwordField.text)
|
||||
|
||||
onAboutToShow: {
|
||||
okClicked = false
|
||||
acceptedPassword = ""
|
||||
passwordValid = null
|
||||
errorMessage.text = ""
|
||||
}
|
||||
onOpened: passwordField.forceActiveFocus()
|
||||
|
||||
|
||||
signal cancelled()
|
||||
|
||||
|
||||
property bool validateWhileTyping: false
|
||||
|
||||
property string acceptedPassword: ""
|
||||
property var passwordValid: null
|
||||
property bool okClicked: false
|
||||
|
||||
property alias field: passwordField
|
||||
readonly property alias summary: summary
|
||||
readonly property alias validateButton: validateButton
|
||||
|
||||
signal cancelled()
|
||||
|
||||
|
||||
function verifyPassword(pass, callback) {
|
||||
@@ -35,40 +28,59 @@ BoxPopup {
|
||||
callback(true)
|
||||
}
|
||||
|
||||
function validate() {
|
||||
const password = passwordField.text
|
||||
okClicked = true
|
||||
validateButton.loading = true
|
||||
errorMessage.text = ""
|
||||
|
||||
box.buttonCallbacks: ({
|
||||
ok: button => {
|
||||
const password = passwordField.text
|
||||
okClicked = true
|
||||
button.loading = true
|
||||
errorMessage.text = ""
|
||||
verifyPassword(password, result => {
|
||||
if (result === true) {
|
||||
passwordValid = true
|
||||
popup.acceptedPassword = password
|
||||
popup.close()
|
||||
} else if (result === false) {
|
||||
passwordValid = false
|
||||
} else {
|
||||
errorMessage.text = result
|
||||
}
|
||||
|
||||
verifyPassword(password, result => {
|
||||
if (result === true) {
|
||||
passwordValid = true
|
||||
popup.acceptedPassword = password
|
||||
popup.close()
|
||||
} else if (result === false) {
|
||||
passwordValid = false
|
||||
} else {
|
||||
errorMessage.text = result
|
||||
}
|
||||
validateButton.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
button.loading = false
|
||||
})
|
||||
},
|
||||
cancel: button => {
|
||||
popup.close()
|
||||
cancelled()
|
||||
},
|
||||
})
|
||||
|
||||
page.footer: ButtonLayout {
|
||||
ApplyButton {
|
||||
id: validateButton
|
||||
text: qsTr("Validate")
|
||||
enabled: Boolean(passwordField.text)
|
||||
onClicked: validate()
|
||||
}
|
||||
|
||||
CancelButton {
|
||||
onClicked: {
|
||||
popup.close()
|
||||
cancelled()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAboutToShow: {
|
||||
okClicked = false
|
||||
acceptedPassword = ""
|
||||
passwordValid = null
|
||||
errorMessage.text = ""
|
||||
}
|
||||
|
||||
onOpened: passwordField.forceActiveFocus()
|
||||
|
||||
|
||||
SummaryLabel { id: summary }
|
||||
|
||||
HRowLayout {
|
||||
spacing: theme.spacing
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
HTextField {
|
||||
id: passwordField
|
||||
echoMode: TextInput.Password
|
||||
@@ -78,6 +90,8 @@ BoxPopup {
|
||||
onTextChanged: passwordValid =
|
||||
validateWhileTyping ? verifyPassword(text) : null
|
||||
|
||||
onAccepted: popup.validate()
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
@@ -91,7 +105,8 @@ BoxPopup {
|
||||
Layout.preferredWidth:
|
||||
passwordValid === null ||
|
||||
(validateWhileTyping && ! okClicked && ! passwordValid) ?
|
||||
0 :implicitWidth
|
||||
0 :
|
||||
implicitWidth
|
||||
|
||||
Behavior on Layout.preferredWidth { HNumberAnimation {} }
|
||||
}
|
||||
|
@@ -3,28 +3,21 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../Base"
|
||||
import "../Base/ButtonLayout"
|
||||
|
||||
BoxPopup {
|
||||
summary.text:
|
||||
isLast ?
|
||||
qsTr("Remove your last message?") :
|
||||
HFlickableColumnPopup {
|
||||
id: popup
|
||||
|
||||
eventSenderAndIds.length > 1 ?
|
||||
qsTr("Remove %1 messages?").arg(eventSenderAndIds.length) :
|
||||
|
||||
qsTr("Remove this message?")
|
||||
property string preferUserId: ""
|
||||
property string roomId: ""
|
||||
|
||||
details.color: theme.colors.warningText
|
||||
details.text:
|
||||
onlyOwnMessageWarning ?
|
||||
qsTr("Only your messages can be removed") :
|
||||
""
|
||||
property var eventSenderAndIds: [] // [[senderId, event.id], ...]
|
||||
property bool onlyOwnMessageWarning: false
|
||||
property bool isLast: false
|
||||
|
||||
okText: qsTr("Remove")
|
||||
// box.focusButton: "ok"
|
||||
|
||||
onOpened: reasonField.item.forceActiveFocus()
|
||||
onOk: {
|
||||
function remove() {
|
||||
const idsForSender = {} // {senderId: [event.id, ...]}
|
||||
|
||||
for (const [senderId, eventClientId] of eventSenderAndIds) {
|
||||
@@ -40,16 +33,44 @@ BoxPopup {
|
||||
"room_mass_redact",
|
||||
[roomId, reasonField.item.text, ...eventClientIds]
|
||||
)
|
||||
|
||||
popup.close()
|
||||
}
|
||||
|
||||
|
||||
property string preferUserId: ""
|
||||
property string roomId: ""
|
||||
page.footer: ButtonLayout {
|
||||
ApplyButton {
|
||||
text: qsTr("Remove")
|
||||
icon.name: "remove-message"
|
||||
onClicked: remove()
|
||||
}
|
||||
|
||||
property var eventSenderAndIds: [] // [[senderId, event.id], ...]
|
||||
property bool onlyOwnMessageWarning: false
|
||||
property bool isLast: false
|
||||
CancelButton {
|
||||
onClicked: popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: reasonField.item.forceActiveFocus()
|
||||
|
||||
|
||||
SummaryLabel {
|
||||
text:
|
||||
isLast ?
|
||||
qsTr("Remove your last message?") :
|
||||
|
||||
eventSenderAndIds.length > 1 ?
|
||||
qsTr("Remove %1 messages?").arg(eventSenderAndIds.length) :
|
||||
|
||||
qsTr("Remove this message?")
|
||||
}
|
||||
|
||||
DetailsLabel {
|
||||
color: theme.colors.warningText
|
||||
text:
|
||||
onlyOwnMessageWarning ?
|
||||
qsTr("Only your messages can be removed") :
|
||||
""
|
||||
}
|
||||
|
||||
HLabeledItem {
|
||||
id: reasonField
|
||||
@@ -59,6 +80,7 @@ BoxPopup {
|
||||
|
||||
HTextField {
|
||||
width: parent.width
|
||||
onAccepted: popup.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,48 +3,65 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../Base"
|
||||
import "../Base/ButtonLayout"
|
||||
|
||||
BoxPopup {
|
||||
summary.textFormat: Text.StyledText
|
||||
summary.text:
|
||||
operation === RemoveMemberPopup.Operation.Disinvite ?
|
||||
qsTr("Disinvite %1 from the room?").arg(coloredTarget) :
|
||||
HFlickableColumnPopup {
|
||||
id: popup
|
||||
|
||||
operation === RemoveMemberPopup.Operation.Kick ?
|
||||
qsTr("Kick %1 out of the room?").arg(coloredTarget) :
|
||||
|
||||
qsTr("Ban %1 from the room?").arg(coloredTarget)
|
||||
|
||||
okText:
|
||||
operation === RemoveMemberPopup.Operation.Disinvite ?
|
||||
qsTr("Disinvite") :
|
||||
|
||||
operation === RemoveMemberPopup.Operation.Kick ?
|
||||
qsTr("Kick") :
|
||||
|
||||
qsTr("Ban")
|
||||
|
||||
onOpened: reasonField.item.forceActiveFocus()
|
||||
onOk: py.callClientCoro(
|
||||
userId,
|
||||
operation === RemoveMemberPopup.Operation.Ban ?
|
||||
"room_ban" : "room_kick",
|
||||
[roomId, targetUserId, reasonField.item.text || null],
|
||||
)
|
||||
|
||||
|
||||
enum Operation { Disinvite, Kick, Ban }
|
||||
|
||||
property string userId
|
||||
property string roomId
|
||||
property string targetUserId
|
||||
property string targetDisplayName
|
||||
property int operation
|
||||
property string operation // "disinvite", "kick" or "ban"
|
||||
|
||||
readonly property string coloredTarget:
|
||||
utils.coloredNameHtml(targetDisplayName, targetUserId)
|
||||
|
||||
|
||||
function remove() {
|
||||
py.callClientCoro(
|
||||
userId,
|
||||
operation === "ban" ? "room_ban" : "room_kick",
|
||||
[roomId, targetUserId, reasonField.item.text || null],
|
||||
)
|
||||
|
||||
popup.close()
|
||||
}
|
||||
|
||||
|
||||
page.footer: ButtonLayout {
|
||||
ApplyButton {
|
||||
text:
|
||||
operation === "disinvite" ? qsTr("Disinvite") :
|
||||
operation === "kick" ? qsTr("Kick") :
|
||||
qsTr("Ban")
|
||||
|
||||
icon.name: operation === "ban" ? "room-ban" : "room-kick"
|
||||
|
||||
onClicked: remove()
|
||||
}
|
||||
|
||||
CancelButton {
|
||||
onClicked: popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: reasonField.item.forceActiveFocus()
|
||||
|
||||
|
||||
SummaryLabel {
|
||||
textFormat: Text.StyledText
|
||||
text:
|
||||
operation === "disinvite" ?
|
||||
qsTr("Disinvite %1 from the room?").arg(coloredTarget) :
|
||||
|
||||
operation === "kick" ?
|
||||
qsTr("Kick %1 out of the room?").arg(coloredTarget) :
|
||||
|
||||
qsTr("Ban %1 from the room?").arg(coloredTarget)
|
||||
}
|
||||
|
||||
HLabeledItem {
|
||||
id: reasonField
|
||||
label.text: qsTr("Optional reason:")
|
||||
@@ -53,6 +70,7 @@ BoxPopup {
|
||||
|
||||
HTextField {
|
||||
width: parent.width
|
||||
onAccepted: popup.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,61 +2,74 @@
|
||||
|
||||
import QtQuick 2.12
|
||||
import ".."
|
||||
import "../Base/ButtonLayout"
|
||||
|
||||
BoxPopup {
|
||||
HFlickableColumnPopup {
|
||||
id: popup
|
||||
summary.text: qsTr("Backup your decryption keys before signing out?")
|
||||
details.text: qsTr(
|
||||
"Signing out will delete your device's information and the keys " +
|
||||
"required to decrypt messages in encrypted rooms.\n\n" +
|
||||
|
||||
"You can export your keys to a passphrase-protected file " +
|
||||
"before signing out.\n\n" +
|
||||
|
||||
"This will allow you to restore access to your messages when " +
|
||||
"you sign in again, by importing this file in your account settings."
|
||||
)
|
||||
property string userId: ""
|
||||
|
||||
box.focusButton: "ok"
|
||||
box.buttonModel: [
|
||||
{ name: "ok", text: qsTr("Export keys"), iconName: "export-keys" },
|
||||
{ name: "signout", text: qsTr("Sign out now"), iconName: "sign-out",
|
||||
iconColor: theme.colors.middleBackground },
|
||||
{ name: "cancel", text: qsTr("Cancel"), iconName: "cancel" },
|
||||
]
|
||||
|
||||
box.buttonCallbacks: ({
|
||||
ok: button => {
|
||||
utils.makeObject(
|
||||
page.footer: ButtonLayout {
|
||||
ApplyButton {
|
||||
id: exportButton
|
||||
text: qsTr("Export keys")
|
||||
icon.name: "export-keys"
|
||||
|
||||
onClicked: utils.makeObject(
|
||||
"Dialogs/ExportKeys.qml",
|
||||
window.mainUI,
|
||||
{ userId },
|
||||
obj => {
|
||||
button.loading = Qt.binding(() => obj.exporting)
|
||||
obj.done.connect(() => {
|
||||
box.buttonCallbacks["signout"](button)
|
||||
})
|
||||
loading = Qt.binding(() => obj.exporting)
|
||||
obj.done.connect(signOutButton.clicked)
|
||||
obj.dialog.open()
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
signout: button => {
|
||||
okClicked = true
|
||||
popup.ok()
|
||||
OtherButton {
|
||||
id: signOutButton
|
||||
text: qsTr("Sign out now")
|
||||
icon.name: "sign-out"
|
||||
icon.color: theme.colors.middleBackground
|
||||
|
||||
if (ModelStore.get("accounts").count < 2 ||
|
||||
window.uiState.pageProperties.userId === userId) {
|
||||
window.mainUI.pageLoader.showPage("AddAccount/AddAccount")
|
||||
onClicked: {
|
||||
if (ModelStore.get("accounts").count < 2 ||
|
||||
window.uiState.pageProperties.userId === userId)
|
||||
{
|
||||
window.mainUI.pageLoader.showPage("AddAccount/AddAccount")
|
||||
}
|
||||
|
||||
py.callCoro("logout_client", [userId])
|
||||
popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
py.callCoro("logout_client", [userId])
|
||||
popup.close()
|
||||
},
|
||||
CancelButton {
|
||||
onClicked: popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
cancel: button => { okClicked = false; popup.cancel(); popup.close() },
|
||||
})
|
||||
onOpened: exportButton.forceActiveFocus()
|
||||
|
||||
|
||||
property string userId: ""
|
||||
SummaryLabel {
|
||||
text: qsTr("Backup your decryption keys before signing out?")
|
||||
}
|
||||
|
||||
DetailsLabel {
|
||||
text: qsTr(
|
||||
"Signing out will delete your device's information and the keys " +
|
||||
"required to decrypt messages in encrypted rooms.\n\n" +
|
||||
|
||||
"You can export your keys to a passphrase-protected file " +
|
||||
"before signing out.\n\n" +
|
||||
|
||||
"This will allow you to restore access to your messages when " +
|
||||
"you sign in again, by importing this file in your account " +
|
||||
"settings."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
13
src/gui/Popups/SummaryLabel.qml
Normal file
13
src/gui/Popups/SummaryLabel.qml
Normal file
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../Base"
|
||||
|
||||
HLabel {
|
||||
wrapMode: Text.Wrap
|
||||
font.bold: true
|
||||
visible: Boolean(text)
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
@@ -4,16 +4,10 @@ import QtQuick 2.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import "../Base"
|
||||
import "../Base/ButtonLayout"
|
||||
|
||||
BoxPopup {
|
||||
summary.text: qsTr("Unexpected error occured: <i>%1</i>").arg(errorType)
|
||||
summary.textFormat: Text.StyledText
|
||||
|
||||
okText: qsTr("Report")
|
||||
okIcon: "report-error"
|
||||
okEnabled: false // TODO
|
||||
cancelText: qsTr("Ignore")
|
||||
box.focusButton: "cancel"
|
||||
HColumnPopup {
|
||||
id: popup
|
||||
|
||||
|
||||
property string errorType
|
||||
@@ -21,17 +15,44 @@ BoxPopup {
|
||||
property string traceback: ""
|
||||
|
||||
|
||||
page.footer: ButtonLayout {
|
||||
ApplyButton {
|
||||
text: qsTr("Report")
|
||||
icon.name: "report-error"
|
||||
enabled: false // TODO
|
||||
}
|
||||
|
||||
CancelButton {
|
||||
id: cancelButton
|
||||
text: qsTr("Ignore")
|
||||
onClicked: popup.close()
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: cancelButton.forceActiveFocus()
|
||||
|
||||
|
||||
SummaryLabel {
|
||||
text: qsTr("Unexpected error occured: <i>%1</i>").arg(errorType)
|
||||
textFormat: Text.StyledText
|
||||
}
|
||||
|
||||
HScrollView {
|
||||
clip: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
HTextArea {
|
||||
text: [message, traceback].join("\n\n") || qsTr("No info available")
|
||||
readOnly: true
|
||||
font.family: theme.fontFamily.mono
|
||||
focusOnTab: hideCheckBox
|
||||
}
|
||||
}
|
||||
|
||||
HCheckBox {
|
||||
id: hideCheckBox
|
||||
text: qsTr("Hide this type of error until restart")
|
||||
onCheckedChanged:
|
||||
checked ?
|
||||
|
Reference in New Issue
Block a user