You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
324 lines
12 KiB
324 lines
12 KiB
part of '../neon_spreed.dart'; |
|
|
|
class SpreedRoomPage extends StatefulWidget { |
|
const SpreedRoomPage({ |
|
required this.bloc, |
|
super.key, |
|
}); |
|
|
|
final SpreedRoomBloc bloc; |
|
|
|
@override |
|
State<SpreedRoomPage> createState() => _SpreedRoomPageState(); |
|
} |
|
|
|
class _SpreedRoomPageState extends State<SpreedRoomPage> { |
|
final defaultChatTheme = const chat_ui.DefaultChatTheme(); |
|
|
|
late final chatTheme = chat_ui.DefaultChatTheme( |
|
backgroundColor: Theme.of(context).colorScheme.background, |
|
primaryColor: Theme.of(context).colorScheme.onBackground, |
|
inputBackgroundColor: Theme.of(context).colorScheme.primary, |
|
inputTextColor: Theme.of(context).colorScheme.onPrimary, |
|
inputTextCursorColor: Theme.of(context).colorScheme.onPrimary, |
|
receivedMessageBodyTextStyle: defaultChatTheme.receivedMessageBodyTextStyle.copyWith( |
|
color: Theme.of(context).colorScheme.onPrimary, |
|
), |
|
sentMessageBodyTextStyle: defaultChatTheme.sentMessageBodyTextStyle.copyWith( |
|
color: Theme.of(context).colorScheme.onPrimary, |
|
), |
|
unreadHeaderTheme: chat_ui.UnreadHeaderTheme( |
|
color: Theme.of(context).colorScheme.background, |
|
textStyle: TextStyle( |
|
color: Theme.of(context).colorScheme.primary, |
|
fontWeight: FontWeight.bold, |
|
), |
|
), |
|
); |
|
|
|
final inputOptions = const chat_ui.InputOptions( |
|
sendButtonVisibilityMode: chat_ui.SendButtonVisibilityMode.always, |
|
); |
|
|
|
late final user = chat_types.User( |
|
id: widget.bloc.account.username, |
|
); |
|
|
|
void onSendPressed(final chat_types.PartialText partialText) { |
|
widget.bloc.sendMessage(partialText.text); |
|
} |
|
|
|
Future<void> openCall(final spreed.Room room) async { |
|
try { |
|
final client = NeonProvider.of<AccountsBloc>(context).activeAccount.value!.client; |
|
final settings = (await client.spreed.signaling.getSettings(token: widget.bloc.roomToken)).body.ocs.data; |
|
final bloc = SpreedCallBloc( |
|
settings, |
|
client, |
|
widget.bloc.roomToken, |
|
room.sessionId, |
|
); |
|
if (!mounted) { |
|
return; |
|
} |
|
await Navigator.of(context).push( |
|
MaterialPageRoute<void>( |
|
builder: (final context) => SpreedCallPage( |
|
bloc: bloc, |
|
), |
|
), |
|
); |
|
await bloc.leaveCall(); |
|
bloc.dispose(); |
|
} catch (e, s) { |
|
debugPrint(e.toString()); |
|
debugPrint(s.toString()); |
|
if (mounted) { |
|
NeonError.showSnackbar(context, e); |
|
} |
|
} |
|
} |
|
|
|
@override |
|
void initState() { |
|
super.initState(); |
|
|
|
widget.bloc.errors.listen((final error) { |
|
NeonError.showSnackbar(context, error); |
|
}); |
|
} |
|
|
|
@override |
|
Widget build(final BuildContext context) => StreamBuilder( |
|
stream: widget.bloc.allLoaded, |
|
builder: (final context, final allLoadedSnapshot) => ResultBuilder( |
|
stream: widget.bloc.room, |
|
builder: (final context, final room) => StreamBuilder( |
|
stream: widget.bloc.lastCommonReadMessageId, |
|
builder: (final context, final lastCommonReadMessageIdSnapshot) => StreamBuilder( |
|
stream: widget.bloc.sendingMessage, |
|
builder: (final context, final sendingMessageSnapshot) => ResultBuilder( |
|
stream: widget.bloc.messages, |
|
builder: (final context, final messages) { |
|
final roomType = room.hasData ? spreed.RoomType.fromValue(room.requireData.type) : null; |
|
return Scaffold( |
|
resizeToAvoidBottomInset: false, |
|
appBar: AppBar( |
|
titleSpacing: 0, |
|
title: Row( |
|
children: [ |
|
if (room.hasData) ...[ |
|
if (roomType!.isSingleUser) ...[ |
|
SpreedRoomIcon( |
|
roomType: roomType, |
|
roomName: room.requireData.name, |
|
backgroundColor: Theme.of(context).colorScheme.onPrimary, |
|
foregroundColor: Theme.of(context).colorScheme.primary, |
|
), |
|
const SizedBox( |
|
width: 10, |
|
), |
|
], |
|
Flexible( |
|
child: Text(room.requireData.displayName), |
|
), |
|
], |
|
if (room.error != null) ...[ |
|
const SizedBox( |
|
width: 8, |
|
), |
|
Icon( |
|
Icons.error_outline, |
|
size: 30, |
|
color: Theme.of(context).colorScheme.onPrimary, |
|
), |
|
], |
|
if (room.isLoading) ...[ |
|
const SizedBox( |
|
width: 8, |
|
), |
|
Expanded( |
|
child: NeonLinearProgressIndicator( |
|
color: Theme.of(context).appBarTheme.foregroundColor, |
|
), |
|
), |
|
], |
|
], |
|
), |
|
actions: [ |
|
if (room.hasData && room.requireData.readOnly == 0) ...[ |
|
if (room.requireData.hasCall) ...[ |
|
SpreedCallButton( |
|
type: SpreedCallButtonType.joinCall, |
|
onPressed: () async { |
|
await openCall(room.requireData); |
|
}, |
|
), |
|
] else if (room.requireData.canStartCall) ...[ |
|
SpreedCallButton( |
|
type: SpreedCallButtonType.startCall, |
|
onPressed: () async { |
|
await openCall(room.requireData); |
|
}, |
|
), |
|
], |
|
], |
|
], |
|
), |
|
body: chat_ui.Chat( |
|
useTopSafeAreaInset: false, |
|
showUserNames: true, |
|
showUserAvatars: !(roomType?.isSingleUser ?? true), |
|
theme: chatTheme, |
|
inputOptions: inputOptions, |
|
scrollToUnreadOptions: chat_ui.ScrollToUnreadOptions( |
|
lastReadMessageId: room.data?.lastReadMessage.toString(), |
|
scrollOnOpen: true, |
|
scrollDelay: Duration.zero, |
|
), |
|
avatarBuilder: (final username) => NeonUserAvatar( |
|
username: username, |
|
account: NeonProvider.of<AccountsBloc>(context).activeAccount.value!, |
|
), |
|
textMessageBuilder: ( |
|
final message, { |
|
required final messageWidth, |
|
required final showName, |
|
}) { |
|
final matchers = [ |
|
if (message.metadata != null) ...[ |
|
NeonRichObject( |
|
parameters: message.metadata!, |
|
), |
|
], |
|
]; |
|
|
|
return chat_ui.TextMessage( |
|
emojiEnlargementBehavior: chat_ui.EmojiEnlargementBehavior.multi, |
|
hideBackgroundOnEmojiMessages: true, |
|
message: message, |
|
showName: showName, |
|
usePreviewData: true, |
|
options: chat_ui.TextMessageOptions( |
|
matchers: matchers, |
|
), |
|
); |
|
}, |
|
systemMessageBuilder: (final message) { |
|
final matchers = [ |
|
if (message.metadata != null) ...[ |
|
NeonRichObject( |
|
parameters: message.metadata!, |
|
), |
|
], |
|
]; |
|
|
|
return chat_ui.SystemMessage( |
|
message: message.text, |
|
options: chat_ui.TextMessageOptions( |
|
matchers: matchers, |
|
), |
|
); |
|
}, |
|
customBottomWidget: Column( |
|
children: [ |
|
NeonError( |
|
messages.error, |
|
onRetry: () async { |
|
await widget.bloc.refresh(); |
|
}, |
|
), |
|
if (messages.isLoading) ...[ |
|
const NeonLinearProgressIndicator( |
|
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 5), |
|
), |
|
], |
|
if ((room.data?.readOnly ?? 0) == 0) ...[ |
|
chat_ui.Input( |
|
onSendPressed: onSendPressed, |
|
options: inputOptions, |
|
), |
|
], |
|
], |
|
), |
|
user: user, |
|
onEndReached: () async { |
|
await widget.bloc.loadMoreMessages(); |
|
}, |
|
onSendPressed: onSendPressed, |
|
isLastPage: allLoadedSnapshot.data ?? false, |
|
messages: [ |
|
if (sendingMessageSnapshot.hasData) ...[ |
|
chat_types.TextMessage( |
|
id: 'sending', |
|
author: user, |
|
text: sendingMessageSnapshot.data!, |
|
showStatus: true, |
|
status: chat_types.Status.sending, |
|
), |
|
], |
|
if (messages.hasData) ...[ |
|
...messages.requireData |
|
.map( |
|
(final message) => _spreedMessageToChatMessage( |
|
message, |
|
lastCommonReadMessageId: lastCommonReadMessageIdSnapshot.data, |
|
), |
|
) |
|
.whereNotNull(), |
|
], |
|
], |
|
), |
|
); |
|
}, |
|
), |
|
), |
|
), |
|
), |
|
); |
|
|
|
chat_types.Message? _spreedMessageToChatMessage( |
|
final spreed.ChatMessageInterface message, { |
|
final int? lastCommonReadMessageId, |
|
}) { |
|
final id = message.id.toString(); |
|
final author = chat_types.User( |
|
id: message.actorId, |
|
firstName: message.actorDisplayName, |
|
imageUrl: message.actorId, |
|
); |
|
final createdAt = message.timestamp * 1000; |
|
// TODO: Doesn't work yet in the UI. See https://github.com/flyerhq/flutter_chat_ui/pull/256 |
|
final repliedMessage = message is spreed.ChatMessageWithParent && message.parent != null |
|
? _spreedMessageToChatMessage(message.parent!) |
|
: null; |
|
final status = lastCommonReadMessageId != null && lastCommonReadMessageId >= message.id |
|
? chat_types.Status.seen |
|
: chat_types.Status.sent; |
|
final metadata = message.messageParameters is Map ? message.messageParameters as Map<String, dynamic> : null; |
|
|
|
switch (spreed.MessageType.values.byName(message.messageType)) { |
|
case spreed.MessageType.comment: |
|
return chat_types.TextMessage( |
|
id: id, |
|
author: author, |
|
createdAt: createdAt, |
|
repliedMessage: repliedMessage, |
|
text: message.message, |
|
showStatus: true, |
|
status: status, |
|
metadata: metadata, |
|
); |
|
case spreed.MessageType.command: |
|
case spreed.MessageType.system: |
|
return chat_types.SystemMessage( |
|
id: id, |
|
createdAt: createdAt, |
|
text: message.message, |
|
metadata: metadata, |
|
); |
|
default: |
|
return null; |
|
} |
|
} |
|
}
|
|
|