Skip to content

Commit 269af9f

Browse files
authored
feat: show unlocked constructs snackbar (#2193)
1 parent b104069 commit 269af9f

File tree

7 files changed

+133
-16
lines changed

7 files changed

+133
-16
lines changed

assets/l10n/intl_en.arb

+2-1
Original file line numberDiff line numberDiff line change
@@ -4827,5 +4827,6 @@
48274827
"startChatting": "Start chatting",
48284828
"referFriends": "Refer friends",
48294829
"referFriendDialogTitle": "Invite a friend to your conversation",
4830-
"referFriendDialogDesc": "Do you have a friend who is excited to learn a new language with you? Then copy and send this invitation link to join and start chatting with you today."
4830+
"referFriendDialogDesc": "Do you have a friend who is excited to learn a new language with you? Then copy and send this invitation link to join and start chatting with you today.",
4831+
"youUnlocked": "You've unlocked"
48314832
}

lib/pages/chat/chat.dart

+18-8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import 'package:fluffychat/pages/chat_details/chat_details.dart';
3030
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
3131
import 'package:fluffychat/pangea/analytics_misc/level_up.dart';
3232
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
33+
import 'package:fluffychat/pangea/chat/utils/unlocked_morphs_snackbar.dart';
3334
import 'package:fluffychat/pangea/chat/widgets/event_too_large_dialog.dart';
3435
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
3536
import 'package:fluffychat/pangea/choreographer/enums/edit_type.dart';
@@ -365,7 +366,9 @@ class ChatController extends State<ChatPageWithRoom>
365366

366367
_levelSubscription = pangeaController.getAnalytics.stateStream
367368
.where(
368-
(update) => update is Map<String, dynamic> && update['level_up'] != null,
369+
(update) =>
370+
update is Map<String, dynamic> &&
371+
(update['level_up'] != null || update['unlocked_constructs'] != null),
369372
)
370373
// .listen(
371374
// (update) => Future.delayed(
@@ -380,13 +383,20 @@ class ChatController extends State<ChatPageWithRoom>
380383
// remove delay now that GetAnalyticsController._onLevelUp
381384
// is async is should take roughly 500ms to make requests anyway
382385
(update) {
383-
LevelUpUtil.showLevelUpDialog(
384-
update['level_up'],
385-
update['analytics_room_id'],
386-
update["construct_summary_state_event_id"],
387-
update['construct_summary'],
388-
context,
389-
);
386+
if (update['level_up'] != null) {
387+
LevelUpUtil.showLevelUpDialog(
388+
update['level_up'],
389+
update['analytics_room_id'],
390+
update["construct_summary_state_event_id"],
391+
update['construct_summary'],
392+
context,
393+
);
394+
} else if (update['unlocked_constructs'] != null) {
395+
showUnlockedMorphsSnackbar(
396+
update['unlocked_constructs'],
397+
context,
398+
);
399+
}
390400
},
391401
);
392402
// Pangea#

lib/pangea/analytics_details_popup/morph_analytics_list_view.dart

