Add profile/verification UI for room members
This commit is contained in:
parent
4ccb774411
commit
9b43bef935
7
TODO.md
7
TODO.md
|
@ -1,6 +1,11 @@
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
- issue templates
|
- trust/blacklist buttons
|
||||||
|
- reload devices when needed
|
||||||
|
- get devices for members with no shared E2E room?
|
||||||
|
- keyboard controls
|
||||||
|
- remove useless Base imports in Base components
|
||||||
|
- HTile enter trigger leftClicked()
|
||||||
|
|
||||||
## Refactoring
|
## Refactoring
|
||||||
|
|
||||||
|
|
|
@ -1286,10 +1286,10 @@ class MatrixClient(nio.AsyncClient):
|
||||||
if device_id == self.device_id:
|
if device_id == self.device_id:
|
||||||
return "current"
|
return "current"
|
||||||
|
|
||||||
if device_id not in self.olm.device_store[self.user_id]:
|
if device_id not in self.device_store[self.user_id]:
|
||||||
return "no_keys"
|
return "no_keys"
|
||||||
|
|
||||||
trust = self.olm.device_store[self.user_id][device_id].trust_state
|
trust = self.device_store[self.user_id][device_id].trust_state
|
||||||
return trust.name
|
return trust.name
|
||||||
|
|
||||||
def get_ed25519(device_id: str) -> str:
|
def get_ed25519(device_id: str) -> str:
|
||||||
|
@ -1297,8 +1297,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
if device_id == self.device_id:
|
if device_id == self.device_id:
|
||||||
key = self.olm.account.identity_keys["ed25519"]
|
key = self.olm.account.identity_keys["ed25519"]
|
||||||
elif device_id in self.olm.device_store[self.user_id]:
|
elif device_id in self.device_store[self.user_id]:
|
||||||
key = self.olm.device_store[self.user_id][device_id].ed25519
|
key = self.device_store[self.user_id][device_id].ed25519
|
||||||
|
|
||||||
return " ".join(textwrap.wrap(key, 4))
|
return " ".join(textwrap.wrap(key, 4))
|
||||||
|
|
||||||
|
@ -1333,6 +1333,32 @@ class MatrixClient(nio.AsyncClient):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def member_devices(self, user_id: str) -> List[Dict[str, Any]]:
|
||||||
|
"""Get list of E2E-aware devices for a user we share a room with."""
|
||||||
|
|
||||||
|
devices = [
|
||||||
|
# types: "verified", "blacklisted", "ignored" or "unset"
|
||||||
|
{
|
||||||
|
"id": device.id,
|
||||||
|
"display_name": device.display_name or "",
|
||||||
|
"type": device.trust_state.name,
|
||||||
|
"ed25519_key": device.ed25519,
|
||||||
|
}
|
||||||
|
for device in self.device_store.active_user_devices(user_id)
|
||||||
|
]
|
||||||
|
|
||||||
|
types_order = {
|
||||||
|
"unset": 0, "verified": 1, "ignored": 2, "blacklisted": 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sort by type, then by display name, then by ID
|
||||||
|
return sorted(
|
||||||
|
devices,
|
||||||
|
key = lambda d:
|
||||||
|
(types_order[d["type"]], d["display_name"], d["id"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def rename_device(self, device_id: str, name: str) -> bool:
|
async def rename_device(self, device_id: str, name: str) -> bool:
|
||||||
"""Rename one of our device, return `False` if it doesn't exist."""
|
"""Rename one of our device, return `False` if it doesn't exist."""
|
||||||
|
|
||||||
|
@ -1346,13 +1372,13 @@ class MatrixClient(nio.AsyncClient):
|
||||||
async def verify_device_id(self, user_id: str, device_id: str) -> None:
|
async def verify_device_id(self, user_id: str, device_id: str) -> None:
|
||||||
"""Mark a device as verified."""
|
"""Mark a device as verified."""
|
||||||
|
|
||||||
self.verify_device(self.olm.device_store[user_id][device_id])
|
self.verify_device(self.device_store[user_id][device_id])
|
||||||
|
|
||||||
|
|
||||||
async def blacklist_device_id(self, user_id: str, device_id: str) -> None:
|
async def blacklist_device_id(self, user_id: str, device_id: str) -> None:
|
||||||
"""Mark a device as blacklisted."""
|
"""Mark a device as blacklisted."""
|
||||||
|
|
||||||
self.blacklist_device(self.olm.device_store[user_id][device_id])
|
self.blacklist_device(self.device_store[user_id][device_id])
|
||||||
|
|
||||||
|
|
||||||
async def delete_devices_with_password(
|
async def delete_devices_with_password(
|
||||||
|
|
|
@ -8,9 +8,10 @@ Button {
|
||||||
id: button
|
id: button
|
||||||
enabled: ! button.loading
|
enabled: ! button.loading
|
||||||
spacing: theme.spacing
|
spacing: theme.spacing
|
||||||
topPadding: padded ? spacing / (circle ? 1.75 : 2) : 0
|
topPadding:
|
||||||
|
padded ? spacing * (circle ? (iconItem.small ? 1.5 : 1.8) : 0.5) : 0
|
||||||
bottomPadding: topPadding
|
bottomPadding: topPadding
|
||||||
leftPadding: padded ? spacing / (circle ? 1.5 : 1) : 0
|
leftPadding: padded ? spacing : 0
|
||||||
rightPadding: leftPadding
|
rightPadding: leftPadding
|
||||||
|
|
||||||
icon.color: theme.icons.colorize
|
icon.color: theme.icons.colorize
|
||||||
|
@ -31,7 +32,7 @@ Button {
|
||||||
background: HButtonBackground {
|
background: HButtonBackground {
|
||||||
button: button
|
button: button
|
||||||
buttonTheme: theme.controls.button
|
buttonTheme: theme.controls.button
|
||||||
radius: circle ? height : enableRadius ? theme.radius : 0
|
radius: circle ? height / 2 : enableRadius ? theme.radius : 0
|
||||||
color: backgroundColor
|
color: backgroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
src/gui/Base/HStackView.qml
Normal file
7
src/gui/Base/HStackView.qml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
|
||||||
|
StackView {
|
||||||
|
}
|
136
src/gui/Pages/Chat/RoomPane/MemberView/DeviceVerification.qml
Normal file
136
src/gui/Pages/Chat/RoomPane/MemberView/DeviceVerification.qml
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import "../../../../Base"
|
||||||
|
import "../../../../Base/ButtonLayout"
|
||||||
|
|
||||||
|
HFlickableColumnPage {
|
||||||
|
id: page
|
||||||
|
|
||||||
|
|
||||||
|
property string userId
|
||||||
|
property string deviceOwner
|
||||||
|
property string deviceOwnerDisplayName
|
||||||
|
property string deviceId
|
||||||
|
property string deviceName
|
||||||
|
property string ed25519Key
|
||||||
|
property HStackView stackView
|
||||||
|
|
||||||
|
|
||||||
|
footer: ButtonLayout {
|
||||||
|
ApplyButton {
|
||||||
|
text: qsTr("They're the same")
|
||||||
|
icon.name: "device-verified"
|
||||||
|
onClicked: {
|
||||||
|
loading = true
|
||||||
|
|
||||||
|
py.callClientCoro(
|
||||||
|
userId,
|
||||||
|
"verify_device_id",
|
||||||
|
[deviceOwner, deviceId],
|
||||||
|
() => {
|
||||||
|
loading = false
|
||||||
|
page.verified()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CancelButton {
|
||||||
|
text: qsTr("They differ")
|
||||||
|
icon.name: "device-blacklisted"
|
||||||
|
onClicked: {
|
||||||
|
loading = true
|
||||||
|
|
||||||
|
py.callClientCoro(
|
||||||
|
userId,
|
||||||
|
"blacklist_device_id",
|
||||||
|
[deviceOwner, deviceId],
|
||||||
|
() => {
|
||||||
|
loading = false
|
||||||
|
page.blacklisted()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CancelButton {
|
||||||
|
id: cancelButton
|
||||||
|
onClicked: stackView.pop()
|
||||||
|
Component.onCompleted: forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyboardCancel: stackView.pop()
|
||||||
|
|
||||||
|
|
||||||
|
HRowLayout {
|
||||||
|
HButton {
|
||||||
|
id: closeButton
|
||||||
|
circle: true
|
||||||
|
icon.name: "close-view"
|
||||||
|
iconItem.small: true
|
||||||
|
onClicked: page.stackView.pop()
|
||||||
|
|
||||||
|
Layout.rightMargin: theme.spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
HLabel {
|
||||||
|
text: qsTr("Verification")
|
||||||
|
font.bold: true
|
||||||
|
elide: HLabel.ElideRight
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: closeButton.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HLabel {
|
||||||
|
wrapMode: HLabel.Wrap
|
||||||
|
textFormat: HLabel.StyledText
|
||||||
|
text: qsTr(
|
||||||
|
"Does %1 sees the same info in their session's account settings?"
|
||||||
|
).arg(utils.coloredNameHtml(deviceOwnerDisplayName, deviceOwner))
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HTextArea {
|
||||||
|
function formatInfo(info, value) {
|
||||||
|
return (
|
||||||
|
`<p style="line-height: 115%">` +
|
||||||
|
info +
|
||||||
|
`<br><span style="font-family: ${theme.fontFamily.mono}">` +
|
||||||
|
value +
|
||||||
|
`</span></p>`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
readOnly: true
|
||||||
|
wrapMode: HSelectableLabel.Wrap
|
||||||
|
textFormat: Qt.RichText
|
||||||
|
text: (
|
||||||
|
formatInfo(qsTr("Session name: "), page.deviceName) +
|
||||||
|
formatInfo(qsTr("Session ID: "), page.deviceId) +
|
||||||
|
formatInfo(qsTr("Session key: "), "<b>"+page.ed25519Key+"</b>")
|
||||||
|
)
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HLabel {
|
||||||
|
wrapMode: HLabel.Wrap
|
||||||
|
text:
|
||||||
|
qsTr(
|
||||||
|
"If you already know this user, exchange these info by using" +
|
||||||
|
" a trusted contact method, such as email or a phone call."
|
||||||
|
)
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import Clipboard 0.1
|
import Clipboard 0.1
|
||||||
import "../../../Base"
|
import "../../../../Base"
|
||||||
import "../../../Base/HTile"
|
import "../../../../Base/HTile"
|
||||||
import "../../../Popups"
|
import "../../../../Popups"
|
||||||
|
|
||||||
HTile {
|
HTile {
|
||||||
id: member
|
id: member
|
|
@ -0,0 +1,64 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import "../../../../Base"
|
||||||
|
import "../../../../Base/ButtonLayout"
|
||||||
|
import "../../../../Base/HTile"
|
||||||
|
|
||||||
|
HTile {
|
||||||
|
id: deviceTile
|
||||||
|
|
||||||
|
|
||||||
|
property string userId
|
||||||
|
property string deviceOwner
|
||||||
|
property string deviceOwnerDisplayName
|
||||||
|
property HStackView stackView
|
||||||
|
|
||||||
|
signal trustChanged()
|
||||||
|
|
||||||
|
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
rightPadding: theme.spacing / 2
|
||||||
|
compact: false
|
||||||
|
|
||||||
|
contentItem: ContentRow {
|
||||||
|
tile: deviceTile
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
HColumnLayout {
|
||||||
|
HRowLayout {
|
||||||
|
spacing: theme.spacing
|
||||||
|
|
||||||
|
TitleLabel {
|
||||||
|
text: model.display_name || qsTr("Unnamed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubtitleLabel {
|
||||||
|
tile: deviceTile
|
||||||
|
font.family: theme.fontFamily.mono
|
||||||
|
text: model.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HIcon {
|
||||||
|
svgName: "device-action-menu"
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: stackView.push(
|
||||||
|
"DeviceVerification.qml",
|
||||||
|
{
|
||||||
|
userId: deviceTile.userId,
|
||||||
|
deviceOwner: deviceTile.deviceOwner,
|
||||||
|
deviceOwnerDisplayName: deviceTile.deviceOwnerDisplayName,
|
||||||
|
deviceId: model.id,
|
||||||
|
deviceName: model.display_name,
|
||||||
|
ed25519Key: model.ed25519_key,
|
||||||
|
stackView: deviceTile.stackView
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
164
src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml
Normal file
164
src/gui/Pages/Chat/RoomPane/MemberView/MemberProfile.qml
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import "../../../.."
|
||||||
|
import "../../../../Base"
|
||||||
|
|
||||||
|
HListView {
|
||||||
|
id: profile
|
||||||
|
|
||||||
|
|
||||||
|
property string userId
|
||||||
|
property string roomId
|
||||||
|
property QtObject member // RoomMember model item
|
||||||
|
property HStackView stackView
|
||||||
|
|
||||||
|
|
||||||
|
function loadDevices() {
|
||||||
|
py.callClientCoro(userId, "member_devices", [member.id], devices => {
|
||||||
|
profile.model.clear()
|
||||||
|
|
||||||
|
for (const device of devices)
|
||||||
|
profile.model.append(device)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
bottomMargin: theme.spacing
|
||||||
|
model: ListModel {}
|
||||||
|
delegate: MemberDeviceDelegate {
|
||||||
|
width: profile.width
|
||||||
|
userId: profile.userId
|
||||||
|
deviceOwner: member.id
|
||||||
|
deviceOwnerDisplayName: member.display_name
|
||||||
|
stackView: profile.stackView
|
||||||
|
}
|
||||||
|
|
||||||
|
section.property: "type"
|
||||||
|
section.delegate: RowLayout {
|
||||||
|
width: profile.width
|
||||||
|
spacing: theme.spacing / 2
|
||||||
|
|
||||||
|
HIcon {
|
||||||
|
svgName: "device-" + section
|
||||||
|
colorize:
|
||||||
|
section === "verified" ? theme.colors.positiveText :
|
||||||
|
section === "blacklisted" ? theme.colors.errorText :
|
||||||
|
theme.colors.warningText
|
||||||
|
|
||||||
|
Layout.preferredHeight: dimension
|
||||||
|
Layout.leftMargin: theme.spacing / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
HLabel {
|
||||||
|
wrapMode: HLabel.Wrap
|
||||||
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
text:
|
||||||
|
section === "unset" ? qsTr("Unverified sessions") :
|
||||||
|
section === "verified" ? qsTr("Verified sessions") :
|
||||||
|
section === "ignored" ? qsTr("Ignored sessions") :
|
||||||
|
qsTr("Blacklisted sessions")
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.topMargin: theme.spacing
|
||||||
|
Layout.bottomMargin: theme.spacing
|
||||||
|
Layout.rightMargin: theme.spacing / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header: HColumnLayout {
|
||||||
|
x: theme.spacing
|
||||||
|
width: profile.width - x * 2
|
||||||
|
spacing: theme.spacing * 1.5
|
||||||
|
|
||||||
|
HUserAvatar {
|
||||||
|
userId: member.id
|
||||||
|
displayName: member.display_name
|
||||||
|
mxc: member.avatar_url
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: width
|
||||||
|
Layout.topMargin: theme.spacing
|
||||||
|
|
||||||
|
HButton {
|
||||||
|
x: -theme.spacing * 0.75
|
||||||
|
y: x
|
||||||
|
z: 999
|
||||||
|
circle: true
|
||||||
|
icon.name: "close-view"
|
||||||
|
iconItem.small: true
|
||||||
|
onClicked: profile.stackView.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
HLabel {
|
||||||
|
textFormat: HLabel.StyledText
|
||||||
|
wrapMode: HLabel.Wrap
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
text:
|
||||||
|
utils.coloredNameHtml(member.display_name, member.user_id) +
|
||||||
|
(member.display_name.trim() ?
|
||||||
|
`<br><font color="${theme.colors.dimText}">${member.id}</font>` :
|
||||||
|
"")
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.bottomMargin: theme.spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// HColumnLayout {
|
||||||
|
// spacing: theme.spacing / 2
|
||||||
|
|
||||||
|
// HLabel {
|
||||||
|
// text: qsTr("Power level:")
|
||||||
|
// wrapMode: HLabel.Wrap
|
||||||
|
// horizontalAlignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
// Layout.fillWidth: true
|
||||||
|
// }
|
||||||
|
|
||||||
|
// HRowLayout {
|
||||||
|
// spacing: theme.spacing
|
||||||
|
|
||||||
|
// HSpacer {}
|
||||||
|
|
||||||
|
// Row {
|
||||||
|
// HButton {
|
||||||
|
// text: qsTr("Default")
|
||||||
|
// checked: levelBox.value >= 0 && levelBox.value < 50
|
||||||
|
// onClicked: levelBox.value = 0
|
||||||
|
// }
|
||||||
|
// HButton {
|
||||||
|
// text: qsTr("Moderator")
|
||||||
|
// checked: levelBox.value >= 50 && levelBox.value < 100
|
||||||
|
// onClicked: levelBox.value = 50
|
||||||
|
// }
|
||||||
|
// HButton {
|
||||||
|
// text: qsTr("Admin")
|
||||||
|
// checked: levelBox.value === 100
|
||||||
|
// onClicked: levelBox.value = 100
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// HSpinBox {
|
||||||
|
// id: levelBox
|
||||||
|
// from: -999
|
||||||
|
// to: 100
|
||||||
|
// defaultValue: member.power_level
|
||||||
|
// }
|
||||||
|
|
||||||
|
// HSpacer {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: loadDevices()
|
||||||
|
|
||||||
|
Keys.onEscapePressed: stackView.pop()
|
||||||
|
}
|
|
@ -2,15 +2,22 @@
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
import "../../.."
|
import "../../../.."
|
||||||
import "../../../Base"
|
import "../../../../Base"
|
||||||
|
|
||||||
HColumnLayout {
|
HColumnLayout {
|
||||||
readonly property alias keybindFocusItem: filterField
|
readonly property alias keybindFocusItem: filterField
|
||||||
readonly property var modelSyncId:
|
readonly property var modelSyncId:
|
||||||
[chat.userId, chat.roomId, "filtered_members"]
|
[chat.userId, chat.roomId, "filtered_members"]
|
||||||
|
|
||||||
HListView {
|
HStackView {
|
||||||
|
id: stackView
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: theme.chat.roomPane.listView.background
|
||||||
|
}
|
||||||
|
|
||||||
|
initialItem: HListView {
|
||||||
id: memberList
|
id: memberList
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
@ -19,16 +26,21 @@ HColumnLayout {
|
||||||
delegate: MemberDelegate {
|
delegate: MemberDelegate {
|
||||||
id: member
|
id: member
|
||||||
width: memberList.width
|
width: memberList.width
|
||||||
|
|
||||||
|
onLeftClicked: stackView.push(
|
||||||
|
"MemberProfile.qml",
|
||||||
|
{
|
||||||
|
userId: chat.userId,
|
||||||
|
roomId: chat.roomId,
|
||||||
|
member: model,
|
||||||
|
stackView: stackView,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
z: -100
|
|
||||||
color: theme.chat.roomPane.listView.background
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
@ -58,8 +70,10 @@ HColumnLayout {
|
||||||
// declared normally
|
// declared normally
|
||||||
Component.onCompleted: placeholderText = qsTr("Filter members")
|
Component.onCompleted: placeholderText = qsTr("Filter members")
|
||||||
|
|
||||||
onTextChanged:
|
onTextChanged: {
|
||||||
|
stackView.pop(stackView.initialItem)
|
||||||
py.callCoro("set_substring_filter", [modelSyncId, text])
|
py.callCoro("set_substring_filter", [modelSyncId, text])
|
||||||
|
}
|
||||||
|
|
||||||
Keys.onEscapePressed: {
|
Keys.onEscapePressed: {
|
||||||
roomPane.toggleFocus()
|
roomPane.toggleFocus()
|
|
@ -3,6 +3,7 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import "../../../Base"
|
import "../../../Base"
|
||||||
import "../../.."
|
import "../../.."
|
||||||
|
import "MemberView"
|
||||||
|
|
||||||
MultiviewPane {
|
MultiviewPane {
|
||||||
id: roomPane
|
id: roomPane
|
||||||
|
|
|
@ -49,8 +49,8 @@ QtObject {
|
||||||
|
|
||||||
|
|
||||||
function makePopup(urlComponent, properties={}, callback=null,
|
function makePopup(urlComponent, properties={}, callback=null,
|
||||||
autoDestruct=true) {
|
autoDestruct=true, parent=window) {
|
||||||
makeObject(urlComponent, window, properties, (popup) => {
|
makeObject(urlComponent, parent, properties, (popup) => {
|
||||||
popup.open()
|
popup.open()
|
||||||
if (autoDestruct) popup.closed.connect(() => { popup.destroy() })
|
if (autoDestruct) popup.closed.connect(() => { popup.destroy() })
|
||||||
if (callback) callback(popup)
|
if (callback) callback(popup)
|
||||||
|
|
3
src/icons/thin/close-view.svg
Normal file
3
src/icons/thin/close-view.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m23 20.168-8.185-8.187 8.185-8.174-2.832-2.807-8.182 8.179-8.176-8.179-2.81 2.81 8.186 8.196-8.186 8.184 2.81 2.81 8.203-8.192 8.18 8.192z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 246 B |
Loading…
Reference in New Issue
Block a user