Browse Source

Add an option to logout

Vishnu Mohandas 4 years ago
parent
commit
a7b2e63b1a

+ 29 - 0
lib/core/configuration.dart

@@ -7,6 +7,13 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
 import 'package:flutter_sodium/flutter_sodium.dart';
 import 'package:logging/logging.dart';
 import 'package:path_provider/path_provider.dart';
+import 'package:photos/core/event_bus.dart';
+import 'package:photos/db/collections_db.dart';
+import 'package:photos/db/files_db.dart';
+import 'package:photos/db/memories_db.dart';
+import 'package:photos/db/public_keys_db.dart';
+import 'package:photos/db/upload_locks_db.dart';
+import 'package:photos/events/user_logged_out_event.dart';
 import 'package:photos/models/key_attributes.dart';
 import 'package:photos/models/key_gen_result.dart';
 import 'package:photos/models/private_key_attributes.dart';
@@ -70,6 +77,28 @@ class Configuration {
     }
   }
 
+  Future<void> logout() async {
+    if (SyncService.instance.isSyncInProgress()) {
+      SyncService.instance.stopSync();
+      try {
+        await SyncService.instance.existingSync();
+      } catch (e) {
+        // ignore
+      }
+    }
+    await _preferences.clear();
+    await _secureStorage.deleteAll();
+    _key = null;
+    _cachedToken = null;
+    _secretKey = null;
+    await FilesDB.instance.clearTable();
+    await CollectionsDB.instance.clearTable();
+    await MemoriesDB.instance.clearTable();
+    await PublicKeysDB.instance.clearTable();
+    await UploadLocksDB.instance.clearTable();
+    Bus.instance.fire(UserLoggedOutEvent());
+  }
+
   Future<KeyGenResult> generateKey(String password) async {
     // Create a master key
     final key = CryptoUtil.generateKey();

+ 5 - 0
lib/db/collections_db.dart

@@ -52,6 +52,11 @@ class CollectionsDB {
     return await openDatabaseWithMigration(path, dbConfig);
   }
 
+  Future<void> clearTable() async {
+    final db = await instance.database;
+    await db.delete(table);
+  }
+
   static List<String> createTable(String tableName) {
     return [
       '''

+ 5 - 0
lib/db/files_db.dart

@@ -119,6 +119,11 @@ class FilesDB {
     ];
   }
 
+  Future<void> clearTable() async {
+    final db = await instance.database;
+    await db.delete(table);
+  }
+
   Future<int> insert(File file) async {
     final db = await instance.database;
     return await db.insert(table, _getRowForFile(file),

+ 5 - 0
lib/db/memories_db.dart

@@ -44,6 +44,11 @@ class MemoriesDB {
                 ''');
   }
 
+  Future<void> clearTable() async {
+    final db = await instance.database;
+    await db.delete(table);
+  }
+
   Future<int> clearMemoriesSeenBeforeTime(int timestamp) async {
     final db = await instance.database;
     return db.delete(

+ 5 - 0
lib/db/public_keys_db.dart

@@ -44,6 +44,11 @@ class PublicKeysDB {
                 ''');
   }
 
+  Future<void> clearTable() async {
+    final db = await instance.database;
+    await db.delete(table);
+  }
+
   Future<int> setKey(PublicKey key) async {
     final db = await instance.database;
     return db.insert(table, _getRow(key),

+ 5 - 0
lib/db/upload_locks_db.dart

@@ -44,6 +44,11 @@ class UploadLocksDB {
                 ''');
   }
 
+  Future<void> clearTable() async {
+    final db = await instance.database;
+    await db.delete(_table);
+  }
+
   Future<void> acquireLock(String id, String owner, int time) async {
     final db = await instance.database;
     final row = new Map<String, dynamic>();

+ 3 - 0
lib/events/user_logged_out_event.dart

@@ -0,0 +1,3 @@
+import 'event.dart';
+
+class UserLoggedOutEvent extends Event {}

+ 10 - 8
lib/services/sync_service.dart

@@ -38,7 +38,7 @@ class SyncService {
   final _diffFetcher = DiffFetcher();
   bool _syncStopRequested = false;
   bool _isBackground = false;
-  Completer<void> _existingSync;
+  Completer<bool> _existingSync;
   SharedPreferences _prefs;
   SyncStatusUpdate _lastSyncStatusEvent;
 
@@ -84,13 +84,17 @@ class SyncService {
     }
   }
 
-  Future<void> sync() async {
+  Future<bool> existingSync() async {
+    return _existingSync.future;
+  }
+
+  Future<bool> sync() async {
     _syncStopRequested = false;
     if (_existingSync != null) {
       _logger.warning("Sync already in progress, skipping.");
       return _existingSync.future;
     }
-    _existingSync = Completer<void>();
+    _existingSync = Completer<bool>();
     bool successful = false;
     try {
       await _doSync();
@@ -98,7 +102,6 @@ class SyncService {
           _lastSyncStatusEvent.status != SyncStatus.applying_local_diff) {
         Bus.instance.fire(SyncStatusUpdate(SyncStatus.completed));
       }
-      _existingSync.complete();
       successful = true;
     } on WiFiUnavailableError {
       _logger.warning("Not uploading over mobile data");
@@ -120,7 +123,7 @@ class SyncService {
         if (errorCode == 111 || errorCode == 7) {
           Bus.instance.fire(SyncStatusUpdate(SyncStatus.paused,
               reason: "waiting for network..."));
-          return;
+          return false;
         }
       } else {
         _logger.severe("backup failed", e, s);
@@ -129,12 +132,11 @@ class SyncService {
         throw e;
       }
     } finally {
-      if (!successful) {
-        _existingSync.completeError(Error());
-      }
+      _existingSync.complete(successful);
       _existingSync = null;
       _logger.info("Syncing completed");
     }
+    return successful;
   }
 
   void stopSync() {

+ 6 - 0
lib/ui/collections_gallery_widget.dart

@@ -10,6 +10,7 @@ import 'package:photos/db/files_db.dart';
 import 'package:photos/events/collection_updated_event.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
 import 'package:photos/events/tab_changed_event.dart';
+import 'package:photos/events/user_logged_out_event.dart';
 import 'package:photos/models/collection_items.dart';
 import 'package:photos/services/billing_service.dart';
 import 'package:photos/services/collections_service.dart';
@@ -35,6 +36,7 @@ class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget>
   final _logger = Logger("CollectionsGallery");
   StreamSubscription<LocalPhotosUpdatedEvent> _localFilesSubscription;
   StreamSubscription<CollectionUpdatedEvent> _collectionUpdatesSubscription;
+  StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
 
   @override
   void initState() {
@@ -46,6 +48,9 @@ class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget>
         Bus.instance.on<CollectionUpdatedEvent>().listen((event) {
       setState(() {});
     });
+    _loggedOutEvent = Bus.instance.on<UserLoggedOutEvent>().listen((event) {
+      setState(() {});
+    });
     super.initState();
   }
 
@@ -271,6 +276,7 @@ class _CollectionsGalleryWidgetState extends State<CollectionsGalleryWidget>
   void dispose() {
     _localFilesSubscription.cancel();
     _collectionUpdatesSubscription.cancel();
+    _loggedOutEvent.cancel();
     super.dispose();
   }
 

+ 13 - 0
lib/ui/home_widget.dart

@@ -8,7 +8,9 @@ import 'package:photos/core/configuration.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
 import 'package:photos/events/permission_granted_event.dart';
+import 'package:photos/events/subscription_purchased_event.dart';
 import 'package:photos/events/tab_changed_event.dart';
+import 'package:photos/events/user_logged_out_event.dart';
 import 'package:photos/models/filters/important_items_filter.dart';
 import 'package:photos/models/file.dart';
 import 'package:photos/repositories/file_repository.dart';
@@ -54,6 +56,8 @@ class _HomeWidgetState extends State<HomeWidget> {
   StreamSubscription<LocalPhotosUpdatedEvent> _photosUpdatedEvent;
   StreamSubscription<TabChangedEvent> _tabChangedEventSubscription;
   StreamSubscription<PermissionGrantedEvent> _permissionGrantedEvent;
+  StreamSubscription<SubscriptionPurchasedEvent> _subscriptionPurchaseEvent;
+  StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
 
   @override
   void initState() {
@@ -82,6 +86,13 @@ class _HomeWidgetState extends State<HomeWidget> {
         Bus.instance.on<PermissionGrantedEvent>().listen((event) {
       setState(() {});
     });
+    _subscriptionPurchaseEvent =
+        Bus.instance.on<SubscriptionPurchasedEvent>().listen((event) {
+      setState(() {});
+    });
+    _loggedOutEvent = Bus.instance.on<UserLoggedOutEvent>().listen((event) {
+      setState(() {});
+    });
     _initDeepLinks();
     super.initState();
   }
@@ -264,6 +275,8 @@ class _HomeWidgetState extends State<HomeWidget> {
     _tabChangedEventSubscription.cancel();
     _photosUpdatedEvent.cancel();
     _permissionGrantedEvent.cancel();
+    _subscriptionPurchaseEvent.cancel();
+    _loggedOutEvent.cancel();
     super.dispose();
   }
 }

+ 83 - 22
lib/ui/settings_page.dart

@@ -39,10 +39,29 @@ class SettingsPage extends StatelessWidget {
       BackupSettingsWidget(),
       SupportSectionWidget(),
       InfoSectionWidget(),
+      AccountSectionWidget(),
+      FutureBuilder(
+        future: _getAppVersion(),
+        builder: (context, snapshot) {
+          if (snapshot.hasData) {
+            return Padding(
+              padding: const EdgeInsets.all(16.0),
+              child: Text(
+                "app version: " + snapshot.data,
+                style: TextStyle(
+                  fontSize: 12,
+                  color: Colors.white.withOpacity(0.6),
+                ),
+              ),
+            );
+          }
+          return Container();
+        },
+      ),
     ];
-    if (kDebugMode) {
-      contents.add(DebugWidget());
-    }
+    // if (kDebugMode) {
+    //   contents.add(DebugWidget());
+    // }
     return SingleChildScrollView(
       child: Padding(
         padding: const EdgeInsets.all(12.0),
@@ -52,6 +71,11 @@ class SettingsPage extends StatelessWidget {
       ),
     );
   }
+
+  static Future<String> _getAppVersion() async {
+    var pkgInfo = await PackageInfo.fromPlatform();
+    return "${pkgInfo.version}";
+  }
 }
 
 class BackupSettingsWidget extends StatefulWidget {
@@ -396,32 +420,69 @@ class InfoSectionWidget extends StatelessWidget {
           child:
               SettingsTextItem(text: "source code", icon: Icons.navigate_next),
         ),
-        FutureBuilder(
-          future: _getAppVersion(),
-          builder: (context, snapshot) {
-            if (snapshot.hasData) {
-              return Padding(
-                padding: const EdgeInsets.all(8.0),
-                child: Text(
-                  "app version: " + snapshot.data,
-                  style: TextStyle(
-                    fontSize: 12,
-                    color: Colors.white.withOpacity(0.6),
+      ]),
+    );
+  }
+}
+
+class AccountSectionWidget extends StatelessWidget {
+  const AccountSectionWidget({Key key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      child: Column(children: [
+        Padding(padding: EdgeInsets.all(12)),
+        SettingsSectionTitle("account"),
+        GestureDetector(
+          behavior: HitTestBehavior.translucent,
+          onTap: () async {
+            AlertDialog alert = AlertDialog(
+              title: Text("logout"),
+              content: Text("are you sure you want to logout?"),
+              actions: [
+                TextButton(
+                  child: Text(
+                    "no",
+                    style: TextStyle(
+                      color: Theme.of(context).buttonColor,
+                    ),
+                  ),
+                  onPressed: () {
+                    Navigator.of(context).pop();
+                  },
+                ),
+                TextButton(
+                  child: Text(
+                    "yes, logout",
+                    style: TextStyle(
+                      color: Colors.red,
+                    ),
                   ),
+                  onPressed: () async {
+                    final dialog =
+                        createProgressDialog(context, "logging out...");
+                    await dialog.show();
+                    await Configuration.instance.logout();
+                    await dialog.hide();
+                    Navigator.of(context).popUntil((route) => route.isFirst);
+                  },
                 ),
-              );
-            }
-            return Container();
+              ],
+            );
+
+            showDialog(
+              context: context,
+              builder: (BuildContext context) {
+                return alert;
+              },
+            );
           },
+          child: SettingsTextItem(text: "logout", icon: Icons.navigate_next),
         ),
       ]),
     );
   }
-
-  static Future<String> _getAppVersion() async {
-    var pkgInfo = await PackageInfo.fromPlatform();
-    return "${pkgInfo.version}";
-  }
 }
 
 class DebugWidget extends StatelessWidget {

+ 6 - 1
lib/ui/shared_collections_gallery.dart

@@ -5,9 +5,9 @@ import 'package:flutter/widgets.dart';
 import 'package:logging/logging.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/event_bus.dart';
-import 'package:photos/db/collections_db.dart';
 import 'package:photos/db/files_db.dart';
 import 'package:photos/events/collection_updated_event.dart';
+import 'package:photos/events/user_logged_out_event.dart';
 import 'package:photos/models/collection.dart';
 import 'package:photos/models/collection_items.dart';
 import 'package:photos/services/collections_service.dart';
@@ -30,12 +30,16 @@ class _SharedCollectionGalleryState extends State<SharedCollectionGallery>
     with AutomaticKeepAliveClientMixin {
   Logger _logger = Logger("SharedCollectionGallery");
   StreamSubscription<CollectionUpdatedEvent> _subscription;
+  StreamSubscription<UserLoggedOutEvent> _loggedOutEvent;
 
   @override
   void initState() {
     _subscription = Bus.instance.on<CollectionUpdatedEvent>().listen((event) {
       setState(() {});
     });
+    _loggedOutEvent = Bus.instance.on<UserLoggedOutEvent>().listen((event) {
+      setState(() {});
+    });
     super.initState();
   }
 
@@ -299,6 +303,7 @@ class _SharedCollectionGalleryState extends State<SharedCollectionGallery>
   @override
   void dispose() {
     _subscription.cancel();
+    _loggedOutEvent.cancel();
     super.dispose();
   }
 

+ 3 - 0
lib/utils/file_uploader.dart

@@ -316,6 +316,9 @@ class FileUploader {
           Sodium.bin2base64(encryptedMetadataData.encryptedData);
       final metadataDecryptionHeader =
           Sodium.bin2base64(encryptedMetadataData.header);
+      if (SyncService.instance.shouldStopSync()) {
+        throw SyncStopRequestedError();
+      }
       var remoteFile;
       if (isAlreadyUploadedFile) {
         remoteFile = await _updateFile(