Skip to content

Commit 83324ee

Browse files
authored
feat: improve input, reactions and archive and commands (#136)
1 parent d07309d commit 83324ee

32 files changed

+4847
-4031
lines changed

lib/chat_master/view/chat_master_clear_archive_button.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ class ChatMasterClearArchiveButton extends StatelessWidget with WatchItMixin {
1717

1818
return IconButton(
1919
tooltip: context.l10n.clearArchive,
20-
onPressed: result.isRunning || result.data!.archivedRooms.isEmpty
20+
onPressed:
21+
result.isRunning || (result.data?.archivedRooms.isEmpty ?? true)
2122
? null
2223
: () {
2324
di<ChatManager>().setSelectedRoom(null);

lib/chat_master/view/chat_master_detail_page.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import '../../authentication/view/chat_login_page.dart';
77
import '../../authentication/view/uia_request_handler.dart';
88
import '../../chat_room/common/view/chat_no_selected_room_page.dart';
99
import '../../chat_room/common/view/chat_room_page.dart';
10+
import '../../chat_room/input/draft_manager.dart';
1011
import '../../common/chat_manager.dart';
1112
import '../../common/platforms.dart';
1213
import '../../common/view/build_context_x.dart';
14+
import '../../common/view/snackbars.dart';
1315
import '../../common/view/ui_constants.dart';
1416
import '../../encryption/encryption_manager.dart';
1517
import '../../encryption/view/key_verification_dialog.dart';
@@ -32,6 +34,15 @@ class ChatMasterDetailPage extends StatelessWidget
3234

3335
registerForgetAllRoomsCommand();
3436

37+
registerHandler(
38+
select: (DraftManager m) => m.sendCommand.errors,
39+
handler: (context, newValue, cancel) {
40+
if (newValue?.error != null) {
41+
showErrorSnackBar(context, newValue!.error.toString());
42+
}
43+
},
44+
);
45+
3546
registerStreamHandler(
3647
select: (EncryptionManager m) => m.onKeyVerificationRequest,
3748
handler: (context, newValue, cancel) {

lib/chat_master/view/chat_master_title_bar.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ class ChatMasterTitleBar extends StatelessWidget with WatchItMixin {
5757
selectedIcon: const Icon(YaruIcons.trash_filled),
5858
isSelected: watchPropertyValue((ChatManager m) => m.archiveActive),
5959
onPressed: loadingArchive
60-
? null
60+
? () {
61+
di<ChatManager>()
62+
..toggleArchiveCommand.cancel()
63+
..toggleArchiveCommand.run();
64+
}
6165
: () => di<ChatManager>().toggleArchiveCommand.run(),
6266
icon: const Icon(YaruIcons.trash),
6367
),

lib/chat_room/common/view/chat_no_selected_room_page.dart

Lines changed: 88 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ class ChatNoSelectedRoomPage extends StatelessWidget with WatchItMixin {
5454
(EditRoomManager m) => m.forgetAllRoomsCommand.progress,
5555
);
5656

57+
final isArchiveActive = watchPropertyValue(
58+
(ChatManager m) => m.archiveActive,
59+
);
60+
61+
final isArchiveEmpty =
62+
watchStream(
63+
(ChatManager m) => m.filteredRoomsStream,
64+
initialValue: di<ChatManager>().filteredRooms,
65+
preserveState: false,
66+
).data?.isEmpty ??
67+
true;
68+
5769
return Scaffold(
5870
appBar: YaruWindowTitleBar(
5971
heroTag: '<Right hero tag>',
@@ -91,11 +103,38 @@ class ChatNoSelectedRoomPage extends StatelessWidget with WatchItMixin {
91103
value: progress,
92104
shapePath: _buildBoatPath(),
93105
)
94-
: Image.asset('assets/nebuchadnezzar.png', width: 100),
95-
Text(
96-
loadingArchive || clearingArchive
97-
? context.l10n.loadingPleaseWait
98-
: 'Please select a chatroom from the side panel.',
106+
: isArchiveActive
107+
? CustomPaint(
108+
size: const Size(90, 90),
109+
painter: _PathPainter(
110+
path: _buildArchiveIconPath(),
111+
color: context.theme.colorScheme.primary,
112+
),
113+
)
114+
: Image.asset(
115+
'assets/nebuchadnezzar.png',
116+
width: 90,
117+
height: 90,
118+
),
119+
SizedBox(
120+
width: 300,
121+
child: Padding(
122+
padding: const EdgeInsets.symmetric(
123+
horizontal: kMediumPadding,
124+
),
125+
child: Text(
126+
loadingArchive
127+
? context.l10n.loadingArchivePleaseWait
128+
: clearingArchive
129+
? context.l10n.clearingArchivePleaseWait
130+
: isArchiveActive
131+
? isArchiveEmpty
132+
? context.l10n.archiveIsEmpty
133+
: context.l10n.pleaseSelectAChatRoom
134+
: context.l10n.pleaseSelectAChatRoom,
135+
textAlign: TextAlign.center,
136+
),
137+
),
99138
),
100139
],
101140
),
@@ -120,34 +159,61 @@ class ChatNoSelectedRoomPage extends StatelessWidget with WatchItMixin {
120159

121160
Path _buildArchiveIconPath() {
122161
final path = Path()
123-
// 1. THE LID (Top Part)
124-
// Starting at the bottom-left of the lid
125162
..moveTo(10, 35)
126-
..lineTo(90, 35) // Bottom edge of lid
127-
..lineTo(90, 20) // Right side
128-
..quadraticBezierTo(90, 10, 80, 10) // Top-right corner
129-
..lineTo(20, 10) // Top edge
130-
..quadraticBezierTo(10, 10, 10, 20) // Top-left corner
131-
..close()
132-
// 2. THE BODY (Bottom Part)
163+
..lineTo(90, 35)
164+
..lineTo(90, 20)
165+
..quadraticBezierTo(90, 10, 80, 10)
166+
..lineTo(20, 10)
167+
..quadraticBezierTo(10, 10, 10, 20)
133168
..moveTo(15, 40)
134-
..lineTo(85, 40) // Top edge of body
135-
..lineTo(85, 80) // Right side
136-
..quadraticBezierTo(85, 90, 75, 90) // Bottom-right corner
137-
..lineTo(25, 90) // Bottom edge
138-
..quadraticBezierTo(15, 90, 15, 80) // Bottom-left corner
169+
..lineTo(85, 40)
170+
..lineTo(85, 80)
171+
..quadraticBezierTo(85, 90, 75, 90)
172+
..lineTo(25, 90)
173+
..quadraticBezierTo(15, 90, 15, 80)
139174
..close()
140-
// 3. THE HANDLE (Slot)
141-
// Adding a rounded rectangle cutout in the center
142175
..addRRect(
143176
RRect.fromRectAndRadius(
144177
const Rect.fromLTWH(35, 50, 30, 10),
145178
const Radius.circular(5),
146179
),
147180
)
148-
// Set the fill type to evenOdd so the handle appears as a "hole"
149181
..fillType = PathFillType.evenOdd;
150182

151183
return path;
152184
}
153185
}
186+
187+
class _PathPainter extends CustomPainter {
188+
final Path path;
189+
final Color color;
190+
191+
_PathPainter({required this.path, required this.color});
192+
193+
@override
194+
void paint(Canvas canvas, Size size) {
195+
final pathBounds = path.getBounds();
196+
final double scaleX = size.width / pathBounds.width;
197+
final double scaleY = size.height / pathBounds.height;
198+
final double scale = scaleX < scaleY ? scaleX : scaleY;
199+
200+
canvas
201+
..save()
202+
..translate(size.width / 2, size.height / 2)
203+
..scale(scale)
204+
..translate(-pathBounds.center.dx, -pathBounds.center.dy);
205+
206+
final paint = Paint()
207+
..color = color
208+
..style = PaintingStyle.fill;
209+
210+
canvas
211+
..drawPath(path, paint)
212+
..restore();
213+
}
214+
215+
@override
216+
bool shouldRepaint(covariant _PathPainter oldDelegate) {
217+
return oldDelegate.path != path || oldDelegate.color != color;
218+
}
219+
}

lib/chat_room/common/view/chat_room_page.dart

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import 'package:flutter_it/flutter_it.dart';
66
import 'package:matrix/matrix.dart';
77
import 'package:mime/mime.dart';
88
import 'package:super_drag_and_drop/super_drag_and_drop.dart';
9+
import 'package:yaru/widgets.dart';
910

1011
import '../../../app/view/error_page.dart';
1112
import '../../../app/view/mouse_and_keyboard_command_wrapper.dart';
13+
import '../../../chat_master/view/chat_space_discover_button.dart';
14+
import '../../../chat_master/view/chat_spaces_search_list.dart';
1215
import '../../../common/chat_manager.dart';
1316
import '../../../common/view/build_context_x.dart';
1417
import '../../../common/view/common_widgets.dart';
@@ -112,6 +115,32 @@ class _ChatRoomPageState extends State<ChatRoomPage> {
112115
),
113116
);
114117
}
118+
if (widget.room.isSpace) {
119+
return Scaffold(
120+
key: chatRoomScaffoldKey,
121+
endDrawer: const ChatRoomInfoDrawer(),
122+
appBar: ChatRoomTitleBar(room: widget.room),
123+
body: const Center(
124+
child: Padding(
125+
padding: EdgeInsets.all(kBigPadding),
126+
child: SizedBox(
127+
width: 500,
128+
height: 1000,
129+
child: YaruBorderContainer(
130+
child: Column(
131+
spacing: kMediumPadding,
132+
children: [
133+
SizedBox(height: kBigPadding),
134+
ChatSpaceDiscoverButton(),
135+
Flexible(child: ChatSpacesSearchList()),
136+
],
137+
),
138+
),
139+
),
140+
),
141+
),
142+
);
143+
}
115144

116145
final l10n = context.l10n;
117146
final colorScheme = context.colorScheme;
@@ -181,14 +210,17 @@ class _ChatRoomPageState extends State<ChatRoomPage> {
181210
children: [
182211
Scaffold(
183212
key: chatRoomScaffoldKey,
184-
endDrawer: ChatRoomInfoDrawer(room: widget.room),
213+
endDrawer: const ChatRoomInfoDrawer(),
185214
appBar: ChatRoomTitleBar(room: widget.room),
186215
bottomNavigationBar: AnimatedSwitcher(
187216
duration: const Duration(milliseconds: 400),
188217
child:
189218
widget.room.isArchived || widget.room.isSpace || threadeMode
190219
? const SizedBox.shrink()
191-
: ChatInput(room: widget.room),
220+
: ChatInput(
221+
key: ValueKey('${widget.room.id}_input'),
222+
room: widget.room,
223+
),
192224
),
193225
body: FutureBuilder<Timeline>(
194226
key: ValueKey(widget.room.id),

lib/chat_room/info_drawer/chat_room_info_drawer.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import 'package:flutter/material.dart';
2-
import 'package:matrix/matrix.dart';
32
import 'package:flutter_it/flutter_it.dart';
3+
import 'package:matrix/matrix.dart';
44

5+
import '../../common/chat_manager.dart';
56
import '../../common/view/ui_constants.dart';
67
import '../../extensions/room_x.dart';
78
import '../create_or_edit/edit_room_service.dart';
@@ -13,12 +14,16 @@ import 'chat_room_info_drawer_group_header.dart';
1314
import 'chat_room_info_drawer_leave_button.dart';
1415

1516
class ChatRoomInfoDrawer extends StatelessWidget with WatchItMixin {
16-
const ChatRoomInfoDrawer({super.key, required this.room});
17-
18-
final Room room;
17+
const ChatRoomInfoDrawer({super.key});
1918

2019
@override
2120
Widget build(BuildContext context) {
21+
final room = watchPropertyValue((ChatManager m) => m.selectedRoom);
22+
23+
if (room == null) {
24+
return const SizedBox.shrink();
25+
}
26+
2227
final unAcceptedDirectChat = watchStream(
2328
(EditRoomService m) => m
2429
.getUsersStreamOfJoinedRoom(
@@ -31,6 +36,7 @@ class ChatRoomInfoDrawer extends StatelessWidget with WatchItMixin {
3136
).data;
3237

3338
return Drawer(
39+
key: ValueKey('drawer_${room.id}'),
3440
child: SizedBox(
3541
width: kSideBarWith,
3642
child: Column(

lib/chat_room/info_drawer/chat_room_info_drawer_group_content.dart

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import 'package:flutter/material.dart';
22
import 'package:matrix/matrix.dart';
33
import 'package:yaru/yaru.dart';
44

5-
import '../../chat_master/view/chat_space_discover_button.dart';
6-
import '../../chat_master/view/chat_spaces_search_list.dart';
75
import '../../common/view/build_context_x.dart';
86
import '../../common/view/ui_constants.dart';
97
import '../../l10n/l10n.dart';
@@ -22,16 +20,6 @@ class ChatRoomInfoDrawerGroupContent extends StatelessWidget {
2220
final l10n = context.l10n;
2321
final theme = context.theme;
2422

25-
if (room.isSpace) {
26-
return const Column(
27-
spacing: kMediumPadding,
28-
children: [
29-
ChatSpaceDiscoverButton(),
30-
Expanded(child: ChatSpacesSearchList()),
31-
],
32-
);
33-
}
34-
3523
return YaruExpansionPanel(
3624
border: Border.all(color: Colors.transparent),
3725
placeDividers: false,
@@ -54,7 +42,8 @@ class ChatRoomInfoDrawerGroupContent extends StatelessWidget {
5442
Text(l10n.users, style: theme.textTheme.titleSmall),
5543
],
5644
),
57-
ChatRoomInfoDrawerMediaGridHeadline(room: room),
45+
if (!room.isSpace)
46+
ChatRoomInfoDrawerMediaGridHeadline(room: room),
5847
]
5948
.map(
6049
(e) => MouseRegion(cursor: SystemMouseCursors.click, child: e),
@@ -66,13 +55,14 @@ class ChatRoomInfoDrawerGroupContent extends StatelessWidget {
6655
height: context.mediaQuerySize.height - 340,
6756
child: ChatRoomUsersList(room: room, sliver: false),
6857
),
69-
SizedBox(
70-
height: context.mediaQuerySize.height - 340,
71-
child: ChatRoomInfoDrawerMediaGridTabs(
72-
key: ValueKey('${room.id}_media_'),
73-
room: room,
58+
if (!room.isSpace)
59+
SizedBox(
60+
height: context.mediaQuerySize.height - 340,
61+
child: ChatRoomInfoDrawerMediaGridTabs(
62+
key: ValueKey('${room.id}_media_'),
63+
room: room,
64+
),
7465
),
75-
),
7666
],
7767
);
7868
}

lib/chat_room/input/draft_manager.dart

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,7 @@ class DraftManager extends SafeChangeNotifier {
2222
required Client client,
2323
required LocalImageService localImageService,
2424
}) : _client = client,
25-
_localImageService = localImageService {
26-
sendCommand = Command.createAsync(
27-
(room) => send(room: room),
28-
initialValue: null,
29-
);
30-
}
31-
25+
_localImageService = localImageService;
3226
final Client _client;
3327
final LocalImageService _localImageService;
3428

@@ -88,7 +82,10 @@ class DraftManager extends SafeChangeNotifier {
8882
_threadRootEventId = null;
8983
}
9084

91-
late final Command<Room, void> sendCommand;
85+
late final Command<Room, void> sendCommand = Command.createAsync(
86+
(room) => send(room: room),
87+
initialValue: null,
88+
);
9289

9390
Future<void> send({required Room room}) async {
9491
try {
@@ -114,7 +111,6 @@ class DraftManager extends SafeChangeNotifier {
114111
bytes: matrixFile.bytes,
115112
name: matrixFile.name,
116113
mimeType: matrixFile.mimeType,
117-
maxDimension: maxUploadSize,
118114
nativeImplementations: _client.nativeImplementations,
119115
);
120116
}

0 commit comments

Comments
 (0)