allow hiding client stickies

Fixes #105
This commit is contained in:
Henry Hiles 2026-10-02 17:01:25 -04:00
commit 8da46dffe4
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
5 changed files with 75 additions and 16 deletions

View file

@ -97,6 +97,25 @@ class RoomChatController(final ChatParams info)
), ),
); );
void hideClientSticky(int eventRowId) => ref
.read(RoomsController.provider.notifier)
.update(
.new({
info.roomId: Room(
resetClientSticky: true,
clientSticky:
ref
.read(
RoomsController.provider.select(
(rooms) => rooms[info.roomId]?.clientSticky,
),
)
?.remove(eventRowId) ??
.new(),
),
}),
);
Future<void> paginate(Direction direction) async { Future<void> paginate(Direction direction) async {
if (state.isLoading) return; if (state.isLoading) return;

View file

@ -46,13 +46,16 @@ class RoomsController extends Notifier<IMap<String, Room>> {
roomId, roomId,
existing?.copyWith( existing?.copyWith(
hasMore: incoming.hasMore, hasMore: incoming.hasMore,
clientSticky: clientSticky: incoming.resetClientSticky
(incoming.clientSticky.isEmpty == true ? incoming.clientSticky
? existing.clientSticky : (incoming.clientSticky.isEmpty == true
: existing.clientSticky.addAll(incoming.clientSticky)) ? existing.clientSticky
.removeWhere( : existing.clientSticky.addAll(
(rowId) => incoming.timeline.values.contains(rowId), incoming.clientSticky,
), ))
.removeWhere(
(rowId) => incoming.timeline.values.contains(rowId),
),
metadata: incoming.metadata ?? existing.metadata, metadata: incoming.metadata ?? existing.metadata,
events: incoming.events.isEmpty events: incoming.events.isEmpty
? existing.events ? existing.events
@ -64,6 +67,7 @@ class RoomsController extends Notifier<IMap<String, Room>> {
(previousValue[event.key] ?? .new()).addAll(event.value), (previousValue[event.key] ?? .new()).addAll(event.value),
), ),
), ),
resetClientSticky: false,
reset: false, reset: false,
hasFetchedMembers: hasFetchedMembers:
incoming.hasFetchedMembers || existing.hasFetchedMembers, incoming.hasFetchedMembers || existing.hasFetchedMembers,

View file

@ -225,10 +225,11 @@ extension BuildEventOptions on Event {
), ),
if (ref.watch( if (ref.watch(
PowerLevelController.provider( PowerLevelController.provider(
.redaction(targetUser: sender, roomId: roomId), .redaction(targetUser: sender, roomId: roomId),
), ),
)) ) &&
!eventId.startsWith("~"))
PopupMenuItem( PopupMenuItem(
onTap: () => showReasonDialog( onTap: () => showReasonDialog(
title: "Delete Message", title: "Delete Message",
@ -245,6 +246,15 @@ extension BuildEventOptions on Event {
), ),
), ),
if (eventId.startsWith("~"))
PopupMenuItem(
onTap: () => ref.watch(notifier).hideClientSticky(rowId),
child: ListTile(
leading: Icon(Icons.delete, color: danger),
title: Text("Hide", style: .new(color: danger)),
),
),
PopupMenuItem( PopupMenuItem(
onTap: () => showReasonDialog( onTap: () => showReasonDialog(
title: "Report", title: "Report",

View file

@ -25,6 +25,8 @@ class const Room({
final IMap<int, Event> events = const IMap.empty(), final IMap<int, Event> events = const IMap.empty(),
final bool reset = false, final bool reset = false,
@JsonKey(includeFromJson: false, includeToJson: false)
final bool resetClientSticky = false,
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
final bool hasFetchedState = false, final bool hasFetchedState = false,

View file

@ -4,6 +4,7 @@ import "package:material_ui/material_ui.dart";
import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/event.dart"; import "package:nexus/controllers/event.dart";
import "package:nexus/controllers/room_chat.dart";
import "package:nexus/controllers/rooms.dart"; import "package:nexus/controllers/rooms.dart";
import "package:nexus/helpers/extensions/show_context_menu.dart"; import "package:nexus/helpers/extensions/show_context_menu.dart";
import "package:nexus/helpers/hooks/chat_scroll.dart"; import "package:nexus/helpers/hooks/chat_scroll.dart";
@ -270,11 +271,34 @@ class const EventRenderer(
if (event.sendError != null && event.sendError != "not sent") if (event.sendError != null && event.sendError != "not sent")
Padding( Padding(
padding: .only(bottom: 4), padding: .only(bottom: 4),
child: Text( child: Row(
event.sendError!, spacing: 4,
style: theme.textTheme.labelSmall?.copyWith( children: [
color: theme.colorScheme.error, Text(
), event.sendError!,
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.error,
),
),
InkWell(
child: Text(
"Hide",
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.primary,
decoration: .underline,
decorationColor: theme.colorScheme.primary,
),
),
onTap: () => ref
.read(
RoomChatController.provider((
roomId: roomId,
contextualEvent: null,
)).notifier,
)
.hideClientSticky(event.rowId),
),
],
), ),
), ),
].map((child) => Padding(padding: .only(left: 4), child: child)), ].map((child) => Padding(padding: .only(left: 4), child: child)),