mirror of
https://github.com/jagandeepbrar/lunasea.git
synced 2026-08-31 12:42:34 +00:00
(chore): Cleanup core folder structure
This commit is contained in:
35
lib/core/firebase/analytics.dart
Normal file
35
lib/core/firebase/analytics.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||
import 'package:firebase_analytics/observer.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaFirebaseAnalytics {
|
||||
/// Returns true if Firebase Analytics is compatible with this build system/platform.
|
||||
static bool get isPlatformCompatible => !Platform.isLinux && !Platform.isMacOS && !Platform.isWindows;
|
||||
|
||||
/// Returns an instance of [FirebaseAnalytics].
|
||||
///
|
||||
/// Throws an error if [LunaFirebase.initialize] has not been called.
|
||||
static FirebaseAnalytics get instance => FirebaseAnalytics();
|
||||
|
||||
/// Returns an instance of [FirebaseAnalyticsObserver].
|
||||
///
|
||||
/// Throws an error if [LunaFirebase.initialize] has not been called.
|
||||
static FirebaseAnalyticsObserver get observer => FirebaseAnalyticsObserver(analytics: instance);
|
||||
|
||||
/// Set the enabled state of Firebase Analytics.
|
||||
///
|
||||
/// If `enabled` is set to false, force-disables Analytics.
|
||||
void setEnabledState() {
|
||||
if(isPlatformCompatible) {
|
||||
bool state = LunaDatabaseValue.ENABLE_FIREBASE_ANALYTICS.data;
|
||||
instance.setAnalyticsCollectionEnabled(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Log an "app_open" event.
|
||||
Future<void> appOpened() async {
|
||||
if(isPlatformCompatible) instance.logAppOpen();
|
||||
}
|
||||
}
|
||||
72
lib/core/firebase/auth.dart
Normal file
72
lib/core/firebase/auth.dart
Normal file
@@ -0,0 +1,72 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaFirebaseAuth {
|
||||
/// Return an instance of [FirebaseAuth].
|
||||
///
|
||||
/// Throws an error if [LunaFirebase.initialize] has not been called.
|
||||
static FirebaseAuth get instance => FirebaseAuth.instance;
|
||||
|
||||
/// Returns the [User] object.
|
||||
///
|
||||
/// If the user is not signed in, returns null.
|
||||
User get user => instance.currentUser;
|
||||
|
||||
/// Returns if a user is signed in.
|
||||
bool get isSignedIn => instance.currentUser != null;
|
||||
|
||||
/// Returns the user's UID.
|
||||
///
|
||||
/// If the user is not signed in, returns null.
|
||||
String get uid => instance.currentUser?.uid;
|
||||
|
||||
/// Return the user's email.
|
||||
///
|
||||
/// If the user is not signed in, returns null.
|
||||
String get email => instance.currentUser?.email;
|
||||
|
||||
/// Sign out a logged in user.
|
||||
///
|
||||
/// If the user is not signed in, this is a non-op.
|
||||
Future<void> signOut() async => instance.signOut();
|
||||
|
||||
/// Register a new user using Firebase Authentication.
|
||||
///
|
||||
/// Returns a [LunaFirebaseAuthResponse] which contains the state (true on success, false on failure), the [User] object, and [FirebaseAuthException] if applicable.
|
||||
Future<LunaFirebaseAuthResponse> registerUser(String email, String password) async {
|
||||
try {
|
||||
assert(email != null && password != null);
|
||||
UserCredential _user = await instance.createUserWithEmailAndPassword(email: email, password: password);
|
||||
LunaFirebaseFirestore().addDeviceToken();
|
||||
return LunaFirebaseAuthResponse(state: true, user: _user.user, error: null);
|
||||
} on FirebaseAuthException catch (error) {
|
||||
return LunaFirebaseAuthResponse(state: false, user: null, error: error);
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error("Failed to register user: $email", error, stack);
|
||||
return LunaFirebaseAuthResponse(state: false, user: null, error: null);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign in a user using Firebase Authentication.
|
||||
///
|
||||
/// Returns a [LunaFirebaseAuthResponse] which contains the state (true on success, false on failure), the [User] object, and [FirebaseAuthException] if applicable.
|
||||
Future<LunaFirebaseAuthResponse> signInUser(String email, String password) async {
|
||||
try {
|
||||
assert(email != null && password != null);
|
||||
UserCredential _user = await instance.signInWithEmailAndPassword(email: email, password: password);
|
||||
LunaFirebaseFirestore().addDeviceToken();
|
||||
return LunaFirebaseAuthResponse(state: true, user: _user.user, error: null);
|
||||
} on FirebaseAuthException catch (error) {
|
||||
return LunaFirebaseAuthResponse(state: false, user: null, error: error);
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error("Failed to login user: $email", error, stack);
|
||||
return LunaFirebaseAuthResponse(state: false, user: null, error: null);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset a user's password by sending them a password reset email.
|
||||
Future<void> resetPassword(String email) async {
|
||||
assert(email != null);
|
||||
instance.sendPasswordResetEmail(email: email);
|
||||
}
|
||||
}
|
||||
13
lib/core/firebase/core.dart
Normal file
13
lib/core/firebase/core.dart
Normal file
@@ -0,0 +1,13 @@
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
|
||||
class LunaFirebase {
|
||||
/// Return an instance of [FirebaseApp].
|
||||
///
|
||||
/// Throws an error if [LunaFirebase.initialize] has not been called.
|
||||
static FirebaseApp get instance => Firebase.app();
|
||||
|
||||
/// Initialize Firebase and configuration.
|
||||
///
|
||||
/// This must be called before anything accesses Firebase services, or an exception will be thrown.
|
||||
Future<void> initialize({ String name }) async => await Firebase.initializeApp();
|
||||
}
|
||||
24
lib/core/firebase/crashlytics.dart
Normal file
24
lib/core/firebase/crashlytics.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'dart:io';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaFirebaseCrashlytics {
|
||||
/// Returns true if Firebase Analytics is compatible with this build system/platform.
|
||||
static bool get isPlatformCompatible => !Platform.isLinux && !Platform.isMacOS && !Platform.isWindows;
|
||||
|
||||
/// Returns an instance of [FirebaseCrashlytics].
|
||||
///
|
||||
/// Throws an error if [LunaFirebase.initialize] has not been called.
|
||||
static FirebaseCrashlytics get instance => FirebaseCrashlytics.instance;
|
||||
|
||||
/// Set the enabled state of Firebase Crashlytics.
|
||||
///
|
||||
/// If `enabled` is set to false, force-disables Analytics.
|
||||
void setEnabledState() {
|
||||
if(isPlatformCompatible) {
|
||||
bool state = kReleaseMode && LunaDatabaseValue.ENABLE_FIREBASE_CRASHLYTICS.data;
|
||||
instance.setCrashlyticsCollectionEnabled(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
lib/core/firebase/firestore.dart
Normal file
67
lib/core/firebase/firestore.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaFirebaseFirestore {
|
||||
/// Returns an instance of [FirebaseFirestore].
|
||||
///
|
||||
/// Throws an errof if [LunaFirebase.initialize] has not been called.
|
||||
static FirebaseFirestore get instance => FirebaseFirestore.instance;
|
||||
|
||||
/// Add a backup entry to Firestore. Returns true if successful, and false on any error.
|
||||
///
|
||||
/// If the user is not signed in, returns false.
|
||||
Future<bool> addBackupEntry(String id, int timestamp, { String title = '', String description = '' }) async {
|
||||
if(!LunaFirebaseAuth().isSignedIn) return false;
|
||||
try {
|
||||
LunaFirebaseBackupDocument entry = LunaFirebaseBackupDocument(id: id, title: title, description: description, timestamp: timestamp);
|
||||
instance.doc('users/${LunaFirebaseAuth().uid}/backups/$id').set(entry.toJSON());
|
||||
return true;
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to add backup entry', error, stack);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a backup entry from Firestore. Returns true if successful, and false on any error.
|
||||
///
|
||||
/// If the user is not signed in, returns false.
|
||||
Future<bool> deleteBackupEntry(String id) async {
|
||||
if(!LunaFirebaseAuth().isSignedIn) return false;
|
||||
try {
|
||||
await instance.doc('users/${LunaFirebaseAuth().uid}/backups/$id').delete();
|
||||
return true;
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to delete backup entry', error, stack);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list of all backups available for this account.
|
||||
///
|
||||
/// If the user is not signed in, returns an empty list.
|
||||
Future<List<LunaFirebaseBackupDocument>> getBackupEntries() async {
|
||||
if(LunaFirebaseAuth().user == null) return [];
|
||||
try {
|
||||
QuerySnapshot snapshot = await instance.collection('users/${LunaFirebaseAuth().uid}/backups').orderBy('timestamp', descending: true).get();
|
||||
return snapshot.docs.map<LunaFirebaseBackupDocument>((document) => LunaFirebaseBackupDocument.fromQueryDocumentSnapshot(document)).toList();
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to get backup list', error, stack);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the current device token to Firestore. Returns true if successful, and false on any error.
|
||||
Future<bool> addDeviceToken() async {
|
||||
if(!LunaFirebaseAuth().isSignedIn) return false;
|
||||
try {
|
||||
String token = await LunaFirebaseMessaging.instance.getToken();
|
||||
instance.doc('users/${LunaFirebaseAuth().uid}').set({
|
||||
'devices': FieldValue.arrayUnion([token]),
|
||||
}, SetOptions(merge: true));
|
||||
return true;
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to add device token', error, stack);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
96
lib/core/firebase/messaging.dart
Normal file
96
lib/core/firebase/messaging.dart
Normal file
@@ -0,0 +1,96 @@
|
||||
import 'dart:async';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaFirebaseMessaging {
|
||||
/// Returns an instance of [FirebaseMessaging].
|
||||
///
|
||||
/// Throws an error if [LunaFirebase.initialize] has not been called.
|
||||
static FirebaseMessaging get instance => FirebaseMessaging.instance;
|
||||
|
||||
/// Returns a [Stream] to handle any new messages that are received while the application is in the open and in foreground.
|
||||
Stream<RemoteMessage> get onMessage => FirebaseMessaging.onMessage;
|
||||
|
||||
/// Returns a [Stream] to handle any notifications that are tapped while the application is in the background (not terminated).
|
||||
Stream<RemoteMessage> get onMessageOpenedApp => FirebaseMessaging.onMessageOpenedApp;
|
||||
|
||||
/// Returns the Firebase Cloud Messaging device token for this device.
|
||||
Future<String> get token async => instance.getToken();
|
||||
|
||||
/// Request for permission to send a user notifications.
|
||||
///
|
||||
/// Returns true if permissions are allowed at either a full or provisional level.
|
||||
/// Returns false if permissions are denied or not determined.
|
||||
Future<bool> requestNotificationPermissions() async {
|
||||
NotificationSettings settings = await instance.requestPermission();
|
||||
switch(settings.authorizationStatus) {
|
||||
case AuthorizationStatus.authorized:
|
||||
case AuthorizationStatus.provisional: return true;
|
||||
case AuthorizationStatus.denied:
|
||||
case AuthorizationStatus.notDetermined:
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a [StreamSubscription] that will show a notification banner on a newly received notification.
|
||||
///
|
||||
/// This listens on [FirebaseMessaging.onMessage], where the application must be open and in the foreground.
|
||||
StreamSubscription<RemoteMessage> onMessageListener() {
|
||||
return onMessage.listen((message) {
|
||||
if(message == null) return;
|
||||
LunaModule module = (message.data ?? {}).isNotEmpty ? LunaModule.DASHBOARD.fromKey(message.data['module']) : null;
|
||||
showLunaSnackBar(
|
||||
title: message.notification?.title ?? 'Unknown Content',
|
||||
message: message.notification?.body ?? LunaUI.TEXT_EMDASH,
|
||||
type: LunaSnackbarType.INFO,
|
||||
position: FlashPosition.top,
|
||||
duration: Duration(seconds: 6, milliseconds: 750),
|
||||
showButton: module != null,
|
||||
buttonOnPressed: () async => _handleWebhook(message),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a [StreamSubscription] that will handle messages/notifications that are opened while LunaSea is running in the background.
|
||||
///
|
||||
/// This listens on [FirebaseMessaging.onMessageOpenedApp], where the application must be open but in the background.
|
||||
StreamSubscription<RemoteMessage> onMessageOpenedAppListener() => onMessageOpenedApp.listen(_handleWebhook);
|
||||
|
||||
/// Check to see if there was an initial [RemoteMessage] available to be accessed.
|
||||
///
|
||||
/// If so, handles the notification webhook.
|
||||
Future<void> checkAndHandleInitialMessage() async {
|
||||
RemoteMessage message = await FirebaseMessaging.instance.getInitialMessage();
|
||||
_handleWebhook(message);
|
||||
}
|
||||
|
||||
/// Shared webhook handler.
|
||||
Future<void> _handleWebhook(RemoteMessage message) async {
|
||||
if(message == null || (message.data ?? {}).isEmpty) return;
|
||||
// Extract module
|
||||
LunaModule module = LunaModule.DASHBOARD.fromKey(message.data['module']);
|
||||
if(module == null) {
|
||||
LunaLogger().warning(
|
||||
'LunaFirebaseMessaging',
|
||||
'_handleWebhook',
|
||||
'Unknown module found inside of RemoteMessage: ${message.data['module'] ?? 'null'}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
String profile = message.data['profile'];
|
||||
if(profile?.isEmpty ?? true) {
|
||||
LunaLogger().warning(
|
||||
'LunaFirebaseMessaging',
|
||||
'_handleWebhook',
|
||||
'Invalid profile received in webhook: ${message.data['profile'] ?? 'null'}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
bool result = await LunaProfile().safelyChangeProfiles(profile);
|
||||
if(result) {
|
||||
module?.handleWebhook(message.data);
|
||||
} else {
|
||||
showLunaErrorSnackBar(title: 'Unknown Profile', message: '"$profile" does not exist in LunaSea');
|
||||
}
|
||||
}
|
||||
}
|
||||
59
lib/core/firebase/storage.dart
Normal file
59
lib/core/firebase/storage.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
import 'package:lunasea/core.dart';
|
||||
|
||||
class LunaFirebaseStorage {
|
||||
static const String _BACKUP_BUCKET = 'backup.lunasea.app';
|
||||
|
||||
/// Returns an instance of [FirebaseStorage] for the default bucket.
|
||||
///
|
||||
/// Throws an error if [LunaFirebase.initialize] has not been called.
|
||||
static FirebaseStorage get instanceDefault => FirebaseStorage.instance;
|
||||
|
||||
/// Returns an instance of [FirebaseStorage] for the backup bucket.
|
||||
///
|
||||
/// Throws an error if [LunaFirebase.initialize] has not been called.
|
||||
static FirebaseStorage get instanceBackup => FirebaseStorage.instanceFor(bucket: _BACKUP_BUCKET);
|
||||
|
||||
/// Upload a backup configuration to Firebase storage.
|
||||
///
|
||||
/// If the user is not signed in, returns null.
|
||||
Future<bool> uploadBackup(String data, String id) async {
|
||||
if(!LunaFirebaseAuth().isSignedIn) return false;
|
||||
try {
|
||||
await instanceBackup.ref('${LunaFirebaseAuth().uid}/$id.lunasea').putString(data);
|
||||
return true;
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to backup to Firebase', error, stack);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a backup configuration from Firebase storage.
|
||||
///
|
||||
/// If the user is not signed in, returns null.
|
||||
Future<bool> deleteBackup(String id) async {
|
||||
if(!LunaFirebaseAuth().isSignedIn) return false;
|
||||
try {
|
||||
await instanceBackup.ref('${LunaFirebaseAuth().uid}/$id.lunasea').delete();
|
||||
return true;
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to delete backup from Firebase', error, stack);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Download a backup configuration from Firebase storage.
|
||||
///
|
||||
/// If the user is not signed in, returns null.
|
||||
Future<String> downloadBackup(String id) async {
|
||||
if(!LunaFirebaseAuth().isSignedIn) return null;
|
||||
try {
|
||||
Uint8List data = await instanceBackup.ref('${LunaFirebaseAuth().uid}/$id.lunasea').getData();
|
||||
return String.fromCharCodes(data);
|
||||
} catch (error, stack) {
|
||||
LunaLogger().error('Failed to download backup from Firebase', error, stack);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
lib/core/firebase/types.dart
Normal file
2
lib/core/firebase/types.dart
Normal file
@@ -0,0 +1,2 @@
|
||||
export 'types/auth_response.dart';
|
||||
export 'types/backup_document.dart';
|
||||
14
lib/core/firebase/types/auth_response.dart
Normal file
14
lib/core/firebase/types/auth_response.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class LunaFirebaseAuthResponse {
|
||||
final bool state;
|
||||
final User user;
|
||||
final FirebaseAuthException error;
|
||||
|
||||
LunaFirebaseAuthResponse({
|
||||
@required this.state,
|
||||
@required this.user,
|
||||
@required this.error,
|
||||
});
|
||||
}
|
||||
31
lib/core/firebase/types/backup_document.dart
Normal file
31
lib/core/firebase/types/backup_document.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class LunaFirebaseBackupDocument {
|
||||
final String id;
|
||||
final int timestamp;
|
||||
final String title;
|
||||
final String description;
|
||||
|
||||
|
||||
LunaFirebaseBackupDocument({
|
||||
@required this.id,
|
||||
@required this.timestamp,
|
||||
@required this.title,
|
||||
@required this.description,
|
||||
});
|
||||
|
||||
factory LunaFirebaseBackupDocument.fromQueryDocumentSnapshot(QueryDocumentSnapshot document) => LunaFirebaseBackupDocument(
|
||||
id: document.data()['id'],
|
||||
timestamp: document.data()['timestamp'],
|
||||
title: document.data()['title'],
|
||||
description: document.data()['description'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJSON() => {
|
||||
'id': id,
|
||||
'title': title,
|
||||
'timestamp': timestamp,
|
||||
'description': description,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user