Add room details dialog

Fixes #20
This commit is contained in:
Henry Hiles 2026-05-26 15:31:37 -04:00
commit e69f04f6e7
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
7 changed files with 105 additions and 41 deletions

View file

@ -5,6 +5,7 @@
"fluttertagger",
"Gomuks",
"Homeserver",
"Linkified",
"localpart",
"msgtype",
"muks",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -9,10 +9,12 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget {
final Color? backgroundColor;
final double? scrolledUnderElevation;
final IList<Widget> 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,

View file

@ -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,

View file

@ -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,
);
}

View file

@ -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) ...[

View file

@ -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,13 +31,29 @@ 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
onTap: room == null
? null
: ExpandableImage(
room.metadata?.avatar
?.mxcToHttps(
: () => 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!,
@ -46,10 +63,42 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget {
.toString(),
child: AvatarOrHash(
room.metadata?.avatar,
room.metadata?.name ?? "Unnamed Rooms",
height: 24,
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,
),
),
],
),
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,
@ -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,
),
),
],