import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'dart:io'; import 'package:auto_route/auto_route.dart'; import 'package:dio/dio.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:mnemo_cards/di/locator.dart'; import 'package:mnemo_cards/main.dart'; import 'package:mnemo_cards/managers/repository/http_repository.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:rxdart/rxdart.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:device_info_plus/device_info_plus.dart'; import '../domain/router/app_router.dart'; import '../domain/router/app_router.gr.dart'; import '../features/analytics/analytics.dart'; import 'user_state_holder.dart'; class UserManager { CompositeSubscription? _subscription; final HttpRepository _repository; GoogleSignIn _googleSignIn = GoogleSignIn( scopes: [ 'email', // 'https://www.googleapis.com/auth/contacts.readonly', ], // clientId: '01767851968-dvbvmte4m90dkqitp013s4nd5c0df1n8.apps.googleusercontent.com', // "701767851968-bud97rud1d9qtqju96addn31nhm0oofu.apps.googleusercontent.com", ); late final SharedPreferences? _sharedPreferences; final UserStateHolder userStateHolder; UserManager(this._repository) : userStateHolder = UserStateHolder(); bool get hasUser => userStateHolder.user != null; int? get userId => userStateHolder.user?.id; Future login() async { if (await _googleSignIn.isSignedIn()) { await _googleSignIn.signOut(); } final account = await _googleSignIn.signIn(); await Future.delayed(Duration(milliseconds: 300)); } Future logout() async { if (await _googleSignIn.isSignedIn()) { await _googleSignIn.signOut(); } Analytics.logout(userStateHolder.user); userStateHolder.clear(); } Future _createUser( String externalId, ExternalIdType idType, String? name, ) async { final (user, token) = await _repository.createOrGetUser(externalId, idType, name); await _setAuthToken(token); userStateHolder.setUser(user); } Future _setAuthToken(String authToken) async { _repository.setAuthToken(authToken); await secureStorage.write(key: 'authToken', value: authToken); } Future _clearAuthToken() async { await secureStorage.delete(key: 'authToken'); } Future getAuthToken() async { return secureStorage.read(key: 'authToken'); } Future applyPromocode(String code) async { final data = await _repository.applyPromocode(code); return data; } Future updateUser() async { String? authToken = await getAuthToken(); if (authToken != null) { await _setAuthToken(authToken); try { final fetchedUser = await _repository.getUser(); userStateHolder.setUser(fetchedUser!); return fetchedUser; } catch (e) { _clearAuthToken(); AppRouter.openAuthOrProfile(); } } // not creating empty user // if (Platform.isAndroid) { // final deviceInfoPlugin = DeviceInfoPlugin(); // final deviceInfo = await deviceInfoPlugin.androidInfo; // final id = deviceInfo.id; // await _createUser(id, ExternalIdType.device); // } return null; } Future init() async { _sharedPreferences = await SharedPreferences.getInstance(); try { await updateUser(); } on Object catch (e, s) { log('User manager init error', error: e, stackTrace: s); } _subscription = CompositeSubscription() ..add(_googleSignIn.onCurrentUserChanged .startWith(_googleSignIn.currentUser) .distinct((p, n) => p?.id == n?.id) .listen((account) async { if (account != null) { final name = account.displayName; final externalToken = await account.authentication.then((value) => value.idToken); Analytics.login(ExternalIdType.google); await _createUser(externalToken!, ExternalIdType.google, name); } })) ..add( userStateHolder.asNullableStream .distinct((a, b) => a?.id == b?.id) // don't want to clear cache on login .skip(1) .listen( (user) async { try { if (user == null) { AppRouter.openAuthOrProfile(); } else { try { AppRouter.closeAuthPage(); await locator.previewPackPoller.poll(); await locator.packManager.clearCache(); await locator.packUpdater.updateAvailablePacks(); } catch (err) { log(err.toString()); } } } catch (e) { log(e.toString()); _clearAuthToken(); if (e is DioException) { if (e.response?.statusCode == 401) { AppRouter.openProfile(); } } } }, ), ); } Future dispose() async { await _subscription?.cancel(); _subscription = null; } } enum ExternalIdType { google, device }