Add ignoring invite sender & fix forgotten rooms
When declining an invite, the popup now offers a checkbox to also add the sender to the account data m.ignored_user_list event. This will hide any further invite or message from that user in syncs, and also immediatly remove the ones we currently have. As a side-effect, the long-time bug that caused forgotten rooms to immediatly reappear due to received a "<user> left" event has been fixed.
This commit is contained in:
parent
8ea951957d
commit
678a56400a
|
@ -210,6 +210,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
self.fully_loaded_rooms: Set[str] = set() # {room_id}
|
self.fully_loaded_rooms: Set[str] = set() # {room_id}
|
||||||
self.loaded_once_rooms: Set[str] = set() # {room_id}
|
self.loaded_once_rooms: Set[str] = set() # {room_id}
|
||||||
self.cleared_events_rooms: Set[str] = set() # {room_id}
|
self.cleared_events_rooms: Set[str] = set() # {room_id}
|
||||||
|
self.ignored_rooms: Set[str] = set() # {room_id}
|
||||||
|
|
||||||
self.event_to_echo_ids: Dict[str, str] = {}
|
self.event_to_echo_ids: Dict[str, str] = {}
|
||||||
|
|
||||||
|
@ -220,7 +221,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
self.unassigned_event_last_read_by: DefaultDict[str, Dict[str, int]] =\
|
self.unassigned_event_last_read_by: DefaultDict[str, Dict[str, int]] =\
|
||||||
DefaultDict(dict)
|
DefaultDict(dict)
|
||||||
|
|
||||||
self.push_rules: nio.PushRulesEvent = nio.PushRulesEvent()
|
self.push_rules: nio.PushRulesEvent = nio.PushRulesEvent()
|
||||||
|
self.ignored_user_ids: Set[str] = set()
|
||||||
|
|
||||||
# {room_id: event}
|
# {room_id: event}
|
||||||
self.power_level_events: Dict[str, nio.PowerLevelsEvent] = {}
|
self.power_level_events: Dict[str, nio.PowerLevelsEvent] = {}
|
||||||
|
@ -523,6 +525,51 @@ 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:
|
||||||
|
ignored = self.ignored_user_ids.copy()
|
||||||
|
|
||||||
|
if ignore:
|
||||||
|
ignored.add(user_id)
|
||||||
|
else:
|
||||||
|
ignored.discard(user_id)
|
||||||
|
|
||||||
|
path = ["user", self.user_id, "account_data", "m.ignored_user_list"]
|
||||||
|
params = {"access_token": self.access_token}
|
||||||
|
|
||||||
|
await self._send(
|
||||||
|
nio.responses.EmptyResponse,
|
||||||
|
"PUT",
|
||||||
|
nio.Api._build_path(path, params),
|
||||||
|
nio.Api.to_json({"ignored_users": {u: {} for u in ignored}}),
|
||||||
|
)
|
||||||
|
|
||||||
|
if not ignore:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Invites and messages from ignored users won't be returned anymore on
|
||||||
|
# syncs, thus will be absent on client restart. Clean up immediatly:
|
||||||
|
|
||||||
|
room_model = self.models[self.user_id, "rooms"]
|
||||||
|
|
||||||
|
with room_model.batch_remove():
|
||||||
|
for room_id, room in room_model.copy().items():
|
||||||
|
if room.inviter_id == user_id:
|
||||||
|
self.ignored_rooms.add(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, "members"), None)
|
||||||
|
continue
|
||||||
|
|
||||||
|
event_model = self.models[self.user_id, room_id, "events"]
|
||||||
|
|
||||||
|
with event_model.batch_remove():
|
||||||
|
for event_id, event in event_model.copy().items():
|
||||||
|
if event.sender_id == user_id:
|
||||||
|
del event_model[event_id]
|
||||||
|
|
||||||
|
await self.update_account_unread_counts()
|
||||||
|
|
||||||
|
|
||||||
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."""
|
||||||
|
|
||||||
|
@ -1172,6 +1219,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
will be marked as suitable for destruction by the server.
|
will be marked as suitable for destruction by the server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
self.ignored_rooms.add(room_id)
|
||||||
self.models[self.user_id, "rooms"].pop(room_id, None)
|
self.models[self.user_id, "rooms"].pop(room_id, None)
|
||||||
self.models.pop((self.user_id, room_id, "events"), None)
|
self.models.pop((self.user_id, room_id, "events"), None)
|
||||||
self.models.pop((self.user_id, room_id, "members"), None)
|
self.models.pop((self.user_id, room_id, "members"), None)
|
||||||
|
@ -2057,6 +2105,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Register/update a `nio.MatrixRoom` as a `models.items.Room`."""
|
"""Register/update a `nio.MatrixRoom` as a `models.items.Room`."""
|
||||||
|
|
||||||
|
self.ignored_rooms.discard(room.room_id)
|
||||||
|
|
||||||
inviter = getattr(room, "inviter", "") or ""
|
inviter = getattr(room, "inviter", "") or ""
|
||||||
levels = room.power_levels
|
levels = room.power_levels
|
||||||
can_send_state = partial(levels.can_user_send_state, self.user_id)
|
can_send_state = partial(levels.can_user_send_state, self.user_id)
|
||||||
|
|
|
@ -65,7 +65,6 @@ class NioCallbacks:
|
||||||
self.client.add_to_device_callback(method, ev_class)
|
self.client.add_to_device_callback(method, ev_class)
|
||||||
elif issubclass(ev_class, nio.AccountDataEvent):
|
elif issubclass(ev_class, nio.AccountDataEvent):
|
||||||
self.client.add_global_account_data_callback(method, ev_class)
|
self.client.add_global_account_data_callback(method, ev_class)
|
||||||
self.client.add_room_account_data_callback(method, ev_class)
|
|
||||||
elif issubclass(ev_class, nio.PresenceEvent):
|
elif issubclass(ev_class, nio.PresenceEvent):
|
||||||
self.client.add_presence_callback(method, ev_class)
|
self.client.add_presence_callback(method, ev_class)
|
||||||
else:
|
else:
|
||||||
|
@ -99,6 +98,10 @@ class NioCallbacks:
|
||||||
|
|
||||||
# TODO: way of knowing if a nio.MatrixRoom is left
|
# TODO: way of knowing if a nio.MatrixRoom is left
|
||||||
for room_id, info in resp.rooms.leave.items():
|
for room_id, info in resp.rooms.leave.items():
|
||||||
|
# We forgot this room or rejected an invite and ignored the sender
|
||||||
|
if room_id in self.client.ignored_rooms:
|
||||||
|
continue
|
||||||
|
|
||||||
# TODO: handle in nio, these are rooms that were left before
|
# TODO: handle in nio, these are rooms that were left before
|
||||||
# starting the client.
|
# starting the client.
|
||||||
if room_id not in self.client.all_rooms:
|
if room_id not in self.client.all_rooms:
|
||||||
|
@ -853,6 +856,15 @@ class NioCallbacks:
|
||||||
self.client.push_rules = ev
|
self.client.push_rules = ev
|
||||||
|
|
||||||
|
|
||||||
|
async def onUnknownAccountDataEvent(
|
||||||
|
self, ev: nio.UnknownAccountDataEvent,
|
||||||
|
) -> None:
|
||||||
|
|
||||||
|
if ev.type == "m.ignored_user_list":
|
||||||
|
user_ids = set(ev.content.get("ignored_users", {}))
|
||||||
|
self.client.ignored_user_ids = user_ids
|
||||||
|
|
||||||
|
|
||||||
# Presence event callbacks
|
# Presence event callbacks
|
||||||
|
|
||||||
async def onPresenceEvent(self, ev: nio.PresenceEvent) -> None:
|
async def onPresenceEvent(self, ev: nio.PresenceEvent) -> None:
|
||||||
|
|
|
@ -16,7 +16,21 @@ HFlickableColumnPopup {
|
||||||
property bool left: false
|
property bool left: false
|
||||||
property var leftCallback: null
|
property var leftCallback: null
|
||||||
|
|
||||||
|
function ignoreInviter() {
|
||||||
|
if (! inviterId) return
|
||||||
|
py.callClientCoro(userId, "ignore_user", [inviterId, true])
|
||||||
|
}
|
||||||
|
|
||||||
|
function leave() {
|
||||||
|
leaveButton.loading = true
|
||||||
|
py.callClientCoro(userId, "room_leave", [roomId], leftCallback)
|
||||||
|
if (ignoreInviterCheck.checked) popup.ignoreInviter()
|
||||||
|
popup.close()
|
||||||
|
}
|
||||||
|
|
||||||
function forget() {
|
function forget() {
|
||||||
|
leaveButton.loading = true
|
||||||
|
|
||||||
py.callClientCoro(userId, "room_forget", [roomId], () => {
|
py.callClientCoro(userId, "room_forget", [roomId], () => {
|
||||||
if (window.uiState.page === "Pages/Chat/Chat.qml" &&
|
if (window.uiState.page === "Pages/Chat/Chat.qml" &&
|
||||||
window.uiState.pageProperties.userRoomId[0] === userId &&
|
window.uiState.pageProperties.userRoomId[0] === userId &&
|
||||||
|
@ -24,15 +38,12 @@ HFlickableColumnPopup {
|
||||||
{
|
{
|
||||||
window.mainUI.pageLoader.showPrevious() ||
|
window.mainUI.pageLoader.showPrevious() ||
|
||||||
window.mainUI.pageLoader.show("Pages/Default.qml")
|
window.mainUI.pageLoader.show("Pages/Default.qml")
|
||||||
|
|
||||||
Qt.callLater(popup.destroy)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function leave() {
|
Qt.callLater(popup.destroy)
|
||||||
py.callClientCoro(userId, "room_leave", [roomId], leftCallback)
|
})
|
||||||
popup.close()
|
|
||||||
|
if (ignoreInviterCheck.checked) popup.ignoreInviter()
|
||||||
}
|
}
|
||||||
|
|
||||||
page.footer: AutoDirectionLayout {
|
page.footer: AutoDirectionLayout {
|
||||||
|
@ -78,15 +89,32 @@ HFlickableColumnPopup {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HCheckBox {
|
||||||
|
id: ignoreInviterCheck
|
||||||
|
visible: Boolean(popup.inviterId)
|
||||||
|
mainText.textFormat: HLabel.StyledText
|
||||||
|
|
||||||
|
// We purposely display inviter's user ID instead of display name here.
|
||||||
|
// Someone could take the name of one of our contact, which would
|
||||||
|
// not be disambiguated and lead to confusion.
|
||||||
|
text: qsTr("Ignore sender %1").arg(
|
||||||
|
utils.coloredNameHtml("", popup.inviterId),
|
||||||
|
)
|
||||||
|
subtitle.text: qsTr("Automatically hide their invites and messages")
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
HCheckBox {
|
HCheckBox {
|
||||||
id: forgetCheck
|
id: forgetCheck
|
||||||
visible: ! popup.left
|
visible: ! popup.left
|
||||||
text: qsTr("Forget this room's history")
|
text: qsTr("Forget this room's history")
|
||||||
subtitle.text: qsTr(
|
subtitle.text: qsTr(
|
||||||
"You will lose access to any previously received messages.\n" +
|
"Access to previously received messages will be lost.\n" +
|
||||||
"If all members forget a room, servers will erase it."
|
"If all members forget a room, servers will erase it."
|
||||||
)
|
)
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user