This commit is contained in:
Henry Hiles 2025-12-16 18:40:55 -05:00
commit 5d4ca4ffe2
No known key found for this signature in database
2 changed files with 227 additions and 104 deletions

View file

@ -6,6 +6,8 @@ import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/scheme_to_theme.dart"; import "package:nexus/helpers/extensions/scheme_to_theme.dart";
import "package:nexus/pages/chat_page.dart"; import "package:nexus/pages/chat_page.dart";
import "package:nexus/pages/login_page.dart"; import "package:nexus/pages/login_page.dart";
import "package:nexus/pages/settings_page.dart";
import "package:nexus/widgets/appbar.dart";
import "package:nexus/widgets/error_dialog.dart"; import "package:nexus/widgets/error_dialog.dart";
import "package:window_manager/window_manager.dart"; import "package:window_manager/window_manager.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
@ -59,7 +61,13 @@ void main() async {
setWindowMinSize(const Size.square(500)); setWindowMinSize(const Size.square(500));
runApp( runApp(
ProviderScope(observers: [if (kDebugMode) Logger()], child: const App()), ProviderScope(
observers: [
// Change false to true if you want debug information on provider reloads
if (false && kDebugMode) Logger(),
],
child: const App(),
),
); );
} }
@ -81,16 +89,43 @@ class App extends ConsumerWidget {
brightness: Brightness.dark, brightness: Brightness.dark,
)) ))
.theme, .theme,
home: ref home: Builder(
.watch(SharedPrefsController.provider) builder: (context) => ref
.betterWhen( .watch(SharedPrefsController.provider)
data: (_) => ref .betterWhen(
.watch(ClientController.provider) data: (_) => ref
.betterWhen( .watch(ClientController.provider)
data: (client) => .betterWhen(
client.accessToken == null ? LoginPage() : ChatPage(), data: (client) =>
), client.accessToken == null ? LoginPage() : ChatPage(),
), loading: () => Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 16,
children: [
Text(
"Syncing...",
style: Theme.of(context).textTheme.headlineMedium,
),
CircularProgressIndicator(),
],
),
),
appBar: Appbar(
actions: [
IconButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(builder: (_) => SettingsPage()),
),
icon: Icon(Icons.settings),
),
],
),
),
),
),
),
), ),
); );
} }

View file