+6-6
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class MorphFeatureBox extends StatelessWidget {
158158
morphFeature: morphFeature,
159159
morphTag: morphTag,
160160
constructAnalytics: analytics,
161-
onTap: analytics.points > 0
161+
onTap: analytics.points > 10
162162
? () => onConstructZoom(id)
163163
: null,
164164
);
@@ -196,16 +196,17 @@ class MorphTagChip extends StatelessWidget {
196196
@override
197197
Widget build(BuildContext context) {
198198
final theme = Theme.of(context);
199+
final unlocked = constructAnalytics.points > 10;
199200

200201
return InkWell(
201202
borderRadius: BorderRadius.circular(32.0),
202203
onTap: onTap,
203204
child: Opacity(
204-
opacity: constructAnalytics.points > 0 ? 1.0 : 0.3,
205+
opacity: unlocked ? 1.0 : 0.3,
205206
child: Container(
206207
decoration: BoxDecoration(
207208
borderRadius: BorderRadius.circular(32.0),
208-
gradient: constructAnalytics.points > 0
209+
gradient: unlocked
209210
? LinearGradient(
210211
begin: Alignment.centerLeft,
211212
end: Alignment.centerRight,
@@ -215,7 +216,7 @@ class MorphTagChip extends StatelessWidget {
215216
],
216217
)
217218
: null,
218-
color: constructAnalytics.points > 0 ? null : theme.disabledColor,
219+
color: unlocked ? null : theme.disabledColor,
219220
),
220221
padding: const EdgeInsets.symmetric(
221222
vertical: 4.0,
@@ -228,8 +229,7 @@ class MorphTagChip extends StatelessWidget {
228229
SizedBox(
229230
width: 28.0,
230231
height: 28.0,
231-
child: constructAnalytics.points > 0 ||
232-
Matrix.of(context).client.isSupportAccount
232+
child: unlocked || Matrix.of(context).client.isSupportAccount
233233
? MorphIcon(
234234
morphFeature: morphFeature,
235235
morphTag: morphTag,

lib/pangea/analytics_misc/construct_list_model.dart

+16
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ class ConstructListModel {
3636
/// A list of unique grammar lemmas
3737
List<String> grammarLemmasList = [];
3838

39+
List<ConstructIdentifier> get unlockedGrammarLemmas {
40+
final morphs = constructList(type: ConstructTypeEnum.morph);
41+
final List<ConstructIdentifier> unlocked = [];
42+
for (final morph in grammarLemmasList) {
43+
final matches = morphs.where((m) => m.lemma == morph);
44+
final totalPoints = matches.fold<int>(
45+
0,
46+
(total, match) => total + match.points,
47+
);
48+
if (totalPoints > 10) {
49+
unlocked.add(matches.first.id);
50+
}
51+
}
52+
return unlocked;
53+
}
54+
3955
/// Analytics data consumed by widgets. Updated each time new analytics come in.
4056
int prevXP = 0;
4157
int totalXP = 0;

lib/pangea/analytics_misc/get_analytics_controller.dart

+13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'package:fluffychat/pangea/common/constants/local.key.dart';
1818
import 'package:fluffychat/pangea/common/controllers/base_controller.dart';
1919
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
2020
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
21+
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
2122
import 'package:fluffychat/pangea/constructs/construct_repo.dart';
2223
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
2324
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
@@ -139,7 +140,12 @@ class GetAnalyticsController extends BaseController {
139140

140141
final offset =
141142
_pangeaController.userController.publicProfile?.xpOffset ?? 0;
143+
final prevUnlockedMorphs = constructListModel.unlockedGrammarLemmas.toSet();
142144
constructListModel.updateConstructs(analyticsUpdate.newConstructs, offset);
145+
final newUnlockedMorphs = constructListModel.unlockedGrammarLemmas
146+
.toSet()
147+
.difference(prevUnlockedMorphs);
148+
143149
if (analyticsUpdate.type == AnalyticsUpdateType.server) {
144150
await _getConstructs(forceUpdate: true);
145151
}
@@ -149,6 +155,9 @@ class GetAnalyticsController extends BaseController {
149155
if (oldLevel > constructListModel.level) {
150156
await _onLevelDown(constructListModel.level, oldLevel);
151157
}
158+
if (newUnlockedMorphs.isNotEmpty) {
159+
_onUnlockMorphLemmas(newUnlockedMorphs);
160+
}
152161
_updateAnalyticsStream(origin: analyticsUpdate.origin);
153162
// Update public profile each time that new analytics are added.
154163
// If the level hasn't changed, this will not send an update to the server.
@@ -187,6 +196,10 @@ class GetAnalyticsController extends BaseController {
187196
);
188197
}
189198

199+
void _onUnlockMorphLemmas(Set<ConstructIdentifier> unlocked) {
200+
setState({'unlocked_constructs': unlocked});
201+
}
202+
190203
/// A local cache of eventIds and construct uses for messages sent since the last update.
191204
/// It's a map of eventIDs to a list of OneConstructUses. Not just a list of OneConstructUses
192205
/// because, with practice activity constructs, we might need to add to the list for a given

lib/pangea/analytics_summary/learning_progress_indicators.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class LearningProgressIndicatorsState
8585
int uniqueLemmas(ProgressIndicatorEnum indicator) {
8686
switch (indicator) {
8787
case ProgressIndicatorEnum.morphsUsed:
88-
return _constructsModel.grammarLemmas;
88+
return _constructsModel.unlockedGrammarLemmas.length;
8989
case ProgressIndicatorEnum.wordsUsed:
9090
return _constructsModel.vocabLemmas;
9191
default:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// ignore_for_file: depend_on_referenced_packages, implementation_imports
2+
3+
import 'package:flutter/material.dart';
4+
5+
import 'package:flutter_gen/gen_l10n/l10n.dart';
6+
7+
import 'package:fluffychat/config/app_config.dart';
8+
import 'package:fluffychat/config/themes.dart';
9+
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
10+
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
11+
import 'package:fluffychat/pangea/morphs/morph_icon.dart';
12+
13+
void showUnlockedMorphsSnackbar(
14+
Set<ConstructIdentifier> unlockedConstructs,
15+
BuildContext context,
16+
) {
17+
for (final construct in unlockedConstructs) {
18+
final copy = getGrammarCopy(
19+
category: construct.category,
20+
lemma: construct.lemma,
21+
context: context,
22+
);
23+
ScaffoldMessenger.of(context).showSnackBar(
24+
SnackBar(
25+
behavior: FluffyThemes.isColumnMode(context)
26+
? SnackBarBehavior.floating
27+
: SnackBarBehavior.fixed,
28+
width: FluffyThemes.isColumnMode(context)
29+
? MediaQuery.of(context).size.width
30+
: null,
31+
showCloseIcon: true,
32+
duration: const Duration(seconds: 5),
33+
dismissDirection: DismissDirection.none,
34+
backgroundColor: Theme.of(context).colorScheme.surface,
35+
content: Padding(
36+
padding: const EdgeInsets.all(8.0),
37+
child: Wrap(
38+
spacing: 16.0,
39+
alignment: WrapAlignment.center,
40+
children: [
41+
Text(
42+
L10n.of(context).youUnlocked,
43+
style: TextStyle(
44+
fontSize: FluffyThemes.isColumnMode(context) ? 32.0 : 16.0,
45+
color: Theme.of(context).colorScheme.onSurface,
46+
fontWeight: FontWeight.bold,
47+
),
48+
),
49+
Row(
50+
mainAxisSize: MainAxisSize.min,
51+
spacing: 16.0,
52+
children: [
53+
Flexible(
54+
child: Text(
55+
copy ?? construct.lemma,
56+
style: TextStyle(
57+
fontSize:
58+
FluffyThemes.isColumnMode(context) ? 32.0 : 16.0,
59+
color: AppConfig.gold,
60+
fontWeight: FontWeight.bold,
61+
),
62+
overflow: TextOverflow.ellipsis,
63+
),
64+
),
65+
MorphIcon(
66+
morphFeature: construct.category,
67+
morphTag: construct.lemma,
68+
),
69+
],
70+
),
71+
],
72+
),
73+
),
74+
),
75+
);
76+
}
77+
}

0 commit comments

Comments
 (0)