fluffychat/lib/pangea/analytics_practice/completed_activity_session_view.dart
ggurdin 117a03089e
5720 vocab practice should have feedback flag (#5761)
* chore: split up analytics activity page widgets into separate files

* started analytics practice refactor

* refactor how UI updates are triggered in analytics practice page

* some fixes
2026-02-20 13:25:21 -05:00

214 lines
8.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/level_up/star_rain_widget.dart';
import 'package:fluffychat/pangea/analytics_practice/analytics_practice_constants.dart';
import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart';
import 'package:fluffychat/pangea/analytics_practice/percent_marker_bar.dart';
import 'package:fluffychat/pangea/analytics_practice/stat_card.dart';
import 'package:fluffychat/pangea/analytics_summary/animated_progress_bar.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
class CompletedActivitySessionView extends StatelessWidget {
final AnalyticsPracticeSessionModel session;
final VoidCallback launchSession;
final Future<double> levelProgress;
const CompletedActivitySessionView({
required this.session,
required this.launchSession,
required this.levelProgress,
super.key,
});
String _formatTime(int seconds) {
final minutes = seconds ~/ 60;
final remainingSeconds = seconds % 60;
return '${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
}
@override
Widget build(BuildContext context) {
final username =
Matrix.of(context).client.userID?.split(':').first.substring(1) ?? '';
final double accuracy = session.state.accuracy;
final int elapsedSeconds = session.state.elapsedSeconds;
final bool accuracyAchievement = accuracy == 100;
final bool timeAchievement = elapsedSeconds <= 60;
return Stack(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16, right: 16, left: 16),
child: Column(
children: [
Text(
session.getCompletionMessage(context),
style: Theme.of(
context,
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: FutureBuilder(
future: Matrix.of(context).client.fetchOwnProfile(),
builder: (context, snapshot) {
final avatarUrl = snapshot.data?.avatarUrl;
return Avatar(
name: username,
showPresence: false,
size: 100,
mxContent: avatarUrl,
userId: Matrix.of(context).client.userID,
);
},
),
),
Column(
children: [
Padding(
padding: const EdgeInsets.only(
top: 16.0,
bottom: 16.0,
),
child: FutureBuilder(
future: levelProgress,
builder: (context, snapshot) => AnimatedProgressBar(
height: 20.0,
widthPercent: snapshot.data ?? 0.0,
backgroundColor: Theme.of(
context,
).colorScheme.surfaceContainerHighest,
duration: const Duration(milliseconds: 500),
),
),
),
Text(
"+ ${session.state.allXPGained} XP",
style: Theme.of(context).textTheme.titleLarge
?.copyWith(
color: AppConfig.goldLight,
fontWeight: FontWeight.bold,
),
),
],
),
StatCard(
icon: Icons.my_location,
text: "${L10n.of(context).accuracy}: $accuracy%",
isAchievement: accuracyAchievement,
achievementText: "+ ${session.state.accuracyBonusXP} XP",
child: PercentMarkerBar(
height: 20.0,
widthPercent: accuracy / 100.0,
markerWidth: 20.0,
markerColor: AppConfig.success,
backgroundColor: !accuracyAchievement
? Theme.of(
context,
).colorScheme.surfaceContainerHighest
: Color.alphaBlend(
AppConfig.goldLight.withValues(alpha: 0.3),
Theme.of(
context,
).colorScheme.surfaceContainerHighest,
),
),
),
StatCard(
icon: Icons.alarm,
text:
"${L10n.of(context).time}: ${_formatTime(elapsedSeconds)}",
isAchievement: timeAchievement,
achievementText: "+ ${session.state.timeBonusXP} XP",
child: TimeStarsWidget(elapsedSeconds: elapsedSeconds),
),
Column(
children: [
//expanded row button
ElevatedButton(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
onPressed: launchSession,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text(L10n.of(context).anotherRound)],
),
),
const SizedBox(height: 16),
ElevatedButton(
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
onPressed: () {
context.go('/rooms/analytics/vocab');
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text(L10n.of(context).done)],
),
),
],
),
],
),
),
],
),
),
const StarRainWidget(
showBlast: true,
rainDuration: Duration(seconds: 5),
),
],
);
}
}
class TimeStarsWidget extends StatelessWidget {
final int elapsedSeconds;
const TimeStarsWidget({required this.elapsedSeconds, super.key});
int get starCount {
const timeForBonus = AnalyticsPracticeConstants.timeForBonus;
if (elapsedSeconds <= timeForBonus) return 5;
if (elapsedSeconds <= timeForBonus * 1.5) return 4;
if (elapsedSeconds <= timeForBonus * 2) return 3;
if (elapsedSeconds <= timeForBonus * 2.5) return 2;
return 1; // anything above 2.5x timeForBonus
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(
5,
(index) => Icon(
index < starCount ? Icons.star : Icons.star_outline,
color: AppConfig.goldLight,
size: 36,
),
),
);
}
}