loading spaces

This commit is contained in:
Henry Hiles 2025-11-10 21:50:21 -05:00
commit 65028a1231
No known key found for this signature in database
14 changed files with 629 additions and 86 deletions

View file

@ -0,0 +1,32 @@
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:flutter_riverpod/misc.dart";
class ErrorDialog extends ConsumerWidget {
final Object error;
final StackTrace? stackTrace;
final ProviderOrFamily? provider;
const ErrorDialog(this.error, this.stackTrace, {this.provider, super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return AlertDialog(
title: Text("An Error Occurred"),
content: SingleChildScrollView(
child: SelectableText("$error\n\n$stackTrace"),
),
actions: [
if (provider != null)
TextButton(
onPressed: () => ref.invalidate(provider!),
child: const Text("Try Again"),
),
TextButton(
onPressed: () =>
Navigator.of(context).popUntil((route) => route.isFirst),
child: const Text("Go Back"),
),
],
);
}
}

13
lib/widgets/loading.dart Normal file
View file

@ -0,0 +1,13 @@
import "package:flutter/material.dart";
class Loading extends StatelessWidget {
const Loading({super.key});
@override
Widget build(BuildContext context) => const Center(
child: Padding(
padding: EdgeInsets.all(16),
child: CircularProgressIndicator(),
),
);
}

View file

@ -14,6 +14,7 @@ class RoomChat extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final urlRegex = RegExp(r"https?://[^\s\]\(\)]+");
final controller = RoomChatController.provider("1");
final theme = Theme.of(context);
return Chat(
@ -25,7 +26,10 @@ class RoomChat extends HookConsumerWidget {
),
),
builders: Builders(
composerBuilder: (_) => Composer(),
composerBuilder: (_) => Composer(
sendIconColor: theme.colorScheme.primary,
sendOnEnter: true,
),
textMessageBuilder:
(
context,
@ -36,9 +40,7 @@ class RoomChat extends HookConsumerWidget {
}) => FlyerChatTextMessage(
message: message.copyWith(
text: message.text.replaceAllMapped(
RegExp(
r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+",
),
urlRegex,
(match) => "[${match.group(0)}](${match.group(0)})",
),
),
@ -49,26 +51,22 @@ class RoomChat extends HookConsumerWidget {
sentLinksColor: Colors.blue,
receivedLinksColor: Colors.blue,
),
linkPreviewBuilder: (_, message, isSentByMe) {
return LinkPreview(
text: message.text,
backgroundColor: isSentByMe
? theme.colorScheme.inversePrimary
: theme.colorScheme.surfaceContainerLow,
insidePadding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
linkPreviewData: message.linkPreviewData,
onLinkPreviewDataFetched: (linkPreviewData) {
ref
.watch(controller)
.updateMessage(
message,
message.copyWith(linkPreviewData: linkPreviewData),
);
},
// You can still customize the appearance
parentContent: message.text,
);
},
linkPreviewBuilder: (_, message, isSentByMe) => LinkPreview(
text: urlRegex.firstMatch(message.text)?.group(0) ?? "",
backgroundColor: isSentByMe
? theme.colorScheme.inversePrimary
: theme.colorScheme.surfaceContainerLow,
insidePadding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
linkPreviewData: message.linkPreviewData,
onLinkPreviewDataFetched: (linkPreviewData) {
ref
.watch(controller)
.updateMessage(
message,
message.copyWith(linkPreviewData: linkPreviewData),
);
},
),
imageMessageBuilder:
(
_,

View file

@ -1,21 +1,22 @@
import "dart:io";
import "package:color_hash/color_hash.dart";
import "package:flutter/material.dart";
import "package:flutter_hooks/flutter_hooks.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/spaces_controller.dart";
class Sidebar extends HookWidget {
class Sidebar extends HookConsumerWidget {
const Sidebar({super.key});
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final index = useState(0);
return Drawer(
shape: Border(),
child: Row(
children: [
NavigationRail(
scrollable: true,
useIndicator: false,
labelType: NavigationRailLabelType.none,
onDestinationSelected: (value) => index.value = value,
destinations: [
NavigationRailDestination(
@ -28,11 +29,39 @@ class Sidebar extends HookWidget {
label: Text("Messages"),
padding: EdgeInsets.only(top: 12),
),
NavigationRailDestination(
icon: Image.file(File("assets/icon.png"), width: 40),
label: Text("Space 1"),
padding: EdgeInsets.only(top: 12),
),
...ref
.watch(SpacesController.provider)
.when(
loading: () => [],
error: (error, stack) {
debugPrintStack(
label: error.toString(),
stackTrace: stack,
);
throw error;
},
data: (spaces) => spaces.map(
(space) => NavigationRailDestination(
icon: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(12)),
child: SizedBox(
width: 40,
height: 40,
child:
space.avatar ??
ColoredBox(
color: ColorHash(space.roomData.name).color,
child: Center(
child: Text(space.roomData.name[0]),
),
),
),
),
label: Text(space.roomData.name),
padding: EdgeInsets.only(top: 12),
),
),
),
],
selectedIndex: index.value,
),
@ -44,6 +73,7 @@ class Sidebar extends HookWidget {
backgroundColor: Colors.transparent,
),
body: NavigationRail(
scrollable: true,
backgroundColor: Colors.transparent,
extended: true,
destinations: [