Skip to content

Commit 40e8341

Browse files
authored
chore: update to matrix 1.1.0 and improve error handling (#79)
1 parent 2061459 commit 40e8341

24 files changed

+415
-217
lines changed

lib/authentication/authentication_model.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:async';
12
import 'dart:io';
23

34
import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';
@@ -38,6 +39,7 @@ class AuthenticationModel extends SafeChangeNotifier {
3839
notifyListeners();
3940
}
4041

42+
final int _timeoutSeconds = 65;
4143
Future<void> login({
4244
required String homeServer,
4345
required String username,
@@ -117,8 +119,14 @@ class AuthenticationModel extends SafeChangeNotifier {
117119
? '${AppConfig.kAppTitle} Web Browser'
118120
: '${AppConfig.kAppTitle} ${Platform.operatingSystem}',
119121
)
120-
.timeout(const Duration(seconds: 55));
122+
.timeout(Duration(seconds: _timeoutSeconds));
121123
await onSuccess();
124+
} on TimeoutException catch (e, s) {
125+
printMessageInDebugMode(
126+
'Login timed out after $_timeoutSeconds seconds: $e',
127+
s,
128+
);
129+
onFail('Failed to login with SSO token: ${e.toString()}');
122130
} catch (e, s) {
123131
printMessageInDebugMode('Error during client.login with token: $e', s);
124132
onFail('Failed to login with SSO token: ${e.toString()}');
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:matrix/matrix.dart';
3+
4+
import '../../chat_room/titlebar/chat_room_pin_button.dart';
5+
6+
class ChatMasterTileMenu extends StatefulWidget {
7+
const ChatMasterTileMenu({
8+
super.key,
9+
required this.child,
10+
required this.room,
11+
});
12+
13+
final Widget child;
14+
final Room room;
15+
16+
@override
17+
State<ChatMasterTileMenu> createState() => _ChatMasterTileMenuState();
18+
}
19+
20+
class _ChatMasterTileMenuState extends State<ChatMasterTileMenu> {
21+
final _controller = MenuController();
22+
23+
@override
24+
Widget build(BuildContext context) {
25+
return GestureDetector(
26+
onSecondaryTap: () =>
27+
_controller.isOpen ? _controller.close() : _controller.open(),
28+
child: MenuAnchor(
29+
alignmentOffset: const Offset(20, -80),
30+
controller: _controller,
31+
consumeOutsideTap: true,
32+
menuChildren: [ChatRoomPinButton.menuEntry(room: widget.room)],
33+
child: widget.child,
34+
),
35+
);
36+
}
37+
}

lib/chat_master/view/chat_room_master_tile.dart

Lines changed: 72 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import '../../common/view/snackbars.dart';
1414
import '../../common/view/ui_constants.dart';
1515
import '../../l10n/l10n.dart';
1616
import 'chat_master_detail_page.dart';
17+
import 'chat_master_tile_menu.dart';
1718
import 'chat_room_master_tile_subtitle.dart';
1819

1920
class ChatRoomMasterTile extends StatelessWidget with WatchItMixin {
@@ -40,79 +41,83 @@ class ChatRoomMasterTile extends StatelessWidget with WatchItMixin {
4041
).data ??
4142
room.pushRuleState;
4243

43-
return Opacity(
44-
opacity: processingJoinOrLeave || loadingArchive ? 0.3 : 1,
45-
child: Padding(
46-
padding: const EdgeInsets.only(bottom: 5),
47-
child: Stack(
48-
alignment: Alignment.center,
49-
children: [
50-
YaruMasterTile(
51-
selected: selectedRoom?.id != null && selectedRoom?.id == room.id,
52-
leading: ChatAvatar(
53-
key: ValueKey(room.avatar?.toString()),
54-
avatarUri: pushRuleState == PushRuleState.dontNotify
44+
return ChatMasterTileMenu(
45+
room: room,
46+
child: Opacity(
47+
opacity: processingJoinOrLeave || loadingArchive ? 0.3 : 1,
48+
child: Padding(
49+
padding: const EdgeInsets.only(bottom: 5),
50+
child: Stack(
51+
alignment: Alignment.center,
52+
children: [
53+
YaruMasterTile(
54+
selected:
55+
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+
),
69+
title: Text(
70+
room.membership == Membership.invite
71+
? context.l10n.invite
72+
: room.getLocalizedDisplayname(),
73+
maxLines: 2,
74+
),
75+
subtitle: room.membership == Membership.invite
76+
? Text(room.getLocalizedDisplayname())
77+
: ChatRoomMasterTileSubTitle(room: room),
78+
onTap: processingJoinOrLeave || loadingArchive
5579
? null
56-
: room.avatar,
57-
fallBackIcon: room.membership != Membership.invite
58-
? pushRuleState == PushRuleState.dontNotify
59-
? pushRuleState.getIconData()
60-
: room.isDirectChat
61-
? YaruIcons.user
62-
: YaruIcons.users
63-
: YaruIcons.mail_unread,
64-
),
65-
title: Text(
66-
room.membership == Membership.invite
67-
? context.l10n.invite
68-
: room.getLocalizedDisplayname(),
69-
maxLines: 2,
70-
),
71-
subtitle: room.membership == Membership.invite
72-
? Text(room.getLocalizedDisplayname())
73-
: ChatRoomMasterTileSubTitle(room: room),
74-
onTap: processingJoinOrLeave || loadingArchive
75-
? null
76-
: () async {
77-
if (room.isArchived) {
78-
chatModel.setSelectedRoom(room);
79-
} else {
80-
if (room.membership == Membership.invite) {
81-
showDialog(
82-
context: context,
83-
builder: (context) =>
84-
ChatInvitationDialog(room: room),
85-
);
80+
: () async {
81+
if (room.isArchived) {
82+
chatModel.setSelectedRoom(room);
8683
} else {
87-
await chatModel.joinRoom(
88-
room,
89-
onFail: (e) =>
90-
showSnackBar(context, content: Text(e)),
91-
);
84+
if (room.membership == Membership.invite) {
85+
showDialog(
86+
context: context,
87+
builder: (context) =>
88+
ChatInvitationDialog(room: room),
89+
);
90+
} else {
91+
await chatModel.joinRoom(
92+
room,
93+
onFail: (e) =>
94+
showSnackBar(context, content: Text(e)),
95+
);
96+
}
9297
}
93-
}
94-
di<DraftModel>().setAttaching(false);
98+
di<DraftModel>().setAttaching(false);
9599

96-
masterScaffoldKey.currentState?.hideDrawer();
97-
},
98-
),
99-
if (!room.isArchived) ...[
100-
if (room.notificationCount > 0)
101-
Positioned(
102-
right: kBigPadding,
103-
child: Badge(
104-
largeSize: 11,
105-
smallSize: 11,
106-
label: Text(room.notificationCount.toString()),
100+
masterScaffoldKey.currentState?.hideDrawer();
101+
},
102+
),
103+
if (!room.isArchived) ...[
104+
if (room.notificationCount > 0)
105+
Positioned(
106+
right: kBigPadding,
107+
child: Badge(
108+
largeSize: 11,
109+
smallSize: 11,
110+
label: Text(room.notificationCount.toString()),
111+
),
112+
)
113+
else if (room.isFavourite)
114+
Positioned(
115+
right: kBigPadding,
116+
child: ChatRoomPinButton(room: room, small: true),
107117
),
108-
)
109-
else if (room.isFavourite)
110-
Positioned(
111-
right: kBigPadding,
112-
child: ChatRoomPinButton(room: room, small: true),
113-
),
118+
],
114119
],
115-
],
120+
),
116121
),
117122
),
118123
);

lib/chat_master/view/chat_room_master_tile_subtitle.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:matrix/matrix.dart';
33
import 'package:watch_it/watch_it.dart';
44

55
import '../../common/chat_model.dart';
6+
import '../../common/event_x.dart';
67
import '../../common/view/build_context_x.dart';
78
import '../../l10n/l10n.dart';
89

@@ -68,6 +69,12 @@ class ChatRoomLastEvent extends StatelessWidget {
6869
if (snapshot.hasError) {
6970
return const Text('?', maxLines: 1);
7071
}
72+
if (lastEvent!.hideInTimeline(
73+
showAvatarChanges: false,
74+
showDisplayNameChanges: false,
75+
)) {
76+
return const Text('...', maxLines: 1);
77+
}
7178
if (snapshot.hasData && lastEvent != null) {
7279
return Text(snapshot.data!, maxLines: 1);
7380
}

lib/chat_master/view/chat_space_control_panel.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,12 @@ class ChatSpaceControlPanel extends StatelessWidget with WatchItMixin {
5959
borderRadius: BorderRadius.circular(kYaruButtonRadius),
6060
),
6161
),
62-
onPressed: () => di<ChatModel>().leaveSelectedRoom(
63-
room: activeSpace,
64-
onFail: (e) => showSnackBar(context, content: Text(e)),
65-
),
62+
onPressed: activeSpace == null
63+
? null
64+
: () => di<ChatModel>().leaveRoom(
65+
room: activeSpace,
66+
onFail: (e) => showSnackBar(context, content: Text(e)),
67+
),
6668
icon: const Icon(YaruIcons.log_out),
6769
),
6870
),

lib/chat_room/common/view/chat_invitation_dialog.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ class ChatInvitationDialog extends StatelessWidget with WatchItMixin {
1414
@override
1515
Widget build(BuildContext context) => ConfirmationDialog(
1616
title: Text(room.getLocalizedDisplayname()),
17-
onCancel: () => room.leave(),
17+
onCancel: () => di<ChatModel>().leaveRoom(
18+
onFail: (error) => showSnackBar(context, content: Text(error)),
19+
room: room,
20+
),
1821
onConfirm: () => di<ChatModel>().joinRoom(
1922
room,
2023
onFail: (error) => showSnackBar(context, content: Text(error)),

lib/chat_room/common/view/chat_room_page.dart

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ class _ChatRoomPageState extends State<ChatRoomPage> {
6868
if (!di<ChatModel>().archiveActive &&
6969
!di<ChatModel>().loadingArchive &&
7070
leftRoomUpdate.hasData) {
71-
di<ChatModel>().leaveSelectedRoom(
71+
di<ChatModel>().leaveRoom(
72+
room: widget.room,
7273
onFail: (error) => showSnackBar(context, content: Text(error)),
7374
);
7475
showDialog(
@@ -85,19 +86,21 @@ class _ChatRoomPageState extends State<ChatRoomPage> {
8586
},
8687
);
8788

88-
final unAcceptedDirectChat = watchStream(
89-
(ChatModel m) => m
90-
.getUsersStreamOfJoinedRoom(
91-
widget.room,
92-
membershipFilter: [Membership.invite],
93-
)
94-
.map(
95-
(invitedUsers) =>
96-
widget.room.isDirectChat && invitedUsers.isNotEmpty,
97-
),
98-
initialValue: widget.room.isUnacceptedDirectChat,
99-
preserveState: false,
100-
).data;
89+
final unAcceptedDirectChat = !widget.room.isUnacceptedDirectChat
90+
? false
91+
: watchStream(
92+
(ChatModel m) => m
93+
.getUsersStreamOfJoinedRoom(
94+
widget.room,
95+
membershipFilter: [Membership.invite],
96+
)
97+
.map(
98+
(invitedUsers) =>
99+
widget.room.isDirectChat && invitedUsers.isNotEmpty,
100+
),
101+
initialValue: widget.room.isUnacceptedDirectChat,
102+
preserveState: false,
103+
).data;
101104

102105
return DropRegion(
103106
formats: Formats.standardFormats,

lib/chat_room/info_drawer/chat_room_info_drawer_leave_button.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ class ChatRoomInfoDrawerForgetButton extends StatelessWidget {
4141
context: context,
4242
builder: (context) => ConfirmationDialog(
4343
showCloseIcon: false,
44-
onConfirm: () async => di<ChatModel>().leaveSelectedRoom(
44+
onConfirm: () async => di<ChatModel>().leaveRoom(
45+
room: room,
4546
onFail: (error) => showSnackBar(context, content: Text(error)),
4647
forget: true,
4748
),

lib/chat_room/info_drawer/chat_room_join_or_leave_button.dart

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,30 +45,27 @@ class ChatRoomJoinOrLeaveButton extends StatelessWidget {
4545
: context.colorScheme.primary,
4646
)
4747
: null,
48-
onPressed: () => showDialog(
49-
barrierDismissible: false,
48+
onPressed: () => ConfirmationDialog.show(
5049
context: context,
51-
builder: (context) => ConfirmationDialog(
52-
showCloseIcon: false,
53-
title: Text(message),
54-
onConfirm: () async {
55-
void onFail(error) => showSnackBar(context, content: Text(error));
56-
57-
if (joinedRoom) {
58-
await chatModel.leaveSelectedRoom(
59-
onFail: onFail,
60-
forget: room.isDirectChat,
61-
);
62-
} else if (!notReJoinable) {
63-
await chatModel.joinRoom(
64-
room,
65-
onFail: onFail,
66-
clear: true,
67-
select: false,
68-
);
69-
}
70-
},
71-
),
50+
title: Text(message),
51+
content: const ForgetCheckBox(),
52+
onConfirm: () async {
53+
void onFail(error) => showSnackBar(context, content: Text(error));
54+
if (joinedRoom) {
55+
await chatModel.leaveRoom(
56+
room: room,
57+
onFail: onFail,
58+
forget: di<ChatModel>().forget,
59+
);
60+
} else if (!notReJoinable) {
61+
await chatModel.joinRoom(
62+
room,
63+
onFail: onFail,
64+
clear: true,
65+
select: false,
66+
);
67+
}
68+
},
7269
),
7370
icon: !room.isArchived
7471
? Icon(
@@ -82,3 +79,16 @@ class ChatRoomJoinOrLeaveButton extends StatelessWidget {
8279
);
8380
}
8481
}
82+
83+
class ForgetCheckBox extends StatelessWidget with WatchItMixin {
84+
const ForgetCheckBox({super.key});
85+
86+
@override
87+
Widget build(BuildContext context) {
88+
return CheckboxListTile.adaptive(
89+
title: Text(context.l10n.delete),
90+
value: watchPropertyValue((ChatModel m) => m.forget),
91+
onChanged: (v) => di<ChatModel>().setForget(v ?? false),
92+
);
93+
}
94+
}

0 commit comments

Comments
 (0)