Skip to content

Commit 93b3c99

Browse files
authored
chore: add share keys with and master tile menu (#82)
1 parent 77b0302 commit 93b3c99

17 files changed

+403
-144
lines changed

lib/chat_master/view/chat_master_tile_menu.dart

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
import 'package:flutter/material.dart';
2+
import 'package:future_loading_dialog/future_loading_dialog.dart';
23
import 'package:matrix/matrix.dart';
4+
import 'package:watch_it/watch_it.dart';
5+
import 'package:yaru/yaru.dart';
36

4-
import '../../chat_room/titlebar/chat_room_pin_button.dart';
7+
import '../../chat_room/create_or_edit/create_or_edit_room_model.dart';
8+
import '../../chat_room/info_drawer/chat_room_join_or_leave_button.dart';
9+
import '../../chat_room/titlebar/chat_room_notification_button.dart';
10+
import '../../common/chat_model.dart';
11+
import '../../common/view/build_context_x.dart';
12+
import '../../common/view/confirm.dart';
13+
import '../../common/view/snackbars.dart';
14+
import '../../l10n/l10n.dart';
515

616
class ChatMasterTileMenu extends StatefulWidget {
717
const ChatMasterTileMenu({
818
super.key,
9-
required this.child,
1019
required this.room,
20+
required this.child,
1121
});
1222

13-
final Widget child;
1423
final Room room;
24+
final Widget child;
1525

1626
@override
1727
State<ChatMasterTileMenu> createState() => _ChatMasterTileMenuState();
@@ -22,14 +32,64 @@ class _ChatMasterTileMenuState extends State<ChatMasterTileMenu> {
2232

2333
@override
2434
Widget build(BuildContext context) {
35+
void onTap() =>
36+
_controller.isOpen ? _controller.close() : _controller.open();
2537
return GestureDetector(
26-
onSecondaryTap: () =>
27-
_controller.isOpen ? _controller.close() : _controller.open(),
38+
onSecondaryTap: onTap,
39+
onLongPress: onTap,
40+
2841
child: MenuAnchor(
29-
alignmentOffset: const Offset(20, -80),
3042
controller: _controller,
31-
consumeOutsideTap: true,
32-
menuChildren: [ChatRoomPinButton.menuEntry(room: widget.room)],
43+
alignmentOffset: const Offset(100, -10),
44+
menuChildren: [
45+
MenuItemButton(
46+
onPressed: () => showFutureLoadingDialog(
47+
context: context,
48+
future: () =>
49+
di<CreateOrEditRoomModel>().toggleFavorite(widget.room),
50+
),
51+
leadingIcon: const Icon(YaruIcons.pin),
52+
child: Text(
53+
context.l10n.toggleFavorite,
54+
style: context.textTheme.bodyMedium,
55+
),
56+
),
57+
MenuItemButton(
58+
onPressed: () => showDialog(
59+
context: context,
60+
builder: (_) => ChatRoomNotificationsDialog(room: widget.room),
61+
),
62+
leadingIcon: const Icon(YaruIcons.notification),
63+
child: Text(
64+
context.l10n.notifications,
65+
style: context.textTheme.bodyMedium,
66+
),
67+
),
68+
MenuItemButton(
69+
onPressed: () => ConfirmationDialog.show(
70+
context: context,
71+
title: Text(
72+
'${context.l10n.leave} ${widget.room.getLocalizedDisplayname()}',
73+
),
74+
content: const ForgetCheckBox(),
75+
onConfirm: () async {
76+
void onFail(error) =>
77+
showSnackBar(context, content: Text(error));
78+
await di<ChatModel>().leaveRoom(
79+
room: widget.room,
80+
onFail: onFail,
81+
forget: di<ChatModel>().forget,
82+
);
83+
},
84+
),
85+
leadingIcon: const Icon(YaruIcons.log_out),
86+
87+
child: Text(
88+
context.l10n.leave,
89+
style: context.textTheme.bodyMedium,
90+
),
91+
),
92+
],
3393
child: widget.child,
3494
),
3595
);

lib/chat_master/view/chat_room_last_event.dart

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,40 @@
11
import 'package:flutter/material.dart';
22
import 'package:matrix/matrix.dart';
3+
import 'package:watch_it/watch_it.dart';
34

5+
import '../../chat_room/timeline/timeline_model.dart';
46
import '../../common/event_x.dart';
57
import '../../l10n/l10n.dart';
68

7-
class ChatRoomLastEvent extends StatelessWidget {
9+
class ChatRoomLastEvent extends StatefulWidget {
810
const ChatRoomLastEvent({required this.lastEvent, super.key});
911

1012
final Event? lastEvent;
1113

14+
@override
15+
State<ChatRoomLastEvent> createState() => _ChatRoomLastEventState();
16+
}
17+
18+
class _ChatRoomLastEventState extends State<ChatRoomLastEvent> {
19+
@override
20+
void initState() {
21+
super.initState();
22+
di<TimelineModel>().loadSingleKeyForEvent(widget.lastEvent);
23+
}
24+
1225
@override
1326
Widget build(BuildContext context) {
1427
return FutureBuilder<String>(
1528
key: ValueKey(
16-
'${lastEvent?.eventId}_${lastEvent?.type}_${lastEvent?.redacted}}',
29+
'${widget.lastEvent?.eventId}_${widget.lastEvent?.type}_${widget.lastEvent?.redacted}}',
1730
),
18-
future: lastEvent?.calcLocalizedBody(
31+
future: widget.lastEvent?.calcLocalizedBody(
1932
const MatrixDefaultLocalizations(),
2033
hideReply: true,
2134
plaintextBody: true,
2235
withSenderNamePrefix: true,
2336
),
24-
initialData: lastEvent?.calcLocalizedBodyFallback(
37+
initialData: widget.lastEvent?.calcLocalizedBodyFallback(
2538
const MatrixDefaultLocalizations(),
2639
hideReply: true,
2740
plaintextBody: true,
@@ -31,8 +44,8 @@ class ChatRoomLastEvent extends StatelessWidget {
3144
if (snapshot.hasError) {
3245
return const Text('?', maxLines: 1);
3346
}
34-
if (lastEvent != null) {
35-
if (lastEvent!.hideInTimeline(
47+
if (widget.lastEvent != null) {
48+
if (widget.lastEvent!.hideInTimeline(
3649
showAvatarChanges: false,
3750
showDisplayNameChanges: false,
3851
)) {

lib/chat_master/view/chat_room_master_tile.dart

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ import '../../chat_room/common/view/chat_invitation_dialog.dart';
77
import '../../chat_room/input/draft_model.dart';
88
import '../../chat_room/titlebar/chat_room_pin_button.dart';
99
import '../../common/chat_model.dart';
10-
import '../../common/push_rule_state_x.dart';
11-
import '../../common/view/chat_avatar.dart';
1210
import '../../common/view/scaffold_state_x.dart';
1311
import '../../common/view/snackbars.dart';
1412
import '../../common/view/ui_constants.dart';
1513
import '../../l10n/l10n.dart';
1614
import 'chat_master_detail_page.dart';
1715
import 'chat_master_tile_menu.dart';
16+
import 'chat_room_master_tile_avatar.dart';
1817
import 'chat_room_master_tile_subtitle.dart';
1918

2019
class ChatRoomMasterTile extends StatelessWidget with WatchItMixin {
@@ -34,38 +33,20 @@ class ChatRoomMasterTile extends StatelessWidget with WatchItMixin {
3433
(ChatModel m) => m.loadingArchive,
3534
);
3635

37-
final pushRuleState =
38-
watchStream(
39-
(ChatModel m) => m.syncStream.map((_) => room.pushRuleState),
40-
initialValue: room.pushRuleState,
41-
).data ??
42-
room.pushRuleState;
43-
4436
return ChatMasterTileMenu(
4537
room: room,
4638
child: Opacity(
4739
opacity: processingJoinOrLeave || loadingArchive ? 0.3 : 1,
4840
child: Padding(
49-
padding: const EdgeInsets.only(bottom: 5),
41+
padding: const EdgeInsets.only(bottom: kSmallPadding),
5042
child: Stack(
5143
alignment: Alignment.center,
5244
children: [
5345
YaruMasterTile(
46+
key: ValueKey('${room.id}_master_tile'),
5447
selected:
5548
selectedRoom?.id != null && selectedRoom?.id == room.id,
56-
leading: ChatAvatar(
57-
key: ValueKey(room.avatar?.toString()),
58-
avatarUri: pushRuleState == PushRuleState.dontNotify
59-
? null
60-
: room.avatar,
61-
fallBackIcon: room.membership != Membership.invite
62-
? pushRuleState == PushRuleState.dontNotify
63-
? pushRuleState.getIconData()
64-
: room.isDirectChat
65-
? YaruIcons.user
66-
: YaruIcons.users
67-
: YaruIcons.mail_unread,
68-
),
49+
leading: ChatRoomMasterTileAvatar(room: room),
6950
title: Text(
7051
room.membership == Membership.invite
7152
? context.l10n.invite
@@ -112,7 +93,7 @@ class ChatRoomMasterTile extends StatelessWidget with WatchItMixin {
11293
)
11394
else if (room.isFavourite)
11495
Positioned(
115-
right: kBigPadding,
96+
right: kBigPadding - 3,
11697
child: ChatRoomPinButton(room: room, small: true),
11798
),
11899
],
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:matrix/matrix.dart';
3+
import 'package:watch_it/watch_it.dart';
4+
import 'package:yaru/yaru.dart';
5+
6+
import '../../common/chat_model.dart';
7+
import '../../common/push_rule_state_x.dart';
8+
import '../../common/view/chat_avatar.dart';
9+
10+
class ChatRoomMasterTileAvatar extends StatelessWidget with WatchItMixin {
11+
const ChatRoomMasterTileAvatar({super.key, required this.room});
12+
13+
final Room room;
14+
15+
@override
16+
Widget build(BuildContext context) {
17+
final pushRuleState =
18+
watchStream(
19+
(ChatModel m) => m.syncStream.map((_) => room.pushRuleState),
20+
initialValue: room.pushRuleState,
21+
).data ??
22+
room.pushRuleState;
23+
return ChatAvatar(
24+
avatarUri: pushRuleState == PushRuleState.dontNotify ? null : room.avatar,
25+
fallBackIcon: room.membership != Membership.invite
26+
? pushRuleState == PushRuleState.dontNotify
27+
? pushRuleState.getIconData()
28+
: room.isDirectChat
29+
? YaruIcons.user
30+
: YaruIcons.users
31+
: YaruIcons.mail_unread,
32+
);
33+
}
34+
}

lib/chat_room/create_or_edit/create_or_edit_room_model.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,25 @@ class CreateOrEditRoomModel extends SafeChangeNotifier {
208208
rethrow;
209209
}
210210
}
211+
212+
Future<void> toggleFavorite(Room room) async {
213+
try {
214+
await room.setFavourite(!room.isFavourite);
215+
} catch (e, s) {
216+
printMessageInDebugMode(e, s);
217+
rethrow;
218+
}
219+
}
220+
221+
Future<void> setPushRuleState(
222+
Room room, {
223+
required PushRuleState value,
224+
}) async {
225+
try {
226+
await room.setPushRuleState(value);
227+
} catch (e, s) {
228+
printMessageInDebugMode(e, s);
229+
rethrow;
230+
}
231+
}
211232
}

lib/chat_room/timeline/chat_room_timeline_list.dart

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import '../../common/view/build_context_x.dart';
1212
import '../../common/view/theme.dart';
1313
import '../../common/view/ui_constants.dart';
1414
import '../../events/view/chat_event_tile.dart';
15+
import '../../l10n/l10n.dart';
1516
import '../../settings/settings_model.dart';
1617
import 'chat_room_pinned_events_dialog.dart';
1718
import 'chat_seen_by_indicator.dart';
@@ -43,7 +44,7 @@ class _ChatRoomTimelineListState extends State<ChatRoomTimelineList> {
4344
super.initState();
4445
WidgetsBinding.instance.addPostFrameCallback(
4546
(_) => di<TimelineModel>()
46-
..loadSingleKeyForLastEvent(widget.timeline)
47+
..loadAllKeysFromRoom(widget.timeline)
4748
..trySetReadMarker(widget.timeline)
4849
..requestHistory(widget.timeline, historyCount: 500),
4950
);
@@ -99,7 +100,18 @@ class _ChatRoomTimelineListState extends State<ChatRoomTimelineList> {
99100
showAvatarChanges: showAvatarChanges,
100101
showDisplayNameChanges: showDisplayNameChanges,
101102
)) {
102-
return const SizedBox.shrink();
103+
return Column(
104+
children: [
105+
const SizedBox.shrink(),
106+
if (i == 0)
107+
ChatEventSeenByIndicator(
108+
key: ValueKey(
109+
'${event.eventId}${widget.timeline.events.length}',
110+
),
111+
event: event,
112+
),
113+
],
114+
);
103115
}
104116

105117
final previous = widget.timeline.events.elementAtOrNull(i + 1);
@@ -175,6 +187,24 @@ class _ChatRoomTimelineListState extends State<ChatRoomTimelineList> {
175187
),
176188
),
177189
),
190+
if (widget.timeline.canRequestHistory)
191+
Positioned(
192+
right: kBigPadding,
193+
top: pinnedEvents.isNotEmpty ? 4 * kBigPadding : kBigPadding,
194+
child: FloatingActionButton.small(
195+
heroTag: 'historyRequestButtonTag',
196+
tooltip: context.l10n.loadMore,
197+
backgroundColor: theme.colorScheme.surface,
198+
child: Icon(
199+
YaruIcons.history,
200+
color: theme.colorScheme.onSurface,
201+
),
202+
onPressed: () => di<TimelineModel>().requestHistory(
203+
widget.timeline,
204+
historyCount: 50,
205+
),
206+
),
207+
),
178208
if (_showScrollButton)
179209
Positioned(
180210
right: kBigPadding,

0 commit comments

Comments
 (0)