@ -2,11 +2,18 @@ import "dart:io";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter/services.dart"; import "package:flutter/services.dart";
import "package:flutter_chat_core/flutter_chat_core.dart"; import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_chat_ui/flutter_chat_ui.dart";
import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_hooks/flutter_hooks.dart";
import "package:fluttertagger/fluttertagger.dart"; import "package:fluttertagger/fluttertagger.dart";
import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:matrix/matrix.dart"; import "package:matrix/matrix.dart";
import "package:nexus/controllers/avatar_controller.dart";
import "package:nexus/controllers/members_controller.dart";
import "package:nexus/controllers/room_chat_controller.dart"; import "package:nexus/controllers/room_chat_controller.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/get_headers.dart";
import "package:nexus/widgets/avatar_or_hash.dart";
import "package:nexus/widgets/loading.dart";
class ChatBox extends HookConsumerWidget { class ChatBox extends HookConsumerWidget {
final Message? replyToMessage; final Message? replyToMessage;
@ -23,6 +30,8 @@ class ChatBox extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context); final theme = Theme.of(context);
final controller = useRef(FlutterTaggerController()); final controller = useRef(FlutterTaggerController());
final triggerCharacter = useState("");
final query = useState("");
Future<void> send() => ref Future<void> send() => ref
.watch(RoomChatController.provider(room).notifier) .watch(RoomChatController.provider(room).notifier)
@ -54,105 +63,184 @@ class ChatBox extends HookConsumerWidget {
padding: EdgeInsetsGeometry.all(12), padding: EdgeInsetsGeometry.all(12),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(12)), borderRadius: BorderRadius.all(Radius.circular(12)),
child: Container( child: Column(
color: theme.colorScheme.surfaceContainerHighest, children: [
padding: EdgeInsets.symmetric(horizontal: 8), if (replyToMessage != null)
child: // TODO: This doesn't work? Container(
room.canSendDefaultMessages color: theme.colorScheme.surfaceContainerHigh,
? Row( padding: EdgeInsets.symmetric(horizontal: 8),
child: Row(
spacing: 8, spacing: 8,
children: [ children: [
PopupMenuButton( SizedBox(width: 4),
itemBuilder: (context) => [], Avatar(
icon: Icon(Icons.add), userId: replyToMessage!.authorId,
headers: room.client.headers,
size: 16,
), ),
Expanded( Text(
child: FlutterTagger( replyToMessage!.metadata?["displayName"] ??
overlay: SizedBox(), replyToMessage!.authorId,
controller: controller.value, style: theme.textTheme.labelMedium?.copyWith(
onSearch: (query, triggerCharacter) { fontWeight: FontWeight.bold,
triggerCharacter == "#";
if (controller.value.tags.isEmpty) {
controller.value.addTag(
id: "id",
name: "name",
); // TODO: RM
}
},
triggerCharacterAndStyles: {
"@": style,
"#": style,
":": style,
},
builder: (context, key) => TextFormField(
maxLines: 12,
minLines: 1,
decoration: InputDecoration(
hintText: "Your message here...",
border: InputBorder.none,
),
controller: controller.value,
key: key,
autofocus: true,
focusNode: node,
),
), ),
), ),
IconButton(onPressed: send, icon: Icon(Icons.send)), Expanded(
child: (replyToMessage is TextMessage)
? Text(
(replyToMessage as TextMessage).text,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.labelMedium,
maxLines: 1,
)
: SizedBox(),
),
IconButton(
onPressed: onDismiss,
icon: Icon(Icons.close),
iconSize: 20,
),
], ],
) ),
: Text("You don't have permission to send messages here..."), ),
// Composer( Container(
// textEditingController: controller.value, color: theme.colorScheme.surfaceContainerHighest,
// key: key, padding: EdgeInsets.symmetric(horizontal: 8),
// sigmaY: 0, child: room.canSendDefaultMessages
// sendIconColor: theme.colorScheme.primary, ? Row(
// sendOnEnter: true, spacing: 8,
// topWidget: replyToMessage == null children: [
// ? null PopupMenuButton(
// : ColoredBox( itemBuilder: (context) => [],
// color: theme.colorScheme.surfaceContainer, icon: Icon(Icons.add),
// child: Padding( ),
// padding: EdgeInsets.symmetric( Expanded(
// horizontal: 16, child: FlutterTagger(
// vertical: 4, triggerStrategy: TriggerStrategy.eager,
// ), overlay: Padding(
// child: Row( padding: EdgeInsets.all(8),
// spacing: 8, child: ClipRRect(
// children: [ borderRadius: BorderRadius.all(
// Avatar( Radius.circular(12),
// userId: replyToMessage!.authorId, ),
// headers: room.client.headers, child: Container(
// size: 16, color:
// ), theme.colorScheme.surfaceContainerHigh,
// Text( padding: EdgeInsets.all(8),
// replyToMessage!.metadata?["displayName"] ?? child: switch (triggerCharacter.value) {
// replyToMessage!.authorId, "@" =>
// style: theme.textTheme.labelMedium?.copyWith( ref
// fontWeight: FontWeight.bold, .watch(
// ), MembersController.provider(room),
// ), )
// Expanded( .betterWhen(
// child: (replyToMessage is TextMessage) data: (members) => ListView(
// ? Text( children:
// (replyToMessage as TextMessage).text, (query.value.isEmpty
// overflow: TextOverflow.ellipsis, ? members
// style: theme.textTheme.labelMedium, : members.where(
// maxLines: 1, (member) =>
// ) member
// : SizedBox(), .senderId
// ), .contains(
// IconButton( query
// onPressed: onDismiss, .value,
// icon: Icon(Icons.close), ) ||
// iconSize: 20, (member.content["displayname"]
// ), as String?)
// ], ?.contains(
// ), query.value,
// ), ) ==
// ), true,
// autofocus: true, ))
// ), .map(
(member) => ListTile(
leading: AvatarOrHash(
ref
.watch(
AvatarController.provider(
member
.content["avatar_url"]
.toString(),
),
)
.whenOrNull(
data:
(
data,
) =>
data,
),
member
.content["displayname"]
.toString(),
headers: room
.client
.headers,
),
title: Text(
member.content["displayname"]
as String? ??
member
.senderId,
),
onTap: () => controller
.value
.addTag(
id: "member",
name: member
.senderId
.substring(
1,
)
.split(
":",
)
.first,
),
),
)
.toList(),
),
),
"#" => Text("Todo"),
_ => Loading(),
},
),
),
),
controller: controller.value,
onSearch: (newQuery, newTriggerCharacter) {
triggerCharacter.value = newTriggerCharacter;
query.value = newQuery;
},
triggerCharacterAndStyles: {
"@": style,
"#": style,
":": style,
},
builder: (context, key) => TextFormField(
maxLines: 12,
minLines: 1,
decoration: InputDecoration(
hintText: "Your message here...",
border: InputBorder.none,
),
controller: controller.value,
key: key,
autofocus: true,
focusNode: node,
),
),
),
IconButton(onPressed: send, icon: Icon(Icons.send)),
],
)
: Text(
"You don't have permission to send messages here...",
),
),
],
), ),
), ),
), ),