parent
4848840538
commit
e69f04f6e7
7 changed files with 105 additions and 41 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -5,6 +5,7 @@
|
||||||
"fluttertagger",
|
"fluttertagger",
|
||||||
"Gomuks",
|
"Gomuks",
|
||||||
"Homeserver",
|
"Homeserver",
|
||||||
|
"Linkified",
|
||||||
"localpart",
|
"localpart",
|
||||||
"msgtype",
|
"msgtype",
|
||||||
"muks",
|
"muks",
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
|
|
@ -9,10 +9,12 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
final double? scrolledUnderElevation;
|
final double? scrolledUnderElevation;
|
||||||
final IList<Widget> actions;
|
final IList<Widget> actions;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
const Appbar({
|
const Appbar({
|
||||||
super.key,
|
super.key,
|
||||||
this.title,
|
this.title,
|
||||||
|
this.onTap,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
this.scrolledUnderElevation,
|
this.scrolledUnderElevation,
|
||||||
this.leading,
|
this.leading,
|
||||||
|
|
@ -37,11 +39,14 @@ class Appbar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onPanStart: (_) => windowManager.startDragging(),
|
onPanStart: (_) => windowManager.startDragging(),
|
||||||
child: AppBar(
|
child: AppBar(
|
||||||
leading: leading,
|
leading: InkWell(onTap: onTap, child: leading),
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
scrolledUnderElevation: scrolledUnderElevation,
|
scrolledUnderElevation: scrolledUnderElevation,
|
||||||
actionsPadding: const EdgeInsets.symmetric(horizontal: 8),
|
actionsPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
title: IgnorePointer(child: title),
|
title: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: IgnorePointer(child: title),
|
||||||
|
),
|
||||||
flexibleSpace: GestureDetector(onDoubleTap: maximize),
|
flexibleSpace: GestureDetector(onDoubleTap: maximize),
|
||||||
actions: [
|
actions: [
|
||||||
...actions,
|
...actions,
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ class Composer extends HookConsumerWidget {
|
||||||
query.value = newQuery;
|
query.value = newQuery;
|
||||||
},
|
},
|
||||||
triggerCharacterAndStyles: {"@": style, "#": style},
|
triggerCharacterAndStyles: {"@": style, "#": style},
|
||||||
builder: (context, key) => TextFormField(
|
builder: (context, key) => TextField(
|
||||||
maxLines: 12,
|
maxLines: 12,
|
||||||
minLines: 1,
|
minLines: 1,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
|
|
@ -150,7 +150,7 @@ class Composer extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
controller: controller.value,
|
controller: controller.value,
|
||||||
key: key,
|
key: key,
|
||||||
onFieldSubmitted: (_) => send(),
|
onSubmitted: (_) => send(),
|
||||||
// Don't defocus on submit
|
// Don't defocus on submit
|
||||||
onEditingComplete: () {},
|
onEditingComplete: () {},
|
||||||
textInputAction: TextInputAction.done,
|
textInputAction: TextInputAction.done,
|
||||||
|
|
|
||||||
23
lib/widgets/linkified_text.dart
Normal file
23
lib/widgets/linkified_text.dart
Normal 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,6 @@ import "package:nexus/controllers/event_controller.dart";
|
||||||
import "package:nexus/helpers/extensions/get_headers.dart";
|
import "package:nexus/helpers/extensions/get_headers.dart";
|
||||||
import "package:nexus/helpers/extensions/mxc_to_https.dart";
|
import "package:nexus/helpers/extensions/mxc_to_https.dart";
|
||||||
import "package:nexus/helpers/extensions/show_context_menu.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/avatar.dart";
|
||||||
import "package:nexus/models/content/content.dart";
|
import "package:nexus/models/content/content.dart";
|
||||||
import "package:nexus/models/content/encrypted.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/html/html.dart";
|
||||||
import "package:nexus/widgets/lazy_loading/message_avatar.dart";
|
import "package:nexus/widgets/lazy_loading/message_avatar.dart";
|
||||||
import "package:nexus/widgets/lazy_loading/message_displayname.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/url_preview.dart";
|
||||||
import "package:nexus/widgets/loading.dart";
|
import "package:nexus/widgets/loading.dart";
|
||||||
import "package:nexus/widgets/players/video.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/renderers/generic_event.dart";
|
||||||
import "package:nexus/widgets/file_card.dart";
|
import "package:nexus/widgets/file_card.dart";
|
||||||
import "package:timeago/timeago.dart";
|
import "package:timeago/timeago.dart";
|
||||||
import "package:flutter_linkify/flutter_linkify.dart";
|
|
||||||
|
|
||||||
class EventRenderer extends ConsumerWidget {
|
class EventRenderer extends ConsumerWidget {
|
||||||
final Event event;
|
final Event event;
|
||||||
|
|
@ -232,24 +231,10 @@ class EventRenderer extends ConsumerWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Linkify(
|
: LinkifiedText(
|
||||||
|
body,
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
text: body,
|
|
||||||
maxLines: maxLines,
|
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) ...[
|
if (!textOnly) ...[
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.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/controllers/rooms_controller.dart";
|
||||||
import "package:nexus/helpers/extensions/mxc_to_https.dart";
|
import "package:nexus/helpers/extensions/mxc_to_https.dart";
|
||||||
import "package:nexus/widgets/appbar.dart";
|
import "package:nexus/widgets/appbar.dart";
|
||||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||||
import "package:nexus/widgets/expandable_image.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";
|
import "package:nexus/widgets/room_menu.dart";
|
||||||
|
|
||||||
class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget {
|
class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget {
|
||||||
|
|
@ -30,13 +31,29 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget {
|
||||||
final room = roomId == null
|
final room = roomId == null
|
||||||
? null
|
? null
|
||||||
: ref.watch(RoomsController.provider.select((value) => value[roomId!]));
|
: ref.watch(RoomsController.provider.select((value) => value[roomId!]));
|
||||||
|
|
||||||
return Appbar(
|
return Appbar(
|
||||||
leading: isDesktop
|
onTap: room == null
|
||||||
? room == null
|
|
||||||
? null
|
? null
|
||||||
: ExpandableImage(
|
: () => showDialog(
|
||||||
room.metadata?.avatar
|
context: context,
|
||||||
?.mxcToHttps(
|
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(
|
ref.watch(
|
||||||
ClientStateController.provider.select(
|
ClientStateController.provider.select(
|
||||||
(value) => value!.homeserverUrl!,
|
(value) => value!.homeserverUrl!,
|
||||||
|
|
@ -46,10 +63,42 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget {
|
||||||
.toString(),
|
.toString(),
|
||||||
child: AvatarOrHash(
|
child: AvatarOrHash(
|
||||||
room.metadata?.avatar,
|
room.metadata?.avatar,
|
||||||
room.metadata?.name ?? "Unnamed Rooms",
|
room.metadata?.name ?? "Unnamed Room",
|
||||||
height: 24,
|
height: 64,
|
||||||
fallback: Icon(Icons.numbers),
|
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)),
|
: DrawerButton(onPressed: () => onOpenDrawer(context)),
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
|
|
@ -70,6 +119,7 @@ class RoomAppbar extends ConsumerWidget implements PreferredSizeWidget {
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: Theme.of(context).textTheme.labelMedium?.copyWith(
|
style: Theme.of(context).textTheme.labelMedium?.copyWith(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue