From d4ce2988669a394c493d6b9cfdd0a2aaf1696d33 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:12:33 -0400 Subject: [PATCH] chore: make activity bookmark ID final to prevent bookmarking the same activity twice (#2310) --- .../activity_planner/activity_plan_card.dart | 7 +- .../activity_planner/activity_plan_model.dart | 12 +-- .../bookmarked_activities_repo.dart | 7 +- .../bookmarked_activity_list.dart | 9 +- .../activity_suggestion_card.dart | 9 +- .../activity_suggestions_area.dart | 100 +++++++++++------- 6 files changed, 73 insertions(+), 71 deletions(-) diff --git a/lib/pangea/activity_planner/activity_plan_card.dart b/lib/pangea/activity_planner/activity_plan_card.dart index 557378476..ad584bafd 100644 --- a/lib/pangea/activity_planner/activity_plan_card.dart +++ b/lib/pangea/activity_planner/activity_plan_card.dart @@ -100,9 +100,7 @@ class ActivityPlanCardState extends State { Future _addBookmark(ActivityPlanModel activity) async { try { - final uniqueID = - "${activity.title.replaceAll(RegExp(r'\s+'), '-')}-${DateTime.now().millisecondsSinceEpoch}"; - return BookmarkedActivitiesRepo.save(activity, uniqueID); + return BookmarkedActivitiesRepo.save(activity); } catch (e, stack) { debugger(when: kDebugMode); ErrorHandler.logError(e: e, s: stack, data: activity.toJson()); @@ -116,9 +114,8 @@ class ActivityPlanCardState extends State { } Future _removeBookmark() async { - if (widget.activity.bookmarkId == null) return; try { - BookmarkedActivitiesRepo.remove(widget.activity.bookmarkId!); + BookmarkedActivitiesRepo.remove(widget.activity.bookmarkId); } catch (e, stack) { debugger(when: kDebugMode); ErrorHandler.logError(e: e, s: stack, data: widget.activity.toJson()); diff --git a/lib/pangea/activity_planner/activity_plan_model.dart b/lib/pangea/activity_planner/activity_plan_model.dart index 4e892cbe0..e99bf4f95 100644 --- a/lib/pangea/activity_planner/activity_plan_model.dart +++ b/lib/pangea/activity_planner/activity_plan_model.dart @@ -4,13 +4,13 @@ import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; class ActivityPlanModel { + final String bookmarkId; final ActivityPlanRequest req; String title; String learningObjective; String instructions; List vocab; String? imageURL; - String? bookmarkId; ActivityPlanModel({ required this.req, @@ -18,9 +18,8 @@ class ActivityPlanModel { required this.learningObjective, required this.instructions, required this.vocab, - this.bookmarkId, this.imageURL, - }); + }) : bookmarkId = req.hashCode.toString(); factory ActivityPlanModel.fromJson(Map json) { return ActivityPlanModel( @@ -32,7 +31,6 @@ class ActivityPlanModel { json[ModelKey.activityPlanVocab].map((vocab) => Vocab.fromJson(vocab)), ), imageURL: json[ModelKey.activityPlanImageURL], - bookmarkId: json[ModelKey.activityPlanBookmarkId], ); } @@ -77,8 +75,7 @@ class ActivityPlanModel { other.learningObjective == learningObjective && other.instructions == instructions && listEquals(other.vocab, vocab) && - other.imageURL == imageURL && - other.bookmarkId == bookmarkId; + other.imageURL == imageURL; } @override @@ -88,8 +85,7 @@ class ActivityPlanModel { learningObjective.hashCode ^ instructions.hashCode ^ Object.hashAll(vocab) ^ - imageURL.hashCode ^ - bookmarkId.hashCode; + imageURL.hashCode; } class Vocab { diff --git a/lib/pangea/activity_planner/bookmarked_activities_repo.dart b/lib/pangea/activity_planner/bookmarked_activities_repo.dart index 33df6fd13..866144f4d 100644 --- a/lib/pangea/activity_planner/bookmarked_activities_repo.dart +++ b/lib/pangea/activity_planner/bookmarked_activities_repo.dart @@ -11,11 +11,9 @@ class BookmarkedActivitiesRepo { /// returns the activity with a bookmarkId static Future save( ActivityPlanModel activity, - String bookmarkID, ) async { - activity.bookmarkId = bookmarkID; await _bookStorage.write( - bookmarkID, + activity.bookmarkId, activity.toJson(), ); @@ -27,8 +25,7 @@ class BookmarkedActivitiesRepo { _bookStorage.remove(bookmarkId); static bool isBookmarked(ActivityPlanModel activity) { - return activity.bookmarkId != null && - _bookStorage.read(activity.bookmarkId!) != null; + return _bookStorage.read(activity.bookmarkId) != null; } static List get() { diff --git a/lib/pangea/activity_planner/bookmarked_activity_list.dart b/lib/pangea/activity_planner/bookmarked_activity_list.dart index 8a453546b..9bbd41ae1 100644 --- a/lib/pangea/activity_planner/bookmarked_activity_list.dart +++ b/lib/pangea/activity_planner/bookmarked_activity_list.dart @@ -52,13 +52,8 @@ class BookmarkedActivitiesListState extends State { activity.imageURL = url.toString(); } - final uniqueID = - "${activity.title.replaceAll(RegExp(r'\s+'), '-')}-${DateTime.now().millisecondsSinceEpoch}"; - - if (activity.bookmarkId != null) { - await BookmarkedActivitiesRepo.remove(activity.bookmarkId!); - } - await BookmarkedActivitiesRepo.save(activity, uniqueID); + await BookmarkedActivitiesRepo.remove(activity.bookmarkId); + await BookmarkedActivitiesRepo.save(activity); if (mounted) setState(() {}); } diff --git a/lib/pangea/activity_suggestions/activity_suggestion_card.dart b/lib/pangea/activity_suggestions/activity_suggestion_card.dart index bbf2393f6..016334287 100644 --- a/lib/pangea/activity_suggestions/activity_suggestion_card.dart +++ b/lib/pangea/activity_suggestions/activity_suggestion_card.dart @@ -189,16 +189,11 @@ class ActivitySuggestionCard extends StatelessWidget { ), onPressed: onPressed != null ? () async { - final uniqueID = - "${activity.title.replaceAll(RegExp(r'\s+'), '-')}-${DateTime.now().millisecondsSinceEpoch}"; await (isBookmarked ? BookmarkedActivitiesRepo.remove( - activity.bookmarkId!, + activity.bookmarkId, ) - : BookmarkedActivitiesRepo.save( - activity, - uniqueID, - )); + : BookmarkedActivitiesRepo.save(activity)); onChange(); } : null, diff --git a/lib/pangea/activity_suggestions/activity_suggestions_area.dart b/lib/pangea/activity_suggestions/activity_suggestions_area.dart index 8fff4450b..abd147974 100644 --- a/lib/pangea/activity_suggestions/activity_suggestions_area.dart +++ b/lib/pangea/activity_suggestions/activity_suggestions_area.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; +import 'package:shimmer/shimmer.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart'; @@ -48,6 +49,7 @@ class ActivitySuggestionsAreaState extends State { super.dispose(); } + bool _loading = true; bool get _isColumnMode => FluffyThemes.isColumnMode(context); final List _activityItems = []; @@ -57,52 +59,72 @@ class ActivitySuggestionsAreaState extends State { double get cardWidth => _isColumnMode ? 225.0 : 150.0; Future _setActivityItems() async { - final ActivityPlanRequest request = ActivityPlanRequest( - topic: "", - mode: "", - objective: "", - media: MediaEnum.nan, - cefrLevel: LanguageLevelTypeEnum.a1, - languageOfInstructions: LanguageKeys.defaultLanguage, - targetLanguage: - MatrixState.pangeaController.languageController.userL2?.langCode ?? - LanguageKeys.defaultLanguage, - numberOfParticipants: 3, - count: 5, - ); - final resp = await ActivitySearchRepo.get(request); - _activityItems.addAll(resp.activityPlans); - if (mounted) setState(() {}); + try { + final ActivityPlanRequest request = ActivityPlanRequest( + topic: "", + mode: "", + objective: "", + media: MediaEnum.nan, + cefrLevel: LanguageLevelTypeEnum.a1, + languageOfInstructions: LanguageKeys.defaultLanguage, + targetLanguage: + MatrixState.pangeaController.languageController.userL2?.langCode ?? + LanguageKeys.defaultLanguage, + numberOfParticipants: 3, + count: 5, + ); + final resp = await ActivitySearchRepo.get(request); + _activityItems.addAll(resp.activityPlans); + } finally { + if (mounted) setState(() => _loading = false); + } } @override Widget build(BuildContext context) { - final List cards = _activityItems - .map((activity) { - return ActivitySuggestionCard( - activity: activity, - onPressed: () { - showDialog( - context: context, - builder: (context) { - return ActivitySuggestionDialog( - activity: activity, - buttonText: L10n.of(context).inviteAndLaunch, - room: widget.room, + final theme = Theme.of(context); + final List cards = _loading + ? List.generate(5, (i) { + return Shimmer.fromColors( + baseColor: theme.colorScheme.primary.withAlpha(20), + highlightColor: theme.colorScheme.primary.withAlpha(50), + child: Container( + height: cardHeight, + width: cardWidth, + margin: EdgeInsets.all(cardPadding), + decoration: BoxDecoration( + color: theme.colorScheme.surfaceContainer, + borderRadius: BorderRadius.circular(24.0), + ), + ), + ); + }) + : _activityItems + .map((activity) { + return ActivitySuggestionCard( + activity: activity, + onPressed: () { + showDialog( + context: context, + builder: (context) { + return ActivitySuggestionDialog( + activity: activity, + buttonText: L10n.of(context).inviteAndLaunch, + room: widget.room, + ); + }, ); }, + width: cardWidth, + height: cardHeight, + padding: cardPadding, + onChange: () { + if (mounted) setState(() {}); + }, ); - }, - width: cardWidth, - height: cardHeight, - padding: cardPadding, - onChange: () { - if (mounted) setState(() {}); - }, - ); - }) - .cast() - .toList(); + }) + .cast() + .toList(); final scrollDirection = widget.scrollDirection ?? (_isColumnMode ? Axis.horizontal : Axis.vertical);