Support for pinned messages (#49)

The first draft to support pinned messages. Fixes #39.

Co-authored-by: Henry-Hiles <henry@henryhiles.com>
Reviewed-on: Nexus/nexus#49
This commit is contained in:
istalri 2026-07-14 19:29:47 -04:00 committed by Henry Hiles
commit 818e582ddd
15 changed files with 570 additions and 279 deletions

View file

@ -0,0 +1,92 @@
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/material.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/pinned_events_controller.dart";
import "package:nexus/models/event.dart";
import "package:nexus/widgets/error_dialog.dart";
import "package:nexus/widgets/loading.dart";
import "package:nexus/widgets/renderers/event.dart";
class PinnedEventsDrawer extends HookConsumerWidget {
final String roomId;
final IList<PopupMenuEntry> Function(Event event) getEventOptions;
final Future<void> Function(String eventId) jumpToId;
const PinnedEventsDrawer(
this.roomId, {
required this.getEventOptions,
required this.jumpToId,
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final pinsProvider = ref.watch(PinnedEventsController.provider(roomId));
final theme = Theme.of(context);
return Drawer(
width: 400,
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
scrolledUnderElevation: 0,
leading: Icon(Icons.push_pin),
title: Text("Pinned Events"),
actionsPadding: .only(right: 4),
actions: [
IconButton(
onPressed: Scaffold.of(context).closeEndDrawer,
icon: Icon(Icons.close),
tooltip: "Close pinned events",
),
],
),
body: switch (pinsProvider) {
AsyncData(:final value) when value.isEmpty => Center(
child: Column(
mainAxisSize: .min,
children: [
Icon(
Icons.push_pin_outlined,
size: 48,
color: theme.colorScheme.onSurface,
),
SizedBox(height: 12),
Text("No pinned events", style: theme.textTheme.headlineSmall),
],
),
),
AsyncData(:final value) ||
AsyncLoading(:final value?) => ListView.builder(
padding: .symmetric(horizontal: 8),
itemCount: value.length,
itemBuilder: (context, index) {
final event = value[index];
return InkWell(
borderRadius: .circular(12),
onTap: () {
Navigator.pop(context);
jumpToId(event.eventId);
},
child: Padding(
padding: .symmetric(vertical: 4),
child: EventRenderer(
event,
maxLines: 2,
isGrouped: false,
getEventOptions: getEventOptions,
),
),
);
},
),
AsyncLoading() => Loading(),
AsyncError(:final error, :final stackTrace) => ErrorDialog(
error,
stackTrace,
),
},
),
);
}
}