Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85596038
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
37 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/contents/ui/Bubble.qml b/src/contents/ui/Bubble.qml
index 76c92d9..40f9679 100644
--- a/src/contents/ui/Bubble.qml
+++ b/src/contents/ui/Bubble.qml
@@ -1,262 +1,262 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import org.kde.kirigami 2.13 as Kirigami
import '.' as Kazv
import 'matrix-helpers.js' as Helpers
ItemDelegate {
id: upper
objectName: 'bubble'
default property var children
property var currentEvent: event
property var menuContent: []
property var shouldDisplayTime: displayTime || (!compactMode && !event.isLocalEcho)
property var eventPinned: event.eventId && pinnedEvents.eventIds.includes(event.eventId)
topPadding: 0
bottomPadding: 0
readonly property var bubbleSpacing: leftPadding + rightPadding
readonly property double replyMaxHeight: Kirigami.Units.gridUnit * 7
Layout.fillWidth: true
property var backgroundItem: Rectangle {
anchors.fill: parent
color: (hovered || isSelected) ? Kirigami.Theme.activeBackgroundColor : Kirigami.Theme.backgroundColor
}
background: compactMode ? null : backgroundItem
property var eventSourcePopupComp: Component {
Kazv.SelfDestroyableOverlaySheet {
shouldSelfDestroy: true
title: l10n.get('event-source-popup-title')
Kazv.EventSourceView {
Layout.preferredWidth: Math.min(Kirigami.Units.gridUnit * 40, Window.width)
event: upper.currentEvent
}
}
}
property var reactionPopupComp: Component {
Kazv.ReactToEventPopup {
shouldSelfDestroy: true
onAccepted: eventView.reactWith(text)
}
}
function getIsEditable(event) {
return event.sender === matrixSdk.userId
&& event.type === 'm.room.message'
&& event.content.msgtype === 'm.text';
}
property var menuComp: Component {
Menu {
objectName: 'bubbleContextMenu'
property list<QtObject> staticItems: [
Kirigami.Action {
objectName: 'replyToMenuItem'
text: l10n.get('event-reply-action')
onTriggered: setDraftRelation('m.in_reply_to', currentEvent.eventId)
enabled: event && !event.redacted
},
Kirigami.Action {
objectName: 'editMenuItem'
text: l10n.get('event-edit-action')
onTriggered: {
setDraftRelation('m.replace', currentEvent.eventId);
replaceDraftRequested(Helpers.getEventBodyForEditing(event));
}
enabled: event && !event.redacted && !event.isLocalEcho && getIsEditable(event)
},
Kirigami.Action {
objectName: 'deleteMenuItem'
text: l10n.get('event-delete')
onTriggered: deleteEventRequested(event.isLocalEcho ? event.txnId : event.eventId, event.isLocalEcho)
enabled: event && !event.redacted
},
Kirigami.Action {
objectName: 'reactMenuItem'
text: l10n.get('event-react-action')
onTriggered: reactionPopupComp.createObject(applicationWindow().overlay).open()
enabled: event && !event.redacted
},
Kirigami.Action {
objectName: 'pinUnpinMenuItem'
enabled: !event.isLocalEcho
text: eventPinned ? l10n.get('event-unpin-action') : l10n.get('event-pin-action')
onTriggered: eventPinned ? unpinEventRequested(event.eventId) : pinEventRequested(event.eventId)
},
MenuSeparator {},
Kirigami.Action {
text: l10n.get('event-view-source')
onTriggered: eventSourcePopupComp.createObject(applicationWindow().overlay).open()
},
Kirigami.Action {
objectName: 'viewHistoryMenuItem'
text: l10n.get('event-view-history')
enabled: event && !!event.isEdited
onTriggered: viewEventHistoryRequested(event.eventId)
}
]
contentData: Array.from(staticItems).concat(upper.menuContent)
onClosed: destroy()
}
}
function shouldPopupMenu() {
return !compactMode
}
function maybePopupMenu() {
if (shouldPopupMenu()) {
menuComp.createObject(parent).popup(parent)
}
}
contentItem: RowLayout {
id: bubbleRootLayout
property var replyToOrAnnotatedEventId: currentEvent.replyingToEventId || (currentEvent.relationType === 'm.annotation' ? currentEvent.relatedEventId : '')
ColumnLayout {
Layout.fillWidth: true
MouseArea {
visible: bubbleRootLayout.replyToOrAnnotatedEventId && !compactMode
Layout.fillWidth: true
Layout.margins: 0
Layout.preferredHeight: inReplyToLayout.implicitHeight
Layout.maximumHeight: upper.replyMaxHeight
clip: true
onClicked: eventListView.goToEvent(bubbleRootLayout.replyToOrAnnotatedEventId)
RowLayout {
id: inReplyToLayout
anchors {
top: parent.top
left: parent.left
right: parent.right
}
Rectangle {
Layout.fillHeight: true
Layout.preferredWidth: Kirigami.Units.smallSpacing
color: Kirigami.Theme.activeTextColor
clip: true
}
Loader {
id: inReplyToLoader
clip: true
source: Qt.resolvedUrl('EventViewCompact.qml')
active: bubbleRootLayout.replyToOrAnnotatedEventId && !compactMode
asynchronous: true
Layout.fillWidth: true
property var event: room.messageById(bubbleRootLayout.replyToOrAnnotatedEventId)
property var props: ({
event,
sender: room.member(event.sender || matrixSdk.userId),
stateKeyUser: event.stateKey ? room.member(event.stateKey) : {},
isGapped: timeline.gaps.includes(event.eventId),
})
}
}
}
ColumnLayout {
id: children
data: upper.children
}
}
property var popupAction: Kirigami.Action {
id: popupAction
text: l10n.get('event-popup-action')
icon.name: 'view-more-horizontal-symbolic'
onTriggered: maybePopupMenu()
}
RowLayout {
Layout.alignment: Qt.AlignBottom
Kirigami.Icon {
source: 'emblem-encrypted-locked'
visible: !compactMode && event && event.encrypted
Layout.preferredHeight: inlineBadgeSize
Layout.preferredWidth: inlineBadgeSize
Accessible.role: Accessible.StaticText
Accessible.name: l10n.get('event-encrypted')
ToolTip.text: l10n.get('event-encrypted')
ToolTip.delay: Kirigami.Units.toolTipDelay
ToolTip.timeout: Helpers.toolTipTimeout
ToolTip.visible: hoverHandlerEncIcon.hovered
HoverHandler { id: hoverHandlerEncIcon }
}
ToolButton {
objectName: 'localEchoIndicator'
visible: !!event.isLocalEcho
hoverEnabled: true
enabled: event.isFailed
icon.name: event.isSending ? 'state-sync' : 'state-warning'
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Accessible.role: event.isSending ? Accessible.StaticText : Accessible.Button
Accessible.name: event.isSending ? l10n.get('event-sending') : l10n.get('event-resend')
ToolTip.text: event.isSending ? l10n.get('event-sending') : l10n.get('event-send-failed')
ToolTip.delay: Kirigami.Units.toolTipDelay
ToolTip.timeout: Helpers.toolTipTimeout
ToolTip.visible: hovered
onClicked: {
room.resendMessage(event.txnId);
}
}
Label {
objectName: 'timeIndicator'
visible: shouldDisplayTime
text: event.formattedTime
ToolTip.text: event.formattedDateTime
ToolTip.delay: Kirigami.Units.toolTipDelay
ToolTip.timeout: Helpers.toolTipTimeout
ToolTip.visible: hoverHandlerTime.hovered
HoverHandler { id: hoverHandlerTime }
- font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8
+ font: Kirigami.Theme.smallFont
}
Label {
objectName: 'editedIndicator'
visible: !compactMode && event.isEdited
text: l10n.get('event-edited-indicator')
- font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8
+ font: Kirigami.Theme.smallFont
}
Kazv.EventReadIndicator {
objectName: 'eventReadIndicator'
shouldShow: !compactMode && !event.isLocalEcho
model: event.readers()
}
ToolButton {
id: moreButton
objectName: 'moreButton'
visible: !compactMode
action: popupAction
display: AbstractButton.IconOnly
}
}
}
}
diff --git a/src/contents/ui/UserPage.qml b/src/contents/ui/UserPage.qml
index 64813df..25973a7 100644
--- a/src/contents/ui/UserPage.qml
+++ b/src/contents/ui/UserPage.qml
@@ -1,395 +1,394 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import org.kde.kirigami as Kirigami
import moe.kazv.mxc.kazv as MK
import '.' as Kazv
import 'device-mgmt' as KazvDM
import 'matrix-helpers.js' as Helpers
Kazv.ClosableScrollablePage {
id: userPage
property string userId: ''
property var user: ({})
property var nameProvider: Kazv.UserNameProvider {
user: userPage.user
}
property var room
property var roomNameProvider: Kazv.RoomNameProvider {
room: userPage.room
}
property var roomName: roomNameProvider.name
property var editingPowerLevel: false
property var submittingPowerLevel: false
property var powerLevelsLoaded: false
property var userPowerLevel: room.userPowerLevel(userId)
property var kickingUser: false
property var canKickUser: !(user.membership === 'ban' || user.membership === 'leave')
property var banningUser: false
property var unbanningUser: false
Connections {
target: room
function onPowerLevelsChanged() {
userPage.userPowerLevel = room.userPowerLevel(userPage.userId);
}
}
title: nameProvider.name
property var ensureMemberEvent: Kazv.AsyncHandler {
trigger: () => room.ensureStateEvent('m.room.member', userId)
}
property var ensurePowerLevels: Kazv.AsyncHandler {
trigger: () => room.ensureStateEvent('m.room.power_levels', '')
onResolved: (success, data) => {
if (success) {
userPage.powerLevelsLoaded = true;
}
}
}
property var updatingNameOverride: false
property var updateNameOverride: Kazv.AsyncHandler {
trigger: () => {
userPage.updatingNameOverride = true;
return sdkVars.userGivenNicknameMap.setAndUpload(
userPage.userId, nameOverrideInput.text || null);
}
onResolved: (success, data) => {
userPage.updatingNameOverride = false;
if (!success) {
showPassiveNotification(l10n.get('user-page-update-name-override-failed-prompt', { errorCode: data.errorCode, errorMsg: data.error }));
}
}
}
property bool updatingSelfName: false
property var updateSelfName: Kazv.AsyncHandler {
trigger: () => {
userPage.updatingSelfName = true;
return userPage.room.setSelfName(selfNameInput.text);
}
onResolved: (success, data) => {
userPage.updatingSelfName = false;
if (!success) {
showPassiveNotification(l10n.get('user-page-update-self-name-failed-prompt', { errorCode: data.errorCode, errorMsg: data.error }));
}
}
}
Component.onCompleted: {
userPage.ensureMemberEvent.call();
userPage.ensurePowerLevels.call();
}
property var setPowerLevel: Kazv.AsyncHandler {
trigger: () => {
userPage.submittingPowerLevel = true;
return room.setUserPowerLevel(userPage.userId, parseInt(newPowerLevel.text));
}
onResolved: (success, data) => {
if (!success) {
showPassiveNotification(l10n.get('user-page-set-power-level-failed-prompt', { errorCode: data.errorCode, errorMsg: data.error }));
} else {
userPage.editingPowerLevel = false;
}
userPage.submittingPowerLevel = false;
}
}
property var kickUser: Kazv.AsyncHandler {
trigger: () => {
userPage.kickingUser = true;
return room.kickUser(userPage.userId, kickUserReasonInput.text);
}
onResolved: {
if (!success) {
showPassiveNotification(l10n.get('user-page-kick-user-failed-prompt', { errorCode: data.errorCode, errorMsg: data.error }));
} else {
kickUserReasonInput.text = '';
}
userPage.kickingUser = false;
}
}
property var banUser: Kazv.AsyncHandler {
trigger: () => {
userPage.banningUser = true;
return room.banUser(userPage.userId, banUserReasonInput.text);
}
onResolved: {
if (!success) {
showPassiveNotification(l10n.get('user-page-ban-user-failed-prompt', { errorCode: data.errorCode, errorMsg: data.error }));
} else {
banUserReasonInput.text = '';
}
userPage.banningUser = false;
}
}
property var unbanUser: Kazv.AsyncHandler {
trigger: () => {
userPage.unbanningUser = true;
return room.unbanUser(userPage.userId);
}
onResolved: {
if (!success) {
showPassiveNotification(l10n.get('user-page-unban-user-failed-prompt', { errorCode: data.errorCode, errorMsg: data.error }));
}
userPage.unbanningUser = false;
}
}
ColumnLayout {
Kazv.AvatarAdapter {
id: avatar
objectName: 'avatar'
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: Kirigami.Units.iconSizes.enormous
Layout.preferredWidth: Kirigami.Units.iconSizes.enormous
source: userPage.user.avatarMxcUri ? matrixSdk.mxcUriToHttp(userPage.user.avatarMxcUri) : ''
name: nameProvider.name
}
ColumnLayout {
Layout.preferredWidth: this.parent.width
Label {
objectName: 'userNameLabel'
Layout.alignment: Qt.AlignHCenter
- font.pixelSize: Kirigami.Units.gridUnit
text: !!userPage.user.name ? userPage.user.name : userPage.userId
}
Label {
objectName: 'userIdLabel'
Layout.alignment: Qt.AlignHCenter
- font.pixelSize: Kirigami.Units.gridUnit * 0.8
+ font: Kirigami.Theme.smallFont
visible: !!userPage.user.name
text: userPage.userId
}
}
RowLayout {
visible: room.membership === MK.MatrixRoom.Join && userPage.userId === matrixSdk.userId
TextField {
id: selfNameInput
objectName: 'selfNameInput'
placeholderText: l10n.get('user-page-self-name-placeholder')
Layout.fillWidth: true
Accessible.name: l10n.get('user-page-self-name-prompt')
enabled: !userPage.updatingSelfName
text: userPage.user.name
}
Button {
objectName: 'saveSelfNameButton'
text: l10n.get('user-page-save-self-name-action')
enabled: !userPage.updatingSelfName
onClicked: userPage.updateSelfName.call()
}
}
RowLayout {
// Do not allow user to set a name override for themselves
visible: userPage.userId !== matrixSdk.userId
TextField {
id: nameOverrideInput
objectName: 'nameOverrideInput'
placeholderText: l10n.get('user-page-overrided-name-placeholder')
Layout.fillWidth: true
enabled: !userPage.updatingNameOverride
text: nameProvider.overridedName
}
Button {
objectName: 'saveNameOverrideButton'
text: l10n.get('user-page-save-name-override-action')
enabled: !userPage.updatingNameOverride
onClicked: updateNameOverride.call()
}
}
RowLayout {
visible: !userPage.editingPowerLevel
Label {
objectName: 'powerLevelLabel'
Layout.fillWidth: true
text: l10n.get('user-page-power-level', { powerLevel: userPage.userPowerLevel })
}
Button {
objectName: 'editPowerLevelButton'
text: l10n.get('user-page-edit-power-level-action')
enabled: userPage.powerLevelsLoaded
onClicked: {
userPage.editingPowerLevel = true;
}
}
}
RowLayout {
visible: userPage.editingPowerLevel
TextField {
objectName: 'newPowerLevelInput'
id: newPowerLevel
Layout.fillWidth: true
text: `${userPage.userPowerLevel}`
readOnly: userPage.submittingPowerLevel
}
Button {
objectName: 'savePowerLevelButton'
text: l10n.get('user-page-save-power-level-action')
onClicked: {
setPowerLevel.call();
}
enabled: !userPage.submittingPowerLevel
}
Button {
objectName: 'discardPowerLevelButton'
text: l10n.get('user-page-discard-power-level-action')
enabled: !userPage.submittingPowerLevel
onClicked: {
userPage.editingPowerLevel = false;
newPowerLevel.text = `${userPage.userPowerLevel}`;
}
}
}
ColumnLayout {
Kirigami.PromptDialog {
id: kickUserReasonDialog
objectName: 'kickUserReasonDialog'
title: l10n.get('user-page-kick-user-confirm-dialog-title')
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
onAccepted: {
userPage.kickUser.call()
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: l10n.get('user-page-kick-user-confirm-dialog-content', {
userId: userPage.user.userId,
name: nameProvider.name,
roomName: userPage.roomName,
})
wrapMode: Text.Wrap
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: l10n.get('user-page-kick-user-confirm-dialog-content', {
userId: userPage.user.userId,
name: userPage.user.name,
roomName: userPage.roomName,
})
wrapMode: Text.Wrap
}
Kirigami.FormLayout {
Layout.fillWidth: true
TextField {
Layout.fillWidth: true
id: kickUserReasonInput
objectName: 'kickUserReasonInput'
readOnly: userPage.kickingUser
Kirigami.FormData.label: l10n.get('user-page-kick-user-reason-prompt')
}
}
}
}
}
Button {
objectName: 'kickUserButton'
icon.name: 'im-kick-user'
Layout.fillWidth: true
enabled: !userPage.kickingUser
text: l10n.get('user-page-kick-user-action')
visible: userPage.canKickUser
onClicked: {
kickUserReasonDialog.open()
}
}
}
RowLayout {
Kirigami.PromptDialog {
id: banUserReasonDialog
objectName: 'banUserReasonDialog'
title: l10n.get('user-page-ban-user-confirm-dialog-title')
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
onAccepted: {
userPage.banUser.call()
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: l10n.get('user-page-ban-user-confirm-dialog-content', {
userId: userPage.user.userId,
name: nameProvider.name,
roomName: userPage.roomName,
})
wrapMode: Text.Wrap
}
Kirigami.FormLayout {
Layout.fillWidth: true
TextField {
Layout.fillWidth: true
id: banUserReasonInput
objectName: 'banUserReasonInput'
readOnly: userPage.banningUser
Kirigami.FormData.label: l10n.get('user-page-ban-user-reason-prompt')
}
}
}
}
Button {
objectName: 'banUserButton'
Layout.fillWidth: true
icon.name: 'im-ban-kick-user'
enabled: !userPage.banningUser
text: l10n.get('user-page-ban-user-action')
visible: !unbanUserButton.visible
onClicked: {
banUserReasonDialog.open()
}
}
Button {
id: unbanUserButton
objectName: 'unbanUserButton'
Layout.fillWidth: true
icon.name: 'im-user-online'
enabled: !userPage.unbanningUser
text: l10n.get('user-page-unban-user-action')
visible: user.membership === "ban"
onClicked: {
userPage.unbanUser.call()
}
}
}
KazvDM.DeviceList {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: childrenRect.height
userId: userPage.userId
devices: matrixSdk.devicesOfUser(userId)
}
}
}
diff --git a/src/contents/ui/device-mgmt/Device.qml b/src/contents/ui/device-mgmt/Device.qml
index 480d980..4b07712 100644
--- a/src/contents/ui/device-mgmt/Device.qml
+++ b/src/contents/ui/device-mgmt/Device.qml
@@ -1,157 +1,157 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.2
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import org.kde.kirigami 2.13 as Kirigami
Kirigami.SwipeListItem {
id: device
property var item
property var userId
contentItem: RowLayout {
ColumnLayout {
Label {
objectName: 'deviceIdLabel'
text: item.deviceId
}
Label {
objectName: 'displayNameLabel'
text: item.displayName
- font.pixelSize: Kirigami.Units.gridUnit * 0.5
+ font: Kirigami.Theme.smallFont
visible: !!item.displayName
}
Layout.fillWidth: true
}
RowLayout {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
Layout.rightMargin: Kirigami.Units.largeSpacing
Kirigami.Icon {
source: device.getTrustLevelIcon(item.trustLevel)
Layout.preferredHeight: Kirigami.Units.iconSizes.sizeForLabels
Layout.preferredWidth: Kirigami.Units.iconSizes.sizeForLabels
Accessible.ignored: true
}
Label {
objectName: 'trustLevelLabel'
text: l10n.get(`device-trust-level-${item.trustLevel}`)
}
}
}
property var sheet: Kirigami.OverlaySheet {
id: popup
title: l10n.get('device-set-trust-level-dialog-title')
parent: applicationWindow().overlay
property var setTrustLevelPromise: null
property var conn: Connections {
target: popup.setTrustLevelPromise
function onResolved(success, data) {
if (!success) {
console.log('Error setting trust level:', JSON.stringify(data));
}
popup.setTrustLevelPromise = null;
popup.close();
}
}
ColumnLayout {
Label {
text: l10n.get('device-set-trust-level-dialog-name-label', { name: item.displayName })
}
Label {
text: l10n.get('device-set-trust-level-dialog-id-label', { id: item.deviceId })
}
Label {
text: l10n.get('device-set-trust-level-dialog-ed25519-key-label', { key: item.ed25519Key })
}
Label {
text: l10n.get('device-set-trust-level-dialog-curve25519-key-label', { key: item.curve25519Key })
}
ButtonGroup {
buttons: trustLevelChoices.children
}
ColumnLayout {
id: trustLevelChoices
objectName: 'trustLevelChoices'
Layout.fillHeight: true
Repeater {
model: ['blocked', 'unseen', 'seen', 'verified']
delegate: RadioButton {
objectName: 'trustLevelChoice_' + modelData
property var level: modelData
autoExclusive: true
checked: item.trustLevel === modelData
text: l10n.get(`device-trust-level-${modelData}`)
icon.name: device.getTrustLevelIcon(modelData)
}
}
}
RowLayout {
Button {
text: l10n.get('device-set-trust-level-dialog-save')
onClicked: {
const current = popup.getNewTrustLevel();
popup.setTrustLevelPromise = matrixSdk.setDeviceTrustLevel(userId, item.deviceId, current);
}
}
Button {
text: l10n.get('device-set-trust-level-dialog-cancel')
onClicked: {
popup.close();
}
}
}
}
function getNewTrustLevel() {
for (let i = 0; i < trustLevelChoices.children.length; ++i) {
const curItem = trustLevelChoices.children[i];
if (curItem.checked) {
return curItem.level;
}
}
return 'unknown';
}
}
actions: [
Kirigami.Action {
text: l10n.get('device-set-trust-level')
icon.name: 'settings-configure'
onTriggered: {
popup.open();
}
}
]
function getTrustLevelIcon(level)
{
switch (level) {
case 'blocked':
return 'emblem-error';
case 'unseen':
return 'emblem-warning';
case 'seen':
return 'emblem-added';
case 'verified':
return 'emblem-checked';
default:
return 'emblem-question';
}
}
}
diff --git a/src/contents/ui/room-settings/RoomSettingsPage.qml b/src/contents/ui/room-settings/RoomSettingsPage.qml
index 852c049..8c38ef4 100644
--- a/src/contents/ui/room-settings/RoomSettingsPage.qml
+++ b/src/contents/ui/room-settings/RoomSettingsPage.qml
@@ -1,370 +1,369 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import org.kde.kirigami 2.20 as Kirigami
import moe.kazv.mxc.kazv 0.0 as MK
import '../matrix-helpers.js' as Helpers
import '..' as Kazv
import '.' as RoomSettings
Kazv.ClosableScrollablePage {
id: roomSettingsPage
property var room
property var roomNameProvider: Kazv.RoomNameProvider {
room: roomSettingsPage.room
}
property var roomDisplayName: roomNameProvider.name
property var customTagIds: room.tagIds.filter(k => k.startsWith('u.'))
property var editingTopic: false
property var submittingTopic: false
property var submitTopic: Kazv.AsyncHandler {
trigger: () => {
roomSettingsPage.submittingTopic = true;
return roomSettingsPage.room.setTopic(roomTopicEdit.text);
}
onResolved: (success, data) => {
roomSettingsPage.submittingTopic = false;
if (success) {
roomSettingsPage.editingTopic = false;
} else {
showPassiveNotification(l10n.get('room-settings-set-topic-failed-prompt', {
room: room.roomId,
errorCode: data.errorCode,
errorMsg: data.error,
}));
}
}
}
title: l10n.get('room-settings-page-title', { room: roomDisplayName })
function tagIdToName(tagId) {
return tagId.slice(2);
}
property var encryptionPopup: Kirigami.PromptDialog {
parent: roomSettingsPage.overlay
objectName: 'encryptionPopup'
title: l10n.get('room-settings-enable-encryption-prompt-dialog-title')
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
ColumnLayout {
Label {
Layout.fillWidth: true
text: l10n.get('room-settings-enable-encryption-prompt-dialog-prompt')
wrapMode: Text.Wrap
}
}
onAccepted: enableEncryption.call()
}
property var enablingEncryption: false
property var enableEncryption: Kazv.AsyncHandler {
trigger: () => {
roomSettingsPage.enablingEncryption = true;
return room.sendStateEvent({
type: 'm.room.encryption',
state_key: '',
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
});
}
onResolved: (success, data) => {
roomSettingsPage.enablingEncryption = false;
if (success) {
showPassiveNotification(l10n.get('room-settings-encryption-enabled-notification'));
} else {
showPassiveNotification(l10n.get('room-settings-encryption-failed-to-enable-notification', { errorCode: data.errorCode, errorMsg: data.error }));
}
}
}
property var tagHandler: RoomSettings.RoomTagHandler {
room: roomSettingsPage.room
}
property var members: room.members()
property var avatarCount: 4
ColumnLayout {
RowLayout {
Layout.alignment: Qt.AlignHCenter
Repeater {
objectName: 'roomMembersAvatarsRepeater'
model: Math.min(members.count, roomSettingsPage.avatarCount)
Kazv.AvatarAdapter {
property var member: members.at(index)
property var nameProvider: Kazv.UserNameProvider {
user: member
}
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
source: member.avatarMxcUri ?
matrixSdk.mxcUriToHttp(member.avatarMxcUri) : ''
name: nameProvider.name
}
}
Kirigami.Icon {
objectName: 'roomMembersAvatarsMore'
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
source: 'go-next-skip'
visible: members.count > roomSettingsPage.avatarCount
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Label {
objectName: 'roomNameLabel'
Layout.alignment: Qt.AlignHCenter
- font.pixelSize: Kirigami.Units.gridUnit
text: !!room.name ? room.name : room.roomId
}
Label {
objectName: 'roomIdLabel'
Layout.alignment: Qt.AlignHCenter
- font.pixelSize: Math.ceil(Kirigami.Units.gridUnit * 0.8)
+ font: Kirigami.Theme.smallFont
text: room.roomId
visible: !!room.name
}
}
RowLayout {
visible: !roomSettingsPage.editingTopic
Kazv.SelectableText {
objectName: 'roomTopicLabel'
text: room.topic || l10n.get('room-settings-topic-missing')
font.italic: !room.topic
wrapMode: Text.Wrap
Kirigami.Theme.colorGroup: !!room.topic ? Kirigami.Theme.Normal : Kirigami.Theme.Inactive
Layout.fillWidth: true
}
Button {
Layout.alignment: Qt.AlignTop
objectName: 'editTopicButton'
text: l10n.get('room-settings-edit-topic-action')
onClicked: {
roomTopicEdit.text = room.topic;
roomSettingsPage.editingTopic = true;
}
}
}
RowLayout {
visible: roomSettingsPage.editingTopic
TextArea {
id: roomTopicEdit
objectName: 'roomTopicEdit'
Layout.fillWidth: true
wrapMode: Text.Wrap
}
Button {
Layout.alignment: Qt.AlignTop
objectName: 'saveTopicButton'
enabled: !roomSettingsPage.submittingTopic
text: l10n.get('room-settings-save-topic-action')
onClicked: roomSettingsPage.submitTopic.call()
}
Button {
Layout.alignment: Qt.AlignTop
objectName: 'discardTopicButton'
enabled: !roomSettingsPage.submittingTopic
text: l10n.get('room-settings-discard-topic-action')
onClicked: roomSettingsPage.editingTopic = false
}
}
Button {
text: l10n.get('room-settings-members-action')
icon.name: 'im-user'
Layout.fillWidth: true
onClicked: pageStack.push(Qt.resolvedUrl('RoomMemberListPage.qml'), { room: room, members: room.members() })
}
Button {
text: l10n.get('room-settings-banned-members-action')
icon.name: 'im-kick-user'
icon.color: Kirigami.Theme.negativeTextColor
Layout.fillWidth: true
onClicked: pageStack.push(Qt.resolvedUrl('RoomMemberListPage.qml'), { room: room, members: room.bannedMembers() })
}
Label {
objectName: 'encryptionIndicator'
Layout.fillWidth: true
text: !!room.encrypted ? l10n.get('room-settings-encrypted') : l10n.get('room-settings-not-encrypted')
}
Button {
objectName: 'enableEncryptionButton'
Layout.fillWidth: true
visible: !room.encrypted
enabled: !roomSettingsPage.enablingEncryption
text: l10n.get('room-settings-enable-encryption-action')
onClicked: encryptionPopup.open()
}
RowLayout {
Layout.fillWidth: true
Label {
text: l10n.get('room-settings-tags')
Layout.fillWidth: true
}
CheckBox {
Layout.alignment: Qt.AlignRight
text: l10n.get('room-settings-favourited')
checkable: true
checked: tagHandler.hasTag('m.favourite')
enabled: tagHandler.available
onToggled: tagHandler.toggleTag('m.favourite')
}
}
ListView {
model: customTagIds.length
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: childrenRect.height
delegate: Kirigami.SwipeListItem {
required property var index
contentItem: RowLayout {
Label {
Layout.fillWidth: true
text: tagIdToName(customTagIds[index])
}
}
actions: [
Kirigami.Action {
enabled: tagHandler.available
icon.name: 'list-remove-symbolic'
text: l10n.get('room-settings-remove-tag')
onTriggered: tagHandler.toggleTag(customTagIds[index])
}
]
}
}
RowLayout {
TextField {
id: newTagName
enabled: tagHandler.available
text: ''
Layout.fillWidth: true
}
Button {
enabled: tagHandler.available
text: l10n.get('room-settings-add-tag')
icon.name: 'list-add'
onClicked: {
tagHandler.toggleTag('u.' + newTagName.text)
}
}
Connections {
target: tagHandler
function onTagAdded(tagId) {
if (tagId === 'u.' + newTagName.text) {
newTagName.text = '';
}
}
}
}
Button {
objectName: 'stickerPacksButton'
text: l10n.get('room-sticker-packs-action')
icon.name: 'smiley'
Layout.fillWidth: true
onClicked: {
activateRoomStickerPacksPage(roomSettingsPage.room);
}
}
Button {
text: l10n.get('room-invite-action')
icon.name: 'list-add-user'
Layout.fillWidth: true
onClicked: {
activateRoomInvitePage(roomSettingsPage.room);
}
}
Button {
objectName: 'leaveRoomButton'
text: l10n.get('room-leave-action')
icon.name: 'im-ban-kick-user'
icon.color: Kirigami.Theme.negativeTextColor
Layout.fillWidth: true
onClicked: {
confirmLeaveOverlay.open();
}
}
Button {
objectName: 'forgetRoomButton'
text: l10n.get('room-forget-action')
Layout.fillWidth: true
visible: room.membership === MK.MatrixRoom.Leave
onClicked: {
confirmForgetOverlay.open();
}
}
}
property var confirmLeaveOverlay: Kazv.ConfirmationOverlay {
objectName: 'confirmLeaveRoomPopup'
parent: roomSettingsPage.overlay
title: l10n.get('room-leave-confirm-popup-title')
message: l10n.get('room-leave-confirm-popup-message')
confirmActionText: l10n.get('room-leave-confirm-popup-confirm-action')
cancelActionText: l10n.get('room-leave-confirm-popup-cancel-action')
onAccepted: leaveRoomHandler.call()
}
property var leaveRoomHandler: Kazv.AsyncHandler {
trigger: () => room.leaveRoom()
onResolved: {
if (success) {
showPassiveNotification(l10n.get('leave-room-success-prompt', { room: room.roomId }));
inviteOverlay.close();
} else {
showPassiveNotification(l10n.get('leave-room-failed-prompt', { room: room.roomId, errorCode: data.errorCode, errorMsg: data.error }));
}
}
}
property var confirmForgetOverlay: Kazv.ConfirmationOverlay {
objectName: 'confirmForgetRoomPopup'
parent: roomSettingsPage.overlay
title: l10n.get('room-forget-confirm-popup-title')
message: l10n.get('room-forget-confirm-popup-message')
confirmActionText: l10n.get('room-forget-confirm-popup-confirm-action')
cancelActionText: l10n.get('room-forget-confirm-popup-cancel-action')
onAccepted: forgetRoomHandler.call()
}
property var forgetRoomHandler: Kazv.AsyncHandler {
trigger: () => room.forgetRoom()
onResolved: (success, data) => {
if (success) {
showPassiveNotification(l10n.get('forget-room-success-prompt', { room: room.roomId }));
} else {
showPassiveNotification(l10n.get('forget-room-failed-prompt', { room: room.roomId, errorCode: data.errorCode, errorMsg: data.error }));
}
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jul 21, 1:20 AM (10 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1695627
Default Alt Text
(37 KB)
Attached To
Mode
rK kazv
Attached
Detach File
Event Timeline
Log In to Comment