4110 playtest 92325 (#4121)
* style activity role tooltip like instruction inline tooltips * style updates to activity details * don't show token underlines in practice mode * show loading activity analytics * use all construct types to calculate activity analytics, include audio messages in activity summary request * update chat context menus for activities * fix positioning on menu in main chat list
This commit is contained in:
parent
d88d1303c6
commit
89bb560347
18 changed files with 797 additions and 781 deletions
|
|
@ -5256,5 +5256,6 @@
|
|||
"activityStatsButtonInstruction": "Click here to view your activity stats and to close the activity when finished",
|
||||
"readingAnalyticsDesc": "Click practice on each message for reading activities.",
|
||||
"speakingAnalyticsDesc": "Record voice messages for speaking practice.",
|
||||
"audioAnalyticsDesc": "Click practice on each message for listening activities."
|
||||
"audioAnalyticsDesc": "Click practice on each message for listening activities.",
|
||||
"endActivity": "End activity"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -460,6 +460,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
selected,
|
||||
highlighted,
|
||||
isNew,
|
||||
readingAssistanceMode ==
|
||||
ReadingAssistanceMode.practiceMode,
|
||||
),
|
||||
),
|
||||
width: tokenWidth,
|
||||
|
|
@ -486,6 +488,8 @@ class HtmlMessage extends StatelessWidget {
|
|||
selected,
|
||||
highlighted,
|
||||
isNew,
|
||||
readingAssistanceMode ==
|
||||
ReadingAssistanceMode.practiceMode,
|
||||
),
|
||||
),
|
||||
linkStyle: linkStyle,
|
||||
|
|
@ -627,6 +631,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -644,6 +649,7 @@ class HtmlMessage extends StatelessWidget {
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -360,7 +360,11 @@ class MessageContent extends StatelessWidget {
|
|||
pangeaMessageEvent: pangeaMessageEvent,
|
||||
nextEvent: nextEvent,
|
||||
prevEvent: prevEvent,
|
||||
onClick: event.isActivityMessage ? null : onClick,
|
||||
onClick: event.isActivityMessage ||
|
||||
readingAssistanceMode ==
|
||||
ReadingAssistanceMode.practiceMode
|
||||
? null
|
||||
: onClick,
|
||||
isTransitionAnimation: isTransitionAnimation,
|
||||
readingAssistanceMode: readingAssistanceMode,
|
||||
// Pangea#
|
||||
|
|
|
|||
|
|
@ -104,14 +104,13 @@ class RoomCreationStateEventState extends State<RoomCreationStateEvent> {
|
|||
),
|
||||
// #Pangea
|
||||
const SizedBox(height: 16.0),
|
||||
InstructionsInlineTooltip(
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.clickMessage,
|
||||
padding: const EdgeInsets.only(
|
||||
padding: EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
top: 16.0,
|
||||
),
|
||||
onClose: () => setState(() {}),
|
||||
animate: false,
|
||||
),
|
||||
if (_members <= 1 && InstructionsEnum.clickMessage.isToggledOff)
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ import 'package:fluffychat/pages/chat_list/chat_list_view.dart';
|
|||
import 'package:fluffychat/pangea/chat_list/utils/app_version_util.dart';
|
||||
import 'package:fluffychat/pangea/chat_list/utils/chat_list_handle_space_tap.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/delete_room.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/delete_space_dialog.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/chat_context_menu_action.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/subscription/widgets/subscription_snackbar.dart';
|
||||
|
|
@ -32,7 +31,6 @@ import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'
|
|||
import 'package:fluffychat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/share_scaffold_dialog.dart';
|
||||
import '../../../utils/account_bundles.dart';
|
||||
|
|
@ -43,6 +41,7 @@ import '../../widgets/matrix.dart';
|
|||
import 'package:fluffychat/utils/tor_stub.dart'
|
||||
if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart';
|
||||
|
||||
|
||||
enum PopupMenuAction {
|
||||
settings,
|
||||
invite,
|
||||
|
|
@ -700,351 +699,290 @@ class ChatListController extends State<ChatList>
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
// #Pangea
|
||||
void chatContextAction(
|
||||
Room room,
|
||||
BuildContext posContext, [
|
||||
Room? space,
|
||||
]) async {
|
||||
final overlay =
|
||||
Overlay.of(posContext).context.findRenderObject() as RenderBox;
|
||||
]) =>
|
||||
chatContextMenuAction(
|
||||
room,
|
||||
posContext,
|
||||
() => onChatTap(room),
|
||||
space,
|
||||
);
|
||||
// void chatContextAction(
|
||||
// Room room,
|
||||
// BuildContext posContext, [
|
||||
// Room? space,
|
||||
// ]) async {
|
||||
// final overlay =
|
||||
// Overlay.of(posContext).context.findRenderObject() as RenderBox;
|
||||
|
||||
final button = posContext.findRenderObject() as RenderBox;
|
||||
// final button = posContext.findRenderObject() as RenderBox;
|
||||
|
||||
final position = RelativeRect.fromRect(
|
||||
Rect.fromPoints(
|
||||
button.localToGlobal(const Offset(0, -65), ancestor: overlay),
|
||||
button.localToGlobal(
|
||||
button.size.bottomRight(Offset.zero) + const Offset(-50, 0),
|
||||
ancestor: overlay,
|
||||
),
|
||||
),
|
||||
Offset.zero & overlay.size,
|
||||
);
|
||||
// final position = RelativeRect.fromRect(
|
||||
// Rect.fromPoints(
|
||||
// button.localToGlobal(const Offset(0, -65), ancestor: overlay),
|
||||
// button.localToGlobal(
|
||||
// button.size.bottomRight(Offset.zero) + const Offset(-50, 0),
|
||||
// ancestor: overlay,
|
||||
// ),
|
||||
// ),
|
||||
// Offset.zero & overlay.size,
|
||||
// );
|
||||
|
||||
final displayname =
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)));
|
||||
// final displayname =
|
||||
// room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)));
|
||||
|
||||
// #Pangea
|
||||
// final spacesWithPowerLevels = room.client.rooms
|
||||
// .where(
|
||||
// (space) =>
|
||||
// space.isSpace &&
|
||||
// space.canChangeStateEvent(EventTypes.SpaceChild) &&
|
||||
// !space.spaceChildren.any((c) => c.roomId == room.id),
|
||||
// )
|
||||
// .toList();
|
||||
// Pangea#
|
||||
// final spacesWithPowerLevels = room.client.rooms
|
||||
// .where(
|
||||
// (space) =>
|
||||
// space.isSpace &&
|
||||
// space.canChangeStateEvent(EventTypes.SpaceChild) &&
|
||||
// !space.spaceChildren.any((c) => c.roomId == room.id),
|
||||
// )
|
||||
// .toList();
|
||||
|
||||
final action = await showMenu<ChatContextAction>(
|
||||
context: posContext,
|
||||
position: position,
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.open,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
// #Pangea
|
||||
userId: room.directChatMatrixID,
|
||||
// Pangea#
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 128),
|
||||
child: Text(
|
||||
displayname,
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
if (space != null)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.goToSpace,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: space.avatar,
|
||||
size: Avatar.defaultSize / 2,
|
||||
name: space.getLocalizedDisplayname(),
|
||||
// #Pangea
|
||||
userId: space.directChatMatrixID,
|
||||
// Pangea#
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
// Pangea#
|
||||
// L10n.of(context).goToSpace(space.getLocalizedDisplayname()),
|
||||
L10n.of(context)
|
||||
.goToCourse(space.getLocalizedDisplayname()),
|
||||
// Pangea#
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (room.membership == Membership.join) ...[
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.mute,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
// #Pangea
|
||||
// room.pushRuleState == PushRuleState.notify
|
||||
// ? Icons.notifications_off_outlined
|
||||
// : Icons.notifications_off,
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? Icons.notifications_on_outlined
|
||||
: Icons.notifications_off_outlined,
|
||||
// Pangea#
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
// #Pangea
|
||||
// room.pushRuleState == PushRuleState.notify
|
||||
// ? L10n.of(context).muteChat
|
||||
// : L10n.of(context).unmuteChat,
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? L10n.of(context).notificationsOn
|
||||
: L10n.of(context).notificationsOff,
|
||||
// Pangea#
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.markUnread,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
room.markedUnread
|
||||
? Icons.mark_as_unread
|
||||
: Icons.mark_as_unread_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.markedUnread
|
||||
? L10n.of(context).markAsRead
|
||||
: L10n.of(context).markAsUnread,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.favorite,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.isFavourite
|
||||
? L10n.of(context).unpin
|
||||
: L10n.of(context).pin,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// if (spacesWithPowerLevels.isNotEmpty)
|
||||
// PopupMenuItem(
|
||||
// value: ChatContextAction.addToSpace,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// const Icon(Icons.group_work_outlined),
|
||||
// const SizedBox(width: 12),
|
||||
// Text(L10n.of(context).addToSpace),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
],
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.leave,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
// #Pangea
|
||||
// Icons.delete_outlined,
|
||||
Icons.logout_outlined,
|
||||
// Pangea#
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.membership == Membership.invite
|
||||
? L10n.of(context).delete
|
||||
: L10n.of(context).leave,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
if (room.isRoomAdmin && !room.isDirectChat)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.delete,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.delete_outlined,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context).delete,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
);
|
||||
// final action = await showMenu<ChatContextAction>(
|
||||
// context: posContext,
|
||||
// position: position,
|
||||
// items: [
|
||||
// PopupMenuItem(
|
||||
// value: ChatContextAction.open,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// spacing: 12.0,
|
||||
// children: [
|
||||
// Avatar(
|
||||
// mxContent: room.avatar,
|
||||
// name: displayname,
|
||||
// ),
|
||||
// ConstrainedBox(
|
||||
// constraints: const BoxConstraints(maxWidth: 128),
|
||||
// child: Text(
|
||||
// displayname,
|
||||
// style:
|
||||
// TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
// maxLines: 2,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// const PopupMenuDivider(),
|
||||
// if (space != null)
|
||||
// PopupMenuItem(
|
||||
// value: ChatContextAction.goToSpace,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Avatar(
|
||||
// mxContent: space.avatar,
|
||||
// size: Avatar.defaultSize / 2,
|
||||
// name: space.getLocalizedDisplayname(),
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// Expanded(
|
||||
// child: Text(
|
||||
// L10n.of(context).goToSpace(space.getLocalizedDisplayname()),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// if (room.membership == Membership.join) ...[
|
||||
// PopupMenuItem(
|
||||
// value: ChatContextAction.mute,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// room.pushRuleState == PushRuleState.notify
|
||||
// ? Icons.notifications_off_outlined
|
||||
// : Icons.notifications_off,
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// Text(
|
||||
// room.pushRuleState == PushRuleState.notify
|
||||
// ? L10n.of(context).muteChat
|
||||
// : L10n.of(context).unmuteChat,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// PopupMenuItem(
|
||||
// value: ChatContextAction.markUnread,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// room.markedUnread
|
||||
// ? Icons.mark_as_unread
|
||||
// : Icons.mark_as_unread_outlined,
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// Text(
|
||||
// room.markedUnread
|
||||
// ? L10n.of(context).markAsRead
|
||||
// : L10n.of(context).markAsUnread,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// PopupMenuItem(
|
||||
// value: ChatContextAction.favorite,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined,
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// Text(
|
||||
// room.isFavourite
|
||||
// ? L10n.of(context).unpin
|
||||
// : L10n.of(context).pin,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// if (spacesWithPowerLevels.isNotEmpty)
|
||||
// PopupMenuItem(
|
||||
// value: ChatContextAction.addToSpace,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// const Icon(Icons.group_work_outlined),
|
||||
// const SizedBox(width: 12),
|
||||
// Text(L10n.of(context).addToSpace),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// PopupMenuItem(
|
||||
// value: ChatContextAction.leave,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// Icons.delete_outlined,
|
||||
// color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// Text(
|
||||
// room.membership == Membership.invite
|
||||
// ? L10n.of(context).delete
|
||||
// : L10n.of(context).leave,
|
||||
// style: TextStyle(
|
||||
// color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// if (room.membership == Membership.invite)
|
||||
// PopupMenuItem(
|
||||
// value: ChatContextAction.block,
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Icon(
|
||||
// Icons.block_outlined,
|
||||
// color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// Text(
|
||||
// L10n.of(context).block,
|
||||
// style: TextStyle(
|
||||
// color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
|
||||
if (action == null) return;
|
||||
if (!mounted) return;
|
||||
// if (action == null) return;
|
||||
// if (!mounted) return;
|
||||
|
||||
switch (action) {
|
||||
case ChatContextAction.open:
|
||||
onChatTap(room);
|
||||
return;
|
||||
case ChatContextAction.goToSpace:
|
||||
setActiveSpace(space!.id);
|
||||
return;
|
||||
case ChatContextAction.favorite:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.setFavourite(!room.isFavourite),
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.markUnread:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.markUnread(!room.markedUnread),
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.mute:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.setPushRuleState(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? PushRuleState.mentionsOnly
|
||||
: PushRuleState.notify,
|
||||
),
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.leave:
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
// #Pangea
|
||||
// message: L10n.of(context).archiveRoomDescription,
|
||||
message: room.isSpace
|
||||
? L10n.of(context).leaveSpaceDescription
|
||||
: L10n.of(context).leaveRoomDescription,
|
||||
// Pangea#
|
||||
okLabel: L10n.of(context).leave,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
);
|
||||
// #Pangea
|
||||
// if (confirmed == OkCancelResult.cancel) return;
|
||||
if (confirmed != OkCancelResult.ok) return;
|
||||
// Pangea#
|
||||
if (!mounted) return;
|
||||
// switch (action) {
|
||||
// case ChatContextAction.open:
|
||||
// onChatTap(room);
|
||||
// return;
|
||||
// case ChatContextAction.goToSpace:
|
||||
// setActiveSpace(space!.id);
|
||||
// return;
|
||||
// case ChatContextAction.favorite:
|
||||
// await showFutureLoadingDialog(
|
||||
// context: context,
|
||||
// future: () => room.setFavourite(!room.isFavourite),
|
||||
// );
|
||||
// return;
|
||||
// case ChatContextAction.markUnread:
|
||||
// await showFutureLoadingDialog(
|
||||
// context: context,
|
||||
// future: () => room.markUnread(!room.markedUnread),
|
||||
// );
|
||||
// return;
|
||||
// case ChatContextAction.mute:
|
||||
// await showFutureLoadingDialog(
|
||||
// context: context,
|
||||
// future: () => room.setPushRuleState(
|
||||
// room.pushRuleState == PushRuleState.notify
|
||||
// ? PushRuleState.mentionsOnly
|
||||
// : PushRuleState.notify,
|
||||
// ),
|
||||
// );
|
||||
// return;
|
||||
// case ChatContextAction.block:
|
||||
// final inviteEvent = room.getState(
|
||||
// EventTypes.RoomMember,
|
||||
// room.client.userID!,
|
||||
// );
|
||||
// context.go(
|
||||
// '/rooms/settings/security/ignorelist',
|
||||
// extra: inviteEvent?.senderId,
|
||||
// );
|
||||
// case ChatContextAction.leave:
|
||||
// final confirmed = await showOkCancelAlertDialog(
|
||||
// context: context,
|
||||
// title: L10n.of(context).areYouSure,
|
||||
// message: L10n.of(context).archiveRoomDescription,
|
||||
// okLabel: L10n.of(context).leave,
|
||||
// cancelLabel: L10n.of(context).cancel,
|
||||
// isDestructive: true,
|
||||
// );
|
||||
// if (confirmed == OkCancelResult.cancel) return;
|
||||
// if (!mounted) return;
|
||||
|
||||
// #Pangea
|
||||
// await showFutureLoadingDialog(context: context, future: room.leave);
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.isSpace ? room.leaveSpace : room.leave,
|
||||
);
|
||||
if (mounted && !resp.isError) {
|
||||
context.go("/rooms");
|
||||
}
|
||||
// Pangea#
|
||||
// await showFutureLoadingDialog(context: context, future: room.leave);
|
||||
|
||||
return;
|
||||
// #Pangea
|
||||
// case ChatContextAction.addToSpace:
|
||||
// final space = await showModalActionPopup(
|
||||
// context: context,
|
||||
// title: L10n.of(context).space,
|
||||
// actions: spacesWithPowerLevels
|
||||
// .map(
|
||||
// (space) => AdaptiveModalAction(
|
||||
// value: space,
|
||||
// label: space
|
||||
// .getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
// ),
|
||||
// )
|
||||
// .toList(),
|
||||
// );
|
||||
// if (space == null) return;
|
||||
// await showFutureLoadingDialog(
|
||||
// context: context,
|
||||
// future: () => space.setSpaceChild(room.id),
|
||||
// );
|
||||
// Pangea#
|
||||
case ChatContextAction.delete:
|
||||
if (room.isSpace) {
|
||||
final resp = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (_) => DeleteSpaceDialog(space: room),
|
||||
);
|
||||
if (resp == true && mounted) {
|
||||
context.go("/rooms");
|
||||
}
|
||||
} else {
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).delete,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
message: room.isSpace
|
||||
? L10n.of(context).deleteSpaceDesc
|
||||
: L10n.of(context).deleteChatDesc,
|
||||
);
|
||||
if (confirmed != OkCancelResult.ok) return;
|
||||
if (!mounted) return;
|
||||
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.delete,
|
||||
);
|
||||
if (mounted && !resp.isError) {
|
||||
activeSpaceId != null
|
||||
? context.go('/rooms/spaces/$activeSpaceId/details')
|
||||
: context.go("/rooms");
|
||||
}
|
||||
}
|
||||
return;
|
||||
// Pangea#
|
||||
}
|
||||
}
|
||||
// return;
|
||||
// case ChatContextAction.addToSpace:
|
||||
// final space = await showModalActionPopup(
|
||||
// context: context,
|
||||
// title: L10n.of(context).space,
|
||||
// actions: spacesWithPowerLevels
|
||||
// .map(
|
||||
// (space) => AdaptiveModalAction(
|
||||
// value: space,
|
||||
// label: space
|
||||
// .getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
// ),
|
||||
// )
|
||||
// .toList(),
|
||||
// );
|
||||
// if (space == null) return;
|
||||
// await showFutureLoadingDialog(
|
||||
// context: context,
|
||||
// future: () => space.setSpaceChild(room.id),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// Pangea#
|
||||
|
||||
void dismissStatusList() async {
|
||||
final result = await showOkCancelAlertDialog(
|
||||
|
|
@ -1302,6 +1240,7 @@ enum ChatContextAction {
|
|||
// #Pangea
|
||||
// addToSpace,
|
||||
delete,
|
||||
endActivity,
|
||||
// Pangea#
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import 'package:fluffychat/pangea/activity_sessions/activity_session_analytics_r
|
|||
import 'package:fluffychat/pangea/activity_summary/activity_summary_analytics_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_summary/activity_summary_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_summary/activity_summary_request_model.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
|
|
@ -160,7 +161,10 @@ extension ActivityRoomExtension on Room {
|
|||
final timeline = this.timeline ?? await getTimeline();
|
||||
for (final event in events) {
|
||||
if (event.type != EventTypes.Message ||
|
||||
event.messageType != MessageTypes.Text) {
|
||||
![
|
||||
MessageTypes.Text,
|
||||
MessageTypes.Audio,
|
||||
].contains(event.messageType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -170,21 +174,38 @@ extension ActivityRoomExtension on Room {
|
|||
ownMessage: client.userID == event.senderId,
|
||||
);
|
||||
|
||||
final activityMessage = ActivitySummaryResultsMessage(
|
||||
userId: event.senderId,
|
||||
sent: pangeaMessage.originalSent?.text ?? event.body,
|
||||
written: pangeaMessage.originalWrittenContent,
|
||||
time: event.originServerTs,
|
||||
tool: [
|
||||
if (pangeaMessage.originalSent?.choreo?.includedIT == true) "it",
|
||||
if (pangeaMessage.originalSent?.choreo?.includedIGC == true) "igc",
|
||||
],
|
||||
);
|
||||
if (event.messageType == MessageTypes.Audio &&
|
||||
pangeaMessage.getSpeechToTextLocal() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final activityMessage = event.messageType == MessageTypes.Text
|
||||
? ActivitySummaryResultsMessage(
|
||||
userId: event.senderId,
|
||||
sent: pangeaMessage.originalSent?.text ?? event.body,
|
||||
written: pangeaMessage.originalWrittenContent,
|
||||
time: event.originServerTs,
|
||||
tool: [
|
||||
if (pangeaMessage.originalSent?.choreo?.includedIT == true)
|
||||
"it",
|
||||
if (pangeaMessage.originalSent?.choreo?.includedIGC == true)
|
||||
"igc",
|
||||
],
|
||||
)
|
||||
: ActivitySummaryResultsMessage(
|
||||
userId: event.senderId,
|
||||
sent:
|
||||
pangeaMessage.getSpeechToTextLocal()!.transcript.text.trim(),
|
||||
written:
|
||||
pangeaMessage.getSpeechToTextLocal()!.transcript.text.trim(),
|
||||
time: event.originServerTs,
|
||||
tool: [],
|
||||
);
|
||||
|
||||
messages.add(activityMessage);
|
||||
|
||||
if (activitySummary?.analytics == null) {
|
||||
analytics.addConstructs(pangeaMessage);
|
||||
analytics.addMessageConstructs(pangeaMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -396,28 +417,33 @@ extension ActivityRoomExtension on Room {
|
|||
final cached = ActivitySessionAnalyticsRepo.get(id);
|
||||
final analytics = cached?.analytics ?? ActivitySummaryAnalyticsModel();
|
||||
|
||||
final eventsSince = await getAllEvents(since: cached?.lastEventId);
|
||||
final timeline = this.timeline ?? await getTimeline();
|
||||
final messageEvents = getPangeaMessageEvents(
|
||||
eventsSince,
|
||||
timeline,
|
||||
msgtypes: [
|
||||
MessageTypes.Text,
|
||||
MessageTypes.Audio,
|
||||
],
|
||||
);
|
||||
DateTime? timestamp = creationTimestamp;
|
||||
if (cached != null) {
|
||||
timestamp = cached.lastUseTimestamp;
|
||||
}
|
||||
|
||||
if (messageEvents.isEmpty) {
|
||||
final List<OneConstructUse> uses = [];
|
||||
|
||||
for (final use
|
||||
in MatrixState.pangeaController.getAnalytics.constructListModel.uses) {
|
||||
final useTimestamp = use.metadata.timeStamp;
|
||||
if (timestamp != null &&
|
||||
(useTimestamp == timestamp || useTimestamp.isBefore(timestamp))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (use.metadata.roomId != id) continue;
|
||||
uses.add(use);
|
||||
}
|
||||
|
||||
if (uses.isEmpty) {
|
||||
return analytics;
|
||||
}
|
||||
|
||||
for (final pangeaMessage in messageEvents) {
|
||||
analytics.addConstructs(pangeaMessage);
|
||||
}
|
||||
|
||||
analytics.addConstructs(client.userID!, uses);
|
||||
await ActivitySessionAnalyticsRepo.set(
|
||||
id,
|
||||
messageEvents.last.eventId,
|
||||
uses.first.metadata.timeStamp,
|
||||
analytics,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import 'package:fluffychat/pangea/activity_summary/activity_summary_analytics_mo
|
|||
|
||||
class CachedActivityAnalytics {
|
||||
final DateTime timestamp;
|
||||
final String lastEventId;
|
||||
final DateTime lastUseTimestamp;
|
||||
final ActivitySummaryAnalyticsModel analytics;
|
||||
|
||||
CachedActivityAnalytics(
|
||||
this.timestamp,
|
||||
this.lastEventId,
|
||||
this.lastUseTimestamp,
|
||||
this.analytics,
|
||||
);
|
||||
}
|
||||
|
|
@ -31,10 +31,11 @@ class ActivitySessionAnalyticsRepo {
|
|||
return null;
|
||||
}
|
||||
|
||||
final lastEventId = json['last_event_id'] as String;
|
||||
final lastUseTimestamp =
|
||||
DateTime.parse(json['last_use_timestamp'] as String);
|
||||
final analyticsJson = json['analytics'] as Map<String, dynamic>;
|
||||
final analytics = ActivitySummaryAnalyticsModel.fromJson(analyticsJson);
|
||||
return CachedActivityAnalytics(timestamp, lastEventId, analytics);
|
||||
return CachedActivityAnalytics(timestamp, lastUseTimestamp, analytics);
|
||||
} catch (e) {
|
||||
_activityAnalyticsStorage.remove(roomId);
|
||||
return null;
|
||||
|
|
@ -43,12 +44,12 @@ class ActivitySessionAnalyticsRepo {
|
|||
|
||||
static Future<void> set(
|
||||
String roomId,
|
||||
String lastEventId,
|
||||
DateTime lastUseTimestamp,
|
||||
ActivitySummaryAnalyticsModel analytics,
|
||||
) async {
|
||||
final json = {
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'last_event_id': lastEventId,
|
||||
'last_use_timestamp': lastUseTimestamp.toIso8601String(),
|
||||
'analytics': analytics.toJson(),
|
||||
};
|
||||
await _activityAnalyticsStorage.write(roomId, json);
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
|
||||
|
||||
class ActivityRoleTooltip extends StatefulWidget {
|
||||
final Choreographer choreographer;
|
||||
|
|
@ -54,34 +54,14 @@ class ActivityRoleTooltipState extends State<ActivityRoleTooltip> {
|
|||
),
|
||||
),
|
||||
),
|
||||
child: AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: room.hasDismissedGoalTooltip
|
||||
? const SizedBox()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
room.ownRole!.goal!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12.0,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () async {
|
||||
await room.dismissGoalTooltip();
|
||||
if (mounted) setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: InlineTooltip(
|
||||
message: room.ownRole!.goal!,
|
||||
isClosed: room.hasDismissedGoalTooltip,
|
||||
onClose: () async {
|
||||
await room.dismissGoalTooltip();
|
||||
if (mounted) setState(() {});
|
||||
},
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class ActivityStatsButton extends StatefulWidget {
|
|||
|
||||
class _ActivityStatsButtonState extends State<ActivityStatsButton> {
|
||||
StreamSubscription? _analyticsSubscription;
|
||||
ActivitySummaryAnalyticsModel analytics = ActivitySummaryAnalyticsModel();
|
||||
ActivitySummaryAnalyticsModel? analytics;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -47,7 +47,8 @@ class _ActivityStatsButtonState extends State<ActivityStatsButton> {
|
|||
Client get client => widget.controller.room.client;
|
||||
|
||||
void _showInstructionPopup() {
|
||||
if (InstructionsEnum.activityStatsMenu.isToggledOff || xpCount <= 0) {
|
||||
if (InstructionsEnum.activityStatsMenu.isToggledOff ||
|
||||
(xpCount ?? 0) <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -112,16 +113,16 @@ class _ActivityStatsButtonState extends State<ActivityStatsButton> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
int get xpCount => analytics.totalXPForUser(
|
||||
int? get xpCount => analytics?.totalXPForUser(
|
||||
client.userID!,
|
||||
);
|
||||
|
||||
int get vocabCount => analytics.uniqueConstructCountForUser(
|
||||
int? get vocabCount => analytics?.uniqueConstructCountForUser(
|
||||
client.userID!,
|
||||
ConstructTypeEnum.vocab,
|
||||
);
|
||||
|
||||
int get grammarCount => analytics.uniqueConstructCountForUser(
|
||||
int? get grammarCount => analytics?.uniqueConstructCountForUser(
|
||||
client.userID!,
|
||||
ConstructTypeEnum.morph,
|
||||
);
|
||||
|
|
@ -131,7 +132,7 @@ class _ActivityStatsButtonState extends State<ActivityStatsButton> {
|
|||
final analytics = await widget.controller.room.getActivityAnalytics();
|
||||
if (mounted) {
|
||||
setState(() => this.analytics = analytics);
|
||||
if (prevXP == 0 && xpCount > 0) _showInstructionPopup();
|
||||
if (prevXP == 0 && (xpCount ?? 0) > 0) _showInstructionPopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,32 +144,34 @@ class _ActivityStatsButtonState extends State<ActivityStatsButton> {
|
|||
!widget.controller.showActivityDropdown,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: xpCount > 0
|
||||
color: (xpCount ?? 0) > 0
|
||||
? AppConfig.gold.withAlpha(180)
|
||||
: theme.colorScheme.surface,
|
||||
depressed: xpCount <= 0 || widget.controller.showActivityDropdown,
|
||||
depressed: (xpCount ?? 0) <= 0 || widget.controller.showActivityDropdown,
|
||||
child: AnimatedContainer(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
width: 300,
|
||||
height: 55,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: xpCount > 0
|
||||
color: (xpCount ?? 0) > 0
|
||||
? AppConfig.gold.withAlpha(180)
|
||||
: theme.colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_StatsBadge(icon: Icons.radar, value: "$xpCount XP"),
|
||||
_StatsBadge(icon: Symbols.dictionary, value: "$vocabCount"),
|
||||
_StatsBadge(
|
||||
icon: Symbols.toys_and_games,
|
||||
value: "$grammarCount",
|
||||
),
|
||||
],
|
||||
),
|
||||
child: analytics == null
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_StatsBadge(icon: Icons.radar, value: "$xpCount XP"),
|
||||
_StatsBadge(icon: Symbols.dictionary, value: "$vocabCount"),
|
||||
_StatsBadge(
|
||||
icon: Symbols.toys_and_games,
|
||||
value: "$grammarCount",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,42 +89,32 @@ class ActivitySummary extends StatelessWidget {
|
|||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InlineEllipsisText(
|
||||
text: activity.description,
|
||||
maxLines: showInstructions ? null : 2,
|
||||
trailingWidth: 50.0,
|
||||
style: DefaultTextStyle.of(context)
|
||||
.style
|
||||
.copyWith(fontSize: 12.0),
|
||||
trailing: WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
Text(
|
||||
activity.description,
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4.0,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).details,
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4.0,
|
||||
),
|
||||
child: TextButton(
|
||||
onPressed: toggleInstructions,
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: Size.zero,
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: Text(
|
||||
InkWell(
|
||||
onTap: toggleInstructions,
|
||||
child: Icon(
|
||||
showInstructions
|
||||
? L10n.of(context).less
|
||||
: L10n.of(context).moreLabel,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
? Icons.arrow_drop_up
|
||||
: Icons.arrow_drop_down,
|
||||
size: 20.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (showInstructions) ...[
|
||||
|
|
@ -134,7 +124,7 @@ class ActivitySummary extends StatelessWidget {
|
|||
children: [
|
||||
Text(
|
||||
activity.req.mode,
|
||||
style: const TextStyle(fontSize: 12.0),
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
Row(
|
||||
spacing: 4.0,
|
||||
|
|
@ -143,7 +133,7 @@ class ActivitySummary extends StatelessWidget {
|
|||
const Icon(Icons.school, size: 12.0),
|
||||
Text(
|
||||
activity.req.cefrLevel.string,
|
||||
style: const TextStyle(fontSize: 12.0),
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -154,7 +144,7 @@ class ActivitySummary extends StatelessWidget {
|
|||
iconSize: 16.0,
|
||||
child: Text(
|
||||
activity.learningObjective,
|
||||
style: const TextStyle(fontSize: 12.0),
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
ActivitySessionDetailsRow(
|
||||
|
|
@ -166,7 +156,9 @@ class ActivitySummary extends StatelessWidget {
|
|||
"body": Style(
|
||||
margin: Margins.all(0),
|
||||
padding: HtmlPaddings.all(0),
|
||||
fontSize: FontSize(12.0),
|
||||
fontSize: FontSize(
|
||||
theme.textTheme.bodyMedium!.fontSize!,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
|
|
@ -194,7 +186,7 @@ class ActivitySummary extends StatelessWidget {
|
|||
),
|
||||
child: Text(
|
||||
vocab.lemma,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
|
||||
|
|
@ -43,16 +44,19 @@ class ActivitySummaryAnalyticsModel {
|
|||
return totalXP;
|
||||
}
|
||||
|
||||
void addConstructs(PangeaMessageEvent event) {
|
||||
void addMessageConstructs(PangeaMessageEvent event) {
|
||||
final uses = event.originalSent?.vocabAndMorphUses();
|
||||
if (uses == null || uses.isEmpty) return;
|
||||
addConstructs(event.senderId, uses);
|
||||
}
|
||||
|
||||
final user =
|
||||
constructs[event.senderId] ??= UserConstructAnalytics(event.senderId);
|
||||
|
||||
void addConstructs(String userId, List<OneConstructUse> uses) {
|
||||
final user = constructs[userId] ??= UserConstructAnalytics(userId);
|
||||
for (final use in uses) {
|
||||
user.addUsage(use.identifier);
|
||||
}
|
||||
|
||||
constructs[userId] = user;
|
||||
}
|
||||
|
||||
Map<String, List> generateSuperlatives() {
|
||||
|
|
|
|||
301
lib/pangea/chat_settings/widgets/chat_context_menu_action.dart
Normal file
301
lib/pangea/chat_settings/widgets/chat_context_menu_action.dart
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/delete_room.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/delete_space_dialog.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
|
||||
void chatContextMenuAction(
|
||||
Room room,
|
||||
BuildContext context,
|
||||
VoidCallback onChatTap, [
|
||||
Room? space,
|
||||
]) async {
|
||||
final overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
|
||||
final button = context.findRenderObject() as RenderBox;
|
||||
|
||||
final position = RelativeRect.fromRect(
|
||||
Rect.fromPoints(
|
||||
button.localToGlobal(const Offset(0, -65), ancestor: overlay),
|
||||
button.localToGlobal(
|
||||
button.size.bottomRight(Offset.zero) + const Offset(-50, 0),
|
||||
ancestor: overlay,
|
||||
),
|
||||
),
|
||||
Offset.zero & overlay.size,
|
||||
);
|
||||
|
||||
final displayname =
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)));
|
||||
|
||||
final action = await showMenu<ChatContextAction>(
|
||||
context: context,
|
||||
position: position,
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.open,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
userId: room.directChatMatrixID,
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 128),
|
||||
child: Text(
|
||||
displayname,
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
if (space != null)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.goToSpace,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: space.avatar,
|
||||
size: Avatar.defaultSize / 2,
|
||||
name: space.getLocalizedDisplayname(),
|
||||
userId: space.directChatMatrixID,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
L10n.of(context).goToCourse(space.getLocalizedDisplayname()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (room.membership == Membership.join) ...[
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.mute,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? Icons.notifications_on_outlined
|
||||
: Icons.notifications_off_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? L10n.of(context).notificationsOn
|
||||
: L10n.of(context).notificationsOff,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!room.isActivitySession)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.markUnread,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
room.markedUnread
|
||||
? Icons.mark_as_unread
|
||||
: Icons.mark_as_unread_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.markedUnread
|
||||
? L10n.of(context).markAsRead
|
||||
: L10n.of(context).markAsUnread,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!room.isActivitySession)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.favorite,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.isFavourite
|
||||
? L10n.of(context).unpin
|
||||
: L10n.of(context).pin,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
if (room.isActiveInActivity)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.endActivity,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.stop_circle_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context).endActivity,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!room.isActivitySession || room.ownRole == null)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.leave,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.logout_outlined,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.membership == Membership.invite
|
||||
? L10n.of(context).delete
|
||||
: L10n.of(context).leave,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (room.isRoomAdmin && !room.isDirectChat)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.delete,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.delete_outlined,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context).delete,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
if (action == null) return;
|
||||
|
||||
switch (action) {
|
||||
case ChatContextAction.open:
|
||||
onChatTap.call();
|
||||
return;
|
||||
case ChatContextAction.goToSpace:
|
||||
context.go("/rooms/spaces/${space!.id}/details");
|
||||
return;
|
||||
case ChatContextAction.favorite:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.setFavourite(!room.isFavourite),
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.markUnread:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.markUnread(!room.markedUnread),
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.mute:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.setPushRuleState(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? PushRuleState.mentionsOnly
|
||||
: PushRuleState.notify,
|
||||
),
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.leave:
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
message: room.isSpace
|
||||
? L10n.of(context).leaveSpaceDescription
|
||||
: L10n.of(context).leaveRoomDescription,
|
||||
okLabel: L10n.of(context).leave,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
);
|
||||
if (confirmed != OkCancelResult.ok) return;
|
||||
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.isSpace ? room.leaveSpace : room.leave,
|
||||
);
|
||||
if (!resp.isError) {
|
||||
context.go("/rooms/spaces/${room.id}/details");
|
||||
}
|
||||
|
||||
return;
|
||||
case ChatContextAction.delete:
|
||||
if (room.isSpace) {
|
||||
final resp = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (_) => DeleteSpaceDialog(space: room),
|
||||
);
|
||||
if (resp == true) {
|
||||
context.go("/rooms");
|
||||
}
|
||||
} else {
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).delete,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
message: room.isSpace
|
||||
? L10n.of(context).deleteSpaceDesc
|
||||
: L10n.of(context).deleteChatDesc,
|
||||
);
|
||||
if (confirmed != OkCancelResult.ok) return;
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.delete,
|
||||
);
|
||||
if (!resp.isError) {
|
||||
context.go("/rooms/spaces/${room.id}/details");
|
||||
}
|
||||
}
|
||||
return;
|
||||
case ChatContextAction.endActivity:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.finishActivity,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,6 @@ import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
|||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/delete_room.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/delete_space_dialog.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/course_chats/course_chats_view.dart';
|
||||
import 'package:fluffychat/pangea/course_chats/extended_space_rooms_chunk.dart';
|
||||
|
|
@ -25,8 +23,6 @@ import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
|
|||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
|
|
@ -472,272 +468,6 @@ class CourseChatsController extends State<CourseChats>
|
|||
context.go("/rooms/spaces/${widget.roomId}/$roomId");
|
||||
}
|
||||
|
||||
void chatContextAction(
|
||||
Room room,
|
||||
BuildContext posContext, [
|
||||
Room? space,
|
||||
]) async {
|
||||
final overlay =
|
||||
Overlay.of(posContext).context.findRenderObject() as RenderBox;
|
||||
|
||||
final button = posContext.findRenderObject() as RenderBox;
|
||||
|
||||
final position = RelativeRect.fromRect(
|
||||
Rect.fromPoints(
|
||||
button.localToGlobal(const Offset(0, -65), ancestor: overlay),
|
||||
button.localToGlobal(
|
||||
button.size.bottomRight(Offset.zero) + const Offset(-50, 0),
|
||||
ancestor: overlay,
|
||||
),
|
||||
),
|
||||
Offset.zero & overlay.size,
|
||||
);
|
||||
|
||||
final displayname =
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)));
|
||||
|
||||
final action = await showMenu<ChatContextAction>(
|
||||
context: posContext,
|
||||
position: position,
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.open,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
userId: room.directChatMatrixID,
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 128),
|
||||
child: Text(
|
||||
displayname,
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.onSurface),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
if (space != null)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.goToSpace,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: space.avatar,
|
||||
size: Avatar.defaultSize / 2,
|
||||
name: space.getLocalizedDisplayname(),
|
||||
userId: space.directChatMatrixID,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
L10n.of(context)
|
||||
.goToCourse(space.getLocalizedDisplayname()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (room.membership == Membership.join) ...[
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.mute,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? Icons.notifications_on_outlined
|
||||
: Icons.notifications_off_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? L10n.of(context).notificationsOn
|
||||
: L10n.of(context).notificationsOff,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.markUnread,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
room.markedUnread
|
||||
? Icons.mark_as_unread
|
||||
: Icons.mark_as_unread_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.markedUnread
|
||||
? L10n.of(context).markAsRead
|
||||
: L10n.of(context).markAsUnread,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.favorite,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.isFavourite
|
||||
? L10n.of(context).unpin
|
||||
: L10n.of(context).pin,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.leave,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.logout_outlined,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.membership == Membership.invite
|
||||
? L10n.of(context).delete
|
||||
: L10n.of(context).leave,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (room.isRoomAdmin && !room.isDirectChat)
|
||||
PopupMenuItem(
|
||||
value: ChatContextAction.delete,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.delete_outlined,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context).delete,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
if (action == null) return;
|
||||
if (!mounted) return;
|
||||
|
||||
switch (action) {
|
||||
case ChatContextAction.open:
|
||||
onChatTap(room);
|
||||
return;
|
||||
case ChatContextAction.goToSpace:
|
||||
context.go("/rooms/spaces/${space!.id}/details");
|
||||
return;
|
||||
case ChatContextAction.favorite:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.setFavourite(!room.isFavourite),
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.markUnread:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.markUnread(!room.markedUnread),
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.mute:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.setPushRuleState(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? PushRuleState.mentionsOnly
|
||||
: PushRuleState.notify,
|
||||
),
|
||||
);
|
||||
return;
|
||||
case ChatContextAction.leave:
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
message: room.isSpace
|
||||
? L10n.of(context).leaveSpaceDescription
|
||||
: L10n.of(context).leaveRoomDescription,
|
||||
okLabel: L10n.of(context).leave,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
);
|
||||
if (confirmed != OkCancelResult.ok) return;
|
||||
if (!mounted) return;
|
||||
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.isSpace ? room.leaveSpace : room.leave,
|
||||
);
|
||||
if (mounted && !resp.isError) {
|
||||
context.go("/rooms/spaces/${widget.roomId}/details");
|
||||
}
|
||||
|
||||
return;
|
||||
case ChatContextAction.delete:
|
||||
if (room.isSpace) {
|
||||
final resp = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (_) => DeleteSpaceDialog(space: room),
|
||||
);
|
||||
if (resp == true && mounted) {
|
||||
context.go("/rooms");
|
||||
}
|
||||
} else {
|
||||
final confirmed = await showOkCancelAlertDialog(
|
||||
context: context,
|
||||
title: L10n.of(context).areYouSure,
|
||||
okLabel: L10n.of(context).delete,
|
||||
cancelLabel: L10n.of(context).cancel,
|
||||
isDestructive: true,
|
||||
message: room.isSpace
|
||||
? L10n.of(context).deleteSpaceDesc
|
||||
: L10n.of(context).deleteChatDesc,
|
||||
);
|
||||
if (confirmed != OkCancelResult.ok) return;
|
||||
if (!mounted) return;
|
||||
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: room.delete,
|
||||
);
|
||||
if (mounted && !resp.isError) {
|
||||
context.go("/rooms/spaces/${widget.roomId}/details");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool _includeSpaceChild(
|
||||
Room space,
|
||||
SpaceRoomsChunk hierarchyMember,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:fluffychat/config/themes.dart';
|
|||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
|
||||
import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicators.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/chat_context_menu_action.dart';
|
||||
import 'package:fluffychat/pangea/course_chats/activity_template_chat_list_item.dart';
|
||||
import 'package:fluffychat/pangea/course_chats/course_chats_page.dart';
|
||||
import 'package:fluffychat/pangea/course_chats/unjoined_chat_list_item.dart';
|
||||
|
|
@ -112,9 +113,10 @@ class CourseChatsView extends StatelessWidget {
|
|||
return ChatListItem(
|
||||
joinedRoom,
|
||||
onTap: () => controller.onChatTap(joinedRoom),
|
||||
onLongPress: (context) => controller.chatContextAction(
|
||||
onLongPress: (context) => chatContextMenuAction(
|
||||
joinedRoom,
|
||||
context,
|
||||
() => controller.onChatTap(joinedRoom),
|
||||
),
|
||||
activeChat: controller.widget.activeChat == joinedRoom.id,
|
||||
);
|
||||
|
|
@ -169,9 +171,10 @@ class CourseChatsView extends StatelessWidget {
|
|||
return ChatListItem(
|
||||
joinedRoom,
|
||||
onTap: () => controller.onChatTap(joinedRoom),
|
||||
onLongPress: (context) => controller.chatContextAction(
|
||||
onLongPress: (context) => chatContextMenuAction(
|
||||
joinedRoom,
|
||||
context,
|
||||
() => controller.onChatTap(joinedRoom),
|
||||
),
|
||||
activeChat: controller.widget.activeChat == joinedRoom.id,
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ part of "pangea_room_extension.dart";
|
|||
extension RoomInformationRoomExtension on Room {
|
||||
String? get creatorId => getState(EventTypes.RoomCreate)?.senderId;
|
||||
|
||||
DateTime? get creationTimestamp {
|
||||
final creationEvent = getState(EventTypes.RoomCreate) as Event?;
|
||||
return creationEvent?.originServerTs;
|
||||
}
|
||||
|
||||
bool isFirstOrSecondChild(String roomId) {
|
||||
return isSpace &&
|
||||
(spaceChildren.any((room) => room.roomId == roomId) ||
|
||||
|
|
|
|||
|
|
@ -5,52 +5,83 @@ import 'package:fluffychat/config/themes.dart';
|
|||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
|
||||
class InstructionsInlineTooltip extends StatefulWidget {
|
||||
class InstructionsInlineTooltip extends StatelessWidget {
|
||||
final InstructionsEnum instructionsEnum;
|
||||
final bool bold;
|
||||
final bool animate;
|
||||
final EdgeInsets? padding;
|
||||
final VoidCallback? onClose;
|
||||
|
||||
const InstructionsInlineTooltip({
|
||||
super.key,
|
||||
required this.instructionsEnum,
|
||||
this.bold = false,
|
||||
this.animate = true,
|
||||
this.padding,
|
||||
this.onClose,
|
||||
});
|
||||
|
||||
@override
|
||||
InstructionsInlineTooltipState createState() =>
|
||||
InstructionsInlineTooltipState();
|
||||
Widget build(BuildContext context) {
|
||||
return InlineTooltip(
|
||||
message: instructionsEnum.body(L10n.of(context)),
|
||||
isClosed: instructionsEnum.isToggledOff,
|
||||
onClose: () => instructionsEnum.setToggledOff(true),
|
||||
animate: animate,
|
||||
padding: padding,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
|
||||
class InlineTooltip extends StatefulWidget {
|
||||
final String message;
|
||||
final bool isClosed;
|
||||
|
||||
final EdgeInsets? padding;
|
||||
final VoidCallback? onClose;
|
||||
final bool animate;
|
||||
|
||||
const InlineTooltip({
|
||||
super.key,
|
||||
required this.message,
|
||||
required this.isClosed,
|
||||
this.onClose,
|
||||
this.animate = true,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
@override
|
||||
InlineTooltipState createState() => InlineTooltipState();
|
||||
}
|
||||
|
||||
class InlineTooltipState extends State<InlineTooltip>
|
||||
with TickerProviderStateMixin {
|
||||
bool _isToggledOff = true;
|
||||
AnimationController? _controller;
|
||||
Animation<double>? _animation;
|
||||
|
||||
bool _isClosed = true;
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant InstructionsInlineTooltip oldWidget) {
|
||||
if (oldWidget.instructionsEnum != widget.instructionsEnum) {
|
||||
setToggled();
|
||||
void initState() {
|
||||
super.initState();
|
||||
_isClosed = widget.isClosed;
|
||||
_openTooltip();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant InlineTooltip oldWidget) {
|
||||
if (oldWidget.message != widget.message) {
|
||||
_isClosed = widget.isClosed;
|
||||
_openTooltip();
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
setToggled();
|
||||
void dispose() {
|
||||
_controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> setToggled() async {
|
||||
_isToggledOff = widget.instructionsEnum.isToggledOff;
|
||||
|
||||
Future<void> _openTooltip() async {
|
||||
if (widget.animate) {
|
||||
// Initialize AnimationController and Animation only if animate is true
|
||||
_controller?.dispose();
|
||||
_controller = AnimationController(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
vsync: this,
|
||||
|
|
@ -62,7 +93,7 @@ class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
|
|||
);
|
||||
|
||||
// Start in correct state
|
||||
if (!_isToggledOff) {
|
||||
if (!_isClosed) {
|
||||
await _controller!.forward();
|
||||
}
|
||||
}
|
||||
|
|
@ -70,37 +101,18 @@ class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
|
|||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _closeTooltip() async {
|
||||
widget.instructionsEnum.setToggledOff(true);
|
||||
setState(() => _isToggledOff = true);
|
||||
widget.onClose?.call();
|
||||
setState(() => _isClosed = true);
|
||||
|
||||
if (widget.animate) {
|
||||
await _controller?.reverse();
|
||||
}
|
||||
widget.onClose?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.animate
|
||||
? SizeTransition(
|
||||
sizeFactor: _animation!,
|
||||
axisAlignment: -1.0,
|
||||
child: _buildTooltipContent(context),
|
||||
)
|
||||
: (_isToggledOff
|
||||
? const SizedBox.shrink()
|
||||
: _buildTooltipContent(context));
|
||||
}
|
||||
|
||||
Widget _buildTooltipContent(BuildContext context) {
|
||||
return Padding(
|
||||
final content = Padding(
|
||||
padding: widget.padding ?? const EdgeInsets.all(0),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -125,7 +137,7 @@ class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
|
|||
Flexible(
|
||||
child: Center(
|
||||
child: Text(
|
||||
widget.instructionsEnum.body(L10n.of(context)),
|
||||
widget.message,
|
||||
style: FluffyThemes.isColumnMode(context)
|
||||
? Theme.of(context).textTheme.titleLarge
|
||||
: Theme.of(context).textTheme.bodyLarge,
|
||||
|
|
@ -147,5 +159,13 @@ class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
|
|||
),
|
||||
),
|
||||
);
|
||||
|
||||
return widget.animate
|
||||
? SizeTransition(
|
||||
sizeFactor: _animation!,
|
||||
axisAlignment: -1.0,
|
||||
child: content,
|
||||
)
|
||||
: (_isClosed ? const SizedBox.shrink() : content);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,9 @@ class TokenRenderingUtil {
|
|||
bool selected,
|
||||
bool highlighted,
|
||||
bool isNew,
|
||||
bool practiceMode,
|
||||
) {
|
||||
if (practiceMode) return Colors.white.withAlpha(0);
|
||||
if (highlighted) return Theme.of(context).colorScheme.primary;
|
||||
if (isNew) return AppConfig.success;
|
||||
return selected
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instruction_settings.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/spaces/models/space_model.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../../learning_settings/models/language_model.dart';
|
||||
|
||||
/// The user's settings learning settings.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue