feat: finish up level-up animation

Take out testing button and functions, lower XP needed in staging to level up for test reasons
This commit is contained in:
avashilling 2025-06-23 15:43:59 -04:00
parent e52d72cdb4
commit 0c244540e1
7 changed files with 69 additions and 148 deletions

View file

@ -1,13 +1,12 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/level_up/level_up_banner.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
import 'package:fluffychat/widgets/settings_switch_list_tile.dart';
import 'package:flutter/material.dart';
import 'settings_chat.dart';
class SettingsChatView extends StatelessWidget {
@ -29,16 +28,6 @@ class SettingsChatView extends StatelessWidget {
child: MaxWidthBody(
child: Column(
children: [
ElevatedButton(
onPressed: () {
LevelUpUtil.showLevelUpDialog(
3,
2,
context,
);
},
child: const Text('Show Level Up Dialog'),
),
// #Pangea
// SettingsSwitchListTile.adaptive(
// title: L10n.of(context).formattedMessages,

View file

@ -1,16 +1,15 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:flutter/material.dart';
/// A wrapper around a list of [OneConstructUse]s, used to simplify
/// the process of filtering / sorting / displaying the events.
@ -35,7 +34,8 @@ class ConstructListModel {
/// [D] is the "compression factor". It determines how quickly
/// or slowly the level grows relative to XP
final double D = 1500;
final double D = Environment.isStagingEnvironment ? 500 : 1500;
List<ConstructIdentifier> unlockedLemmas(
ConstructTypeEnum type, {

View file

@ -1,11 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';
import 'package:matrix/matrix.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_list_model.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
@ -23,6 +17,10 @@ import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
import 'package:fluffychat/pangea/practice_activities/practice_selection_repo.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';
import 'package:matrix/matrix.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
/// A minimized version of AnalyticsController that get the logged in user's analytics
class GetAnalyticsController extends BaseController {
@ -484,7 +482,6 @@ class GetAnalyticsController extends BaseController {
final int maxXP = constructListModel.calculateXpWithLevel(upperLevel);
final int minXP = constructListModel.calculateXpWithLevel(lowerLevel);
int diffXP = maxXP - minXP;
debugPrint("minXP: $minXP, maxXP: $maxXP, diffXP: $diffXP");
if (diffXP < 0) diffXP = 0;
// compute construct use of current level

View file

@ -1,10 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
@ -13,6 +10,7 @@ import 'package:fluffychat/pangea/analytics_misc/level_up/level_up_popup.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/common/utils/overlay.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
class LevelUpConstants {
static const String starFileName = "star.png";
@ -103,7 +101,6 @@ class LevelUpBannerState extends State<LevelUpBanner>
context,
widget.level,
widget.prevLevel,
true, //value true if testing, false if real data
);
_slideController = AnimationController(

View file

@ -1,12 +1,10 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/constructs/construct_repo.dart';
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
class LevelUpManager {
// Singleton instance so analytics can be generated when level up is initiated, and be ready by the time user clicks on banner
@ -34,14 +32,13 @@ class LevelUpManager {
BuildContext context,
int level,
int prevLevel,
bool test,
) async {
this.level = level;
this.prevLevel = prevLevel;
//For on route change behavior, if added in the future
shouldAutoPopup = true;
//grammar and vocab
nextGrammar = MatrixState
.pangeaController.getAnalytics.constructListModel.grammarLemmas;
nextVocab = MatrixState
@ -51,12 +48,7 @@ class LevelUpManager {
.activeL2Code()
?.toUpperCase();
// fetch construct summary based on test value
if (test) {
getConstructFromButton();
} else {
getConstructFromLevelUp();
}
getConstructFromLevelUp();
final LanguageModel? l2 =
MatrixState.pangeaController.languageController.userL2;
@ -75,10 +67,6 @@ class LevelUpManager {
)
.toList();
debugPrint("List of all previous level up summaries: $summaryEvents");
for (final summary in summaryEvents) {
debugPrint("${summary.toJson()}");
}
//Find previous summary to get grammar constructs and vocab numbers from
final lastSummary = summaryEvents
.where((summary) => summary.upperLevel == prevLevel)
@ -89,21 +77,20 @@ class LevelUpManager {
: null;
//Set grammar and vocab from last level summary, if there is one. Otherwise set to placeholder data
debugPrint("Last construct summary is: ${lastSummary?.toJson()}");
if (lastSummary != null &&
lastSummary.levelVocabConstructs != null &&
lastSummary.levelGrammarConstructs != null) {
prevVocab = lastSummary.levelVocabConstructs!;
prevGrammar = lastSummary.levelGrammarConstructs!;
} else {
prevGrammar = nextGrammar < 30 ? 0 : nextGrammar - 30;
prevVocab = nextVocab < 30 ? 0 : nextVocab - 30;
prevGrammar = (nextGrammar / prevLevel) as int;
prevVocab = (nextVocab / prevLevel) as int;
}
}
}
//for testing, just fetch last level up from saved analytics
void getConstructFromButton() {
//for testing, just fetch last level up from saved analytics
constructSummary = MatrixState.pangeaController.getAnalytics
.getConstructSummaryFromStateEvent();
debugPrint(
@ -111,8 +98,8 @@ class LevelUpManager {
);
}
//for getting real level up data when leveled up
void getConstructFromLevelUp() async {
//for getting real level up data when leveled up
try {
constructSummary = await MatrixState.pangeaController.getAnalytics
.generateLevelUpAnalytics(
@ -124,19 +111,6 @@ class LevelUpManager {
}
}
// void printAnalytics() {
// debugPrint('Level Up Analytics:');
// debugPrint('Current Level: $level');
// debugPrint('Previous Level: $prevLevel');
// debugPrint('Next Grammar: $nextGrammar');
// debugPrint('Next Vocab: $nextVocab');
// if (constructSummary != null) {
// debugPrint('Construct Summary: ${constructSummary!.toJson()}');
// } else {
// debugPrint('Construct Summary: Not available');
// }
// }
void markPopupSeen() {
hasSeenPopup = true;
shouldAutoPopup = false;
@ -153,6 +127,5 @@ class LevelUpManager {
nextVocab = 0;
constructSummary = null;
error = null;
// Reset any other state if necessary
}
}

View file

@ -1,15 +1,9 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:animated_flip_counter/animated_flip_counter.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:confetti/confetti.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:matrix/matrix_api_lite/generated/model.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/learning_skills_enum.dart';
@ -23,6 +17,10 @@ import 'package:fluffychat/pangea/constructs/construct_repo.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:matrix/matrix_api_lite/generated/model.dart';
class LevelUpPopup extends StatelessWidget {
const LevelUpPopup({
@ -74,24 +72,21 @@ class _LevelUpPopupContentState extends State<LevelUpPopupContent>
with SingleTickerProviderStateMixin {
late int _endGrammar;
late int _endVocab;
late final AnimationController _controller;
Uri? avatarUrl;
late final Future<Profile> profile;
late final ConfettiController _confettiController;
int displayedLevel = -1;
bool _hasBlastedConfetti = false;
final int _startGrammar = LevelUpManager.instance.prevGrammar;
final int _startVocab = LevelUpManager.instance.prevVocab;
late ConstructSummary? _constructSummary;
Timer? _summaryPollTimer;
final String? _error = LevelUpManager.instance.error;
String language = LevelUpManager.instance.userL2Code ?? "N/A";
static const Duration _animationDuration = Duration(seconds: 5);
late final AnimationController _controller;
late final ConfettiController _confettiController;
bool _hasBlastedConfetti = false;
final Duration _animationDuration = const Duration(seconds: 5);
Uri? avatarUrl;
late final Future<Profile> profile;
int displayedLevel = -1;
late ConstructSummary? _constructSummary;
@override
void initState() {
@ -269,7 +264,7 @@ class _LevelUpPopupContentState extends State<LevelUpPopupContent>
builder: (context, constraints) {
return LevelBar(
details: const LevelBarDetails(
fillColor: Colors.green,
fillColor: AppConfig.goldLight,
currentPoints: 0,
widthMultiplier: 1,
),
@ -392,7 +387,6 @@ class _LevelUpPopupContentState extends State<LevelUpPopupContent>
style: Theme.of(context).textTheme.bodyMedium,
),
),
//const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.all(24.0),
child: CachedNetworkImage(
@ -419,41 +413,41 @@ class _LevelUpPopupContentState extends State<LevelUpPopupContent>
),
),
// Share button, currently no functionality
ElevatedButton(
onPressed: () {
// Add share functionality
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 24,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Share with Friends",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 8,
),
Icon(
Icons.ios_share,
size: 20,
),
],
),
),
// ElevatedButton(
// onPressed: () {
// // Add share functionality
// },
// style: ElevatedButton.styleFrom(
// backgroundColor: Colors.white,
// foregroundColor: Colors.black,
// padding: const EdgeInsets.symmetric(
// vertical: 12,
// horizontal: 24,
// ),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(8),
// ),
// ),
// child: const Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// Text(
// "Share with Friends",
// style: TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.bold,
// ),
// ),
// SizedBox(
// width: 8,
// ),
// Icon(
// Icons.ios_share,
// size: 20,
// ),
// ],
// ),
// ),
],
),
),
@ -467,7 +461,7 @@ class _LevelUpPopupContentState extends State<LevelUpPopupContent>
.toList();
const itemsPerRow = 4;
// chunk into rows of up to 3
// chunk into rows of up to 4
final rows = <List<LearningSkillsEnum>>[
for (var i = 0; i < visibleSkills.length; i += itemsPerRow)
visibleSkills.sublist(
@ -522,32 +516,4 @@ class _LevelUpPopupContentState extends State<LevelUpPopupContent>
}).toList(),
);
}
Path drawStar(Size size) {
// Method to convert degrees to radians
double degToRad(double deg) => deg * (pi / 180.0);
const numberOfPoints = 5;
final halfWidth = size.width / 2;
final externalRadius = halfWidth;
final internalRadius = halfWidth / 2.5;
final degreesPerStep = degToRad(360 / numberOfPoints);
final halfDegreesPerStep = degreesPerStep / 2;
final path = Path();
final fullAngle = degToRad(360);
path.moveTo(size.width, halfWidth);
for (double step = 0; step < fullAngle; step += degreesPerStep) {
path.lineTo(
halfWidth + externalRadius * cos(step),
halfWidth + externalRadius * sin(step),
);
path.lineTo(
halfWidth + internalRadius * cos(step + halfDegreesPerStep),
halfWidth + internalRadius * sin(step + halfDegreesPerStep),
);
}
path.close();
return path;
}
}

View file

@ -20,7 +20,6 @@ void rainConfetti(BuildContext context) {
_rainController!.play();
final screenWidth = MediaQuery.of(context).size.width;
final screenHeight = MediaQuery.of(context).size.height;
final isSmallScreen = screenWidth < 600;
final count = isSmallScreen ? 2 : 5;
final spacing = screenWidth / (count + 1);