Refactors

This commit is contained in:
Henry Hiles 2025-11-28 18:24:27 -05:00
commit 5dc8fe14bd
No known key found for this signature in database
18 changed files with 126 additions and 95 deletions

View file

@ -1,6 +1,6 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/spaces_controller.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/helpers/extensions/get_full_room.dart";
import "package:nexus/models/full_room.dart";
class CurrentRoomController extends AsyncNotifier<FullRoom?> {

View file

@ -1,7 +1,7 @@
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/current_room_controller.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/helpers/extensions/to_message.dart";
class MessageController extends AsyncNotifier<TextMessage?> {
final String id;

View file

@ -4,7 +4,8 @@ import "package:flutter_chat_core/flutter_chat_core.dart" as chat;
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:matrix/matrix.dart";
import "package:nexus/controllers/events_controller.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/helpers/extensions/to_message.dart";
import "package:nexus/helpers/extensions/to_messages.dart";
class RoomChatController extends AsyncNotifier<ChatController> {
final Room room;
@ -32,6 +33,7 @@ class RoomChatController extends AsyncNotifier<ChatController> {
}
}).cancel,
);
return InMemoryChatController(
messages: await response.chunk.toMessages(room),
);

View file

@ -3,7 +3,7 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_controller.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/helpers/extensions/get_full_room.dart";
import "package:nexus/models/space.dart";
class SpacesController extends AsyncNotifier<IList<Space>> {

View file

@ -0,0 +1,17 @@
import "package:flutter/widgets.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/widgets/error_dialog.dart";
import "package:nexus/widgets/loading.dart";
extension BetterWhen<T> on AsyncValue<T> {
Widget betterWhen({
required Widget Function(T value) data,
Widget Function() loading = Loading.new,
bool skipLoadingOnRefresh = false,
}) => when(
data: data,
error: (error, stackTrace) => ErrorDialog(error, stackTrace),
loading: loading,
skipLoadingOnRefresh: skipLoadingOnRefresh,
);
}

View file

@ -0,0 +1,13 @@
import "package:matrix/matrix.dart";
import "package:nexus/models/full_room.dart";
extension GetFullRoom on Room {
Future<FullRoom> get fullRoom async {
await loadHeroUsers();
return FullRoom(
roomData: this,
title: getLocalizedDisplayname(),
avatar: await avatar?.getThumbnailUri(client, width: 24, height: 24),
);
}
}

View file

@ -0,0 +1,5 @@
import "package:matrix/matrix.dart";
extension GetHeaders on Client {
Map<String, String> get headers => {"authorization": "Bearer $accessToken"};
}

View file

@ -1,38 +1,5 @@
import "package:flutter/material.dart";
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:matrix/matrix.dart";
import "package:nexus/models/full_room.dart";
import "package:nexus/widgets/error_dialog.dart";
import "package:nexus/widgets/loading.dart";
extension BetterWhen<T> on AsyncValue<T> {
Widget betterWhen({
required Widget Function(T value) data,
Widget Function() loading = Loading.new,
bool skipLoadingOnRefresh = false,
}) => when(
data: data,
error: (error, stackTrace) => ErrorDialog(error, stackTrace),
loading: loading,
skipLoadingOnRefresh: skipLoadingOnRefresh,
);
}
extension GetFullRoom on Room {
Future<FullRoom> get fullRoom async {
await loadHeroUsers();
return FullRoom(
roomData: this,
title: getLocalizedDisplayname(),
avatar: await avatar?.getThumbnailUri(client, width: 24, height: 24),
);
}
}
extension GetHeaders on Client {
Map<String, String> get headers => {"authorization": "Bearer $accessToken"};
}
extension ToMessage on Event {
Future<Message?> toMessage({bool mustBeText = false}) async {
@ -129,28 +96,3 @@ extension ToMessage on Event {
};
}
}
extension ToTheme on ColorScheme {
ThemeData get theme => ThemeData.from(colorScheme: this).copyWith(
cardTheme: CardThemeData(color: primaryContainer),
appBarTheme: AppBarTheme(
titleSpacing: 0,
backgroundColor: surfaceContainerLow,
),
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
),
);
}
extension ToMessages on List<MatrixEvent> {
Future<List<Message>> toMessages(Room room) async {
final messages = await Future.wait(
map((event) => Event.fromMatrixEvent(event, room).toMessage()),
);
return {
for (var msg in messages.nonNulls.toList().reversed.toList()) msg.id: msg,
}.values.toList();
}
}

View file

@ -0,0 +1,15 @@
import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:matrix/matrix.dart";
import "package:nexus/helpers/extensions/to_message.dart";
extension ToMessages on List<MatrixEvent> {
Future<List<Message>> toMessages(Room room) async {
final messages = await Future.wait(
map((event) => Event.fromMatrixEvent(event, room).toMessage()),
);
return {
for (var msg in messages.nonNulls.toList().reversed.toList()) msg.id: msg,
}.values.toList();
}
}

View file

@ -0,0 +1,14 @@
import "package:flutter/material.dart";
extension ToTheme on ColorScheme {
ThemeData get theme => ThemeData.from(colorScheme: this).copyWith(
cardTheme: CardThemeData(color: primaryContainer),
appBarTheme: AppBarTheme(
titleSpacing: 0,
backgroundColor: surfaceContainerLow,
),
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
),
);
}

View file

@ -1,6 +1,7 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/client_controller.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/to_theme.dart";
import "package:nexus/pages/chat_page.dart";
import "package:nexus/pages/login_page.dart";
import "package:window_manager/window_manager.dart";

View file

@ -3,7 +3,8 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:matrix/matrix.dart";
import "package:nexus/controllers/avatar_controller.dart";
import "package:nexus/controllers/members_controller.dart";
import "package:nexus/helpers/extension_helper.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";
class MemberList extends ConsumerWidget {

View file

@ -1,5 +1,5 @@
import "package:flutter/material.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/helpers/extensions/get_headers.dart";
import "package:nexus/models/full_room.dart";
import "package:nexus/widgets/appbar.dart";
import "package:nexus/widgets/avatar_or_hash.dart";

View file

@ -12,7 +12,8 @@ import "package:flyer_chat_text_message/flyer_chat_text_message.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:nexus/controllers/current_room_controller.dart";
import "package:nexus/controllers/room_chat_controller.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/get_headers.dart";
import "package:nexus/helpers/launch_helper.dart";
import "package:nexus/widgets/chat_page/chat_box.dart";
import "package:nexus/widgets/chat_page/code_block.dart";
@ -36,20 +37,34 @@ class RoomChat extends HookConsumerWidget {
required BuildContext context,
required Offset globalPosition,
required VoidCallback onTap,
}) => showMenu(
context: context,
position: RelativeRect.fromRect(
Rect.fromPoints(globalPosition, globalPosition),
Offset.zero & (context.findRenderObject() as RenderBox).size,
),
color: Theme.of(context).colorScheme.surfaceContainerHighest,
items: [
PopupMenuItem(
onTap: onTap,
child: ListTile(leading: Icon(Icons.reply), title: Text("Reply")),
}) {
final overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
showMenu(
context: context,
position: RelativeRect.fromLTRB(
globalPosition.dx,
globalPosition.dy,
overlay.size.width - globalPosition.dx,
overlay.size.height - globalPosition.dy,
),
],
);
color: Theme.of(context).colorScheme.surfaceContainerHighest,
items: [
PopupMenuItem(
onTap: onTap,
child: ListTile(leading: Icon(Icons.reply), title: Text("Reply")),
),
PopupMenuItem(
onTap: onTap,
child: ListTile(leading: Icon(Icons.edit), title: Text("Edit")),
),
PopupMenuItem(
onTap: onTap,
child: ListTile(leading: Icon(Icons.delete), title: Text("Delete")),
),
],
);
}
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -143,14 +158,15 @@ class RoomChat extends HookConsumerWidget {
MessageGroupStatus? groupStatus,
}) => FlyerChatTextMessage(
customWidget: HtmlWidget(
message.metadata?["formatted"].replaceAllMapped(
RegExp(
r'(?<!href="|">)(https?:\/\/[^\s<]+)',
caseSensitive: false,
),
(m) =>
"<a href=\"${m.group(0)!}\">${m.group(0)!}</a>",
) +
message.metadata?["formatted"]
.replaceAllMapped(
RegExp(
regexLink,
caseSensitive: false,
),
(m) =>
"<a href=\"${m.group(0)!}\">${m.group(0)!}</a>",
) +
((message.editedAt != null)
? "<sub edited>(edited)</sub>"
: ""),

View file

@ -5,7 +5,8 @@ import "package:nexus/controllers/current_room_controller.dart";
import "package:nexus/controllers/selected_room_controller.dart";
import "package:nexus/controllers/selected_space_controller.dart";
import "package:nexus/controllers/spaces_controller.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/helpers/extensions/better_when.dart";
import "package:nexus/helpers/extensions/get_headers.dart";
import "package:nexus/pages/settings_page.dart";
import "package:nexus/widgets/avatar_or_hash.dart";

View file

@ -4,7 +4,7 @@ import "package:flutter_chat_core/flutter_chat_core.dart";
import "package:flutter_chat_ui/flutter_chat_ui.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/message_controller.dart";
import "package:nexus/helpers/extension_helper.dart";
import "package:nexus/helpers/extensions/better_when.dart";
class TopWidget extends ConsumerWidget {
final Message message;

View file

@ -459,7 +459,7 @@ packages:
description:
path: "packages/flutter_chat_ui"
ref: HEAD
resolved-ref: f6718923519db812762ff27eb402f70076d8676c
resolved-ref: bedc5d90130ad8a15b13f34f67b5b371e82cde7d
url: "https://github.com/Henry-Hiles/flutter_chat_ui"
source: git
version: "2.9.1"
@ -482,10 +482,11 @@ packages:
flutter_link_previewer:
dependency: "direct main"
description:
name: flutter_link_previewer
sha256: "62520ee224515f826dd40e4ccbb95e6d65ac060fbcf94d4620ce840ee1a15b83"
url: "https://pub.dev"
source: hosted
path: "packages/flutter_link_previewer"
ref: HEAD
resolved-ref: bedc5d90130ad8a15b13f34f67b5b371e82cde7d
url: "https://github.com/Henry-Hiles/flutter_chat_ui"
source: git
version: "4.1.2"
flutter_lints:
dependency: "direct dev"

View file

@ -50,7 +50,10 @@ dependencies:
git:
url: https://github.com/Henry-Hiles/flutter_chat_ui
path: packages/flutter_chat_ui
flutter_link_previewer: ^4.1.2
flutter_link_previewer:
git:
url: https://github.com/Henry-Hiles/flutter_chat_ui
path: packages/flutter_link_previewer
matrix: ^3.0.2
sqflite_common_ffi: ^2.3.6
color_hash: ^1.0.1