From fba7a6faccfdd3bd958e9b9a9767532cb2065c14 Mon Sep 17 00:00:00 2001 From: Takaya Funabiki <937552+numa08@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:39:25 +0900 Subject: [PATCH] log analytics events (#47) --- app/radio_qth_map/lib/main.dart | 4 ++++ .../lib/repository/auth_state_notifier.dart | 11 ++++++++++- .../lib/screen/add_operation_screen.dart | 12 ++++++++++++ .../lib/screen/share_operation_dialog.dart | 12 ++++++++++++ .../lib/widget/operation_map.dart | 19 +++++++++++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/app/radio_qth_map/lib/main.dart b/app/radio_qth_map/lib/main.dart index 86b0257..a759d0b 100644 --- a/app/radio_qth_map/lib/main.dart +++ b/app/radio_qth_map/lib/main.dart @@ -1,4 +1,5 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; @@ -49,10 +50,12 @@ void main() async { } else { defaultLocale = const Locale("en"); } + final analytics = FirebaseAnalytics.instance; final prefs = await SharedPreferences.getInstance(); runApp( MultiProvider( providers: [ + Provider(create: (_) => analytics), Provider( create: (_) => FirestoreRepository(firestore: firestore), ), @@ -60,6 +63,7 @@ void main() async { ChangeNotifierProvider( create: (_) => AuthStateNotifier( auth: auth, + analytics: analytics, prefs: prefs, )), ], diff --git a/app/radio_qth_map/lib/repository/auth_state_notifier.dart b/app/radio_qth_map/lib/repository/auth_state_notifier.dart index ac097a0..c0fb3b7 100644 --- a/app/radio_qth_map/lib/repository/auth_state_notifier.dart +++ b/app/radio_qth_map/lib/repository/auth_state_notifier.dart @@ -1,13 +1,16 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class AuthStateNotifier extends ChangeNotifier { final FirebaseAuth auth; + final FirebaseAnalytics analytics; final SharedPreferences prefs; AuthStateNotifier({ required this.auth, + required this.analytics, required this.prefs, }) { auth.authStateChanges().listen((_) { @@ -21,7 +24,13 @@ class AuthStateNotifier extends ChangeNotifier { return null; } try { - await auth.signInWithEmailLink(email: email, emailLink: emailLink); + final cred = + await auth.signInWithEmailLink(email: email, emailLink: emailLink); + if (cred.additionalUserInfo?.isNewUser == true) { + analytics.logSignUp(signUpMethod: "emailLink"); + } else { + analytics.logLogin(loginMethod: "emailLink"); + } } catch (e) { debugPrint('Failed to sign in with email link: $e'); } finally { diff --git a/app/radio_qth_map/lib/screen/add_operation_screen.dart b/app/radio_qth_map/lib/screen/add_operation_screen.dart index a537694..501fa13 100644 --- a/app/radio_qth_map/lib/screen/add_operation_screen.dart +++ b/app/radio_qth_map/lib/screen/add_operation_screen.dart @@ -1,5 +1,6 @@ import 'package:file_picker/_internal/file_picker_web.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -64,6 +65,7 @@ class _AddOperationScreenState extends State { : () async { final repository = context.read(); final auth = context.read().auth; + final analytics = context.read(); // コールサインの表示設定を変更してから保存する final id = await repository.storeOperations( _logList @@ -75,6 +77,16 @@ class _AddOperationScreenState extends State { .toList(), ownerId: auth.currentUser?.uid, ); + analytics.setUserProperty( + name: "operation_added", + value: "true", + ); + analytics.logEvent( + name: "operation_added", + parameters: { + "operation_id": id, + }, + ); if (context.mounted) { context.pop(id); } diff --git a/app/radio_qth_map/lib/screen/share_operation_dialog.dart b/app/radio_qth_map/lib/screen/share_operation_dialog.dart index e2fab4c..3eb0da3 100644 --- a/app/radio_qth_map/lib/screen/share_operation_dialog.dart +++ b/app/radio_qth_map/lib/screen/share_operation_dialog.dart @@ -1,3 +1,4 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -17,6 +18,7 @@ class ShareOperationDialog extends StatelessWidget { Widget build(BuildContext context) { final repository = context.read(); final dateFormat = DateFormat.yMd(AppLocalizations.of(context)!.localeName); + final analytics = context.read(); return AlertDialog( title: Text(AppLocalizations.of(context)!.share), content: FutureBuilder( @@ -36,6 +38,11 @@ class ShareOperationDialog extends StatelessWidget { children: [ FilledButton.tonalIcon( onPressed: () { + analytics.logShare( + contentType: "operation", + itemId: operationId, + method: "X", + ); final url = '${Uri.base.origin}/map/${operation.id!}'; launchUrlString( 'https://twitter.com/intent/tweet?text=${Uri.encodeComponent(shareText)}&hashtags=QTHMap&url=${Uri.encodeComponent(url)}'); @@ -52,6 +59,11 @@ class ShareOperationDialog extends StatelessWidget { const SizedBox(height: 16), FilledButton.tonalIcon( onPressed: () async { + analytics.logShare( + contentType: "operation", + itemId: operationId, + method: "clipboard", + ); final text = """ $shareText ${Uri.base.origin}/map/${operation.id!} diff --git a/app/radio_qth_map/lib/widget/operation_map.dart b/app/radio_qth_map/lib/widget/operation_map.dart index 29d6124..583b9c4 100644 --- a/app/radio_qth_map/lib/widget/operation_map.dart +++ b/app/radio_qth_map/lib/widget/operation_map.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:intl/intl.dart'; @@ -263,7 +264,12 @@ class OperationMapState extends State void _showOperationDetail() async { assert(_selectedOperation != null); + final analytics = context.read(); final operation = _selectedOperation!; + analytics.logSelectContent( + contentType: "operation", + itemId: operation.id!, + ); final sheetController = showBottomSheet( context: context, builder: (context) { @@ -304,7 +310,14 @@ class OperationMapState extends State onPressed: () async { final repository = context.read(); + final analytics = context.read(); await repository.deleteOperation(operation.id!); + analytics.logEvent( + name: "operation_deleted", + parameters: { + "operation_id": operation.id, + }, + ); if (context.mounted) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( @@ -323,6 +336,12 @@ class OperationMapState extends State .deleted_operation_snackbar_action_undo, onPressed: () async { await repository.undoDeletion(operation); + analytics.logEvent( + name: "operation_deleted_undo", + parameters: { + "operation_id": operation.id, + }, + ); }, ), ),