diff --git a/lib/backend/analytics/frederic_analytics_service.dart b/lib/backend/analytics/frederic_analytics_service.dart index 501acdb..03f0615 100644 --- a/lib/backend/analytics/frederic_analytics_service.dart +++ b/lib/backend/analytics/frederic_analytics_service.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:frederic/backend/util/frederic_profiler.dart'; import 'package:frederic/theme/frederic_theme.dart'; abstract class FredericAnalyticsService { @@ -18,6 +19,8 @@ abstract class FredericAnalyticsService { Future logSignUp(String method); + Future logPurchaseApp(); + Future logGoalCreated(); Future logGoalDeleted(); @@ -40,132 +43,9 @@ abstract class FredericAnalyticsService { Future logCompleteCalendarDay(); - Future logEnableCustomWorkout(); - - Future logEnableGlobalWorkout(); - - Future logDisableCustomWorkout(); - - Future logDisableGlobalWorkout(); - - Future logEnterSettingsScreen(); - - Future logEnterUserSettingsScreen(); - Future logChangeColorTheme(FredericColorTheme theme); } -// class GoogleAnalyticsService extends FredericAnalyticsService { -// // Future initialize() async { -// // return; -// // } -// // -// // Future enable() async { -// // await analytics.setAnalyticsCollectionEnabled(true); -// // await FirebasePerformance.instance.setPerformanceCollectionEnabled(true); -// // return FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); -// // } -// // -// // Future disable() async { -// // await analytics.setAnalyticsCollectionEnabled(false); -// // await FirebasePerformance.instance.setPerformanceCollectionEnabled(false); -// // return FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(false); -// // } -// // -// // void logCurrentScreen(String screen) { -// // analytics.setCurrentScreen(screenName: screen); -// // } -// // -// // Future logLogin(String method) { -// // return analytics.logLogin(loginMethod: method); -// // } -// // -// // Future logSignUp(String method) { -// // return analytics.logSignUp(signUpMethod: method); -// // } -// // -// // Future logGoalCreated() { -// // return analytics.logEvent(name: 'create_goal'); -// // } -// // -// // Future logGoalDeleted() { -// // return analytics.logEvent(name: 'delete_goal'); -// // } -// // -// // Future logGoalSavedAsAchievement() { -// // return analytics.logEvent(name: 'goal_saved_as_achievement'); -// // } -// // -// // Future logAchievementDeleted() { -// // return analytics.logEvent(name: 'delete_achievement'); -// // } -// // -// // Future logWorkoutCreated() { -// // return analytics.logEvent(name: 'create_workout'); -// // } -// // -// // Future logWorkoutSaved() { -// // return analytics.logEvent(name: 'save_workout'); -// // } -// // -// // Future logWorkoutDeleted() { -// // return analytics.logEvent(name: 'delete_workout'); -// // } -// // -// // Future logAddProgressOnActivity([bool useSmartSuggestions = false]) { -// // return analytics.logEvent( -// // name: 'add_progress_using_activity', -// // parameters: {'used_smart_suggestions': useSmartSuggestions ? 1 : 0}); -// // } -// // -// // Future logAddProgressOnCalendar([bool useSmartSuggestions = false]) { -// // return analytics.logEvent( -// // name: 'add_progress_using_calendar', -// // parameters: {'used_smart_suggestions': useSmartSuggestions ? 1 : 0}); -// // } -// // -// // Future logAddProgressOnWorkoutPlayer() { -// // return analytics.logEvent(name: 'add_progress_using_workout_player'); -// // } -// // -// // Future logCompleteCalendarDay() { -// // return analytics.logEvent(name: 'complete_calendar_day'); -// // } -// // -// // Future logEnableCustomWorkout() { -// // return analytics.logEvent(name: 'enable_custom_workout'); -// // } -// // -// // Future logEnableGlobalWorkout() { -// // return analytics.logEvent(name: 'enable_global_workout'); -// // } -// // -// // Future logDisableCustomWorkout() { -// // return analytics.logEvent(name: 'disable_custom_workout'); -// // } -// // -// // Future logDisableGlobalWorkout() { -// // return analytics.logEvent(name: 'disable_global_workout'); -// // } -// // -// // Future logEnterSettingsScreen() { -// // return analytics.setCurrentScreen(screenName: 'settings_screen'); -// // } -// // -// // Future logEnterUserSettingsScreen() { -// // return analytics.setCurrentScreen(screenName: 'user_settings_screen'); -// // } -// // -// // Future logEnterHomeScreen() { -// // return analytics.setCurrentScreen(screenName: 'Home'); -// // } -// //e -// // Future logChangeColorTheme(FredericColorTheme theme) { -// // return analytics.logEvent( -// // name: 'change_color_theme', parameters: {'theme': theme.name}); -// // } -// } - class UmamiAnalyticsService extends FredericAnalyticsService { Dio? dio; String trackingID = '7ad92f04-e9c1-4044-914e-a0a55946de40'; @@ -188,6 +68,15 @@ class UmamiAnalyticsService extends FredericAnalyticsService { 'User-Agent': userAgent })); + try { + await dio?.get('/'); + } catch (e) { + enabled = false; + print(e); + FredericProfiler.log( + 'cant reach analytics server, disabling analytics service.'); + } + if (kDebugMode) { enabled = false; } @@ -207,7 +96,7 @@ class UmamiAnalyticsService extends FredericAnalyticsService { "type": "event" }; - print("Analytics: $payload"); + FredericProfiler.log("sending analytics: $payload"); try { dio?.post('/api/send', data: payload); @@ -334,42 +223,6 @@ class UmamiAnalyticsService extends FredericAnalyticsService { return trackEvent(type: 'complete-calendar-day'); } - @override - Future logEnableCustomWorkout([String? name]) async { - if (!enabled) return; - return trackEvent(type: 'enable-custom-workout'); - } - - @override - Future logEnableGlobalWorkout([String? name]) async { - if (!enabled) return; - return trackEvent(type: 'enable-global-workout'); - } - - @override - Future logDisableCustomWorkout([String? name]) async { - if (!enabled) return; - return trackEvent(type: 'disable-custom-workout'); - } - - @override - Future logDisableGlobalWorkout([String? name]) async { - if (!enabled) return; - return trackEvent(type: 'disable-global-workout'); - } - - @override - Future logEnterSettingsScreen() async { - if (!enabled) return; - return trackScreen('settings-screen'); - } - - @override - Future logEnterUserSettingsScreen() async { - if (!enabled) return; - return trackScreen('user-settings-screen'); - } - @override Future logChangeColorTheme(FredericColorTheme theme) async { if (!enabled) return; @@ -381,4 +234,10 @@ class UmamiAnalyticsService extends FredericAnalyticsService { if (!enabled) return; return trackScreen(screen); } + + @override + Future logPurchaseApp() async { + if (!enabled) return; + return trackEvent(type: 'purchased-app'); + } } diff --git a/lib/backend/authentication/streak_manager.dart b/lib/backend/authentication/streak_manager.dart index ff37987..a32d958 100644 --- a/lib/backend/authentication/streak_manager.dart +++ b/lib/backend/authentication/streak_manager.dart @@ -13,6 +13,7 @@ class StreakManager { } }); } + final FredericUserManager userManager; void handleUserDataChange() { @@ -25,7 +26,7 @@ class StreakManager { if (!userManager.state.finishedLoading) return; final now = DateTime.now(); //if (userManager.state.streakLatestDate?.isSameDay(now) ?? false) return; - + FredericBackend.instance.analytics.logCompleteCalendarDay(); if (userManager.state.hasStreak) { if (userManager.state.streakLatestDate?.isNotSameDay(now) ?? true) { userManager.state.streakLatestDate = now; diff --git a/lib/backend/goals/frederic_goal_manager.dart b/lib/backend/goals/frederic_goal_manager.dart index 8f2144b..90b041a 100644 --- a/lib/backend/goals/frederic_goal_manager.dart +++ b/lib/backend/goals/frederic_goal_manager.dart @@ -3,6 +3,7 @@ import 'dart:collection'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:frederic/backend/database/frederic_data_interface.dart'; +import 'package:frederic/backend/frederic_backend.dart'; import 'package:frederic/backend/goals/frederic_goal_list_data.dart'; import 'frederic_goal.dart'; @@ -57,10 +58,12 @@ class FredericGoalManager final newGoalFromDatabase = await _dataInterface.create(event.newGoal); _goals[event.newGoal.id] = newGoalFromDatabase; emit(FredericGoalListData(event.changed, _goals)); + FredericBackend.instance.analytics.logGoalCreated(); } else if (event is FredericGoalDeleteEvent) { _goals.remove(event.goal.id); await _dataInterface.delete(event.goal); emit(FredericGoalListData(event.changed, _goals)); + FredericBackend.instance.analytics.logGoalDeleted(); } else { emit(FredericGoalListData(event.changed, _goals)); } diff --git a/lib/backend/workouts/frederic_workout_manager.dart b/lib/backend/workouts/frederic_workout_manager.dart index 52f3624..1b2c0e4 100644 --- a/lib/backend/workouts/frederic_workout_manager.dart +++ b/lib/backend/workouts/frederic_workout_manager.dart @@ -79,14 +79,17 @@ class FredericWorkoutManager FredericWorkoutEvent event, Emitter emit) async { if (event is FredericWorkoutUpdateEvent) { emit(FredericWorkoutListData(_workouts, event.changed)); + FredericBackend.instance.analytics.logWorkoutSaved(); } else if (event is FredericWorkoutCreateEvent) { var workout = await dataInterface.create(event.workout); workout.onUpdate = updateWorkoutInDB; _workouts[workout.id] = workout; emit(FredericWorkoutListData(_workouts, event.changed)); + FredericBackend.instance.analytics.logWorkoutCreated(); } else if (event is FredericWorkoutDeleteEvent) { _deleteWorkout(event.workout); emit(FredericWorkoutListData(_workouts, event.changed)); + FredericBackend.instance.analytics.logWorkoutDeleted(); } else { emit(FredericWorkoutListData(_workouts, event.changed)); } @@ -95,6 +98,7 @@ class FredericWorkoutManager class FredericWorkoutEvent { FredericWorkoutEvent(this.changed); + List changed; } diff --git a/lib/screens/edit_workout_data_screen.dart b/lib/screens/edit_workout_data_screen.dart index f980a1a..bd0b6fc 100644 --- a/lib/screens/edit_workout_data_screen.dart +++ b/lib/screens/edit_workout_data_screen.dart @@ -255,8 +255,6 @@ class _EditWorkoutDataScreenState extends State { context: context, builder: (ctx) => FredericActionDialog( onConfirm: () { - FredericBackend.instance.analytics - .logWorkoutDeleted(); FredericBackend .instance.workoutManager .add(FredericWorkoutDeleteEvent( @@ -311,12 +309,9 @@ class _EditWorkoutDataScreenState extends State { ? (selectedStartDate ?? DateTime.now()) : selectedStartDate); if (widget.isNewWorkout) { - FredericBackend.instance.analytics.logWorkoutCreated(); - FredericBackend.instance.workoutManager .add(FredericWorkoutCreateEvent(widget.workout)); } else { - FredericBackend.instance.analytics.logWorkoutSaved(); FredericBackend.instance.workoutManager.updateWorkoutInDB(widget.workout); } } diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 91eec3c..30eea9a 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -26,7 +26,7 @@ class SettingsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - FredericBackend.instance.analytics.logEnterSettingsScreen(); + FredericBackend.instance.analytics.logCurrentScreen('settings-screen'); return FredericScaffold( body: BlocBuilder( builder: (context, user) => CustomScrollView( diff --git a/lib/screens/user_settings_screen.dart b/lib/screens/user_settings_screen.dart index 6660d84..98eabfa 100644 --- a/lib/screens/user_settings_screen.dart +++ b/lib/screens/user_settings_screen.dart @@ -24,7 +24,7 @@ class UserSettingsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - FredericBackend.instance.analytics.logEnterUserSettingsScreen(); + FredericBackend.instance.analytics.logCurrentScreen('user-settings-screen'); return FredericScaffold( body: BlocBuilder( builder: (context, user) => CustomScrollView( @@ -54,72 +54,77 @@ class UserSettingsScreen extends StatelessWidget { ), ), ), - SettingsSegment(title: tr('settings.user.your_data'), elements: < - SettingsElement>[ - SettingsElement( - text: tr('settings.user.name.title'), - icon: Icons.drive_file_rename_outline, - subText: user.name, - changerTitle: tr('settings.user.name.changer_title'), - changeAttributeWidget: TextAttributeChanger( - placeholder: 'Name', - currentValue: () => user.name, - updateValue: (newValue) { - user.name = newValue; - FredericBackend.instance.userManager.userDataChanged(); - }, - ), - ), - // SettingsElement( - // text: 'Username', - // subText: user.username, - // icon: Icons.supervised_user_circle_outlined, - // changerTitle: 'Update your username', - // infoText: - // 'Others can find your profile using your username if your profile is discoverable. You can change your username every month. It must be unique.', - // changeAttributeWidget: TextAttributeChanger( - // placeholder: 'Username', - // currentValue: () => user.username, - // updateValue: (newValue) { - // user.username = newValue; - // FredericBackend.instance.userManager.userDataChanged(); - // }, - // ), - // ), - SettingsElement( - text: tr('settings.user.picture.title'), - changerTitle: tr('settings.user.picture.changer_title'), - icon: Icons.person, - changeAttributeWidget: ImageAttributeChanger( - currentValue: () => user.image, - updateValue: (imageFile) async { - bool success = await user.updateProfilePicture(imageFile); - if (success) { + SettingsSegment( + title: tr('settings.user.your_data'), + elements: [ + SettingsElement( + text: tr('settings.user.name.title'), + icon: Icons.drive_file_rename_outline, + subText: user.name, + changerTitle: tr('settings.user.name.changer_title'), + changeAttributeWidget: TextAttributeChanger( + placeholder: 'Name', + currentValue: () => user.name, + updateValue: (newValue) { + user.name = newValue; FredericBackend.instance.userManager.userDataChanged(); - } - return success; - }), - ), - SettingsElement( - text: tr('settings.user.date_of_birth.title'), - subText: user.birthdayFormatted, - infoText: tr('settings.user.date_of_birth.description'), - changerTitle: tr('settings.user.date_of_birth.changer_title'), - changeAttributeWidget: DateTimeAttributeChanger( - currentValue: () => user.birthday, - updateValue: (newDate) { - user.birthday = newDate; - FredericBackend.instance.userManager.userDataChanged(); - }, + }, + ), ), - icon: Icons.cake_outlined), - SettingsElement( - text: tr('settings.user.email_address_title'), - subText: FirebaseAuth.instance.currentUser?.email, - icon: Icons.mail_outline_rounded, - clickable: false, - ), - ]), + // SettingsElement( + // text: 'Username', + // subText: user.username, + // icon: Icons.supervised_user_circle_outlined, + // changerTitle: 'Update your username', + // infoText: + // 'Others can find your profile using your username if your profile is discoverable. You can change your username every month. It must be unique.', + // changeAttributeWidget: TextAttributeChanger( + // placeholder: 'Username', + // currentValue: () => user.username, + // updateValue: (newValue) { + // user.username = newValue; + // FredericBackend.instance.userManager.userDataChanged(); + // }, + // ), + // ), + SettingsElement( + text: tr('settings.user.picture.title'), + changerTitle: tr('settings.user.picture.changer_title'), + icon: Icons.person, + changeAttributeWidget: ImageAttributeChanger( + currentValue: () => user.image, + updateValue: (imageFile) async { + bool success = + await user.updateProfilePicture(imageFile); + if (success) { + FredericBackend.instance.userManager + .userDataChanged(); + } + return success; + }), + ), + SettingsElement( + text: tr('settings.user.date_of_birth.title'), + subText: user.birthdayFormatted, + infoText: tr('settings.user.date_of_birth.description'), + changerTitle: + tr('settings.user.date_of_birth.changer_title'), + changeAttributeWidget: DateTimeAttributeChanger( + currentValue: () => user.birthday, + updateValue: (newDate) { + user.birthday = newDate; + FredericBackend.instance.userManager + .userDataChanged(); + }, + ), + icon: Icons.cake_outlined), + SettingsElement( + text: tr('settings.user.email_address_title'), + subText: FirebaseAuth.instance.currentUser?.email, + icon: Icons.mail_outline_rounded, + clickable: false, + ), + ]), SliverPadding(padding: const EdgeInsets.symmetric(vertical: 12)), FutureBuilder( future: SharedPreferences.getInstance(),