diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 52f75dde6..180202947 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -146,7 +146,7 @@ class Message extends StatelessWidget { toggleInstructions: controller.toggleShowInstructions, getParticipantOpacity: (role) => role == null || role.isFinished ? 0.5 : 1.0, - isParticipantSelected: (id) => controller.room.ownRole?.id == id, + isParticipantSelected: (id) => controller.room.ownRoleState?.id == id, ); } diff --git a/lib/pangea/activity_sessions/activity_role_model.dart b/lib/pangea/activity_sessions/activity_role_model.dart index d827cd784..52edb1c73 100644 --- a/lib/pangea/activity_sessions/activity_role_model.dart +++ b/lib/pangea/activity_sessions/activity_role_model.dart @@ -6,6 +6,7 @@ class ActivityRoleModel { final String? role; DateTime? finishedAt; DateTime? archivedAt; + bool dismissedGoalTooltip; ActivityRoleModel({ required this.id, @@ -13,6 +14,7 @@ class ActivityRoleModel { this.role, this.finishedAt, this.archivedAt, + this.dismissedGoalTooltip = false, }); bool get isFinished => finishedAt != null; @@ -45,6 +47,7 @@ class ActivityRoleModel { archivedAt: json['archived_at'] != null ? DateTime.parse(json['archived_at']) : null, + dismissedGoalTooltip: json['dismissed_goal_tooltip'] as bool? ?? false, ); } @@ -55,6 +58,7 @@ class ActivityRoleModel { 'role': role, 'finished_at': finishedAt?.toIso8601String(), 'archived_at': archivedAt?.toIso8601String(), + 'dismissed_goal_tooltip': dismissedGoalTooltip, }; } @@ -67,7 +71,8 @@ class ActivityRoleModel { other.role == role && other.finishedAt == finishedAt && other.archivedAt == archivedAt && - other.id == id; + other.id == id && + other.dismissedGoalTooltip == dismissedGoalTooltip; } @override @@ -76,5 +81,6 @@ class ActivityRoleModel { role.hashCode ^ (finishedAt?.hashCode ?? 0) ^ (archivedAt?.hashCode ?? 0) ^ - id.hashCode; + id.hashCode ^ + dismissedGoalTooltip.hashCode; } diff --git a/lib/pangea/activity_sessions/activity_roles_model.dart b/lib/pangea/activity_sessions/activity_roles_model.dart index 8d045fcbd..a2942e751 100644 --- a/lib/pangea/activity_sessions/activity_roles_model.dart +++ b/lib/pangea/activity_sessions/activity_roles_model.dart @@ -15,6 +15,10 @@ class ActivityRolesModel { roles[role.id] = role; } + void dismissTooltip(ActivityRoleModel role) { + roles[role.id]?.dismissedGoalTooltip = true; + } + void finishAll() { for (final id in roles.keys) { if (roles[id]!.isFinished) continue; diff --git a/lib/pangea/activity_sessions/activity_room_extension.dart b/lib/pangea/activity_sessions/activity_room_extension.dart index b4004668d..b5ab6aff3 100644 --- a/lib/pangea/activity_sessions/activity_room_extension.dart +++ b/lib/pangea/activity_sessions/activity_room_extension.dart @@ -43,7 +43,7 @@ extension ActivityRoomExtension on Room { Future continueActivity() async { final currentRoles = activityRoles ?? ActivityRolesModel.empty; - final role = ownRole; + final role = ownRoleState; if (role == null || !role.isFinished) return; role.finishedAt = null; // Reset finished state @@ -58,7 +58,7 @@ extension ActivityRoomExtension on Room { Future finishActivity() async { final currentRoles = activityRoles ?? ActivityRolesModel.empty; - final role = ownRole; + final role = ownRoleState; if (role == null || role.isFinished) return; role.finishedAt = DateTime.now(); currentRoles.updateRole(role); @@ -84,7 +84,7 @@ extension ActivityRoomExtension on Room { Future archiveActivity() async { final currentRoles = activityRoles ?? ActivityRolesModel.empty; - final role = ownRole; + final role = ownRoleState; if (role == null || !role.isFinished || role.isArchived) return; role.archivedAt = DateTime.now(); @@ -97,6 +97,21 @@ extension ActivityRoomExtension on Room { ); } + Future dismissGoalTooltip() async { + final currentRoles = activityRoles ?? ActivityRolesModel.empty; + final role = ownRoleState; + if (role == null) return; + role.finishedAt = DateTime.now(); + currentRoles.dismissTooltip(role); + + await client.setRoomStateWithKey( + id, + PangeaEventTypes.activityRole, + "", + currentRoles.toJson(), + ); + } + Future setActivitySummary( ActivitySummaryModel summary, ) async { @@ -270,7 +285,14 @@ extension ActivityRoomExtension on Room { ); } - ActivityRoleModel? get ownRole => activityRoles?.role(client.userID!); + ActivityRole? get ownRole { + final role = ownRoleState; + if (role == null || activityPlan == null) return null; + + return activityPlan!.roles[role.id]; + } + + ActivityRoleModel? get ownRoleState => activityRoles?.role(client.userID!); int get remainingRoles { final availableRoles = activityPlan?.roles; @@ -289,17 +311,17 @@ extension ActivityRoomExtension on Room { bool get isActiveInActivity { if (!showActivityChatUI) return false; - final role = ownRole; + final role = ownRoleState; return role != null && !role.isFinished; } bool get isInactiveInActivity { if (!showActivityChatUI) return false; - final role = ownRole; + final role = ownRoleState; return role == null || role.isFinished; } - bool get hasCompletedActivity => ownRole?.isFinished ?? false; + bool get hasCompletedActivity => ownRoleState?.isFinished ?? false; bool get activityIsFinished { final roles = activityRoles?.roles.values.where( @@ -321,7 +343,10 @@ extension ActivityRoomExtension on Room { }); } - bool get isHiddenActivityRoom => ownRole?.isArchived ?? false; + bool get isHiddenActivityRoom => ownRoleState?.isArchived ?? false; + + bool get hasDismissedGoalTooltip => + ownRoleState?.dismissedGoalTooltip ?? false; Room? get courseParent => pangeaSpaceParents.firstWhereOrNull( (parent) => parent.coursePlan != null, @@ -333,7 +358,7 @@ extension ActivityRoomExtension on Room { bool get isActivitySession => isActivityRoomType || activityPlan != null; bool get showActivityFinished => - showActivityChatUI && ownRole != null && hasCompletedActivity; + showActivityChatUI && ownRoleState != null && hasCompletedActivity; String? get activityId { if (!isActivitySession) return null; diff --git a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart index a6720f8eb..9f1df636e 100644 --- a/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart +++ b/lib/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart @@ -84,7 +84,7 @@ class ActivitySessionStartController extends State { false; SessionState get state { - if (room?.ownRole != null) return SessionState.confirmedRole; + if (room?.ownRoleState != null) return SessionState.confirmedRole; if (_selectedRoleId != null) return SessionState.selectedRole; if (room == null) { return widget.isNew @@ -99,7 +99,7 @@ class ActivitySessionStartController extends State { case SessionState.confirmedRole: return L10n.of(context).waitingToFillRole(room!.remainingRoles); case SessionState.selectedRole: - return activity!.description; + return activity!.roles[_selectedRoleId!]!.goal; case SessionState.notStarted: return null; @@ -131,7 +131,7 @@ class ActivitySessionStartController extends State { bool isParticipantSelected(String id) { if (state == SessionState.confirmedRole) { - return room?.ownRole?.id == id; + return room?.ownRoleState?.id == id; } return _selectedRoleId == id; } diff --git a/lib/pangea/chat/widgets/chat_input_bar.dart b/lib/pangea/chat/widgets/chat_input_bar.dart index 7c03b8b92..1890f6f53 100644 --- a/lib/pangea/chat/widgets/chat_input_bar.dart +++ b/lib/pangea/chat/widgets/chat_input_bar.dart @@ -6,6 +6,7 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/chat_emoji_picker.dart'; import 'package:fluffychat/pages/chat/reply_display.dart'; +import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/chat/widgets/pangea_chat_input_row.dart'; import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart'; @@ -34,6 +35,12 @@ class ChatInputBarState extends State { }); } + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) => _updateHeight()); + } + @override void dispose() { _debounceTimer?.cancel(); @@ -48,46 +55,92 @@ class ChatInputBarState extends State { return true; }, child: SizeChangedLayoutNotifier( - child: Container( - padding: EdgeInsets.only( - bottom: widget.padding, - left: widget.padding, - right: widget.padding, - ), - constraints: const BoxConstraints( - maxWidth: FluffyThemes.maxTimelineWidth, - ), - alignment: Alignment.center, - child: Material( - clipBehavior: Clip.hardEdge, - type: MaterialType.transparency, - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), - child: Column( - children: [ - ITBar(choreographer: widget.controller.choreographer), - DecoratedBox( - decoration: BoxDecoration( - color: - Theme.of(context).colorScheme.surfaceContainerHighest, + child: Column( + children: [ + if (widget.controller.room.showActivityChatUI && + widget.controller.room.ownRole?.goal != null) + Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border( + top: BorderSide( + color: Theme.of(context).colorScheme.outline, + width: 0.1, + ), ), - child: Column( - children: [ - // #Pangea - if (!widget.controller.obscureText) - // Pangea# - ReplyDisplay(widget.controller), - PangeaChatInputRow( - controller: widget.controller, + ), + child: AnimatedSize( + duration: FluffyThemes.animationDuration, + child: widget.controller.room.hasDismissedGoalTooltip + ? const SizedBox() + : Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + spacing: 10.0, + children: [ + Expanded( + child: Text( + widget.controller.room.ownRole!.goal!, + style: const TextStyle( + fontSize: 12.0, + ), + textAlign: TextAlign.center, + ), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () async { + await widget.controller.room + .dismissGoalTooltip(); + if (mounted) setState(() {}); + }, + ), + ], + ), + ), + ), + ), + Container( + padding: EdgeInsets.only( + bottom: widget.padding, + left: widget.padding, + right: widget.padding, + ), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.maxTimelineWidth, + ), + alignment: Alignment.center, + child: Material( + clipBehavior: Clip.hardEdge, + type: MaterialType.transparency, + borderRadius: const BorderRadius.all( + Radius.circular(24), + ), + child: Column( + children: [ + ITBar(choreographer: widget.controller.choreographer), + DecoratedBox( + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .surfaceContainerHighest, ), - ChatEmojiPicker(widget.controller), - ], - ), + child: Column( + children: [ + if (!widget.controller.obscureText) + ReplyDisplay(widget.controller), + PangeaChatInputRow( + controller: widget.controller, + ), + ChatEmojiPicker(widget.controller), + ], + ), + ), + ], ), - ], + ), ), - ), + ], ), ), );