mass migrate to adaptive sheets
This commit is contained in:
parent
ba258c2fcd
commit
9117663c48
13 changed files with 292 additions and 289 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/widgets/sheet_layout.dart";
|
||||||
import "package:flutter/services.dart";
|
import "package:flutter/services.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
|
|
@ -46,27 +47,13 @@ extension BuildEventOptions on Event {
|
||||||
required String action,
|
required String action,
|
||||||
required Future<void> Function(String reason) onConfirm,
|
required Future<void> Function(String reason) onConfirm,
|
||||||
}) {
|
}) {
|
||||||
showDialog(
|
context.showAdaptiveSheet(
|
||||||
context: context,
|
(context) => HookBuilder(
|
||||||
builder: (context) => HookBuilder(
|
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final reasonController = useTextEditingController();
|
final reasonController = useTextEditingController();
|
||||||
|
|
||||||
return AlertDialog(
|
return SheetLayout(
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
content: Column(
|
|
||||||
mainAxisSize: .min,
|
|
||||||
crossAxisAlignment: .start,
|
|
||||||
children: [
|
|
||||||
Text(description),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
TextField(
|
|
||||||
controller: reasonController,
|
|
||||||
textCapitalization: .sentences,
|
|
||||||
decoration: .new(labelText: "Reason (optional)"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: Navigator.of(context).pop,
|
onPressed: Navigator.of(context).pop,
|
||||||
|
|
@ -82,6 +69,14 @@ extension BuildEventOptions on Event {
|
||||||
child: Text(action),
|
child: Text(action),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
children: [
|
||||||
|
Text(description),
|
||||||
|
TextField(
|
||||||
|
controller: reasonController,
|
||||||
|
textCapitalization: .sentences,
|
||||||
|
decoration: .new(labelText: "Reason (optional)"),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/helpers/extensions/show_adaptive_sheet.dart";
|
||||||
|
import "package:nexus/widgets/sheet_layout.dart";
|
||||||
import "package:flutter_svg/flutter_svg.dart";
|
import "package:flutter_svg/flutter_svg.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:m3e_card_list/m3e_card_list.dart";
|
import "package:m3e_card_list/m3e_card_list.dart";
|
||||||
|
|
@ -10,64 +12,8 @@ extension ShowAboutDialog on BuildContext {
|
||||||
final packageInfo = await PackageInfo.fromPlatform();
|
final packageInfo = await PackageInfo.fromPlatform();
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
showDialog(
|
showAdaptiveSheet(
|
||||||
context: this,
|
(context) => SheetLayout(
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: .min,
|
|
||||||
spacing: 16,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
spacing: 12,
|
|
||||||
children: [
|
|
||||||
SvgPicture.asset("assets/bundled/icon.svg", width: 64),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: .start,
|
|
||||||
children: [
|
|
||||||
Wrap(
|
|
||||||
crossAxisAlignment: .center,
|
|
||||||
spacing: 4,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"Nexus",
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"(${packageInfo.version}+${const String.fromEnvironment("GIT_COMMIT", defaultValue: "dev")})",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
Text(
|
|
||||||
"A simple and user-friendly Matrix client",
|
|
||||||
overflow: .ellipsis,
|
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
M3ECardColumn(
|
|
||||||
onTap: (index) =>
|
|
||||||
ref.read(LaunchHelper.provider).launchUrl(switch (index) {
|
|
||||||
0 => .https("git.federated.nexus", "nexus/nexus"),
|
|
||||||
_ => .https("liberapay.com", "QuadRadical"),
|
|
||||||
}),
|
|
||||||
children: [
|
|
||||||
ListTile(
|
|
||||||
leading: Icon(Icons.commit),
|
|
||||||
title: Text("Source Code"),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: Icon(Icons.favorite, color: Colors.pinkAccent),
|
|
||||||
title: Text("Donate"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => showLicensePage(context: context),
|
onPressed: () => showLicensePage(context: context),
|
||||||
|
|
@ -78,6 +24,65 @@ extension ShowAboutDialog on BuildContext {
|
||||||
child: Text("Close"),
|
child: Text("Close"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisSize: .min,
|
||||||
|
spacing: 16,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
spacing: 12,
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset("assets/bundled/icon.svg", width: 64),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: .start,
|
||||||
|
children: [
|
||||||
|
Wrap(
|
||||||
|
crossAxisAlignment: .center,
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Nexus",
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.headlineMedium,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"(${packageInfo.version}+${const String.fromEnvironment("GIT_COMMIT", defaultValue: "dev")})",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"A simple and user-friendly Matrix client",
|
||||||
|
overflow: .ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
M3ECardColumn(
|
||||||
|
onTap: (index) =>
|
||||||
|
ref.read(LaunchHelper.provider).launchUrl(switch (index) {
|
||||||
|
0 => .https("git.federated.nexus", "nexus/nexus"),
|
||||||
|
_ => .https("liberapay.com", "QuadRadical"),
|
||||||
|
}),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.commit),
|
||||||
|
title: Text("Source Code"),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.favorite, color: Colors.pinkAccent),
|
||||||
|
title: Text("Donate"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/helpers/extensions/show_adaptive_sheet.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/controllers/event.dart";
|
import "package:nexus/controllers/event.dart";
|
||||||
import "package:nexus/controllers/room_summary.dart";
|
import "package:nexus/controllers/room_summary.dart";
|
||||||
|
|
@ -8,7 +9,7 @@ import "package:nexus/helpers/extensions/get_link_info.dart";
|
||||||
import "package:nexus/helpers/extensions/show_user_popover.dart";
|
import "package:nexus/helpers/extensions/show_user_popover.dart";
|
||||||
import "package:nexus/models/content/membership.dart";
|
import "package:nexus/models/content/membership.dart";
|
||||||
import "package:nexus/models/room_summary.dart";
|
import "package:nexus/models/room_summary.dart";
|
||||||
import "package:nexus/widgets/room_summary_dialog.dart";
|
import "package:nexus/widgets/room_summary_sheet.dart";
|
||||||
|
|
||||||
class const MentionChip(final String content, final String? roomId, {super.key})
|
class const MentionChip(final String content, final String? roomId, {super.key})
|
||||||
extends ConsumerWidget {
|
extends ConsumerWidget {
|
||||||
|
|
@ -58,10 +59,9 @@ class const MentionChip(final String content, final String? roomId, {super.key})
|
||||||
|
|
||||||
if (!await ref.focusRoom(summary.roomId, eventRowId) &&
|
if (!await ref.focusRoom(summary.roomId, eventRowId) &&
|
||||||
context.mounted) {
|
context.mounted) {
|
||||||
showDialog(
|
context.showAdaptiveSheet(
|
||||||
context: context,
|
(context) => Consumer(
|
||||||
builder: (context) => Consumer(
|
builder: (context, ref, _) => RoomSummarySheet(
|
||||||
builder: (context, ref, _) => RoomSummaryDialog(
|
|
||||||
ref
|
ref
|
||||||
.watch(
|
.watch(
|
||||||
RoomSummaryController.provider(
|
RoomSummaryController.provider(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/helpers/extensions/show_adaptive_sheet.dart";
|
||||||
|
import "package:nexus/widgets/sheet_layout.dart";
|
||||||
import "package:nexus/controllers/invites.dart";
|
import "package:nexus/controllers/invites.dart";
|
||||||
import "package:nexus/helpers/extensions/get_localpart.dart";
|
import "package:nexus/helpers/extensions/get_localpart.dart";
|
||||||
import "package:nexus/main.dart";
|
import "package:nexus/main.dart";
|
||||||
|
|
@ -8,7 +10,7 @@ import "package:nexus/models/content/create.dart";
|
||||||
import "package:nexus/models/nav_page.dart";
|
import "package:nexus/models/nav_page.dart";
|
||||||
import "package:nexus/models/room_summary.dart";
|
import "package:nexus/models/room_summary.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
import "package:nexus/widgets/room_summary_dialog.dart";
|
import "package:nexus/widgets/room_summary_sheet.dart";
|
||||||
|
|
||||||
class const InvitesList({super.key})
|
class const InvitesList({super.key})
|
||||||
extends HookConsumerWidget
|
extends HookConsumerWidget
|
||||||
|
|
@ -33,13 +35,9 @@ class const InvitesList({super.key})
|
||||||
final decliningAll = useState(false);
|
final decliningAll = useState(false);
|
||||||
|
|
||||||
Future<void> declineAll() async {
|
Future<void> declineAll() async {
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await context.showAdaptiveSheet<bool>(
|
||||||
context: context,
|
(context) => SheetLayout(
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: const Text("Decline All Invites"),
|
title: const Text("Decline All Invites"),
|
||||||
content: Text(
|
|
||||||
"Are you sure you want to decline all ${invites.length} invites?",
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(false),
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
|
@ -50,6 +48,11 @@ class const InvitesList({super.key})
|
||||||
child: const Text("Decline All"),
|
child: const Text("Decline All"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Are you sure you want to decline all ${invites.length} invites?",
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (confirmed != true) return;
|
if (confirmed != true) return;
|
||||||
|
|
@ -103,10 +106,7 @@ class const InviteCard(final RoomSummary invite, {super.key})
|
||||||
return Card(
|
return Card(
|
||||||
clipBehavior: .antiAlias,
|
clipBehavior: .antiAlias,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
onTap: () => showDialog(
|
onTap: () => context.showAdaptiveSheet((_) => RoomSummarySheet(invite)),
|
||||||
context: context,
|
|
||||||
builder: (_) => RoomSummaryDialog(invite),
|
|
||||||
),
|
|
||||||
leading: AvatarOrHash(
|
leading: AvatarOrHash(
|
||||||
invite.avatarUrl,
|
invite.avatarUrl,
|
||||||
name,
|
name,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/helpers/extensions/show_adaptive_sheet.dart";
|
||||||
|
import "package:nexus/widgets/sheet_layout.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:nexus/controllers/room_summary.dart";
|
import "package:nexus/controllers/room_summary.dart";
|
||||||
import "package:nexus/helpers/extensions/get_link_info.dart";
|
import "package:nexus/helpers/extensions/get_link_info.dart";
|
||||||
import "package:nexus/widgets/room_summary_dialog.dart";
|
import "package:nexus/widgets/room_summary_sheet.dart";
|
||||||
|
|
||||||
class const JoinDialog(final WidgetRef ref, {super.key}) extends HookWidget {
|
class const JoinSheet(final WidgetRef ref, {super.key}) extends HookWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final roomAlias = useTextEditingController();
|
final roomAlias = useTextEditingController();
|
||||||
|
|
@ -17,16 +19,15 @@ class const JoinDialog(final WidgetRef ref, {super.key}) extends HookWidget {
|
||||||
final via = linkInfo?.via ?? const .empty();
|
final via = linkInfo?.via ?? const .empty();
|
||||||
final roomIdOrAlias = linkInfo?.identifier ?? roomAlias.text;
|
final roomIdOrAlias = linkInfo?.identifier ?? roomAlias.text;
|
||||||
|
|
||||||
showDialog(
|
context.showAdaptiveSheet(
|
||||||
context: context,
|
(context) => Consumer(
|
||||||
builder: (context) => Consumer(
|
|
||||||
builder: (context, ref, _) => switch (ref.watch(
|
builder: (context, ref, _) => switch (ref.watch(
|
||||||
RoomSummaryController.provider(
|
RoomSummaryController.provider(
|
||||||
.new(roomIdOrAlias: roomIdOrAlias, via: via),
|
.new(roomIdOrAlias: roomIdOrAlias, via: via),
|
||||||
),
|
),
|
||||||
)) {
|
)) {
|
||||||
AsyncData(:final value) => RoomSummaryDialog(value, via: via),
|
AsyncData(:final value) => RoomSummarySheet(value, via: via),
|
||||||
AsyncError _ || AsyncLoading _ => RoomSummaryDialog(
|
AsyncError _ || AsyncLoading _ => RoomSummarySheet(
|
||||||
.new(roomId: roomIdOrAlias),
|
.new(roomId: roomIdOrAlias),
|
||||||
via: via,
|
via: via,
|
||||||
),
|
),
|
||||||
|
|
@ -35,25 +36,27 @@ class const JoinDialog(final WidgetRef ref, {super.key}) extends HookWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AlertDialog(
|
return SheetLayout(
|
||||||
title: Text("Join a Room"),
|
title: Text("Join a Room"),
|
||||||
content: Column(
|
|
||||||
mainAxisSize: .min,
|
|
||||||
crossAxisAlignment: .start,
|
|
||||||
children: [
|
|
||||||
Text("Enter the room alias, Matrix URI, or Matrix.to link."),
|
|
||||||
SizedBox(height: 12),
|
|
||||||
TextField(
|
|
||||||
controller: roomAlias,
|
|
||||||
decoration: .new(hintText: "#room:server.name"),
|
|
||||||
onEditingComplete: onJoin,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: Navigator.of(context).pop, child: Text("Cancel")),
|
TextButton(onPressed: Navigator.of(context).pop, child: Text("Cancel")),
|
||||||
TextButton(onPressed: onJoin, child: Text("Join")),
|
TextButton(onPressed: onJoin, child: Text("Join")),
|
||||||
],
|
],
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisSize: .min,
|
||||||
|
crossAxisAlignment: .start,
|
||||||
|
children: [
|
||||||
|
Text("Enter the room alias, Matrix URI, or Matrix.to link."),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
TextField(
|
||||||
|
controller: roomAlias,
|
||||||
|
decoration: .new(hintText: "#room:server.name"),
|
||||||
|
onEditingComplete: onJoin,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/helpers/extensions/show_adaptive_sheet.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
import "package:nexus/controllers/rooms.dart";
|
import "package:nexus/controllers/rooms.dart";
|
||||||
import "package:nexus/widgets/appbar.dart";
|
import "package:nexus/widgets/appbar.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
import "package:nexus/widgets/count_badge.dart";
|
import "package:nexus/widgets/count_badge.dart";
|
||||||
import "package:nexus/widgets/room_menu_button.dart";
|
import "package:nexus/widgets/room_menu_button.dart";
|
||||||
import "package:nexus/widgets/room_summary_dialog.dart";
|
import "package:nexus/widgets/room_summary_sheet.dart";
|
||||||
import "package:nexus/widgets/sidebar.dart";
|
import "package:nexus/widgets/sidebar.dart";
|
||||||
|
|
||||||
final class const RoomAppbar({
|
final class const RoomAppbar({
|
||||||
|
|
@ -28,9 +29,8 @@ final class const RoomAppbar({
|
||||||
return Appbar(
|
return Appbar(
|
||||||
onTap: room?.metadata == null
|
onTap: room?.metadata == null
|
||||||
? null
|
? null
|
||||||
: () => showDialog(
|
: () => context.showAdaptiveSheet(
|
||||||
context: context,
|
(context) => RoomSummarySheet(
|
||||||
builder: (context) => RoomSummaryDialog(
|
|
||||||
.new(
|
.new(
|
||||||
roomId: room!.metadata!.id,
|
roomId: room!.metadata!.id,
|
||||||
joinedMembers:
|
joinedMembers:
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import "package:nexus/widgets/start_dm_sheet.dart";
|
||||||
import "package:nexus/widgets/divider_widget.dart";
|
import "package:nexus/widgets/divider_widget.dart";
|
||||||
import "package:nexus/widgets/error_dialog.dart";
|
import "package:nexus/widgets/error_dialog.dart";
|
||||||
import "package:nexus/widgets/pages/settings.dart";
|
import "package:nexus/widgets/pages/settings.dart";
|
||||||
import "package:nexus/widgets/join_dialog.dart";
|
import "package:nexus/widgets/join_sheet.dart";
|
||||||
import "package:nexus/widgets/room_menu_button.dart";
|
import "package:nexus/widgets/room_menu_button.dart";
|
||||||
import "package:nexus/controllers/key.dart";
|
import "package:nexus/controllers/key.dart";
|
||||||
import "package:nexus/controllers/rooms.dart";
|
import "package:nexus/controllers/rooms.dart";
|
||||||
|
|
@ -157,10 +157,8 @@ class const SpacesRail({super.key}) extends ConsumerWidget {
|
||||||
tooltip: "Add a room",
|
tooltip: "Add a room",
|
||||||
itemBuilder: (_) => [
|
itemBuilder: (_) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () => showDialog(
|
onTap: () =>
|
||||||
context: context,
|
context.showAdaptiveSheet((_) => JoinSheet(ref)),
|
||||||
builder: (_) => JoinDialog(ref),
|
|
||||||
),
|
|
||||||
child: const ListTile(
|
child: const ListTile(
|
||||||
title: Text("Join an existing room (or space)"),
|
title: Text("Join an existing room (or space)"),
|
||||||
leading: Icon(Icons.numbers),
|
leading: Icon(Icons.numbers),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/helpers/extensions/show_adaptive_sheet.dart";
|
||||||
|
import "package:nexus/widgets/sheet_layout.dart";
|
||||||
import "package:flutter/services.dart";
|
import "package:flutter/services.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/controllers/client.dart";
|
import "package:nexus/controllers/client.dart";
|
||||||
|
|
@ -50,9 +52,8 @@ final class const RoomMenuButton(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () => showDialog(
|
onTap: () => context.showAdaptiveSheet(
|
||||||
context: context,
|
(context) => HookBuilder(
|
||||||
builder: (context) => HookBuilder(
|
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final userIdController = useTextEditingController();
|
final userIdController = useTextEditingController();
|
||||||
final reasonController = useTextEditingController();
|
final reasonController = useTextEditingController();
|
||||||
|
|
@ -75,35 +76,8 @@ final class const RoomMenuButton(
|
||||||
.onError(showError);
|
.onError(showError);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AlertDialog(
|
return SheetLayout(
|
||||||
title: Text("Invite User"),
|
title: Text("Invite User"),
|
||||||
content: Column(
|
|
||||||
mainAxisSize: .min,
|
|
||||||
spacing: 12,
|
|
||||||
children: [
|
|
||||||
TextField(
|
|
||||||
controller: userIdController,
|
|
||||||
autofocus: true,
|
|
||||||
keyboardType: .emailAddress,
|
|
||||||
onSubmitted: (_) => submit(),
|
|
||||||
decoration: .new(
|
|
||||||
labelText: "User ID",
|
|
||||||
hintText: "@user:example.com",
|
|
||||||
errorText: text.isEmpty || valid
|
|
||||||
? null
|
|
||||||
: "Enter a valid user ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextField(
|
|
||||||
controller: reasonController,
|
|
||||||
textCapitalization: .sentences,
|
|
||||||
onSubmitted: (_) => submit(),
|
|
||||||
decoration: const .new(
|
|
||||||
labelText: "Invite reason (optional)",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: Navigator.of(context).pop,
|
onPressed: Navigator.of(context).pop,
|
||||||
|
|
@ -114,6 +88,29 @@ final class const RoomMenuButton(
|
||||||
child: Text("Invite"),
|
child: Text("Invite"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: userIdController,
|
||||||
|
autofocus: true,
|
||||||
|
keyboardType: .emailAddress,
|
||||||
|
onSubmitted: (_) => submit(),
|
||||||
|
decoration: .new(
|
||||||
|
labelText: "User ID",
|
||||||
|
hintText: "@user:example.com",
|
||||||
|
errorText: text.isEmpty || valid
|
||||||
|
? null
|
||||||
|
: "Enter a valid user ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: reasonController,
|
||||||
|
textCapitalization: .sentences,
|
||||||
|
onSubmitted: (_) => submit(),
|
||||||
|
decoration: const .new(
|
||||||
|
labelText: "Invite reason (optional)",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -124,13 +121,9 @@ final class const RoomMenuButton(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () => showDialog(
|
onTap: () => context.showAdaptiveSheet(
|
||||||
context: context,
|
(context) => SheetLayout(
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: Text("Leave Room"),
|
title: Text("Leave Room"),
|
||||||
content: Text(
|
|
||||||
"Are you sure you want to leave \"${room!.metadata?.name ?? "Unnamed Room"}\"?",
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: Navigator.of(context).pop,
|
onPressed: Navigator.of(context).pop,
|
||||||
|
|
@ -154,6 +147,11 @@ final class const RoomMenuButton(
|
||||||
child: Text("Leave"),
|
child: Text("Leave"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Are you sure you want to leave \"${room!.metadata?.name ?? "Unnamed Room"}\"?",
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/widgets/sheet_layout.dart";
|
||||||
import "package:nexus/controllers/invites.dart";
|
import "package:nexus/controllers/invites.dart";
|
||||||
import "package:nexus/helpers/extensions/get_localpart.dart";
|
import "package:nexus/helpers/extensions/get_localpart.dart";
|
||||||
import "package:nexus/helpers/extensions/join_room.dart";
|
import "package:nexus/helpers/extensions/join_room.dart";
|
||||||
|
|
@ -10,7 +11,7 @@ import "package:nexus/widgets/expandable_image.dart";
|
||||||
import "package:nexus/widgets/linkified_text.dart";
|
import "package:nexus/widgets/linkified_text.dart";
|
||||||
import "package:nexus/models/room_summary.dart";
|
import "package:nexus/models/room_summary.dart";
|
||||||
|
|
||||||
class const RoomSummaryDialog(
|
class const RoomSummarySheet(
|
||||||
final RoomSummary summary, {
|
final RoomSummary summary, {
|
||||||
final IList<String>? via,
|
final IList<String>? via,
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -21,10 +22,7 @@ class const RoomSummaryDialog(
|
||||||
final isInvite = summary.membership == .invite;
|
final isInvite = summary.membership == .invite;
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
return AlertDialog(
|
return SheetLayout(
|
||||||
constraints: .loose(.fromWidth(400)),
|
|
||||||
scrollable: true,
|
|
||||||
contentPadding: EdgeInsets.all(24).copyWith(top: 8),
|
|
||||||
title: Row(
|
title: Row(
|
||||||
spacing: 12,
|
spacing: 12,
|
||||||
mainAxisSize: .min,
|
mainAxisSize: .min,
|
||||||
|
|
@ -48,56 +46,6 @@ class const RoomSummaryDialog(
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
content: Column(
|
|
||||||
children: [
|
|
||||||
if (summary.invitedBy case final invitedBy?)
|
|
||||||
ListTile(
|
|
||||||
leading: AvatarOrHash(
|
|
||||||
summary.inviterProfile?.avatarUrl,
|
|
||||||
summary.inviterProfile?.displayName ?? invitedBy.localpart,
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
"${summary.isDirect ? "Direct message from" : "Invited by"} ${summary.inviterProfile?.displayName ?? invitedBy.localpart}",
|
|
||||||
),
|
|
||||||
subtitle: SelectableText(invitedBy),
|
|
||||||
),
|
|
||||||
if (summary.inviteReason case final reason?)
|
|
||||||
ListTile(
|
|
||||||
titleAlignment: ListTileTitleAlignment.titleHeight,
|
|
||||||
leading: const Icon(Icons.format_quote),
|
|
||||||
title: const Text("Invite reason"),
|
|
||||||
subtitle: SelectableText(reason),
|
|
||||||
),
|
|
||||||
// Invite state never includes the member count
|
|
||||||
if (summary.joinedMembers != null || !isInvite)
|
|
||||||
ListTile(
|
|
||||||
title: Text(
|
|
||||||
"${summary.joinedMembers ?? "Unknown number of"} members",
|
|
||||||
),
|
|
||||||
leading: Icon(Icons.people),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text(
|
|
||||||
summary.canonicalAlias == null ? "Room ID" : "Canonical Alias",
|
|
||||||
),
|
|
||||||
subtitle: SelectableText(summary.canonicalAlias ?? summary.roomId),
|
|
||||||
leading: Icon(Icons.numbers),
|
|
||||||
),
|
|
||||||
if (summary.topic != null)
|
|
||||||
ListTile(
|
|
||||||
title: Text("Topic"),
|
|
||||||
subtitle: LinkifiedText(summary.topic!),
|
|
||||||
leading: Icon(Icons.info),
|
|
||||||
),
|
|
||||||
if (summary.encryption != null)
|
|
||||||
ListTile(title: Text("Encrypted"), leading: Icon(Icons.lock)),
|
|
||||||
if (summary.worldReadable == true)
|
|
||||||
ListTile(
|
|
||||||
title: Text("Anyone can read room history"),
|
|
||||||
leading: Icon(Icons.public),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: Navigator.of(context).pop, child: Text("Cancel")),
|
TextButton(onPressed: Navigator.of(context).pop, child: Text("Cancel")),
|
||||||
if (isInvite) ...[
|
if (isInvite) ...[
|
||||||
|
|
@ -146,6 +94,60 @@ class const RoomSummaryDialog(
|
||||||
child: Text("Join"),
|
child: Text("Join"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
if (summary.invitedBy case final invitedBy?)
|
||||||
|
ListTile(
|
||||||
|
leading: AvatarOrHash(
|
||||||
|
summary.inviterProfile?.avatarUrl,
|
||||||
|
summary.inviterProfile?.displayName ?? invitedBy.localpart,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"${summary.isDirect ? "Direct message from" : "Invited by"} ${summary.inviterProfile?.displayName ?? invitedBy.localpart}",
|
||||||
|
),
|
||||||
|
subtitle: SelectableText(invitedBy),
|
||||||
|
),
|
||||||
|
if (summary.inviteReason case final reason?)
|
||||||
|
ListTile(
|
||||||
|
titleAlignment: ListTileTitleAlignment.titleHeight,
|
||||||
|
leading: const Icon(Icons.format_quote),
|
||||||
|
title: const Text("Invite reason"),
|
||||||
|
subtitle: SelectableText(reason),
|
||||||
|
),
|
||||||
|
// Invite state never includes the member count
|
||||||
|
if (summary.joinedMembers != null || !isInvite)
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
"${summary.joinedMembers ?? "Unknown number of"} members",
|
||||||
|
),
|
||||||
|
leading: Icon(Icons.people),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(
|
||||||
|
summary.canonicalAlias == null ? "Room ID" : "Canonical Alias",
|
||||||
|
),
|
||||||
|
subtitle: SelectableText(
|
||||||
|
summary.canonicalAlias ?? summary.roomId,
|
||||||
|
),
|
||||||
|
leading: Icon(Icons.numbers),
|
||||||
|
),
|
||||||
|
if (summary.topic != null)
|
||||||
|
ListTile(
|
||||||
|
title: Text("Topic"),
|
||||||
|
subtitle: LinkifiedText(summary.topic!),
|
||||||
|
leading: Icon(Icons.info),
|
||||||
|
),
|
||||||
|
if (summary.encryption != null)
|
||||||
|
ListTile(title: Text("Encrypted"), leading: Icon(Icons.lock)),
|
||||||
|
if (summary.worldReadable == true)
|
||||||
|
ListTile(
|
||||||
|
title: Text("Anyone can read room history"),
|
||||||
|
leading: Icon(Icons.public),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/helpers/extensions/show_adaptive_sheet.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:nexus/widgets/settings/radio_dialog.dart";
|
import "package:nexus/widgets/settings/radio_sheet.dart";
|
||||||
|
|
||||||
final class const DialogListTile<T>({
|
final class const DialogListTile<T>({
|
||||||
required final T? initialValue,
|
required final T? initialValue,
|
||||||
|
|
@ -26,9 +27,8 @@ final class const DialogListTile<T>({
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
enabled: onChanged != null,
|
enabled: onChanged != null,
|
||||||
onTap: () => showDialog(
|
onTap: () => context.showAdaptiveSheet(
|
||||||
context: context,
|
(context) => RadioSheet<T>(
|
||||||
builder: (context) => RadioDialog<T>(
|
|
||||||
title: title,
|
title: title,
|
||||||
getName: getName,
|
getName: getName,
|
||||||
onChanged: onChanged == null
|
onChanged: onChanged == null
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/widgets/sheet_layout.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
|
|
||||||
final class const RadioDialog<T>({
|
final class const RadioSheet<T>({
|
||||||
required final T? value,
|
required final T? value,
|
||||||
required final String title,
|
required final String title,
|
||||||
required final List<T> options,
|
required final List<T> options,
|
||||||
|
|
@ -12,26 +13,8 @@ final class const RadioDialog<T>({
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final mutValue = useState<T?>(null);
|
final mutValue = useState<T?>(null);
|
||||||
return AlertDialog(
|
return SheetLayout(
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
content: RadioGroup<T>(
|
|
||||||
groupValue: mutValue.value ?? value,
|
|
||||||
onChanged: (value) => mutValue.value = value ?? mutValue.value,
|
|
||||||
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: options
|
|
||||||
.map(
|
|
||||||
(option) => RadioListTile<T>(
|
|
||||||
enabled: onChanged != null,
|
|
||||||
value: option,
|
|
||||||
title: Text(getName(option)),
|
|
||||||
dense: true,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: Navigator.of(context).pop, child: Text("Cancel")),
|
TextButton(onPressed: Navigator.of(context).pop, child: Text("Cancel")),
|
||||||
if (onChanged != null)
|
if (onChanged != null)
|
||||||
|
|
@ -43,6 +26,26 @@ final class const RadioDialog<T>({
|
||||||
child: Text("OK"),
|
child: Text("OK"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
children: [
|
||||||
|
RadioGroup<T>(
|
||||||
|
groupValue: mutValue.value ?? value,
|
||||||
|
onChanged: (value) => mutValue.value = value ?? mutValue.value,
|
||||||
|
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: options
|
||||||
|
.map(
|
||||||
|
(option) => RadioListTile<T>(
|
||||||
|
enabled: onChanged != null,
|
||||||
|
value: option,
|
||||||
|
title: Text(getName(option)),
|
||||||
|
dense: true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
|
||||||
class const SheetLayout({
|
class const SheetLayout({
|
||||||
required final Widget title,
|
final Widget? title,
|
||||||
required final List<Widget> children,
|
required final List<Widget> children,
|
||||||
required final List<Widget> actions,
|
required final List<Widget> actions,
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -16,10 +16,11 @@ class const SheetLayout({
|
||||||
crossAxisAlignment: .stretch,
|
crossAxisAlignment: .stretch,
|
||||||
spacing: 12,
|
spacing: 12,
|
||||||
children: [
|
children: [
|
||||||
DefaultTextStyle(
|
if (title case final title?)
|
||||||
style: Theme.of(context).textTheme.headlineSmall!,
|
DefaultTextStyle(
|
||||||
child: title,
|
style: Theme.of(context).textTheme.headlineSmall!,
|
||||||
),
|
child: title,
|
||||||
|
),
|
||||||
...children,
|
...children,
|
||||||
Row(mainAxisAlignment: .end, spacing: 8, children: actions),
|
Row(mainAxisAlignment: .end, spacing: 8, children: actions),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:collection/collection.dart";
|
import "package:collection/collection.dart";
|
||||||
import "package:material_ui/material_ui.dart";
|
import "package:material_ui/material_ui.dart";
|
||||||
|
import "package:nexus/widgets/sheet_layout.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
import "package:intl/intl.dart";
|
import "package:intl/intl.dart";
|
||||||
|
|
@ -35,54 +36,51 @@ final class const UserBottomSheet(
|
||||||
);
|
);
|
||||||
final client = ref.read(ClientController.provider.notifier);
|
final client = ref.read(ClientController.provider.notifier);
|
||||||
|
|
||||||
void showMembershipDialog(MembershipAction action) => showDialog(
|
void showMembershipDialog(MembershipAction action) =>
|
||||||
context: context,
|
context.showAdaptiveSheet(
|
||||||
builder: (context) => HookBuilder(
|
(context) => HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final actionReasonController = useTextEditingController();
|
final actionReasonController = useTextEditingController();
|
||||||
return AlertDialog(
|
return SheetLayout(
|
||||||
title: Text("${toBeginningOfSentenceCase(action.name)} $userId"),
|
title: Text(
|
||||||
content: Column(
|
"${toBeginningOfSentenceCase(action.name)} $userId",
|
||||||
mainAxisSize: .min,
|
|
||||||
crossAxisAlignment: .start,
|
|
||||||
children: [
|
|
||||||
Text("Are you sure you want to ${action.name} $userId?"),
|
|
||||||
SizedBox(height: 12),
|
|
||||||
TextField(
|
|
||||||
textCapitalization: .sentences,
|
|
||||||
controller: actionReasonController,
|
|
||||||
decoration: .new(
|
|
||||||
labelText: "Reason for ${action.name} (optional)",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
actions: [
|
||||||
),
|
TextButton(
|
||||||
actions: [
|
onPressed: Navigator.of(context).pop,
|
||||||
TextButton(
|
child: Text("Cancel"),
|
||||||
onPressed: Navigator.of(context).pop,
|
),
|
||||||
child: Text("Cancel"),
|
TextButton(
|
||||||
),
|
onPressed: () {
|
||||||
TextButton(
|
Navigator.of(context).pop();
|
||||||
onPressed: () {
|
client
|
||||||
Navigator.of(context).pop();
|
.setMembership(
|
||||||
client
|
.new(
|
||||||
.setMembership(
|
userId: userId,
|
||||||
.new(
|
roomId: roomId!,
|
||||||
userId: userId,
|
action: action,
|
||||||
roomId: roomId!,
|
reason: actionReasonController.text,
|
||||||
action: action,
|
),
|
||||||
reason: actionReasonController.text,
|
)
|
||||||
),
|
.onError(showError);
|
||||||
)
|
},
|
||||||
.onError(showError);
|
child: Text(toBeginningOfSentenceCase(action.name)),
|
||||||
},
|
),
|
||||||
child: Text(toBeginningOfSentenceCase(action.name)),
|
],
|
||||||
),
|
children: [
|
||||||
],
|
Text("Are you sure you want to ${action.name} $userId?"),
|
||||||
);
|
TextField(
|
||||||
},
|
textCapitalization: .sentences,
|
||||||
),
|
controller: actionReasonController,
|
||||||
);
|
decoration: .new(
|
||||||
|
labelText: "Reason for ${action.name} (optional)",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue