parent
9bc2d9955a
commit
c8b9654219
8 changed files with 113 additions and 107 deletions
|
|
@ -81,10 +81,10 @@ A simple and user-friendly Matrix client made with Flutter and a Gomuks backend.
|
|||
- [x] Users
|
||||
- [x] Clickable
|
||||
- [x] Rooms
|
||||
- [ ] Clickable
|
||||
- [x] Clickable
|
||||
- [x] Matrix URIs
|
||||
- [x] Matrix.to links
|
||||
- [x] Events
|
||||
- [ ] Events
|
||||
- [ ] Render more nicely
|
||||
- [ ] Clickable
|
||||
- [x] Custom emojis/stickers
|
||||
|
|
|
|||
|
|
@ -5,13 +5,14 @@ import "package:nexus/controllers/spaces.dart";
|
|||
import "package:nexus/models/space.dart";
|
||||
|
||||
extension FocusRoom on MutationTarget {
|
||||
Future<void> focusRoom(String id) async {
|
||||
Future<bool> focusRoom(String id) async {
|
||||
final spaces = container.read(SpacesController.provider);
|
||||
|
||||
if (spaces.firstWhereOrNull((space) => space.id == id) case Space _?) {
|
||||
return await container
|
||||
await container
|
||||
.read(KeyController.provider(KeyController.spaceKey).notifier)
|
||||
.set(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
final parent = spaces.firstWhereOrNull(
|
||||
|
|
@ -23,7 +24,7 @@ extension FocusRoom on MutationTarget {
|
|||
sub.children.any((room) => room.metadata?.id == id),
|
||||
),
|
||||
);
|
||||
if (parent == null) return;
|
||||
if (parent == null) return false;
|
||||
|
||||
await container
|
||||
.read(KeyController.provider(KeyController.spaceKey).notifier)
|
||||
|
|
@ -32,5 +33,7 @@ extension FocusRoom on MutationTarget {
|
|||
await container
|
||||
.read(KeyController.provider(KeyController.roomKey).notifier)
|
||||
.set(id);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
|
||||
extension LinkToMention on String {
|
||||
/// Extracts a Matrix identifier from this string.
|
||||
///
|
||||
|
|
@ -43,4 +45,7 @@ extension LinkToMention on String {
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
IList<String> get via =>
|
||||
.new(Uri.tryParse(replaceAll("/#", ""))?.queryParametersAll["via"] ?? []);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ part "room_summary.g.dart";
|
|||
@JsonSerializable()
|
||||
class const RoomSummary({
|
||||
required final String roomId,
|
||||
@JsonKey(name: "num_joined_members") required final int joinedMembers,
|
||||
@JsonKey(name: "num_joined_members") final int? joinedMembers,
|
||||
final JoinRule? joinRule,
|
||||
final String? name,
|
||||
final Uri? avatarUrl,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ import "package:material_ui/material_ui.dart";
|
|||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/room_summary.dart";
|
||||
import "package:nexus/controllers/user.dart";
|
||||
import "package:nexus/helpers/extensions/focus_room.dart";
|
||||
import "package:nexus/helpers/extensions/link_to_mention.dart";
|
||||
import "package:nexus/helpers/extensions/show_user_popover.dart";
|
||||
import "package:nexus/models/content/membership.dart";
|
||||
import "package:nexus/models/room_summary.dart";
|
||||
import "package:nexus/widgets/room_summary_dialog.dart";
|
||||
|
||||
class const MentionChip(final String content, final String? roomId, {super.key})
|
||||
extends ConsumerWidget {
|
||||
|
|
@ -22,10 +24,11 @@ class const MentionChip(final String content, final String? roomId, {super.key})
|
|||
|
||||
"#" || "!" =>
|
||||
ref
|
||||
.watch(
|
||||
RoomSummaryController.provider(.new(roomIdOrAlias: mention!)),
|
||||
)
|
||||
.whenOrNull(data: (data) => data),
|
||||
.watch(
|
||||
RoomSummaryController.provider(.new(roomIdOrAlias: mention!)),
|
||||
)
|
||||
.whenOrNull(data: (data) => data) ??
|
||||
RoomSummary(roomId: mention),
|
||||
|
||||
_ => null,
|
||||
};
|
||||
|
|
@ -33,11 +36,28 @@ class const MentionChip(final String content, final String? roomId, {super.key})
|
|||
return mention == null
|
||||
? SizedBox.shrink()
|
||||
: InkWell(
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
if (data case MembershipContent membership) {
|
||||
context.showUserPopover(membership, mention, roomId: roomId);
|
||||
} else if (data case RoomSummary summary) {
|
||||
// TODO: Handle summary
|
||||
if (!await ref.focusRoom(summary.roomId) && context.mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Consumer(
|
||||
builder: (context, ref, _) => RoomSummaryDialog(
|
||||
ref
|
||||
.watch(
|
||||
RoomSummaryController.provider(
|
||||
.new(roomIdOrAlias: mention),
|
||||
),
|
||||
)
|
||||
.whenOrNull(data: (data) => data) ??
|
||||
summary,
|
||||
via: content.via,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:material_ui/material_ui.dart";
|
||||
import "package:flutter_hooks/flutter_hooks.dart";
|
||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/controllers/room_summary.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/helpers/extensions/focus_room.dart";
|
||||
import "package:nexus/helpers/extensions/link_to_mention.dart";
|
||||
import "package:nexus/widgets/room_summary_dialog.dart";
|
||||
import "package:nexus/main.dart";
|
||||
|
||||
class const JoinDialog(final WidgetRef ref, {super.key}) extends HookWidget {
|
||||
@override
|
||||
|
|
@ -19,90 +14,21 @@ class const JoinDialog(final WidgetRef ref, {super.key}) extends HookWidget {
|
|||
Navigator.of(context).pop();
|
||||
|
||||
final roomIdOrAlias = roomAlias.text.mention ?? roomAlias.text;
|
||||
final IList<String> via = .new(
|
||||
Uri.tryParse(roomAlias.text.replaceAll("/#", ""))
|
||||
?.queryParametersAll["via"] ??
|
||||
[],
|
||||
);
|
||||
final via = roomAlias.text.via;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => ref
|
||||
.watch(
|
||||
RoomSummaryController.provider(
|
||||
.new(roomIdOrAlias: roomIdOrAlias, via: via),
|
||||
),
|
||||
)
|
||||
.betterWhen(
|
||||
data: (summary) => RoomSummaryDialog(
|
||||
summary,
|
||||
extraActions: .new([
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
// Capture everything before the dialog closes.
|
||||
final container = ProviderScope.containerOf(context);
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
Navigator.of(context).pop();
|
||||
|
||||
final snackbar = scaffoldMessenger.showSnackBar(
|
||||
.new(
|
||||
content: Text("Joining room $roomIdOrAlias."),
|
||||
duration: Duration(days: 999),
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
final id = await container
|
||||
.read(ClientController.provider.notifier)
|
||||
.joinRoom(
|
||||
.new(roomIdOrAlias: roomIdOrAlias, via: via),
|
||||
);
|
||||
|
||||
snackbar.close();
|
||||
|
||||
scaffoldMessenger.showSnackBar(
|
||||
.new(
|
||||
content: Text(
|
||||
"Room $roomIdOrAlias successfully joined.",
|
||||
),
|
||||
action: .new(
|
||||
label: "Open",
|
||||
onPressed: () =>
|
||||
container.focusRoom(id).onError(showError),
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
snackbar.close();
|
||||
scaffoldMessenger.showSnackBar(
|
||||
.new(
|
||||
backgroundColor: colors.errorContainer,
|
||||
content: Text(
|
||||
error.toString(),
|
||||
style: .new(color: colors.onErrorContainer),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text("Join"),
|
||||
),
|
||||
]),
|
||||
),
|
||||
loading: () => Dialog(
|
||||
child: Row(
|
||||
mainAxisAlignment: .center,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: .all(24),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
builder: (context) => switch (ref.watch(
|
||||
RoomSummaryController.provider(
|
||||
.new(roomIdOrAlias: roomIdOrAlias, via: via),
|
||||
),
|
||||
)) {
|
||||
AsyncData(:final value) => RoomSummaryDialog(value, via: via),
|
||||
AsyncError _ || AsyncLoading _ => RoomSummaryDialog(
|
||||
.new(roomId: roomAlias.text),
|
||||
via: via,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ final class const RoomAppbar({
|
|||
.new(
|
||||
roomId: room!.metadata!.id,
|
||||
joinedMembers:
|
||||
room.metadata!.lazyLoadSummary?.joinedMemberCount ?? -1,
|
||||
room.metadata!.lazyLoadSummary?.joinedMemberCount,
|
||||
avatarUrl: room.metadata?.avatar,
|
||||
canonicalAlias: room.metadata?.canonicalAlias,
|
||||
name: room.metadata?.name,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:material_ui/material_ui.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/helpers/extensions/focus_room.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
import "package:nexus/widgets/expandable_image.dart";
|
||||
import "package:nexus/widgets/linkified_text.dart";
|
||||
|
|
@ -8,11 +10,11 @@ import "package:nexus/models/room_summary.dart";
|
|||
|
||||
class const RoomSummaryDialog(
|
||||
final RoomSummary summary, {
|
||||
final IList<Widget> extraActions = const IList.empty(),
|
||||
final IList<String>? via,
|
||||
super.key,
|
||||
}) extends ConsumerWidget {
|
||||
}) extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) => AlertDialog(
|
||||
Widget build(BuildContext context) => AlertDialog(
|
||||
constraints: .loose(.fromWidth(400)),
|
||||
scrollable: true,
|
||||
contentPadding: EdgeInsets.all(24).copyWith(top: 8),
|
||||
|
|
@ -25,14 +27,14 @@ class const RoomSummaryDialog(
|
|||
summary.avatarUrl == null ? null : .new(mxc: summary.avatarUrl!),
|
||||
child: AvatarOrHash(
|
||||
summary.avatarUrl,
|
||||
summary.name ?? "Unnamed Room",
|
||||
"",
|
||||
height: 64,
|
||||
fallback: Icon(Icons.numbers),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
summary.name ?? summary.canonicalAlias ?? summary.roomId,
|
||||
summary.name ?? summary.canonicalAlias ?? "Unnamed Room",
|
||||
overflow: .ellipsis,
|
||||
maxLines: 3,
|
||||
),
|
||||
|
|
@ -42,7 +44,9 @@ class const RoomSummaryDialog(
|
|||
content: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text("${summary.joinedMembers} members"),
|
||||
title: Text(
|
||||
"${summary.joinedMembers ?? "Unknown number of"} members",
|
||||
),
|
||||
leading: Icon(Icons.people),
|
||||
),
|
||||
ListTile(
|
||||
|
|
@ -66,7 +70,55 @@ class const RoomSummaryDialog(
|
|||
|
||||
actions: [
|
||||
TextButton(onPressed: Navigator.of(context).pop, child: Text("Cancel")),
|
||||
...extraActions,
|
||||
if (via != null)
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
// Capture everything before the dialog closes.
|
||||
final container = ProviderScope.containerOf(context);
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
Navigator.of(context).pop();
|
||||
|
||||
final roomIdOrAlias = summary.canonicalAlias ?? summary.roomId;
|
||||
|
||||
final snackbar = scaffoldMessenger.showSnackBar(
|
||||
.new(
|
||||
content: Text("Joining room $roomIdOrAlias."),
|
||||
duration: Duration(days: 999),
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
final id = await container
|
||||
.read(ClientController.provider.notifier)
|
||||
.joinRoom(.new(roomIdOrAlias: summary.roomId, via: via!));
|
||||
|
||||
snackbar.close();
|
||||
|
||||
scaffoldMessenger.showSnackBar(
|
||||
.new(
|
||||
content: Text("Room $roomIdOrAlias successfully joined."),
|
||||
action: .new(
|
||||
label: "Open",
|
||||
onPressed: () => container.focusRoom(id),
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
snackbar.close();
|
||||
scaffoldMessenger.showSnackBar(
|
||||
.new(
|
||||
backgroundColor: colors.errorContainer,
|
||||
content: Text(
|
||||
error.toString(),
|
||||
style: .new(color: colors.onErrorContainer),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text("Join"),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue