3927 analytics tweaks (#3953)

* analytics page updates

* add back download buttons
pull/2245/head
ggurdin 2 months ago committed by GitHub
parent 141d3c5175
commit 7d46892a39
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -29,6 +29,7 @@ import 'package:fluffychat/pages/settings_password/settings_password.dart';
import 'package:fluffychat/pages/settings_security/settings_security.dart';
import 'package:fluffychat/pages/settings_style/settings_style.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_page/analytics_page.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/chat_settings/pages/pangea_invitation_selection.dart';
@ -344,29 +345,112 @@ abstract class AppRoutes {
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
AnalyticsPage(
selectedIndicator: ProgressIndicatorEnum.fromString(
state.uri.queryParameters['mode'] ?? 'vocab',
),
constructZoom: state.extra is ConstructIdentifier
? state.extra as ConstructIdentifier
: null,
),
const AnalyticsPage(),
),
routes: [
GoRoute(
path: ':roomid',
path: ConstructTypeEnum.morph.string,
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
ChatPage(
roomId: state.pathParameters['roomid']!,
eventId: state.uri.queryParameters['event'],
backButton: BackButton(
onPressed: () => context.go(
"/rooms/analytics?mode=activities",
AnalyticsPage(
indicator: FluffyThemes.isColumnMode(context)
? null
: ProgressIndicatorEnum.morphsUsed,
),
),
redirect: loggedOutRedirect,
routes: [
GoRoute(
path: ':construct',
pageBuilder: (context, state) {
final construct = ConstructIdentifier.fromString(
state.pathParameters['construct']!,
);
return defaultPageBuilder(
context,
state,
AnalyticsPage(
indicator: ProgressIndicatorEnum.morphsUsed,
construct: construct,
),
);
},
),
],
),
GoRoute(
path: ConstructTypeEnum.vocab.string,
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
AnalyticsPage(
indicator: FluffyThemes.isColumnMode(context)
? null
: ProgressIndicatorEnum.wordsUsed,
),
),
redirect: loggedOutRedirect,
routes: [
GoRoute(
path: ':construct',
pageBuilder: (context, state) {
final construct = ConstructIdentifier.fromString(
state.pathParameters['construct']!,
);
return defaultPageBuilder(
context,
state,
AnalyticsPage(
indicator: ProgressIndicatorEnum.wordsUsed,
construct: construct,
),
);
},
),
],
),
GoRoute(
path: 'activities',
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
AnalyticsPage(
indicator: FluffyThemes.isColumnMode(context)
? null
: ProgressIndicatorEnum.activities,
),
),
redirect: loggedOutRedirect,
routes: [
GoRoute(
path: ':roomid',
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
ChatPage(
roomId: state.pathParameters['roomid']!,
eventId: state.uri.queryParameters['event'],
backButton: BackButton(
onPressed: () => context.go(
"/rooms/analytics/activities",
),
),
),
),
redirect: loggedOutRedirect,
),
],
),
GoRoute(
path: 'level',
pageBuilder: (context, state) => defaultPageBuilder(
context,
state,
AnalyticsPage(
indicator: FluffyThemes.isColumnMode(context)
? null
: ProgressIndicatorEnum.level,
),
),
redirect: loggedOutRedirect,

@ -21,7 +21,6 @@ import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart
import 'package:fluffychat/pangea/chat_settings/utils/delete_room.dart';
import 'package:fluffychat/pangea/chat_settings/widgets/delete_space_dialog.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/utils/firebase_analytics.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/subscription/widgets/subscription_snackbar.dart';
import 'package:fluffychat/utils/localized_exception_extension.dart';
@ -1164,8 +1163,6 @@ class ChatListController extends State<ChatList>
// #Pangea
void _initPangeaControllers(Client client) {
GoogleAnalytics.analyticsUserUpdate(client.userID);
MatrixState.pangeaController.initControllers();
if (mounted) {
MatrixState.pangeaController.classController.joinCachedSpaceCode(context);
}

@ -4,7 +4,6 @@ import 'package:fluffychat/pangea/analytics_details_popup/morph_analytics_list_v
import 'package:fluffychat/pangea/analytics_details_popup/morph_details_view.dart';
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_details_view.dart';
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_list_view.dart';
import 'package:fluffychat/pangea/analytics_downloads/analytics_download_button.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
@ -14,57 +13,38 @@ import 'package:fluffychat/pangea/morphs/morph_models.dart';
import 'package:fluffychat/pangea/morphs/morph_repo.dart';
import 'package:fluffychat/widgets/matrix.dart';
class AnalyticsPopupWrapper extends StatefulWidget {
const AnalyticsPopupWrapper({
class ConstructAnalyticsView extends StatefulWidget {
const ConstructAnalyticsView({
super.key,
this.constructZoom,
required this.view,
this.backButtonOverride,
this.construct,
});
final ConstructTypeEnum view;
final ConstructIdentifier? constructZoom;
final Widget? backButtonOverride;
final ConstructIdentifier? construct;
@override
AnalyticsPopupWrapperState createState() => AnalyticsPopupWrapperState();
ConstructAnalyticsViewState createState() => ConstructAnalyticsViewState();
}
class AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
ConstructIdentifier? localConstructZoom;
ConstructTypeEnum localView = ConstructTypeEnum.vocab;
class ConstructAnalyticsViewState extends State<ConstructAnalyticsView> {
final TextEditingController searchController = TextEditingController();
MorphFeaturesAndTags morphs = defaultMorphMapping;
List<MorphFeature> features = defaultMorphMapping.displayFeatures;
bool isSearching = false;
final TextEditingController searchController = TextEditingController();
ConstructLevelEnum? selectedConstructLevel;
@override
void initState() {
super.initState();
localView = widget.view;
setConstructZoom(widget.constructZoom);
_setMorphs();
searchController.addListener(() {
if (mounted) setState(() {});
});
}
@override
void didUpdateWidget(covariant AnalyticsPopupWrapper oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.constructZoom != oldWidget.constructZoom) {
setConstructZoom(widget.constructZoom);
}
if (widget.view != oldWidget.view) {
localView = widget.view;
localConstructZoom = null;
setState(() {});
}
}
@override
void dispose() {
searchController.dispose();
@ -92,14 +72,6 @@ class AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
}
}
void setConstructZoom(ConstructIdentifier? id) {
if (id != null && id.type != localView) {
localView = id.type;
}
localConstructZoom = id;
setState(() => {});
}
void setSelectedConstructLevel(ConstructLevelEnum level) {
setState(() {
selectedConstructLevel = selectedConstructLevel == level ? null : level;
@ -116,34 +88,12 @@ class AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
localConstructZoom != null
? IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
setConstructZoom(null);
},
)
: const SizedBox(),
const DownloadAnalyticsButton(),
],
),
Expanded(
child: localView == ConstructTypeEnum.morph
? localConstructZoom == null
? MorphAnalyticsListView(controller: this)
: MorphDetailsView(constructId: localConstructZoom!)
: localConstructZoom == null
? VocabAnalyticsListView(controller: this)
: VocabDetailsView(constructId: localConstructZoom!),
),
],
),
);
return widget.view == ConstructTypeEnum.morph
? widget.construct == null
? MorphAnalyticsListView(controller: this)
: MorphDetailsView(constructId: widget.construct!)
: widget.construct == null
? VocabAnalyticsListView(controller: this)
: VocabDetailsView(constructId: widget.construct!);
}
}

@ -1,10 +1,13 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart';
import 'package:fluffychat/pangea/analytics_downloads/analytics_download_button.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/common/config/environment.dart';
@ -18,7 +21,7 @@ import 'package:fluffychat/pangea/morphs/morph_icon.dart';
import 'package:fluffychat/widgets/matrix.dart';
class MorphAnalyticsListView extends StatelessWidget {
final AnalyticsPopupWrapperState controller;
final ConstructAnalyticsViewState controller;
const MorphAnalyticsListView({
required this.controller,
@ -40,6 +43,13 @@ class MorphAnalyticsListView extends StatelessWidget {
),
if (!InstructionsEnum.morphAnalyticsList.isToggledOff)
const SizedBox(height: 16.0),
if (kIsWeb)
const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
DownloadAnalyticsButton(),
],
),
Expanded(
child: ListView.builder(
key: const PageStorageKey<String>('morph-analytics'),
@ -55,7 +65,6 @@ class MorphAnalyticsListView extends StatelessWidget {
.getDisplayTags(feature.feature)
.map((tag) => tag.toLowerCase())
.toSet(),
onConstructZoom: controller.setConstructZoom,
),
)
: const SizedBox.shrink();
@ -71,13 +80,11 @@ class MorphAnalyticsListView extends StatelessWidget {
class MorphFeatureBox extends StatelessWidget {
final String morphFeature;
final Set<String> allTags;
final void Function(ConstructIdentifier) onConstructZoom;
const MorphFeatureBox({
super.key,
required this.morphFeature,
required this.allTags,
required this.onConstructZoom,
});
MorphFeaturesEnum get feature =>
@ -148,7 +155,9 @@ class MorphFeatureBox extends StatelessWidget {
morphFeature: morphFeature,
morphTag: morphTag,
constructAnalytics: analytics,
onTap: () => onConstructZoom(id),
onTap: () => context.go(
"/rooms/analytics/${id.type.string}/${id.string}",
),
);
},
)

@ -36,87 +36,11 @@ class MorphDetailsView extends StatelessWidget {
morphTag: _morphTag,
textColor: textColor,
),
headerContent: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: MorphMeaningWidget(
feature: _morphFeature,
tag: _morphTag,
style: Theme.of(context).textTheme.bodyLarge,
// leading: TextSpan(
// text: L10n.of(context).meaningSectionHeader,
// style: const TextStyle(
// fontWeight: FontWeight.bold,
// ),
// ),
),
),
],
),
headerContent: MorphMeaningWidget(
feature: _morphFeature,
tag: _morphTag,
style: Theme.of(context).textTheme.bodyLarge,
),
// headerContent: Padding(
// padding: const EdgeInsets.all(25.0),
// child: Align(
// alignment: Alignment.topLeft,
// child: FutureBuilder(
// future: _getDefinition(context),
// builder: (
// BuildContext context,
// AsyncSnapshot<String?> snapshot,
// ) {
// if (snapshot.hasData) {
// return MorphMeaningWidget(
// feature: _morphFeature,
// tag: _morphTag,
// style: Theme.of(context).textTheme.bodyLarge,
// leading: TextSpan(
// text: L10n.of(context).meaningSectionHeader,
// style: const TextStyle(
// fontWeight: FontWeight.bold,
// ),
// ),
// );
// } else if (snapshot.hasError) {
// return Wrap(
// children: [
// Text(
// L10n.of(context).meaningSectionHeader,
// style: Theme.of(context).textTheme.bodyLarge?.copyWith(
// fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(
// width: 10,
// ),
// Text(
// L10n.of(context).meaningNotFound,
// style: Theme.of(context).textTheme.bodyLarge,
// ),
// ],
// );
// } else {
// return Wrap(
// children: [
// Text(
// L10n.of(context).meaningSectionHeader,
// style: Theme.of(context).textTheme.bodyLarge?.copyWith(
// fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(
// width: 10,
// ),
// const TextLoadingShimmer(width: 100),
// ],
// );
// }
// },
// ),
// ),
// ),
xpIcon: ConstructXpWidget(id: constructId),
constructId: constructId,
);

@ -1,10 +1,13 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart';
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_list_tile.dart';
import 'package:fluffychat/pangea/analytics_downloads/analytics_download_button.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/constructs/construct_level_enum.dart';
@ -15,7 +18,7 @@ import 'package:fluffychat/widgets/matrix.dart';
/// Displays vocab analytics, sorted into categories
/// (flowers, greens, and seeds) by points
class VocabAnalyticsListView extends StatelessWidget {
final AnalyticsPopupWrapperState controller;
final ConstructAnalyticsViewState controller;
const VocabAnalyticsListView({
super.key,
@ -79,6 +82,10 @@ class VocabAnalyticsListView extends StatelessWidget {
),
);
if (kIsWeb) {
filters.add(const DownloadAnalyticsButton());
}
return Column(
children: [
const InstructionsInlineTooltip(
@ -123,7 +130,7 @@ class VocabAnalyticsListView extends StatelessWidget {
)
: Row(
spacing: FluffyThemes.isColumnMode(context) ? 16.0 : 4.0,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
key: const ValueKey('filters'),
children: filters,
),
@ -143,7 +150,9 @@ class VocabAnalyticsListView extends StatelessWidget {
itemBuilder: (context, index) {
final vocabItem = _filteredVocab[index];
return VocabAnalyticsListTile(
onTap: () => controller.setConstructZoom(vocabItem.id),
onTap: () => context.go(
"/rooms/analytics/${vocabItem.id.type.string}/${vocabItem.id.string}",
),
constructUse: vocabItem,
emoji: vocabItem.id.userSetEmoji.firstOrNull ??
vocabItem.id.getLemmaInfoCached()?.emoji.firstOrNull,

@ -1,29 +1,108 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/analytics_page/activity_archive_view.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
import 'package:fluffychat/widgets/hover_builder.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../config/themes.dart';
import '../../widgets/avatar.dart';
class ActivityArchive extends StatefulWidget {
class ActivityArchive extends StatelessWidget {
const ActivityArchive({super.key});
@override
ActivityArchiveState createState() => ActivityArchiveState();
}
class ActivityArchiveState extends State<ActivityArchive> {
List<Room> get archive =>
MatrixState.pangeaController.getAnalytics.archivedActivities;
Future<void> removeArchivedChat(Room room) async {
await room.leave();
await MatrixState.pangeaController.putAnalytics
.removeActivityAnalytics(room.id);
setState(() {});
@override
Widget build(BuildContext context) {
return MaxWidthBody(
withScrolling: false,
child: Builder(
builder: (BuildContext context) {
if (archive.isEmpty) {
return const Center(
child: Icon(Icons.archive_outlined, size: 80),
);
}
return ListView.builder(
itemCount: archive.length,
itemBuilder: (BuildContext context, int i) => AnalyticsActivityItem(
room: archive[i],
),
);
},
),
);
}
}
class AnalyticsActivityItem extends StatelessWidget {
final Room room;
const AnalyticsActivityItem({
super.key,
required this.room,
});
@override
Widget build(BuildContext context) => ActivityArchiveView(controller: this);
Widget build(BuildContext context) {
final objective = room.activityPlan?.learningObjective ?? '';
final cefrLevel = room.activitySummary?.summary?.participants
.firstWhereOrNull(
(p) => p.participantId == room.client.userID,
)
?.cefrLevel;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 1,
),
child: Material(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
clipBehavior: Clip.hardEdge,
child: ListTile(
visualDensity: const VisualDensity(vertical: -0.5),
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
leading: HoverBuilder(
builder: (context, hovered) => AnimatedScale(
duration: FluffyThemes.animationDuration,
curve: FluffyThemes.animationCurve,
scale: hovered ? 1.1 : 1.0,
child: Avatar(
borderRadius: BorderRadius.circular(4.0),
mxContent: room.avatar,
name: room.getLocalizedDisplayname(),
),
),
),
title: Text(
objective,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 12.0),
),
trailing: cefrLevel != null
? Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
child: Text(
cefrLevel.toUpperCase(),
style: const TextStyle(fontSize: 14.0),
),
)
: null,
onTap: () => context.go(
'/rooms/analytics/activities/${room.id}',
),
),
),
);
}
}

@ -1,48 +0,0 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/pages/chat_list/chat_list_item.dart';
import 'package:fluffychat/pangea/analytics_page/activity_archive.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
class ActivityArchiveView extends StatelessWidget {
final ActivityArchiveState controller;
const ActivityArchiveView({
super.key,
required this.controller,
});
@override
Widget build(BuildContext context) {
return MaxWidthBody(
withScrolling: false,
child: Builder(
builder: (BuildContext context) {
if (controller.archive.isEmpty) {
return const Center(
child: Icon(Icons.archive_outlined, size: 80),
);
}
return ListView.builder(
itemCount: controller.archive.length,
itemBuilder: (BuildContext context, int i) => ChatListItem(
controller.archive[i],
onForget: () {
showFutureLoadingDialog(
context: context,
future: () => controller.removeArchivedChat(
controller.archive[i],
),
);
},
onTap: () =>
context.go('/rooms/analytics/${controller.archive[i].id}'),
),
);
},
),
);
}
}

@ -1,46 +1,89 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics_page/analytics_page_view.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_details_popup/analytics_details_popup.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_page/activity_archive.dart';
import 'package:fluffychat/pangea/analytics_page/analytics_page_constants.dart';
import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicators.dart';
import 'package:fluffychat/pangea/analytics_summary/level_dialog_content.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/widgets/matrix.dart';
class AnalyticsPage extends StatefulWidget {
final ProgressIndicatorEnum? selectedIndicator;
final ConstructIdentifier? constructZoom;
class AnalyticsPage extends StatelessWidget {
final ProgressIndicatorEnum? indicator;
final ConstructIdentifier? construct;
final bool isSidebar;
const AnalyticsPage({
super.key,
this.selectedIndicator,
this.constructZoom,
this.indicator,
this.construct,
this.isSidebar = false,
});
@override
AnalyticsPageState createState() => AnalyticsPageState();
}
class AnalyticsPageState extends State<AnalyticsPage> {
ProgressIndicatorEnum? selectedIndicator = ProgressIndicatorEnum.wordsUsed;
@override
void initState() {
super.initState();
// init the analytics controllers
MatrixState.pangeaController.initControllers();
selectedIndicator = widget.selectedIndicator ??
ProgressIndicatorEnum.wordsUsed; // Default to wordsUsed if not set
}
Widget build(BuildContext context) {
return Scaffold(
appBar: construct != null ? AppBar() : null,
body: SafeArea(
child: Padding(
padding: const EdgeInsetsGeometry.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (isSidebar ||
(!FluffyThemes.isColumnMode(context) && construct == null))
LearningProgressIndicators(
selected: indicator,
canSelect: indicator != ProgressIndicatorEnum.level,
),
Expanded(
child: StreamBuilder(
stream: MatrixState
.pangeaController.getAnalytics.analyticsStream.stream,
builder: (context, _) {
if (indicator == ProgressIndicatorEnum.level) {
return const LevelDialogContent();
} else if (indicator == ProgressIndicatorEnum.morphsUsed) {
return ConstructAnalyticsView(
construct: construct,
view: ConstructTypeEnum.morph,
);
} else if (indicator == ProgressIndicatorEnum.wordsUsed) {
return ConstructAnalyticsView(
construct: construct,
view: ConstructTypeEnum.vocab,
);
} else if (indicator == ProgressIndicatorEnum.activities) {
return const ActivityArchive();
}
@override
void didUpdateWidget(covariant AnalyticsPage oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.selectedIndicator != widget.selectedIndicator &&
widget.selectedIndicator != null) {
setState(
() => selectedIndicator = widget.selectedIndicator!,
); // Update to new value
}
return Center(
child: SizedBox(
width: 250.0,
child: CachedNetworkImage(
imageUrl:
"${AppConfig.assetsBaseURL}/${AnalyticsPageConstants.dinoBotFileName}",
errorWidget: (context, url, error) =>
const SizedBox(),
placeholder: (context, url) => const Center(
child: CircularProgressIndicator.adaptive(),
),
),
),
);
},
),
),
],
),
),
),
);
}
@override
Widget build(BuildContext context) => AnalyticsPageView(controller: this);
}

@ -1,68 +0,0 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_page/activity_archive.dart';
import 'package:fluffychat/pangea/analytics_page/analytics_page.dart';
import 'package:fluffychat/pangea/analytics_summary/learning_progress_indicators.dart';
import 'package:fluffychat/pangea/analytics_summary/level_dialog_content.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/widgets/matrix.dart';
class AnalyticsPageView extends StatelessWidget {
final AnalyticsPageState controller;
const AnalyticsPageView({
super.key,
required this.controller,
});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsetsGeometry.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LearningProgressIndicators(
selected: controller.selectedIndicator,
canSelect:
controller.selectedIndicator != ProgressIndicatorEnum.level,
),
Expanded(
child: StreamBuilder(
stream: MatrixState
.pangeaController.getAnalytics.analyticsStream.stream,
builder: (context, _) {
if (controller.selectedIndicator ==
ProgressIndicatorEnum.level) {
return const LevelDialogContent();
} else if (controller.selectedIndicator ==
ProgressIndicatorEnum.morphsUsed) {
return AnalyticsPopupWrapper(
constructZoom: controller.widget.constructZoom,
view: ConstructTypeEnum.morph,
);
} else if (controller.selectedIndicator ==
ProgressIndicatorEnum.wordsUsed) {
return AnalyticsPopupWrapper(
constructZoom: controller.widget.constructZoom,
view: ConstructTypeEnum.vocab,
);
} else if (controller.selectedIndicator ==
ProgressIndicatorEnum.activities) {
return const ActivityArchive();
}
return const SizedBox();
},
),
),
],
),
),
),
);
}
}

@ -123,7 +123,7 @@ class LearningProgressIndicatorsState
selected: widget.selected == c.indicator,
onPressed: () {
context.go(
"/rooms/analytics?mode=${c.string}",
"/rooms/analytics/${c.string}",
);
},
child: ProgressIndicatorBadge(
@ -138,7 +138,7 @@ class LearningProgressIndicatorsState
ProgressIndicatorEnum.activities,
onPressed: () {
context.go(
"/rooms/analytics?mode=activities",
"/rooms/analytics/activities",
);
},
child: Tooltip(
@ -232,7 +232,7 @@ class LearningProgressIndicatorsState
child: GestureDetector(
onTap: widget.canSelect
? () {
context.go("/rooms/analytics?mode=level");
context.go("/rooms/analytics/level");
}
: null,
child: Row(

@ -9,6 +9,7 @@ import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/common/utils/overlay.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
@ -167,8 +168,7 @@ class ConstructNotificationOverlayState
void _showDetails() {
context.go(
"/rooms/analytics?mode=morph",
extra: widget.construct,
"/rooms/analytics/${ConstructTypeEnum.morph.string}/${widget.construct.string}",
);
}

@ -102,11 +102,11 @@ class MessageAnalyticsFeedbackState extends State<MessageAnalyticsFeedback>
void _showAnalyticsDialog(ConstructTypeEnum? type) {
switch (type) {
case ConstructTypeEnum.morph:
context.go("/rooms/analytics?mode=morph");
context.go("/rooms/analytics/${ConstructTypeEnum.morph.string}");
break;
case ConstructTypeEnum.vocab:
default:
context.go("/rooms/analytics?mode=vocab");
context.go("/rooms/analytics/${ConstructTypeEnum.vocab.string}");
break;
}
}

@ -62,6 +62,7 @@ class PangeaController {
PangeaController({required this.matrix, required this.matrixState}) {
_setup();
_setLanguageSubscription();
_initControllers();
randomint = Random().nextInt(2000);
}
@ -74,12 +75,10 @@ class PangeaController {
/// While many of these functions are asynchronous, they are not awaited here,
/// because of order of execution does not matter,
/// and running them at the same times speeds them up.
void initControllers() {
putAnalytics.initialize();
getAnalytics.initialize();
void _initControllers() {
_initAnalyticsControllers();
subscriptionController.initialize();
setPangeaPushRules();
TtsController.setAvailableLanguages();
}
@ -181,8 +180,7 @@ class PangeaController {
case LoginState.loggedOut:
case LoginState.softLoggedOut:
// Reset cached analytics data
putAnalytics.dispose();
getAnalytics.dispose();
_disposeAnalyticsControllers();
userController.clear();
_languageStream?.cancel();
_languageStream = null;
@ -190,8 +188,7 @@ class PangeaController {
break;
case LoginState.loggedIn:
// Initialize analytics data
putAnalytics.initialize();
getAnalytics.initialize();
_initControllers();
_setLanguageSubscription();
userController.reinitialize().then((_) {
@ -213,13 +210,21 @@ class PangeaController {
GoogleAnalytics.analyticsUserUpdate(userID);
}
Future<void> resetAnalytics() async {
putAnalytics.dispose();
getAnalytics.dispose();
Future<void> _initAnalyticsControllers() async {
putAnalytics.initialize();
await getAnalytics.initialize();
}
void _disposeAnalyticsControllers() {
putAnalytics.dispose();
getAnalytics.dispose();
}
Future<void> resetAnalytics() async {
_disposeAnalyticsControllers();
await _initAnalyticsControllers();
}
void _setLanguageSubscription() {
_languageStream?.cancel();
_languageStream = userController.languageStream.stream.listen(
@ -228,6 +233,7 @@ class PangeaController {
}
Future<void> setPangeaPushRules() async {
if (!matrixState.client.isLogged()) return;
final List<Room> analyticsRooms =
matrixState.client.rooms.where((room) => room.isAnalyticsRoom).toList();

@ -119,6 +119,28 @@ class ConstructIdentifier {
return "$lemma:${type.string}-$category".toLowerCase();
}
static ConstructIdentifier? fromString(String s) {
final parts = s.split(':');
if (parts.length != 2) return null;
final lemma = parts[0];
final typeAndCategory = parts[1].split('-');
if (typeAndCategory.length != 2) return null;
final typeString = typeAndCategory[0];
final category = typeAndCategory[1];
final type = ConstructTypeEnum.values.firstWhereOrNull(
(e) => e.string == typeString,
);
if (type == null) return null;
return ConstructIdentifier(
lemma: lemma,
type: type,
category: category,
);
}
String get partialKey => "$lemma-${type.string}";
ConstructUses get constructUses =>

@ -279,8 +279,7 @@ class MorphMeaningPopupState extends State<MorphMeaningPopup> {
ConstructXpWidget(
id: widget.cId,
onTap: () => context.go(
"/rooms/analytics?mode=morph",
extra: widget.cId,
"/rooms/analytics/${ConstructTypeEnum.morph.string}/${widget.cId.string}",
),
),
],

@ -4,6 +4,7 @@ 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/construct_type_enum.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
@ -107,8 +108,7 @@ class WordZoomWidget extends StatelessWidget {
ConstructXpWidget(
id: token.vocabConstructID,
onTap: () => context.go(
"/rooms/analytics?mode=vocab",
extra: token.vocabConstructID,
"/rooms/analytics/${ConstructTypeEnum.vocab.string}/${token.vocabConstructID.string}",
),
),
],

@ -7,7 +7,9 @@ import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/chat_list/chat_list.dart';
import 'package:fluffychat/pages/settings/settings.dart';
import 'package:fluffychat/pangea/analytics_page/analytics_page_constants.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_page/analytics_page.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/find_your_people/find_your_people_constants.dart';
import 'package:fluffychat/widgets/navigation_rail.dart';
@ -37,7 +39,7 @@ class TwoColumnLayout extends StatelessWidget {
final spaceID = state.pathParameters['spaceid'];
if (roomID == null && spaceID == null) {
showNavRail = !["newcourse"].any(
showNavRail = !["newcourse", ":construct"].any(
(p) => state.fullPath?.contains(p) ?? false,
);
} else if (roomID == null) {
@ -100,17 +102,6 @@ class _MainView extends StatelessWidget {
required this.state,
});
String get _asset {
const defaultAsset = FindYourPeopleConstants.sideBearFileName;
if (state.fullPath == null || state.fullPath!.isEmpty) return defaultAsset;
if (state.fullPath!.contains('analytics')) {
return AnalyticsPageConstants.dinoBotFileName;
}
return defaultAsset;
}
@override
Widget build(BuildContext context) {
final path = state.fullPath;
@ -121,16 +112,35 @@ class _MainView extends StatelessWidget {
);
}
if (path.contains("analytics")) {
ProgressIndicatorEnum indicator = ProgressIndicatorEnum.wordsUsed;
if (path.contains("analytics/level")) {
indicator = ProgressIndicatorEnum.level;
} else if (path.contains("analytics/${ConstructTypeEnum.morph.string}")) {
indicator = ProgressIndicatorEnum.morphsUsed;
} else if (path.contains("analytics/${ConstructTypeEnum.vocab.string}")) {
indicator = ProgressIndicatorEnum.wordsUsed;
} else if (path.contains("analytics/activities")) {
indicator = ProgressIndicatorEnum.activities;
}
return AnalyticsPage(
indicator: indicator,
isSidebar: true,
);
}
if (path.contains("settings")) {
return Settings(key: state.pageKey);
}
if (['communities', 'analytics'].any((p) => path.contains(p))) {
if (path.contains('communities')) {
return Center(
child: SizedBox(
width: 250.0,
child: CachedNetworkImage(
imageUrl: "${AppConfig.assetsBaseURL}/$_asset",
imageUrl:
"${AppConfig.assetsBaseURL}/${FindYourPeopleConstants.sideBearFileName}",
errorWidget: (context, url, error) => const SizedBox(),
placeholder: (context, url) => const Center(
child: CircularProgressIndicator.adaptive(),

Loading…
Cancel
Save