Skip to content

Commit

Permalink
crushing firestore errors
Browse files Browse the repository at this point in the history
  • Loading branch information
Desync-o-tron committed Sep 28, 2024
1 parent 42564d7 commit 62c0758
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 62 deletions.
13 changes: 10 additions & 3 deletions lib/DOM/basic_user_info.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:open_fitness_tracker/DOM/training_metadata.dart';
Expand All @@ -12,15 +13,21 @@ dart run build_runner watch --delete-conflicting-outputs
*/

class BasicUserInfoCubit extends Cubit<BasicUserInfo> {
BasicUserInfoCubit() : super(BasicUserInfo());
BasicUserInfoCubit() : super(BasicUserInfo()) {
try {
get();
} catch (authEx) {
//
}
}

void set(BasicUserInfo userInfo) {
myStorage.setBasicUserInfo(userInfo);
cloudStorage.setBasicUserInfo(userInfo);
emit(userInfo);
}

void get() async {
var userInfo = await myStorage.getBasicUserInfo();
var userInfo = await cloudStorage.getBasicUserInfo();
emit(userInfo);
}
}
Expand Down
117 changes: 94 additions & 23 deletions lib/cloud_io/firestore_sync.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,41 @@ import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:open_fitness_tracker/DOM/training_metadata.dart';
import 'package:open_fitness_tracker/DOM/basic_user_info.dart';
import 'package:open_fitness_tracker/navigation/routes.dart';
import 'package:shared_preferences/shared_preferences.dart';

MyStorage myStorage = MyStorage();
/*
TODO is there a smoke test I can run on web/devices deploy?
just want to see if the damn pages load & waht load times are like..
*/

class MyStorage {
MyStorage() {
CloudStorage cloudStorage = CloudStorage();

class CloudStorage {
CloudStorage() {
_firestore.settings = const Settings(
persistenceEnabled: true,
cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED,
);
if (kIsWeb) {
// ignore: deprecated_member_use
_firestore.enablePersistence(
const PersistenceSettings(synchronizeTabs: true),
);
}
_userDoc = FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid);
// ignore: deprecated_member_use
// _firestore.enablePersistence(
// const PersistenceSettings(synchronizeTabs: true),
// );

FirebaseAuth.instance.userChanges().listen((User? user) {
routerConfig.refresh(); //https://stackoverflow.com/a/77448906/3894291
if (user != null) {
cloudStorage.refreshTrainingHistoryCacheIfItsBeenXHours(12);
}
});

if (!isUserEmailVerified()) return;
refreshTrainingHistoryCacheIfItsBeenXHours(12);
}

final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final _historyCacheClock = CollectionCacheUpdateClock(_historyKey);
late final DocumentReference _userDoc;

static const _historyKey = 'TrainingHistory';
static const _basicUserInfoKey = 'BasicUserInfo';

Expand Down Expand Up @@ -71,20 +82,38 @@ class MyStorage {
}
}

bool isUserEmailVerified() {
return (FirebaseAuth.instance.currentUser != null &&
FirebaseAuth.instance.currentUser!.emailVerified);
}

Future<void> addTrainingSessionToHistory(TrainingSession session) async {
if (!isUserEmailVerified()) {
return Future.error(
"Sign in. Make sure to verify your email if not signing in with Google Sign In, etc...");
}
await _retryWithExponentialBackoff(() async {
await _userDoc.collection(_historyKey).add(session.toJson());
await _firestore
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection(_historyKey)
.add(session.toJson());
});
}

Stream<List<TrainingSession>> getUserTrainingHistoryStream({
required int limit,
DateTime? startAfterTimestamp,
}) {
if (!isUserEmailVerified()) {
return Stream.error(
"Sign in. Make sure to verify your email if not signing in with Google Sign In, etc...");
}

final String userUid = FirebaseAuth.instance.currentUser!.uid;
final String collectionPath = 'users/$userUid/$_historyKey';

Query query = FirebaseFirestore.instance
Query query = _firestore
.collection(collectionPath)
.orderBy('date', descending: true)
.limit(limit);
Expand All @@ -101,6 +130,10 @@ class MyStorage {
}

Future<void> refreshTrainingHistoryCacheIfItsBeenXHours(int hours) async {
if (!isUserEmailVerified()) {
return Future.error(
"Sign in. Make sure to verify your email if not signing in with Google Sign In, etc...");
}
if (await _historyCacheClock.timeSinceCacheWasUpdated() > Duration(hours: hours)) {
await getEntireUserTrainingHistory(useCache: false);
}
Expand All @@ -109,14 +142,22 @@ class MyStorage {
Future<List<TrainingSession>> getEntireUserTrainingHistory({
required bool useCache,
}) async {
if (!isUserEmailVerified()) {
return Future.error(
"Sign in. Make sure to verify your email if not signing in with Google Sign In, etc...");
}
return await _retryWithExponentialBackoff(() async {
QuerySnapshot<Object?> cloudTrainingHistory;
if (useCache) {
cloudTrainingHistory = await _userDoc
cloudTrainingHistory = await _firestore
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection(_historyKey)
.get(const GetOptions(source: Source.cache));
} else {
cloudTrainingHistory = await _userDoc
cloudTrainingHistory = await _firestore
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection(_historyKey)
.get(const GetOptions(source: Source.server));
_historyCacheClock.resetClock();
Expand All @@ -134,19 +175,35 @@ class MyStorage {
}

Future<void> removeTrainingSessionFromHistory(final TrainingSession sesh) async {
if (!isUserEmailVerified()) {
return Future.error(
"Sign in. Make sure to verify your email if not signing in with Google Sign In, etc...");
}
if (sesh.id == '') {
throw Exception("No session ID!");
throw Exception("No training session ID! does this training session exist?");
}
await _retryWithExponentialBackoff(() async {
await _userDoc.collection(_historyKey).doc(sesh.id).delete();
await _firestore
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection(_historyKey)
.doc(sesh.id)
.delete();
});
}

Future<void> deleteEntireTrainingHistory() async {
if (!isUserEmailVerified()) {
return Future.error(
"Sign in. Make sure to verify your email if not signing in with Google Sign In, etc...");
}
await _retryWithExponentialBackoff(() async {
CollectionReference historyCollection = _userDoc.collection(_historyKey);
CollectionReference historyCollection = _firestore
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection(_historyKey);
QuerySnapshot snapshot = await historyCollection.get();
WriteBatch batch = FirebaseFirestore.instance.batch();
WriteBatch batch = _firestore.batch();
for (DocumentSnapshot doc in snapshot.docs) {
batch.delete(doc.reference);
}
Expand All @@ -155,8 +212,15 @@ class MyStorage {
}

Future<BasicUserInfo> getBasicUserInfo() async {
if (!isUserEmailVerified()) {
return Future.error(
"Sign in. Make sure to verify your email if not signing in with Google Sign In, etc...");
}
return await _retryWithExponentialBackoff(() async {
final docSnapshot = await _userDoc.get(const GetOptions(source: Source.server));
final docSnapshot = await _firestore
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get(const GetOptions(source: Source.server));
final data = docSnapshot.data() as Map<String, dynamic>?;

if (data != null && data.containsKey(_basicUserInfoKey)) {
Expand All @@ -169,8 +233,15 @@ class MyStorage {
}

Future<void> setBasicUserInfo(BasicUserInfo userInfo) async {
if (!isUserEmailVerified()) {
return Future.error(
"Sign in. Make sure to verify your email if not signing in with Google Sign In, etc...");
}
await _retryWithExponentialBackoff(() async {
await _userDoc.update({_basicUserInfoKey: userInfo.toJson()});
await _firestore
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.update({_basicUserInfoKey: userInfo.toJson()});
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/community/charts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class _CoolChartState extends State<CoolChart> {

void _loadData() async {
List<TrainingSession> trainHist =
await myStorage.getEntireUserTrainingHistory(useCache: true);
await cloudStorage.getEntireUserTrainingHistory(useCache: true);
if (trainHist.isEmpty) return;

for (var trainSesh in trainHist) {
Expand Down
12 changes: 7 additions & 5 deletions lib/community/profile_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ class ProfileScreenWrapper extends StatelessWidget {
}),
],
appBar: AppBar(
leading: BackButton(
onPressed: () {
context.pop();
},
),
leading: Navigator.of(context).canPop()
? BackButton(
onPressed: () {
context.pop();
},
)
: Container(),
),
children: [
if (!FirebaseAuth.instance.currentUser!.emailVerified)
Expand Down
22 changes: 9 additions & 13 deletions lib/exercises/ex_search_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,6 @@ import 'package:open_fitness_tracker/DOM/exercise_metadata.dart';
import 'package:fuzzy/fuzzy.dart';
import 'package:open_fitness_tracker/utils/utils.dart';

// //does this make sense here?
// class SelectedExercises extends Cubit<List<Exercise>> {
// SelectedExercises() : super([]);

// void updateSelectedExercises(List<Exercise> exercises) {
// emit(exercises);
// }
// }

class ExSearchState {
List<Exercise> filteredExercises = ExDB.exercises;
List<String> categoriesFilter = [];
Expand All @@ -30,7 +21,9 @@ class ExSearchState {

/// Every time we're going to update we re-apply every filter. not the most efficient but it's fine for now
ExSearchState copyWithNewFilters(
{List<String>? categoriesFilter, List<String>? musclesFilter, String? enteredKeyword}) {
{List<String>? categoriesFilter,
List<String>? musclesFilter,
String? enteredKeyword}) {
ExSearchState newState = ExSearchState(this);
newState.filteredExercises = ExDB.exercises;
newState.categoriesFilter = categoriesFilter ?? newState.categoriesFilter;
Expand All @@ -52,15 +45,18 @@ class ExSearchState {
}

for (String category in newState.categoriesFilter) {
newState.filteredExercises = newState.filteredExercises.where((e) => e.category == category).toList();
newState.filteredExercises =
newState.filteredExercises.where((e) => e.category == category).toList();
}

// filter by keyword
if (newState.enteredKeyword.isNotEmpty) {
var fuseForNames = Fuzzy(ExDB.names, options: FuzzyOptions(findAllMatches: true, threshold: 0.25));
var fuseForNames =
Fuzzy(ExDB.names, options: FuzzyOptions(findAllMatches: true, threshold: 0.25));
var resultsByName = fuseForNames.search(newState.enteredKeyword);
var matchedNames = resultsByName.map((r) => r.item as String).toSet();
var fuseForMuscles = Fuzzy(ExDB.muscles, options: FuzzyOptions(findAllMatches: true, threshold: 0.25));
var fuseForMuscles = Fuzzy(ExDB.muscles,
options: FuzzyOptions(findAllMatches: true, threshold: 0.25));
var resultsByMuscles = fuseForMuscles.search(newState.enteredKeyword);
var matchedMuscles = resultsByMuscles.map((r) => r.item as String).toSet();
tempExs = [];
Expand Down
11 changes: 6 additions & 5 deletions lib/history/history_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class _HistoryPageState extends State<HistoryPage> {
_isLoading = true;
});

final stream = myStorage.getUserTrainingHistoryStream(
final stream = cloudStorage.getUserTrainingHistoryStream(
limit: _pageSize,
startAfterTimestamp: _lastTimestamp,
);
Expand Down Expand Up @@ -83,7 +83,8 @@ class _HistoryPageState extends State<HistoryPage> {
//todo error handling?
}
});
numSessions = (await myStorage.getEntireUserTrainingHistory(useCache: true)).length;
numSessions =
(await cloudStorage.getEntireUserTrainingHistory(useCache: true)).length;
setState(() {
//numSessions
});
Expand Down Expand Up @@ -148,7 +149,7 @@ class _HistoryPageState extends State<HistoryPage> {
});
}
if (result == 'refresh training history') {
myStorage.getEntireUserTrainingHistory(useCache: false);
cloudStorage.getEntireUserTrainingHistory(useCache: false);
}
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
Expand All @@ -164,7 +165,7 @@ class _HistoryPageState extends State<HistoryPage> {
value: 'delete history',
child: ElevatedButton(
onPressed: () {
myStorage.deleteEntireTrainingHistory();
cloudStorage.deleteEntireTrainingHistory();
},
child: const Text("del history..dont click me"),
)),
Expand Down Expand Up @@ -238,7 +239,7 @@ class TrainingHistoryCardManagementDialog extends StatelessWidget {
Widget build(BuildContext context) {
final scaffoldMessenger = ScaffoldMessenger.of(context);
delSesh() async {
myStorage
cloudStorage
.removeTrainingSessionFromHistory(sesh)
.onError((error, stackTrace) => scaffoldMessenger.showSnackBar(const SnackBar(
content: Text('Failed to delete training session'),
Expand Down
2 changes: 1 addition & 1 deletion lib/importing/import_matching_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class _ImportInspectionPageState extends State<ImportInspectionPage> {
var cleanedTrainingSessions =
_removeUnwantedExercisesFromIncomingTrainingData(exNamestoRm);
for (var session in cleanedTrainingSessions) {
myStorage.addTrainingSessionToHistory(session);
cloudStorage.addTrainingSessionToHistory(session);
}

if (mounted) {
Expand Down
Loading

0 comments on commit 62c0758

Please sign in to comment.