added ability to save key value data in matrix profile so it persists across sessions

pull/1091/head
Gabby Gurdin 2 years ago
parent 8314b2a0c0
commit e891ab138a

@ -1,6 +1,5 @@
class PLocalKey { class PLocalKey {
static const String user = 'user'; static const String user = 'user';
static const String matrixProfile = 'matrixProfile';
static const String classes = 'classes'; static const String classes = 'classes';

@ -49,12 +49,14 @@ class ClassController extends BaseController {
final String? classCode = _pangeaController.pStoreService.read( final String? classCode = _pangeaController.pStoreService.read(
PLocalKey.cachedClassCodeToJoin, PLocalKey.cachedClassCodeToJoin,
addClientIdToKey: false, addClientIdToKey: false,
local: true,
); );
if (classCode != null) { if (classCode != null) {
_pangeaController.pStoreService.delete( await _pangeaController.pStoreService.delete(
PLocalKey.cachedClassCodeToJoin, PLocalKey.cachedClassCodeToJoin,
addClientIdToKey: false, addClientIdToKey: false,
local: true,
); );
await joinClasswithCode( await joinClasswithCode(
context, context,

@ -33,8 +33,10 @@ class AnalyticsController extends BaseController {
TimeSpan get currentAnalyticsTimeSpan { TimeSpan get currentAnalyticsTimeSpan {
try { try {
final String? str = final String? str = _pangeaController.pStoreService.read(
_pangeaController.pStoreService.read(_analyticsTimeSpanKey); _analyticsTimeSpanKey,
local: true,
);
return str != null return str != null
? TimeSpan.values.firstWhere((e) { ? TimeSpan.values.firstWhere((e) {
final spanString = e.toString(); final spanString = e.toString();
@ -48,8 +50,11 @@ class AnalyticsController extends BaseController {
} }
Future<void> setCurrentAnalyticsTimeSpan(TimeSpan timeSpan) async { Future<void> setCurrentAnalyticsTimeSpan(TimeSpan timeSpan) async {
await _pangeaController.pStoreService await _pangeaController.pStoreService.save(
.save(_analyticsTimeSpanKey, timeSpan.toString()); _analyticsTimeSpanKey,
timeSpan.toString(),
local: true,
);
} }
Future<List<ChartAnalyticsModel?>> allClassAnalytics() async { Future<List<ChartAnalyticsModel?>> allClassAnalytics() async {

@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/controllers/base_controller.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/models/class_model.dart'; import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/models/user_model.dart';
import 'package:fluffychat/pangea/utils/p_extension.dart'; import 'package:fluffychat/pangea/utils/p_extension.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
@ -31,7 +32,9 @@ class PermissionsController extends BaseController {
/// Returns false if user is null /// Returns false if user is null
bool isUser18() { bool isUser18() {
final dob = _pangeaController.userController.matrixProfile?.dateOfBirth; final dob = _pangeaController.pStoreService.read(
MatrixProfile.dateOfBirth.title,
);
return dob != null return dob != null
? DateTime.parse(dob).isAtLeastYearsOld(AgeLimits.toAccessFeatures) ? DateTime.parse(dob).isAtLeastYearsOld(AgeLimits.toAccessFeatures)
: false; : false;

@ -8,6 +8,7 @@ import 'package:fluffychat/pangea/controllers/base_controller.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/models/base_subscription_info.dart'; import 'package:fluffychat/pangea/models/base_subscription_info.dart';
import 'package:fluffychat/pangea/models/mobile_subscriptions.dart'; import 'package:fluffychat/pangea/models/mobile_subscriptions.dart';
import 'package:fluffychat/pangea/models/user_model.dart';
import 'package:fluffychat/pangea/models/web_subscriptions.dart'; import 'package:fluffychat/pangea/models/web_subscriptions.dart';
import 'package:fluffychat/pangea/network/requests.dart'; import 'package:fluffychat/pangea/network/requests.dart';
import 'package:fluffychat/pangea/network/urls.dart'; import 'package:fluffychat/pangea/network/urls.dart';
@ -78,9 +79,13 @@ class SubscriptionController extends BaseController {
} else { } else {
final bool? beganWebPayment = _pangeaController.pStoreService.read( final bool? beganWebPayment = _pangeaController.pStoreService.read(
PLocalKey.beganWebPayment, PLocalKey.beganWebPayment,
local: true,
); );
if (beganWebPayment ?? false) { if (beganWebPayment ?? false) {
_pangeaController.pStoreService.delete(PLocalKey.beganWebPayment); await _pangeaController.pStoreService.delete(
PLocalKey.beganWebPayment,
local: true,
);
if (_pangeaController.subscriptionController.isSubscribed) { if (_pangeaController.subscriptionController.isSubscribed) {
subscriptionStream.add(true); subscriptionStream.add(true);
} }
@ -116,9 +121,10 @@ class SubscriptionController extends BaseController {
selectedSubscription.duration!, selectedSubscription.duration!,
isPromo: isPromo, isPromo: isPromo,
); );
_pangeaController.pStoreService.save( await _pangeaController.pStoreService.save(
PLocalKey.beganWebPayment, PLocalKey.beganWebPayment,
true, true,
local: true,
); );
setState(); setState();
launchUrlString( launchUrlString(
@ -160,12 +166,18 @@ class SubscriptionController extends BaseController {
bool get _activatedNewUserTrial => bool get _activatedNewUserTrial =>
_pangeaController.userController.inTrialWindow && _pangeaController.userController.inTrialWindow &&
(_pangeaController.pStoreService.read(PLocalKey.activatedTrialKey) ?? (_pangeaController.pStoreService.read(
MatrixProfile.activatedFreeTrial.title,
) ??
false); false);
void activateNewUserTrial() { void activateNewUserTrial() {
_pangeaController.pStoreService.save(PLocalKey.activatedTrialKey, true); _pangeaController.pStoreService
setNewUserTrial(); .save(
MatrixProfile.activatedFreeTrial.title,
true,
)
.then((_) => setNewUserTrial());
} }
void setNewUserTrial() { void setNewUserTrial() {
@ -206,6 +218,7 @@ class SubscriptionController extends BaseController {
DateTime? get _lastDismissedPaywall { DateTime? get _lastDismissedPaywall {
final lastDismissed = _pangeaController.pStoreService.read( final lastDismissed = _pangeaController.pStoreService.read(
PLocalKey.dismissedPaywall, PLocalKey.dismissedPaywall,
local: true,
); );
if (lastDismissed == null) return null; if (lastDismissed == null) return null;
return DateTime.tryParse(lastDismissed); return DateTime.tryParse(lastDismissed);
@ -214,6 +227,7 @@ class SubscriptionController extends BaseController {
int? get _paywallBackoff { int? get _paywallBackoff {
final backoff = _pangeaController.pStoreService.read( final backoff = _pangeaController.pStoreService.read(
PLocalKey.paywallBackoff, PLocalKey.paywallBackoff,
local: true,
); );
if (backoff == null) return null; if (backoff == null) return null;
return backoff; return backoff;
@ -227,18 +241,24 @@ class SubscriptionController extends BaseController {
(24 * (_paywallBackoff ?? 1))); (24 * (_paywallBackoff ?? 1)));
} }
void dismissPaywall() { void dismissPaywall() async {
_pangeaController.pStoreService.save( await _pangeaController.pStoreService.save(
PLocalKey.dismissedPaywall, PLocalKey.dismissedPaywall,
DateTime.now().toString(), DateTime.now().toString(),
local: true,
); );
if (_paywallBackoff == null) { if (_paywallBackoff == null) {
_pangeaController.pStoreService.save(PLocalKey.paywallBackoff, 1); await _pangeaController.pStoreService.save(
PLocalKey.paywallBackoff,
1,
local: true,
);
} else { } else {
_pangeaController.pStoreService.save( await _pangeaController.pStoreService.save(
PLocalKey.paywallBackoff, PLocalKey.paywallBackoff,
_paywallBackoff! + 1, _paywallBackoff! + 1,
local: true,
); );
} }
} }

@ -1,14 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/constants/language_keys.dart';
import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart';
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/material.dart';
import 'package:fluffychat/widgets/fluffy_chat_app.dart';
import 'package:jwt_decode/jwt_decode.dart'; import 'package:jwt_decode/jwt_decode.dart';
import 'package:matrix/matrix.dart' as matrix; import 'package:matrix/matrix.dart' as matrix;
@ -23,6 +20,17 @@ class UserController extends BaseController {
_pangeaController = pangeaController; _pangeaController = pangeaController;
} }
Future<void> createPangeaUser({required String dob}) async {
final PUserModel newUserModel = await PUserRepo.repoCreatePangeaUser(
userID: userId!,
fullName: fullname,
dob: dob,
matrixAccessToken: _matrixAccessToken!,
);
newUserModel.save(_pangeaController);
await updateMatrixProfile(dateOfBirth: dob);
}
Future<PUserModel?> fetchUserModel() async { Future<PUserModel?> fetchUserModel() async {
try { try {
if (_matrixAccessToken == null) { if (_matrixAccessToken == null) {
@ -30,48 +38,209 @@ class UserController extends BaseController {
"calling fetchUserModel with matrixAccesstoken == null", "calling fetchUserModel with matrixAccesstoken == null",
); );
} }
final PUserModel? newUserModel = await PUserRepo.fetchPangeaUserInfo( final PUserModel? newUserModel = await PUserRepo.fetchPangeaUserInfo(
userID: userId!, userID: userId!,
matrixAccessToken: _matrixAccessToken!, matrixAccessToken: _matrixAccessToken!,
); );
newUserModel?.save(_pangeaController);
if (newUserModel != null) { await migrateMatrixProfile();
_savePUserModel(newUserModel);
if (newUserModel.profile!.dateOfBirth != null) {
await setMatrixProfile(newUserModel.profile!.dateOfBirth!);
}
final MatrixProfile? matrixProfile = await getMatrixProfile();
_saveMatrixProfile(matrixProfile);
}
_completeCompleter(); _completeCompleter();
return newUserModel; return newUserModel;
} catch (err) { } catch (err) {
log("User model not found. Probably first signup and needs Pangea account"); debugPrint(
"User model not found. Probably first signup and needs Pangea account",
);
rethrow; rethrow;
} }
} }
Future<void> setMatrixProfile(String dob) async { dynamic migratedProfileInfo(MatrixProfile key) {
await _pangeaController.matrixState.client.setAccountData( final dynamic localValue = _pangeaController.pStoreService.read(
userId!, key.title,
PangeaEventTypes.userAge, local: true,
{ModelKey.userDateOfBirth: dob},
); );
final MatrixProfile? matrixProfile = await getMatrixProfile(); final dynamic matrixValue = _pangeaController.pStoreService.read(
_saveMatrixProfile(matrixProfile); key.title,
);
return localValue != null && matrixValue != localValue ? localValue : null;
} }
Future<MatrixProfile?> getMatrixProfile() async { Future<void> migrateMatrixProfile() async {
try { final String? pangeaDob = userModel?.profile?.dateOfBirth;
final Map<String, dynamic> accountData = final String? matrixDob = _pangeaController.pStoreService.read(
await _pangeaController.matrixState.client.getAccountData( ModelKey.userDateOfBirth,
userId!, );
PangeaEventTypes.userAge, final String? dob =
pangeaDob != null && matrixDob != pangeaDob ? pangeaDob : null;
final bool? autoPlay = migratedProfileInfo(MatrixProfile.autoPlayMessages);
final bool? trial = migratedProfileInfo(MatrixProfile.activatedFreeTrial);
final bool? interactiveTranslator =
migratedProfileInfo(MatrixProfile.interactiveTranslator);
final bool? interactiveGrammar =
migratedProfileInfo(MatrixProfile.interactiveGrammar);
final bool? immersionMode =
migratedProfileInfo(MatrixProfile.immersionMode);
final bool? definitions = migratedProfileInfo(MatrixProfile.definitions);
final bool? translations = migratedProfileInfo(MatrixProfile.translations);
final bool? showItInstructions =
migratedProfileInfo(MatrixProfile.showedItInstructions);
final bool? showClickMessage =
migratedProfileInfo(MatrixProfile.showedClickMessage);
final bool? showBlurMeansTranslate =
migratedProfileInfo(MatrixProfile.showedBlurMeansTranslate);
await updateMatrixProfile(
dateOfBirth: dob,
autoPlayMessages: autoPlay,
activatedFreeTrial: trial,
interactiveTranslator: interactiveTranslator,
interactiveGrammar: interactiveGrammar,
immersionMode: immersionMode,
definitions: definitions,
translations: translations,
showedItInstructions: showItInstructions,
showedClickMessage: showClickMessage,
showedBlurMeansTranslate: showBlurMeansTranslate,
);
}
Future<void> updateUserProfile({
String? dateOfBirth,
String? targetLanguage,
String? sourceLanguage,
String? country,
List<String>? interests,
List<String>? speaks,
bool? publicProfile,
}) async {
if (userModel == null) throw Exception("Local userModel not defined");
final profileJson = userModel!.profile!.toJson();
if (dateOfBirth != null) {
profileJson[ModelKey.userDateOfBirth] = dateOfBirth;
}
if (targetLanguage != null) {
profileJson[ModelKey.userTargetLanguage] = targetLanguage;
}
if (sourceLanguage != null) {
profileJson[ModelKey.userSourceLanguage] = sourceLanguage;
}
if (interests != null) {
profileJson[ModelKey.userInterests] = interests.toString();
}
if (speaks != null) {
profileJson[ModelKey.userSpeaks] = speaks.toString();
}
if (country != null) {
profileJson[ModelKey.userCountry] = country;
}
if (publicProfile != null) {
profileJson[ModelKey.publicProfile] = publicProfile;
}
final Profile updatedUserProfile = await PUserRepo.updateUserProfile(
Profile.fromJson(profileJson),
await accessToken,
);
PUserModel(
access: await accessToken,
refresh: userModel!.refresh,
profile: updatedUserProfile,
).save(_pangeaController);
if (dateOfBirth != null) {
await updateMatrixProfile(dateOfBirth: dateOfBirth);
}
}
PUserModel? get userModel {
final data = _pangeaController.pStoreService.read(
PLocalKey.user,
local: true,
);
return data != null ? PUserModel.fromJson(data) : null;
}
Future<void> updateMatrixProfile({
String? dateOfBirth,
bool? autoPlayMessages,
bool? activatedFreeTrial,
bool? interactiveTranslator,
bool? interactiveGrammar,
bool? immersionMode,
bool? definitions,
bool? translations,
bool? showedItInstructions,
bool? showedClickMessage,
bool? showedBlurMeansTranslate,
}) async {
if (dateOfBirth != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.dateOfBirth.title,
dateOfBirth,
);
}
if (autoPlayMessages != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.autoPlayMessages.title,
autoPlayMessages,
);
}
if (activatedFreeTrial != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.activatedFreeTrial.title,
activatedFreeTrial,
);
}
if (interactiveTranslator != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.interactiveTranslator.title,
interactiveTranslator,
);
}
if (interactiveGrammar != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.interactiveGrammar.title,
interactiveGrammar,
);
}
if (immersionMode != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.immersionMode.title,
immersionMode,
);
}
if (definitions != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.definitions.title,
definitions,
);
}
if (translations != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.translations.title,
translations,
);
}
if (showedItInstructions != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.showedItInstructions.title,
showedItInstructions,
);
}
if (showedClickMessage != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.showedClickMessage.title,
showedClickMessage,
);
}
if (showedBlurMeansTranslate != null) {
await _pangeaController.pStoreService.save(
MatrixProfile.showedBlurMeansTranslate.title,
showedBlurMeansTranslate,
); );
return MatrixProfile.fromJson(accountData);
} catch (_) {
return null;
} }
} }
@ -116,16 +285,6 @@ class UserController extends BaseController {
return userID.substring(0, userID.indexOf(":")).replaceAll("@", ""); return userID.substring(0, userID.indexOf(":")).replaceAll("@", "");
} }
PUserModel? get userModel {
final data = _pangeaController.pStoreService.read(PLocalKey.user);
return data != null ? PUserModel.fromJson(data) : null;
}
MatrixProfile? get matrixProfile {
final data = _pangeaController.pStoreService.read(PLocalKey.matrixProfile);
return data != null ? MatrixProfile.fromJson(data) : null;
}
Future<bool> get isPUserDataAvailable async { Future<bool> get isPUserDataAvailable async {
try { try {
final PUserModel? toCheck = userModel ?? (await fetchUserModel()); final PUserModel? toCheck = userModel ?? (await fetchUserModel());
@ -137,10 +296,15 @@ class UserController extends BaseController {
Future<bool> get isUserDataAvailableAndDateOfBirthSet async { Future<bool> get isUserDataAvailableAndDateOfBirthSet async {
try { try {
if (matrixProfile == null) { final client = _pangeaController.matrixState.client;
await fetchUserModel(); if (client.prevBatch == null) {
await client.onSync.stream.first;
} }
return matrixProfile?.dateOfBirth != null ? true : false; await fetchUserModel();
final localAccountData = _pangeaController.pStoreService.read(
ModelKey.userDateOfBirth,
);
return localAccountData != null;
} catch (err) { } catch (err) {
return false; return false;
} }
@ -175,99 +339,6 @@ class UserController extends BaseController {
} }
} }
redirectToUserInfo() {
// _pangeaController.matrix.router!.currentState!.to(
// "/home/connect/user_age",
// queryParameters:
// _pangeaController.matrix.router!.currentState!.queryParameters,
// );
FluffyChatApp.router.go("/rooms/user_age");
}
_saveMatrixProfile(MatrixProfile? matrixProfile) {
if (matrixProfile != null) {
_pangeaController.pStoreService.save(
PLocalKey.matrixProfile,
matrixProfile.toJson(),
);
setState(data: matrixProfile);
}
}
_savePUserModel(PUserModel? pUserModel) {
if (pUserModel == null) {
ErrorHandler.logError(e: "trying to save null userModel");
return;
}
final jsonUser = pUserModel.toJson();
_pangeaController.pStoreService.save(PLocalKey.user, jsonUser);
setState(data: pUserModel);
}
Future<void> updateUserProfile({
String? dateOfBirth,
String? targetLanguage,
String? sourceLanguage,
String? country,
List<String>? interests,
List<String>? speaks,
bool? publicProfile,
}) async {
if (userModel == null) throw Exception("Local userModel not defined");
final profileJson = userModel!.profile!.toJson();
if (dateOfBirth != null) {
profileJson[ModelKey.userDateOfBirth] = dateOfBirth;
}
if (targetLanguage != null) {
profileJson[ModelKey.userTargetLanguage] = targetLanguage;
}
if (sourceLanguage != null) {
profileJson[ModelKey.userSourceLanguage] = sourceLanguage;
}
if (interests != null) {
profileJson[ModelKey.userInterests] = interests.toString();
}
if (speaks != null) {
profileJson[ModelKey.userSpeaks] = speaks.toString();
}
if (country != null) {
profileJson[ModelKey.userCountry] = country;
}
if (publicProfile != null) {
profileJson[ModelKey.publicProfile] = publicProfile;
}
final Profile updatedUserProfile = await PUserRepo.updateUserProfile(
Profile.fromJson(profileJson),
await accessToken,
);
await _savePUserModel(
PUserModel(
access: await accessToken,
refresh: userModel!.refresh,
profile: updatedUserProfile,
),
);
if (dateOfBirth != null) {
await setMatrixProfile(dateOfBirth);
}
}
Future<void> createPangeaUser({required String dob}) async {
final PUserModel newUserModel = await PUserRepo.repoCreatePangeaUser(
userID: userId!,
fullName: fullname,
dob: dob,
matrixAccessToken: _matrixAccessToken!,
);
await _savePUserModel(newUserModel);
await setMatrixProfile(dob);
}
String? get _matrixAccessToken => String? get _matrixAccessToken =>
_pangeaController.matrixState.client.accessToken; _pangeaController.matrixState.client.accessToken;

@ -490,7 +490,11 @@ extension PangeaRoom on Room {
final String migratedAnalyticsKey = final String migratedAnalyticsKey =
"MIGRATED_ANALYTICS_KEY${id.localpart}"; "MIGRATED_ANALYTICS_KEY${id.localpart}";
if (storageService?.read(migratedAnalyticsKey) ?? false) return; if (storageService?.read(
migratedAnalyticsKey,
local: true,
) ??
false) return;
if (!isPangeaClass && !isExchange) { if (!isPangeaClass && !isExchange) {
throw Exception( throw Exception(
@ -522,7 +526,11 @@ extension PangeaRoom on Room {
); );
myAnalEvent.bulkUpdate(updateMessages); myAnalEvent.bulkUpdate(updateMessages);
storageService?.save(migratedAnalyticsKey, true); await storageService?.save(
migratedAnalyticsKey,
true,
local: true,
);
} catch (err, s) { } catch (err, s) {
if (kDebugMode) rethrow; if (kDebugMode) rethrow;
// debugger(when: kDebugMode); // debugger(when: kDebugMode);

@ -1,10 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../controllers/pangea_controller.dart'; import '../controllers/pangea_controller.dart';
class PAuthGaurd { class PAuthGaurd {
@ -16,10 +15,10 @@ class PAuthGaurd {
GoRouterState state, GoRouterState state,
) async { ) async {
if (pController != null) { if (pController != null) {
final bool setDob = await pController!
.userController.isUserDataAvailableAndDateOfBirthSet;
if (Matrix.of(context).client.isLogged()) { if (Matrix.of(context).client.isLogged()) {
return !setDob ? '/user_age' : '/rooms'; final bool dobIsSet = await pController!
.userController.isUserDataAvailableAndDateOfBirthSet;
return dobIsSet ? '/rooms' : '/user_age';
} }
return null; return null;
} else { } else {
@ -34,13 +33,12 @@ class PAuthGaurd {
GoRouterState state, GoRouterState state,
) async { ) async {
if (pController != null) { if (pController != null) {
final bool setDob = await pController! if (!Matrix.of(context).client.isLogged()) {
return '/home';
}
final bool dobIsSet = await pController!
.userController.isUserDataAvailableAndDateOfBirthSet; .userController.isUserDataAvailableAndDateOfBirthSet;
return !Matrix.of(context).client.isLogged() return dobIsSet ? null : '/user_age';
? '/home'
: !setDob
? '/user_age'
: null;
} else { } else {
debugPrint("controller is null in pguard check"); debugPrint("controller is null in pguard check");
return Matrix.of(context).client.isLogged() ? null : '/home'; return Matrix.of(context).client.isLogged() ? null : '/home';

@ -1,7 +1,11 @@
import 'dart:convert'; import 'dart:convert';
import 'package:country_picker/country_picker.dart'; import 'package:country_picker/country_picker.dart';
import 'package:fluffychat/pangea/constants/local.key.dart';
import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/models/class_model.dart';
import 'package:fluffychat/pangea/utils/instructions.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -37,23 +41,56 @@ class PUserModel {
} }
return data; return data;
} }
}
class MatrixProfile {
String dateOfBirth;
MatrixProfile({ Future<void> save(PangeaController pangeaController) async {
required this.dateOfBirth, await pangeaController.pStoreService.save(
}); PLocalKey.user,
toJson(),
local: true,
);
}
}
factory MatrixProfile.fromJson(Map<String, dynamic> json) => MatrixProfile( enum MatrixProfile {
dateOfBirth: json[ModelKey.userDateOfBirth], dateOfBirth,
); autoPlayMessages,
activatedFreeTrial,
interactiveTranslator,
interactiveGrammar,
immersionMode,
definitions,
translations,
showedItInstructions,
showedClickMessage,
showedBlurMeansTranslate,
}
Map<String, dynamic> toJson() { extension MatrixProfileExtension on MatrixProfile {
final Map<String, dynamic> data = <String, dynamic>{}; String get title {
data[ModelKey.userDateOfBirth] = dateOfBirth; switch (this) {
return data; case MatrixProfile.dateOfBirth:
return ModelKey.userDateOfBirth;
case MatrixProfile.autoPlayMessages:
return PLocalKey.autoPlayMessages;
case MatrixProfile.activatedFreeTrial:
return PLocalKey.activatedTrialKey;
case MatrixProfile.interactiveTranslator:
return ToolSetting.interactiveTranslator.toString();
case MatrixProfile.interactiveGrammar:
return ToolSetting.interactiveGrammar.toString();
case MatrixProfile.immersionMode:
return ToolSetting.immersionMode.toString();
case MatrixProfile.definitions:
return ToolSetting.definitions.toString();
case MatrixProfile.translations:
return ToolSetting.translations.toString();
case MatrixProfile.showedItInstructions:
return InstructionsEnum.itInstructions.toString();
case MatrixProfile.showedClickMessage:
return InstructionsEnum.clickMessage.toString();
case MatrixProfile.showedBlurMeansTranslate:
return InstructionsEnum.blurMeansTranslate.toString();
}
} }
} }

@ -59,6 +59,7 @@ class SettingsLearningView extends StatelessWidget {
title: setting.toolName(context), title: setting.toolName(context),
subtitle: setting.toolDescription(context), subtitle: setting.toolDescription(context),
pStoreKey: setting.toString(), pStoreKey: setting.toString(),
local: false,
), ),
PSettingsSwitchListTile.adaptive( PSettingsSwitchListTile.adaptive(
defaultValue: controller.pangeaController.pStoreService.read( defaultValue: controller.pangeaController.pStoreService.read(
@ -68,6 +69,7 @@ class SettingsLearningView extends StatelessWidget {
title: L10n.of(context)!.autoPlayTitle, title: L10n.of(context)!.autoPlayTitle,
subtitle: L10n.of(context)!.autoPlayDesc, subtitle: L10n.of(context)!.autoPlayDesc,
pStoreKey: PLocalKey.autoPlayMessages, pStoreKey: PLocalKey.autoPlayMessages,
local: false,
), ),
], ],
), ),

@ -25,8 +25,14 @@ class InstructionsController {
_instructionsClosed[key] ?? _instructionsClosed[key] ??
false; false;
void updateEnableInstructions(InstructionsEnum key, bool value) => Future<void> updateEnableInstructions(
_pangeaController.pStoreService.save(key.toString(), value); InstructionsEnum key,
bool value,
) async =>
await _pangeaController.pStoreService.save(
key.toString(),
value,
);
Future<void> show( Future<void> show(
BuildContext context, BuildContext context,
@ -149,9 +155,11 @@ class InstructionsToggleState extends State<InstructionsToggle> {
title: Text(L10n.of(context)!.doNotShowAgain), title: Text(L10n.of(context)!.doNotShowAgain),
value: pangeaController.instructions value: pangeaController.instructions
.wereInstructionsTurnedOff(widget.instructionsKey), .wereInstructionsTurnedOff(widget.instructionsKey),
onChanged: ((value) { onChanged: ((value) async {
pangeaController.instructions await pangeaController.instructions.updateEnableInstructions(
.updateEnableInstructions(widget.instructionsKey, value); widget.instructionsKey,
value,
);
setState(() {}); setState(() {});
}), }),
); );

@ -1,6 +1,7 @@
import 'package:get_storage/get_storage.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:get_storage/get_storage.dart';
import 'package:matrix/matrix.dart';
class PLocalStore { class PLocalStore {
final GetStorage _box = GetStorage(); final GetStorage _box = GetStorage();
@ -13,24 +14,112 @@ class PLocalStore {
String key, String key,
dynamic data, { dynamic data, {
bool addClientIdToKey = true, bool addClientIdToKey = true,
bool local = false,
}) async {
local
? await saveLocal(
key,
data,
addClientIdToKey: addClientIdToKey,
)
: await saveProfile(key, data);
}
/// fetch data from local
dynamic read(
String key, {
bool addClientIdToKey = true,
local = false,
}) {
return local
? readLocal(
key,
addClientIdToKey: addClientIdToKey,
)
: readProfile(key);
}
/// delete data from local
Future<void> delete(
String key, {
bool addClientIdToKey = true,
local = false,
}) async {
return local
? deleteLocal(
key,
addClientIdToKey: addClientIdToKey,
)
: deleteProfile(key);
}
/// save data in local
Future<void> saveLocal(
String key,
dynamic data, {
bool addClientIdToKey = true,
}) async { }) async {
await _box.write(_key(key, addClientIdToKey: addClientIdToKey), data); await _box.write(_key(key, addClientIdToKey: addClientIdToKey), data);
} }
Future<void> saveProfile(
String key,
dynamic data,
) async {
final waitForAccountSync =
pangeaController.matrixState.client.onSync.stream.firstWhere(
(sync) =>
sync.accountData != null &&
sync.accountData!.any(
(event) => event.content.keys.any(
(k) => k == key,
),
),
);
await pangeaController.matrixState.client.setAccountData(
pangeaController.matrixState.client.userID!,
key,
{key: data},
);
await waitForAccountSync;
await pangeaController.matrixState.client.onSyncStatus.stream.firstWhere(
(syncStatus) => syncStatus.status == SyncStatus.finished,
);
}
/// fetch data from local /// fetch data from local
dynamic read(String key, {bool addClientIdToKey = true}) { dynamic readLocal(String key, {bool addClientIdToKey = true}) {
return pangeaController.matrixState.client.userID != null return pangeaController.matrixState.client.userID != null
? _box.read(_key(key, addClientIdToKey: addClientIdToKey)) ? _box.read(_key(key, addClientIdToKey: addClientIdToKey))
: null; : null;
} }
dynamic readProfile(String key) {
try {
return pangeaController.matrixState.client.accountData[key]?.content[key];
} catch (err) {
ErrorHandler.logError(e: err);
return null;
}
}
/// delete data from local /// delete data from local
Future<void> delete(String key, {bool addClientIdToKey = true}) async { Future<void> deleteLocal(String key, {bool addClientIdToKey = true}) async {
return pangeaController.matrixState.client.userID != null return pangeaController.matrixState.client.userID != null
? _box.remove(_key(key, addClientIdToKey: addClientIdToKey)) ? _box.remove(_key(key, addClientIdToKey: addClientIdToKey))
: null; : null;
} }
Future<void> deleteProfile(key) async {
return pangeaController.matrixState.client.userID != null
? pangeaController.matrixState.client.setAccountData(
pangeaController.matrixState.client.userID!,
key,
{key: null},
)
: null;
}
_key(String key, {bool addClientIdToKey = true}) { _key(String key, {bool addClientIdToKey = true}) {
return addClientIdToKey return addClientIdToKey
? pangeaController.matrixState.client.userID! + key ? pangeaController.matrixState.client.userID! + key

@ -1,12 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:go_router/go_router.dart';
import 'package:fluffychat/pangea/constants/url_query_parameter_keys.dart'; import 'package:fluffychat/pangea/constants/url_query_parameter_keys.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/utils/class_code.dart'; import 'package:fluffychat/pangea/utils/class_code.dart';
import 'package:fluffychat/widgets/layouts/empty_page.dart'; import 'package:fluffychat/widgets/layouts/empty_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:go_router/go_router.dart';
import '../../../widgets/matrix.dart'; import '../../../widgets/matrix.dart';
import '../../constants/local.key.dart'; import '../../constants/local.key.dart';
import '../../utils/error_handler.dart'; import '../../utils/error_handler.dart';
@ -49,6 +48,7 @@ class _JoinClassWithLinkState extends State<JoinClassWithLink> {
PLocalKey.cachedClassCodeToJoin, PLocalKey.cachedClassCodeToJoin,
classCode, classCode,
addClientIdToKey: false, addClientIdToKey: false,
local: true,
); );
context.go("/home"); context.go("/home");
}); });

@ -1,14 +1,15 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
class PSettingsSwitchListTile extends StatefulWidget { class PSettingsSwitchListTile extends StatefulWidget {
final bool defaultValue; final bool defaultValue;
final String pStoreKey; final String pStoreKey;
final String title; final String title;
final String? subtitle; final String? subtitle;
final bool local;
const PSettingsSwitchListTile.adaptive({ const PSettingsSwitchListTile.adaptive({
super.key, super.key,
@ -16,6 +17,7 @@ class PSettingsSwitchListTile extends StatefulWidget {
required this.pStoreKey, required this.pStoreKey,
required this.title, required this.title,
this.subtitle, this.subtitle,
this.local = false,
}); });
@override @override
@ -23,17 +25,41 @@ class PSettingsSwitchListTile extends StatefulWidget {
} }
class PSettingsSwitchListTileState extends State<PSettingsSwitchListTile> { class PSettingsSwitchListTileState extends State<PSettingsSwitchListTile> {
bool currentValue = true;
@override
void initState() {
currentValue = MatrixState.pangeaController.pStoreService.read(
widget.pStoreKey,
local: widget.local,
) ??
widget.defaultValue;
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final PangeaController pangeaController = MatrixState.pangeaController; final PangeaController pangeaController = MatrixState.pangeaController;
return SwitchListTile.adaptive( return SwitchListTile.adaptive(
value: pangeaController.pStoreService.read(widget.pStoreKey) ?? value: currentValue,
widget.defaultValue,
title: Text(widget.title), title: Text(widget.title),
activeColor: AppConfig.activeToggleColor, activeColor: AppConfig.activeToggleColor,
subtitle: widget.subtitle != null ? Text(widget.subtitle!) : null, subtitle: widget.subtitle != null ? Text(widget.subtitle!) : null,
onChanged: (bool newValue) async { onChanged: (bool newValue) async {
pangeaController.pStoreService.save(widget.pStoreKey, newValue); try {
await pangeaController.pStoreService.save(
widget.pStoreKey,
newValue,
local: widget.local,
);
currentValue = newValue;
} catch (err, s) {
ErrorHandler.logError(
e: err,
m: "Failed to updates user setting ${widget.pStoreKey}",
s: s,
);
}
setState(() {}); setState(() {});
}, },
); );

Loading…
Cancel
Save