lots of stuff
This commit is contained in:
parent
8bc010cfc7
commit
ba9e99a951
19 changed files with 608 additions and 360 deletions
38
lib/widgets/avatar_or_hash.dart
Normal file
38
lib/widgets/avatar_or_hash.dart
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import "package:color_hash/color_hash.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
|
||||
class AvatarOrHash extends StatelessWidget {
|
||||
final Uri? avatar;
|
||||
final String title;
|
||||
final Widget? fallback;
|
||||
final Map<String, String> headers;
|
||||
const AvatarOrHash(
|
||||
this.avatar,
|
||||
this.title, {
|
||||
this.fallback,
|
||||
required this.headers,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final box = ColoredBox(
|
||||
color: ColorHash(title).color,
|
||||
child: Center(child: Text(title[0])),
|
||||
);
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: avatar == null
|
||||
? fallback ?? box
|
||||
: Image.network(
|
||||
avatar.toString(),
|
||||
headers: headers,
|
||||
errorBuilder: (_, _, _) => box,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
64
lib/widgets/chat_box.dart
Normal file
64
lib/widgets/chat_box.dart
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
import "package:flutter_chat_ui/flutter_chat_ui.dart";
|
||||
|
||||
class ChatBox extends StatelessWidget {
|
||||
final Message? replyToMessage;
|
||||
final VoidCallback onDismiss;
|
||||
final Map<String, String> headers;
|
||||
const ChatBox({
|
||||
required this.replyToMessage,
|
||||
required this.onDismiss,
|
||||
required this.headers,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Composer(
|
||||
sigmaX: 0,
|
||||
sigmaY: 0,
|
||||
sendIconColor: Theme.of(context).colorScheme.primary,
|
||||
sendOnEnter: true,
|
||||
topWidget: replyToMessage == null
|
||||
? null
|
||||
: ColoredBox(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Avatar(
|
||||
userId: replyToMessage!.authorId,
|
||||
headers: headers,
|
||||
size: 16,
|
||||
),
|
||||
Text(
|
||||
replyToMessage!.metadata?["displayName"] ??
|
||||
replyToMessage!.authorId,
|
||||
style: Theme.of(context).textTheme.labelMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: (replyToMessage is TextMessage)
|
||||
? Text(
|
||||
(replyToMessage as TextMessage).text,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
maxLines: 1,
|
||||
)
|
||||
: SizedBox(),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: onDismiss,
|
||||
icon: Icon(Icons.close),
|
||||
iconSize: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
autofocus: true,
|
||||
);
|
||||
}
|
||||
53
lib/widgets/member_list.dart
Normal file
53
lib/widgets/member_list.dart
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import "package:flutter/material.dart";
|
||||
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/widgets/avatar_or_hash.dart";
|
||||
|
||||
class MemberList extends ConsumerWidget {
|
||||
final Room room;
|
||||
const MemberList(this.room, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) => ColoredBox(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerLow,
|
||||
child: SizedBox(
|
||||
width: 240,
|
||||
child: ref
|
||||
.watch(MembersController.provider(room))
|
||||
.betterWhen(
|
||||
data: (members) => ListView(
|
||||
children: [
|
||||
...members
|
||||
.where(
|
||||
(membership) =>
|
||||
membership.content["membership"] ==
|
||||
Membership.join.name,
|
||||
)
|
||||
.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"].toString(),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
66
lib/widgets/room_appbar.dart
Normal file
66
lib/widgets/room_appbar.dart
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import "dart:io";
|
||||
|
||||
import "package:flutter/material.dart";
|
||||
import "package:nexus/helpers/extension_helper.dart";
|
||||
import "package:nexus/models/full_room.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
|
||||
class RoomAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
final bool isDesktop;
|
||||
final FullRoom room;
|
||||
final VoidCallback onOpenMemberList;
|
||||
final VoidCallback onOpenDrawer;
|
||||
const RoomAppbar(
|
||||
this.room, {
|
||||
required this.isDesktop,
|
||||
required this.onOpenMemberList,
|
||||
required this.onOpenDrawer,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Size get preferredSize => Size.fromHeight(AppBar().preferredSize.height + 16);
|
||||
|
||||
@override
|
||||
AppBar build(BuildContext context) => AppBar(
|
||||
bottom: PreferredSize(
|
||||
preferredSize: Size.zero, // Does this even matter??
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8).copyWith(top: 0),
|
||||
child: Text(
|
||||
room.roomData.topic,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.labelMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
leading: isDesktop ? null : DrawerButton(onPressed: onOpenDrawer),
|
||||
actionsPadding: EdgeInsets.symmetric(horizontal: 8),
|
||||
title: Row(
|
||||
children: [
|
||||
AvatarOrHash(
|
||||
room.avatar,
|
||||
room.title,
|
||||
fallback: Icon(Icons.numbers),
|
||||
headers: room.roomData.client.headers,
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(child: Text(room.title, overflow: TextOverflow.ellipsis)),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
IconButton(onPressed: onOpenMemberList, icon: Icon(Icons.people)),
|
||||
if (!(Platform.isAndroid || Platform.isIOS))
|
||||
IconButton(onPressed: () => exit(0), icon: Icon(Icons.close)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
import "package:color_hash/color_hash.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
|
||||
class RoomAvatar extends StatelessWidget {
|
||||
final Widget? avatar;
|
||||
final String title;
|
||||
final Widget? fallback;
|
||||
const RoomAvatar(this.avatar, this.title, {this.fallback, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child:
|
||||
avatar ??
|
||||
fallback ??
|
||||
ColoredBox(
|
||||
color: ColorHash(title).color,
|
||||
child: Center(child: Text(title[0])),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
import "dart:io";
|
||||
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_chat_core/flutter_chat_core.dart";
|
||||
|
|
@ -15,8 +13,10 @@ 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/launch_helper.dart";
|
||||
import "package:nexus/widgets/chat_box.dart";
|
||||
import "package:nexus/widgets/member_list.dart";
|
||||
import "package:nexus/widgets/room_appbar.dart";
|
||||
import "package:nexus/widgets/top_widget.dart";
|
||||
import "package:nexus/widgets/room_avatar.dart";
|
||||
|
||||
class RoomChat extends HookConsumerWidget {
|
||||
final bool isDesktop;
|
||||
|
|
@ -33,12 +33,18 @@ class RoomChat extends HookConsumerWidget {
|
|||
Offset.zero & (context.findRenderObject() as RenderBox).size,
|
||||
),
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
items: [PopupMenuItem(onTap: onTap, child: Text("Reply"))],
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
onTap: onTap,
|
||||
child: ListTile(leading: Icon(Icons.reply), title: Text("Reply")),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final replyToMessage = useState<Message?>(null);
|
||||
final memberListOpened = useState<bool>(isDesktop);
|
||||
final urlRegex = RegExp(r"https?://[^\s\]\(\)]+");
|
||||
final theme = Theme.of(context);
|
||||
return ref
|
||||
|
|
@ -48,250 +54,200 @@ class RoomChat extends HookConsumerWidget {
|
|||
final controllerProvider = RoomChatController.provider(
|
||||
room.roomData,
|
||||
);
|
||||
final headers = {
|
||||
"authorization": "Bearer ${room.roomData.client.accessToken}",
|
||||
};
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: isDesktop
|
||||
? null
|
||||
: DrawerButton(onPressed: Scaffold.of(context).openDrawer),
|
||||
actionsPadding: EdgeInsets.symmetric(horizontal: 8),
|
||||
title: Row(
|
||||
children: [
|
||||
RoomAvatar(
|
||||
room.avatar,
|
||||
room.title,
|
||||
fallback: Icon(Icons.numbers),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(room.title, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
if (!(Platform.isAndroid || Platform.isIOS))
|
||||
IconButton(
|
||||
onPressed: () => exit(0),
|
||||
icon: Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
appBar: RoomAppbar(
|
||||
room,
|
||||
isDesktop: isDesktop,
|
||||
onOpenDrawer: Scaffold.of(context).openDrawer,
|
||||
onOpenMemberList: () =>
|
||||
memberListOpened.value = !memberListOpened.value,
|
||||
),
|
||||
body: ref
|
||||
.watch(controllerProvider)
|
||||
.betterWhen(
|
||||
data: (controller) => Chat(
|
||||
currentUserId: room.roomData.client.userID!,
|
||||
theme: ChatTheme.fromThemeData(theme).copyWith(
|
||||
colors: ChatColors.fromThemeData(theme).copyWith(
|
||||
primary: theme.colorScheme.primaryContainer,
|
||||
onPrimary: theme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
onMessageSecondaryTap:
|
||||
(
|
||||
context,
|
||||
message, {
|
||||
required details,
|
||||
required index,
|
||||
}) => showContextMenu(
|
||||
context: context,
|
||||
globalPosition: details.globalPosition,
|
||||
onTap: () => replyToMessage.value = message,
|
||||
),
|
||||
onMessageLongPress:
|
||||
(
|
||||
context,
|
||||
message, {
|
||||
required details,
|
||||
required index,
|
||||
}) => showContextMenu(
|
||||
context: context,
|
||||
globalPosition: details.globalPosition,
|
||||
onTap: () => replyToMessage.value = message,
|
||||
),
|
||||
builders: Builders(
|
||||
composerBuilder: (_) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
if (replyToMessage.value != null)
|
||||
ColoredBox(
|
||||
color: theme.colorScheme.surfaceContainer,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 4,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Avatar(
|
||||
userId: replyToMessage.value!.authorId,
|
||||
headers: headers,
|
||||
size: 16,
|
||||
),
|
||||
Text(
|
||||
replyToMessage
|
||||
.value!
|
||||
.metadata?["displayName"] ??
|
||||
replyToMessage.value!.authorId,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium
|
||||
?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: (replyToMessage.value as dynamic)
|
||||
? Text(
|
||||
(replyToMessage.value
|
||||
as TextMessage)
|
||||
.text,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.labelMedium,
|
||||
maxLines: 1,
|
||||
)
|
||||
: SizedBox(),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () =>
|
||||
replyToMessage.value = null,
|
||||
icon: Icon(Icons.close),
|
||||
iconSize: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ref
|
||||
.watch(controllerProvider)
|
||||
.betterWhen(
|
||||
data: (controller) => Chat(
|
||||
currentUserId: room.roomData.client.userID!,
|
||||
theme: ChatTheme.fromThemeData(theme).copyWith(
|
||||
colors: ChatColors.fromThemeData(theme).copyWith(
|
||||
primary: theme.colorScheme.primaryContainer,
|
||||
onPrimary: theme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
Composer(
|
||||
sigmaX: 0,
|
||||
sigmaY: 0,
|
||||
sendIconColor: theme.colorScheme.primary,
|
||||
sendOnEnter: true,
|
||||
autofocus: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
unsupportedMessageBuilder:
|
||||
(
|
||||
_,
|
||||
message,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => kDebugMode
|
||||
? Text(
|
||||
"${message.authorId} sent ${message.metadata?["eventType"]}",
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: Colors.grey,
|
||||
onMessageSecondaryTap:
|
||||
(
|
||||
context,
|
||||
message, {
|
||||
required details,
|
||||
required index,
|
||||
}) => showContextMenu(
|
||||
context: context,
|
||||
globalPosition: details.globalPosition,
|
||||
onTap: () => replyToMessage.value = message,
|
||||
),
|
||||
)
|
||||
: SizedBox.shrink(),
|
||||
textMessageBuilder:
|
||||
(
|
||||
context,
|
||||
message,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => FlyerChatTextMessage(
|
||||
topWidget: TopWidget(message, headers: headers),
|
||||
message: message.copyWith(
|
||||
text: message.text.replaceAllMapped(
|
||||
urlRegex,
|
||||
(match) =>
|
||||
"[${match.group(0)}](${match.group(0)})",
|
||||
onMessageLongPress:
|
||||
(
|
||||
context,
|
||||
message, {
|
||||
required details,
|
||||
required index,
|
||||
}) => showContextMenu(
|
||||
context: context,
|
||||
globalPosition: details.globalPosition,
|
||||
onTap: () => replyToMessage.value = message,
|
||||
),
|
||||
builders: Builders(
|
||||
chatAnimatedListBuilder: (context, itemBuilder) {
|
||||
return ChatAnimatedList(
|
||||
itemBuilder: itemBuilder,
|
||||
onEndReached: ref
|
||||
.watch(controllerProvider.notifier)
|
||||
.loadOlder,
|
||||
);
|
||||
},
|
||||
composerBuilder: (_) => ChatBox(
|
||||
replyToMessage: replyToMessage.value,
|
||||
onDismiss: () => replyToMessage.value = null,
|
||||
headers: room.roomData.client.headers,
|
||||
),
|
||||
showTime: true,
|
||||
index: index,
|
||||
onLinkTap: (url, _) => ref
|
||||
.watch(LaunchHelper.provider)
|
||||
.launchUrl(Uri.parse(url)),
|
||||
linksDecoration: TextDecoration.underline,
|
||||
sentLinksColor: Colors.blue,
|
||||
receivedLinksColor: Colors.blue,
|
||||
),
|
||||
linkPreviewBuilder: (_, message, isSentByMe) =>
|
||||
LinkPreview(
|
||||
text:
|
||||
urlRegex.firstMatch(message.text)?.group(0) ??
|
||||
"",
|
||||
backgroundColor: isSentByMe
|
||||
? theme.colorScheme.inversePrimary
|
||||
: theme.colorScheme.surfaceContainerLow,
|
||||
insidePadding: EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 16,
|
||||
),
|
||||
linkPreviewData: message.linkPreviewData,
|
||||
onLinkPreviewDataFetched: (linkPreviewData) => ref
|
||||
.watch(controllerProvider.notifier)
|
||||
.updateMessage(
|
||||
textMessageBuilder:
|
||||
(
|
||||
context,
|
||||
message,
|
||||
message.copyWith(
|
||||
linkPreviewData: linkPreviewData,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => FlyerChatTextMessage(
|
||||
topWidget: TopWidget(
|
||||
message,
|
||||
headers: room.roomData.client.headers,
|
||||
groupStatus: groupStatus,
|
||||
),
|
||||
message: message.copyWith(
|
||||
text: message.text.replaceAllMapped(
|
||||
urlRegex,
|
||||
(match) =>
|
||||
"[${match.group(0)}](${match.group(0)})",
|
||||
),
|
||||
),
|
||||
showTime: true,
|
||||
index: index,
|
||||
onLinkTap: (url, _) => ref
|
||||
.watch(LaunchHelper.provider)
|
||||
.launchUrl(Uri.parse(url)),
|
||||
linksDecoration: TextDecoration.underline,
|
||||
sentLinksColor: Colors.blue,
|
||||
receivedLinksColor: Colors.blue,
|
||||
),
|
||||
linkPreviewBuilder: (_, message, isSentByMe) =>
|
||||
LinkPreview(
|
||||
text:
|
||||
urlRegex
|
||||
.firstMatch(message.text)
|
||||
?.group(0) ??
|
||||
"",
|
||||
backgroundColor: isSentByMe
|
||||
? theme.colorScheme.inversePrimary
|
||||
: theme.colorScheme.surfaceContainerLow,
|
||||
insidePadding: EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
horizontal: 16,
|
||||
),
|
||||
linkPreviewData: message.linkPreviewData,
|
||||
onLinkPreviewDataFetched:
|
||||
(linkPreviewData) => ref
|
||||
.watch(controllerProvider.notifier)
|
||||
.updateMessage(
|
||||
message,
|
||||
message.copyWith(
|
||||
linkPreviewData:
|
||||
linkPreviewData,
|
||||
),
|
||||
),
|
||||
),
|
||||
imageMessageBuilder:
|
||||
(
|
||||
_,
|
||||
message,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => FlyerChatImageMessage(
|
||||
topWidget: TopWidget(
|
||||
message,
|
||||
headers: room.roomData.client.headers,
|
||||
groupStatus: groupStatus,
|
||||
),
|
||||
message: message,
|
||||
index: index,
|
||||
headers: room.roomData.client.headers,
|
||||
),
|
||||
fileMessageBuilder:
|
||||
(
|
||||
_,
|
||||
message,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => InkWell(
|
||||
onTap: () => showAboutDialog(
|
||||
context: context,
|
||||
), // TODO: Download
|
||||
child: FlyerChatFileMessage(
|
||||
topWidget: TopWidget(
|
||||
message,
|
||||
headers: room.roomData.client.headers,
|
||||
groupStatus: groupStatus,
|
||||
),
|
||||
message: message,
|
||||
index: index,
|
||||
),
|
||||
),
|
||||
systemMessageBuilder:
|
||||
(
|
||||
_,
|
||||
message,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => FlyerChatSystemMessage(
|
||||
message: message,
|
||||
index: index,
|
||||
),
|
||||
unsupportedMessageBuilder:
|
||||
(
|
||||
_,
|
||||
message,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => kDebugMode
|
||||
? Text(
|
||||
"${message.authorId} sent ${message.metadata?["eventType"]}",
|
||||
style: theme.textTheme.labelSmall
|
||||
?.copyWith(color: Colors.grey),
|
||||
)
|
||||
: SizedBox.shrink(),
|
||||
),
|
||||
imageMessageBuilder:
|
||||
(
|
||||
_,
|
||||
message,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => FlyerChatImageMessage(
|
||||
topWidget: TopWidget(message, headers: headers),
|
||||
message: message,
|
||||
index: index,
|
||||
headers: headers,
|
||||
),
|
||||
fileMessageBuilder:
|
||||
(
|
||||
_,
|
||||
message,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => InkWell(
|
||||
onTap: () => showAboutDialog(
|
||||
context: context,
|
||||
), // TODO: Download
|
||||
child: FlyerChatFileMessage(
|
||||
topWidget: TopWidget(message, headers: headers),
|
||||
message: message,
|
||||
index: index,
|
||||
),
|
||||
),
|
||||
systemMessageBuilder:
|
||||
(
|
||||
_,
|
||||
message,
|
||||
index, {
|
||||
required bool isSentByMe,
|
||||
MessageGroupStatus? groupStatus,
|
||||
}) => FlyerChatSystemMessage(
|
||||
message: message,
|
||||
index: index,
|
||||
),
|
||||
),
|
||||
onMessageSend: (message) {
|
||||
ref
|
||||
.watch(controllerProvider.notifier)
|
||||
.send(message, replyTo: replyToMessage.value);
|
||||
replyToMessage.value = null;
|
||||
},
|
||||
resolveUser: ref
|
||||
.watch(controllerProvider.notifier)
|
||||
.resolveUser,
|
||||
chatController: controller,
|
||||
),
|
||||
onMessageSend: (message) {
|
||||
ref
|
||||
.watch(controllerProvider.notifier)
|
||||
.send(message, replyTo: replyToMessage.value);
|
||||
replyToMessage.value = null;
|
||||
},
|
||||
resolveUser: ref
|
||||
.watch(controllerProvider.notifier)
|
||||
.resolveUser,
|
||||
chatController: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
if (memberListOpened.value == true) MemberList(room.roomData),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import "package:hooks_riverpod/hooks_riverpod.dart";
|
|||
import "package:nexus/controllers/current_room_controller.dart";
|
||||
import "package:nexus/controllers/spaces_controller.dart";
|
||||
import "package:nexus/helpers/extension_helper.dart";
|
||||
import "package:nexus/widgets/room_avatar.dart";
|
||||
import "package:nexus/widgets/avatar_or_hash.dart";
|
||||
|
||||
class Sidebar extends HookConsumerWidget {
|
||||
const Sidebar({super.key});
|
||||
|
|
@ -38,7 +38,12 @@ class Sidebar extends HookConsumerWidget {
|
|||
destinations: spaces
|
||||
.map(
|
||||
(space) => NavigationRailDestination(
|
||||
icon: RoomAvatar(space.avatar, space.title),
|
||||
icon: AvatarOrHash(
|
||||
space.avatar,
|
||||
fallback: space.icon,
|
||||
space.title,
|
||||
headers: space.client.headers,
|
||||
),
|
||||
label: Text(space.title),
|
||||
padding: EdgeInsets.only(top: 4),
|
||||
),
|
||||
|
|
@ -58,7 +63,12 @@ class Sidebar extends HookConsumerWidget {
|
|||
appBar: AppBar(
|
||||
title: Row(
|
||||
children: [
|
||||
RoomAvatar(space.avatar, space.title),
|
||||
AvatarOrHash(
|
||||
space.avatar,
|
||||
fallback: space.icon,
|
||||
space.title,
|
||||
headers: space.client.headers,
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
|
|
@ -80,12 +90,13 @@ class Sidebar extends HookConsumerWidget {
|
|||
destinations: space.children
|
||||
.map(
|
||||
(room) => NavigationRailDestination(
|
||||
icon: RoomAvatar(
|
||||
icon: AvatarOrHash(
|
||||
room.avatar,
|
||||
room.title,
|
||||
fallback: selectedSpace.value == 1
|
||||
? null
|
||||
: Icon(Icons.numbers),
|
||||
headers: space.client.headers,
|
||||
),
|
||||
label: Text(room.title),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -10,7 +10,13 @@ import "package:nexus/helpers/extension_helper.dart";
|
|||
class TopWidget extends ConsumerWidget {
|
||||
final Message message;
|
||||
final Map<String, String> headers;
|
||||
const TopWidget(this.message, {required this.headers, super.key});
|
||||
final MessageGroupStatus? groupStatus;
|
||||
const TopWidget(
|
||||
this.message, {
|
||||
required this.headers,
|
||||
required this.groupStatus,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) => Column(
|
||||
|
|
@ -32,16 +38,14 @@ class TopWidget extends ConsumerWidget {
|
|||
(message as TextMessage).text.length - 20,
|
||||
replyMessage.text.length,
|
||||
),
|
||||
40,
|
||||
5,
|
||||
),
|
||||
replyMessage.text.length,
|
||||
),
|
||||
)
|
||||
: replyMessage.text;
|
||||
return InkWell(
|
||||
onTap: () => showAboutDialog(
|
||||
context: context,
|
||||
), // TODO: Scroll to message
|
||||
onTap: () => showAboutDialog(context: context),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
|
|
@ -62,11 +66,14 @@ class TopWidget extends ConsumerWidget {
|
|||
headers: headers,
|
||||
size: 16,
|
||||
),
|
||||
Text(
|
||||
replyMessage.metadata?["displayName"] ??
|
||||
replyMessage.authorId,
|
||||
style: Theme.of(context).textTheme.labelMedium
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
Flexible(
|
||||
child: Text(
|
||||
replyMessage.metadata?["displayName"] ??
|
||||
replyMessage.authorId,
|
||||
style: Theme.of(context).textTheme.labelMedium
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
|
|
@ -85,23 +92,27 @@ class TopWidget extends ConsumerWidget {
|
|||
),
|
||||
SizedBox(height: 12),
|
||||
],
|
||||
InkWell(
|
||||
onTap: () =>
|
||||
showAboutDialog(context: context), // TODO: Show user profile
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Avatar(userId: message.authorId, headers: headers),
|
||||
Text(
|
||||
message.metadata?["displayName"] ?? message.authorId,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
if (groupStatus?.isFirst != false)
|
||||
InkWell(
|
||||
onTap: () =>
|
||||
showAboutDialog(context: context), // TODO: Show user profile
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Avatar(userId: message.authorId, headers: headers),
|
||||
Flexible(
|
||||
child: Text(
|
||||
message.metadata?["displayName"] ?? message.authorId,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue