diff --git a/.vscode/settings.json b/.vscode/settings.json index a0d46c9..068916b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "fluttertagger", "Gomuks", "Homeserver", + "Linkified", "localpart", "msgtype", "muks", diff --git a/assets/reactions.png b/assets/reactions.png deleted file mode 100644 index c413051..0000000 Binary files a/assets/reactions.png and /dev/null differ diff --git a/lib/widgets/appbar.dart b/lib/widgets/appbar.dart index aae6c13..08c717b 100644 --- a/lib/widgets/appbar.dart +++ b/lib/widgets/appbar.dart @@ -9,10 +9,12 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget { final Color? backgroundColor; final double? scrolledUnderElevation; final IList actions; + final VoidCallback? onTap; const Appbar({ super.key, this.title, + this.onTap, this.backgroundColor, this.scrolledUnderElevation, this.leading, @@ -37,11 +39,14 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget { return GestureDetector( onPanStart: (_) => windowManager.startDragging(), child: AppBar( - leading: leading, + leading: InkWell(onTap: onTap, child: leading), backgroundColor: backgroundColor, scrolledUnderElevation: scrolledUnderElevation, actionsPadding: const EdgeInsets.symmetric(horizontal: 8), - title: IgnorePointer(child: title), + title: InkWell( + onTap: onTap, + child: IgnorePointer(child: title), + ), flexibleSpace: GestureDetector(onDoubleTap: maximize), actions: [ ...actions, diff --git a/lib/widgets/composer/composer.dart b/lib/widgets/composer/composer.dart index e5f7319..9f37999 100644 --- a/lib/widgets/composer/composer.dart +++ b/lib/widgets/composer/composer.dart @@ -140,7 +140,7 @@ class Composer extends HookConsumerWidget { query.value = newQuery; }, triggerCharacterAndStyles: {"@": style, "#": style}, - builder: (context, key) => TextFormField( + builder: (context, key) => TextField( maxLines: 12, minLines: 1, autofocus: true, @@ -150,7 +150,7 @@ class Composer extends HookConsumerWidget { ), controller: controller.value, key: key, - onFieldSubmitted: (_) => send(), + onSubmitted: (_) => send(), // Don't defocus on submit onEditingComplete: () {}, textInputAction: TextInputAction.done, diff --git a/lib/widgets/linkified_text.dart b/lib/widgets/linkified_text.dart new file mode 100644 index 0000000..c6f1c11 --- /dev/null +++ b/lib/widgets/linkified_text.dart @@ -0,0 +1,23 @@ +import "package:flutter/material.dart"; +import "package:flutter_linkify/flutter_linkify.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/helpers/launch_helper.dart"; + +class LinkifiedText extends ConsumerWidget { + final String text; + final int? maxLines; + final TextStyle? style; + const LinkifiedText(this.text, {this.maxLines, this.style, super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) => Linkify( + text: text, + maxLines: maxLines, + style: style, + options: LinkifyOptions(humanize: false), + onOpen: (link) => + ref.watch(LaunchHelper.provider).launchUrl(Uri.parse(link.url)), + linkStyle: TextStyle(color: Theme.of(context).colorScheme.primary), + overflow: maxLines == null ? null : TextOverflow.ellipsis, + ); +} diff --git a/lib/widgets/renderers/event.dart b/lib/widgets/renderers/event.dart index 2302156..1ee7806 100644 --- a/lib/widgets/renderers/event.dart +++ b/lib/widgets/renderers/event.dart @@ -11,7 +11,6 @@ import "package:nexus/controllers/event_controller.dart"; import "package:nexus/helpers/extensions/get_headers.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/helpers/extensions/show_context_menu.dart"; -import "package:nexus/helpers/launch_helper.dart"; import "package:nexus/models/content/avatar.dart"; import "package:nexus/models/content/content.dart"; import "package:nexus/models/content/encrypted.dart"; @@ -24,6 +23,7 @@ import "package:nexus/widgets/expandable_image.dart"; import "package:nexus/widgets/html/html.dart"; import "package:nexus/widgets/lazy_loading/message_avatar.dart"; import "package:nexus/widgets/lazy_loading/message_displayname.dart"; +import "package:nexus/widgets/linkified_text.dart"; import "package:nexus/widgets/url_preview.dart"; import "package:nexus/widgets/loading.dart"; import "package:nexus/widgets/players/video.dart"; @@ -33,7 +33,6 @@ import "package:nexus/widgets/renderers/membership.dart"; import "package:nexus/widgets/renderers/generic_event.dart"; import "package:nexus/widgets/file_card.dart"; import "package:timeago/timeago.dart"; -import "package:flutter_linkify/flutter_linkify.dart"; class EventRenderer extends ConsumerWidget { final Event event; @@ -232,24 +231,10 @@ class EventRenderer extends ConsumerWidget { }, ), ) - : Linkify( + : LinkifiedText( + body, style: textStyle, - text: body, maxLines: maxLines, - overflow: maxLines == null - ? null - : TextOverflow.ellipsis, - options: LinkifyOptions( - humanize: false, - ), - onOpen: (link) => ref - .watch(LaunchHelper.provider) - .launchUrl(Uri.parse(link.url)), - linkStyle: TextStyle( - color: Theme.of( - context, - ).colorScheme.primary, - ), ), if (!textOnly) ...[ diff --git a/lib/widgets/room_appbar.dart b/lib/widgets/room_appbar.dart index a77e101..02ba69d 100644 --- a/lib/widgets/room_appbar.dart +++ b/lib/widgets/room_appbar.dart @@ -1,12 +1,13 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; -import "package:nexus/controllers/client_state_controller.dart"; import "package:nexus/controllers/rooms_controller.dart"; import "package:nexus/helpers/extensions/mxc_to_https.dart"; import "package:nexus/widgets/appbar.dart"; import "package:nexus/widgets/avatar_or_hash.dart"; import "package:nexus/widgets/expandable_image.dart"; +import "package:nexus/controllers/client_state_controller.dart"; +import "package:nexus/widgets/linkified_text.dart"; import "package:nexus/widgets/room_menu.dart"; class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { @@ -30,27 +31,75 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { final room = roomId == null ? null : ref.watch(RoomsController.provider.select((value) => value[roomId!])); + return Appbar( - leading: isDesktop - ? room == null - ? null - : ExpandableImage( - room.metadata?.avatar - ?.mxcToHttps( - ref.watch( - ClientStateController.provider.select( - (value) => value!.homeserverUrl!, + onTap: room == null + ? null + : () => showDialog( + context: context, + builder: (context) => Dialog( + constraints: BoxConstraints.loose(Size.fromWidth(400)), + child: Padding( + padding: EdgeInsetsGeometry.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8, + children: [ + Row( + spacing: 12, + mainAxisSize: MainAxisSize.min, + children: [ + if (room.metadata?.avatar != null) + ExpandableImage( + room.metadata!.avatar! + .mxcToHttps( + ref.watch( + ClientStateController.provider.select( + (value) => value!.homeserverUrl!, + ), + ), + ) + .toString(), + child: AvatarOrHash( + room.metadata?.avatar, + room.metadata?.name ?? "Unnamed Room", + height: 64, + fallback: Icon(Icons.numbers), + ), + ), + Expanded( + child: Text( + room.metadata?.name ?? "Unnamed Room", + overflow: TextOverflow.ellipsis, + maxLines: 3, + style: Theme.of(context).textTheme.headlineSmall, ), ), - ) - .toString(), - child: AvatarOrHash( - room.metadata?.avatar, - room.metadata?.name ?? "Unnamed Rooms", - height: 24, - fallback: Icon(Icons.numbers), - ), - ) + ], + ), + if (room.metadata?.topic?.isNotEmpty == true) + LinkifiedText( + room.metadata!.topic!, + style: Theme.of(context).textTheme.bodyLarge + ?.copyWith( + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + ), + ), + leading: isDesktop && room != null + ? AvatarOrHash( + room.metadata?.avatar, + room.metadata?.name ?? "Unnamed Room", + height: 24, + fallback: Icon(Icons.numbers), + ) : DrawerButton(onPressed: () => onOpenDrawer(context)), scrolledUnderElevation: 0, title: room == null @@ -70,6 +119,7 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget { overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.labelMedium?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, + decoration: TextDecoration.underline, ), ), ],