Add ignored users list to account settings
This commit is contained in:
parent
719938d1db
commit
93404559b9
|
@ -525,13 +525,10 @@ class MatrixClient(nio.AsyncClient):
|
||||||
await asyncio.sleep(0.2)
|
await asyncio.sleep(0.2)
|
||||||
|
|
||||||
|
|
||||||
async def ignore_user(self, user_id: str, ignore: bool) -> None:
|
async def set_ignored_users(self, *user_ids: str) -> None:
|
||||||
ignored = self.ignored_user_ids.copy()
|
previous_ignored = self.ignored_user_ids
|
||||||
|
now_ignored = set(user_ids)
|
||||||
if ignore:
|
no_longer_ignored = previous_ignored - now_ignored
|
||||||
ignored.add(user_id)
|
|
||||||
else:
|
|
||||||
ignored.discard(user_id)
|
|
||||||
|
|
||||||
path = ["user", self.user_id, "account_data", "m.ignored_user_list"]
|
path = ["user", self.user_id, "account_data", "m.ignored_user_list"]
|
||||||
params = {"access_token": self.access_token}
|
params = {"access_token": self.access_token}
|
||||||
|
@ -540,18 +537,18 @@ class MatrixClient(nio.AsyncClient):
|
||||||
nio.responses.EmptyResponse,
|
nio.responses.EmptyResponse,
|
||||||
"PUT",
|
"PUT",
|
||||||
nio.Api._build_path(path, params),
|
nio.Api._build_path(path, params),
|
||||||
nio.Api.to_json({"ignored_users": {u: {} for u in ignored}}),
|
nio.Api.to_json({"ignored_users": {u: {} for u in now_ignored}}),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Invites and messages from ignored users won't be returned anymore on
|
# Invites and messages from ignored users won't be returned anymore on
|
||||||
# syncs, thus will be absent on client restart. Clean up immediatly,
|
# syncs, thus will be absent on client restart.
|
||||||
# and also update Member.ignored fields:
|
# Clean up immediatly, and also update Member.ignored fields:
|
||||||
|
|
||||||
room_model = self.models[self.user_id, "rooms"]
|
room_model = self.models[self.user_id, "rooms"]
|
||||||
|
|
||||||
with room_model.batch_remove():
|
with room_model.batch_remove():
|
||||||
for room_id, room in room_model.copy().items():
|
for room_id, room in room_model.copy().items():
|
||||||
if ignore and room.inviter_id == user_id:
|
if room.inviter_id in now_ignored:
|
||||||
self.ignored_rooms.add(room_id)
|
self.ignored_rooms.add(room_id)
|
||||||
del room_model[room_id]
|
del room_model[room_id]
|
||||||
self.models.pop((self.user_id, room_id, "events"), None)
|
self.models.pop((self.user_id, room_id, "events"), None)
|
||||||
|
@ -561,18 +558,28 @@ class MatrixClient(nio.AsyncClient):
|
||||||
event_model = self.models[self.user_id, room_id, "events"]
|
event_model = self.models[self.user_id, room_id, "events"]
|
||||||
member_model = self.models[self.user_id, room_id, "members"]
|
member_model = self.models[self.user_id, room_id, "members"]
|
||||||
|
|
||||||
if user_id in member_model:
|
for user_id in now_ignored:
|
||||||
member_model[user_id].ignored = ignore
|
if user_id in member_model:
|
||||||
|
member_model[user_id].ignored = True
|
||||||
|
|
||||||
if ignore:
|
for user_id in no_longer_ignored:
|
||||||
with event_model.batch_remove():
|
if user_id in member_model:
|
||||||
for event_id, event in event_model.copy().items():
|
member_model[user_id].ignored = False
|
||||||
if event.sender_id == user_id:
|
|
||||||
del event_model[event_id]
|
with event_model.batch_remove():
|
||||||
|
for event_id, event in event_model.copy().items():
|
||||||
|
if event.sender_id in now_ignored:
|
||||||
|
del event_model[event_id]
|
||||||
|
|
||||||
await self.update_account_unread_counts()
|
await self.update_account_unread_counts()
|
||||||
|
|
||||||
|
|
||||||
|
async def ignore_user(self, user_id: str, ignore: bool) -> None:
|
||||||
|
current = self.ignored_user_ids
|
||||||
|
new = current | {user_id} if ignore else current - {user_id}
|
||||||
|
await self.set_ignored_users(*new)
|
||||||
|
|
||||||
|
|
||||||
async def can_kick(self, room_id: str, target_user_id: str) -> bool:
|
async def can_kick(self, room_id: str, target_user_id: str) -> bool:
|
||||||
"""Return whether we can kick a certain user in a room."""
|
"""Return whether we can kick a certain user in a room."""
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import json
|
||||||
from dataclasses import asdict, dataclass, field
|
from dataclasses import asdict, dataclass, field
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
from typing import Any, Dict, List, Optional, Set, Tuple, Type, Union
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
import lxml # nosec
|
import lxml # nosec
|
||||||
|
@ -79,6 +79,7 @@ class Account(ModelItem):
|
||||||
total_unread: int = 0
|
total_unread: int = 0
|
||||||
total_highlights: int = 0
|
total_highlights: int = 0
|
||||||
local_unreads: bool = False
|
local_unreads: bool = False
|
||||||
|
ignored_users: Set[str] = field(default_factory=set)
|
||||||
|
|
||||||
# For some reason, Account cannot inherit Presence, because QML keeps
|
# For some reason, Account cannot inherit Presence, because QML keeps
|
||||||
# complaining type error on unknown file
|
# complaining type error on unknown file
|
||||||
|
|
|
@ -861,8 +861,9 @@ class NioCallbacks:
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
if ev.type == "m.ignored_user_list":
|
if ev.type == "m.ignored_user_list":
|
||||||
user_ids = set(ev.content.get("ignored_users", {}))
|
users = set(ev.content.get("ignored_users", {}))
|
||||||
self.client.ignored_user_ids = user_ids
|
self.client.ignored_user_ids = users
|
||||||
|
self.models["accounts"][self.client.user_id].ignored_users = users
|
||||||
|
|
||||||
|
|
||||||
# Presence event callbacks
|
# Presence event callbacks
|
||||||
|
|
|
@ -21,25 +21,6 @@ HFlickableColumnPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyChanges() {
|
function applyChanges() {
|
||||||
if (nameField.item.changed) {
|
|
||||||
saveButton.nameChangeRunning = true
|
|
||||||
|
|
||||||
py.callClientCoro(
|
|
||||||
userId, "set_displayname", [nameField.item.text], () => {
|
|
||||||
py.callClientCoro(userId, "update_own_profile", [], () => {
|
|
||||||
saveButton.nameChangeRunning = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aliasFieldItem.changed) {
|
|
||||||
window.settings.Chat.Composer.Aliases[userId] =
|
|
||||||
aliasFieldItem.text
|
|
||||||
|
|
||||||
window.saveSettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avatar.changed) {
|
if (avatar.changed) {
|
||||||
saveButton.avatarChangeRunning = true
|
saveButton.avatarChangeRunning = true
|
||||||
|
|
||||||
|
@ -50,11 +31,39 @@ HFlickableColumnPage {
|
||||||
py.callClientCoro(userId, "update_own_profile", [], () => {
|
py.callClientCoro(userId, "update_own_profile", [], () => {
|
||||||
saveButton.avatarChangeRunning = false
|
saveButton.avatarChangeRunning = false
|
||||||
})
|
})
|
||||||
|
|
||||||
}, (errType, [httpCode]) => {
|
}, (errType, [httpCode]) => {
|
||||||
console.error("Avatar upload failed:", httpCode, errType)
|
console.error("Avatar upload failed:", httpCode, errType)
|
||||||
saveButton.avatarChangeRunning = false
|
saveButton.avatarChangeRunning = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nameField.item.changed) {
|
||||||
|
saveButton.nameChangeRunning = true
|
||||||
|
const name = [nameField.item.text]
|
||||||
|
|
||||||
|
py.callClientCoro(userId, "set_displayname", [name] , () => {
|
||||||
|
py.callClientCoro(userId, "update_own_profile", [], () => {
|
||||||
|
saveButton.nameChangeRunning = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aliasFieldItem.changed) {
|
||||||
|
window.settings.Chat.Composer.Aliases[userId] =
|
||||||
|
aliasFieldItem.text
|
||||||
|
|
||||||
|
window.saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignoredUsersAreaItem.changed) {
|
||||||
|
saveButton.ignoredUsersChangeRunning = true
|
||||||
|
const users = ignoredUsersAreaItem.userIds
|
||||||
|
|
||||||
|
py.callClientCoro(userId, "set_ignored_users", users, () => {
|
||||||
|
saveButton.ignoredUsersChangeRunning = false
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
|
@ -80,13 +89,19 @@ HFlickableColumnPage {
|
||||||
|
|
||||||
property bool nameChangeRunning: false
|
property bool nameChangeRunning: false
|
||||||
property bool avatarChangeRunning: false
|
property bool avatarChangeRunning: false
|
||||||
|
property bool ignoredUsersChangeRunning: false
|
||||||
|
|
||||||
disableWhileLoading: false
|
disableWhileLoading: false
|
||||||
loading: nameChangeRunning || avatarChangeRunning
|
loading:
|
||||||
|
nameChangeRunning ||
|
||||||
|
avatarChangeRunning ||
|
||||||
|
ignoredUsersChangeRunning
|
||||||
|
|
||||||
enabled:
|
enabled:
|
||||||
avatar.changed ||
|
avatar.changed ||
|
||||||
nameField.item.changed ||
|
nameField.item.changed ||
|
||||||
(aliasFieldItem.changed && ! aliasFieldItem.error)
|
(aliasFieldItem.changed && ! aliasFieldItem.error) ||
|
||||||
|
(ignoredUsersAreaItem.changed && ! ignoredUsersAreaItem.error)
|
||||||
|
|
||||||
onClicked: applyChanges()
|
onClicked: applyChanges()
|
||||||
}
|
}
|
||||||
|
@ -314,4 +329,65 @@ HFlickableColumnPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HLabeledItem {
|
||||||
|
id: ignoredUsers
|
||||||
|
|
||||||
|
readonly property var userIds:
|
||||||
|
! ignoredUsersAreaItem.text.trim() ?
|
||||||
|
[] :
|
||||||
|
ignoredUsersAreaItem.text.trim().split(/\s+/)
|
||||||
|
|
||||||
|
readonly property var invalidUserIds: {
|
||||||
|
const result = []
|
||||||
|
|
||||||
|
for (const user of userIds)
|
||||||
|
if (! /@.+:.+/.test(user))
|
||||||
|
result.push(user)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
loading: ! ready
|
||||||
|
label.text: qsTr("Ignored users:")
|
||||||
|
errorLabel.text:
|
||||||
|
invalidUserIds.length ?
|
||||||
|
qsTr("Incomplete user ID: %1").arg(invalidUserIds.join(", ")) :
|
||||||
|
""
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
HRowLayout {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
HTextArea {
|
||||||
|
id: ignoredUsersAreaItem
|
||||||
|
error: ignoredUsers.invalidUserIds.length > 0
|
||||||
|
focusItemOnTab: ignoredUsersHelpButton
|
||||||
|
placeholderText: qsTr("@user1:example.org @user2:ex.org")
|
||||||
|
defaultText:
|
||||||
|
ready ?
|
||||||
|
JSON.parse(account.ignored_users).sort().join(" ") :
|
||||||
|
""
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldHelpButton {
|
||||||
|
id: ignoredUsersHelpButton
|
||||||
|
helpText: qsTr(
|
||||||
|
"List of user IDs, separated by a space, from which you " +
|
||||||
|
"will not receive messages or room invites.\n\n" +
|
||||||
|
|
||||||
|
"Their display name, avatar and online status will also " +
|
||||||
|
"be hidden from room member lists.\n\n" +
|
||||||
|
|
||||||
|
"When removing an user from the ignore list, restarting " +
|
||||||
|
"%1 is needed to receive anything they might have sent " +
|
||||||
|
"while being ignored."
|
||||||
|
).arg(Qt.application.displayName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user