Forráskód Böngészése

Merge branch 'master' into redesign-storage-card

ashilkn 2 éve
szülő
commit
7803c3b6b1

+ 2 - 2
ios/Podfile.lock

@@ -1,5 +1,5 @@
 PODS:
-  - background_fetch (1.1.0):
+  - background_fetch (1.1.1):
     - Flutter
   - connectivity (0.0.1):
     - Flutter
@@ -309,7 +309,7 @@ EXTERNAL SOURCES:
     :path: ".symlinks/plugins/wakelock/ios"
 
 SPEC CHECKSUMS:
-  background_fetch: 3795af8a49054dc526477cc2f60d2ed41de60587
+  background_fetch: ef7bc433c96131e4f284d8616d2e0d4e18fa6af4
   connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467
   device_info: d7d233b645a32c40dfdc212de5cf646ca482f175
   Firebase: 800f16f07af493d98d017446a315c27af0552f41

+ 4 - 2
lib/db/device_files_db.dart

@@ -372,8 +372,10 @@ extension DeviceFiles on FilesDB {
         deviceCollections.add(deviceCollection);
       }
       if (includeCoverThumbnail) {
-        deviceCollections.sort((a, b) =>
-            b.thumbnail.creationTime.compareTo(a.thumbnail.creationTime));
+        deviceCollections.sort(
+          (a, b) =>
+              b.thumbnail.creationTime.compareTo(a.thumbnail.creationTime),
+        );
       }
       return deviceCollections;
     } catch (e) {

+ 2 - 2
lib/services/remote_sync_service.dart

@@ -129,6 +129,7 @@ class RemoteSyncService {
           // session are not processed now
           sync();
         } else {
+          debugPrint("Fire backup completed event");
           Bus.instance.fire(SyncStatusUpdate(SyncStatus.completedBackup));
         }
       } else {
@@ -259,7 +260,6 @@ class RemoteSyncService {
         await _db.getDevicePathIDToLocalIDMap();
     bool moreFilesMarkedForBackup = false;
     for (final deviceCollection in deviceCollections) {
-      _logger.fine("processing ${deviceCollection.name}");
       final Set<String> localIDsToSync =
           pathIdToLocalIDs[deviceCollection.id] ?? {};
       if (deviceCollection.uploadStrategy == UploadStrategy.ifMissing) {
@@ -400,7 +400,7 @@ class RemoteSyncService {
         if (collectionByID == null || collectionByID.isDeleted) {
           _logger.info(
             "Collection $deviceCollectionID either deleted or missing "
-            "for path ${deviceCollection.name}",
+            "for path ${deviceCollection.id}",
           );
           deviceCollectionID = -1;
         }

+ 3 - 12
lib/ui/account/verify_recovery_page.dart

@@ -148,16 +148,14 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
                       SizedBox(
                         width: double.infinity,
                         child: Text(
-                          'Verify recovery key',
+                          'Confirm recovery key',
                           style: enteTheme.textTheme.h3Bold,
                           textAlign: TextAlign.left,
                         ),
                       ),
                       const SizedBox(height: 18),
                       Text(
-                        "If you forget your password, your recovery key is the "
-                        "only way to recover your photos.\n\nPlease verify that "
-                        "you have safely backed up your 24 word recovery key by re-entering it.",
+                        "Your recovery key is the only way to recover your photos if you forget your password. You can find your recovery key in Settings > Account.\n\nPlease enter your recovery key here to verify that you have saved it correctly.",
                         style: enteTheme.textTheme.small
                             .copyWith(color: enteTheme.colorScheme.textMuted),
                       ),
@@ -187,12 +185,6 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
                         },
                       ),
                       const SizedBox(height: 12),
-                      Text(
-                        "If you saved the recovery key from older app versions, you might have a 64 character recovery code instead of 24 words. You can enter that too.",
-                        style: enteTheme.textTheme.mini
-                            .copyWith(color: enteTheme.colorScheme.textMuted),
-                      ),
-                      const SizedBox(height: 8),
                       Expanded(
                         child: Container(
                           alignment: Alignment.bottomCenter,
@@ -204,8 +196,7 @@ class _VerifyRecoveryPageState extends State<VerifyRecoveryPage> {
                             children: [
                               GradientButton(
                                 onTap: _verifyRecoveryKey,
-                                text: "Verify",
-                                iconData: Icons.shield_outlined,
+                                text: "Confirm",
                               ),
                               const SizedBox(height: 8),
                             ],

+ 1 - 0
lib/ui/components/notification_warning_widget.dart

@@ -35,6 +35,7 @@ class NotificationWarningWidget extends StatelessWidget {
             child: Padding(
               padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
               child: Row(
+                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                 children: [
                   Icon(
                     warningIcon,

+ 0 - 0
lib/ui/grant_permissions_widget.dart → lib/ui/home/grant_permissions_widget.dart


+ 0 - 0
lib/ui/header_error_widget.dart → lib/ui/home/header_error_widget.dart


+ 26 - 0
lib/ui/home/header_widget.dart

@@ -0,0 +1,26 @@
+import 'package:flutter/widgets.dart';
+import 'package:logging/logging.dart';
+import 'package:photos/ui/home/memories_widget.dart';
+import 'package:photos/ui/home/status_bar_widget.dart';
+
+class HeaderWidget extends StatelessWidget {
+  static const _memoriesWidget = MemoriesWidget();
+  static const _statusBarWidget = StatusBarWidget();
+
+  const HeaderWidget({
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    Logger("Header").info("Building header widget");
+    const list = [
+      _statusBarWidget,
+      _memoriesWidget,
+    ];
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: list,
+    );
+  }
+}

+ 176 - 0
lib/ui/home/home_bottom_nav_bar.dart

@@ -0,0 +1,176 @@
+import 'dart:async';
+import 'dart:ui';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:photos/core/event_bus.dart';
+import 'package:photos/ente_theme_data.dart';
+import 'package:photos/events/tab_changed_event.dart';
+import 'package:photos/models/selected_files.dart';
+import 'package:photos/theme/colors.dart';
+import 'package:photos/theme/effects.dart';
+import 'package:photos/theme/ente_theme.dart';
+import 'package:photos/ui/nav_bar.dart';
+
+class HomeBottomNavigationBar extends StatefulWidget {
+  const HomeBottomNavigationBar(
+    this.selectedFiles, {
+    required this.selectedTabIndex,
+    Key? key,
+  }) : super(key: key);
+
+  final SelectedFiles selectedFiles;
+  final int selectedTabIndex;
+
+  @override
+  State<HomeBottomNavigationBar> createState() =>
+      _HomeBottomNavigationBarState();
+}
+
+class _HomeBottomNavigationBarState extends State<HomeBottomNavigationBar> {
+  late StreamSubscription<TabChangedEvent> _tabChangedEventSubscription;
+  int currentTabIndex = 0;
+
+  @override
+  void initState() {
+    super.initState();
+    currentTabIndex = widget.selectedTabIndex;
+    widget.selectedFiles.addListener(() {
+      setState(() {});
+    });
+    _tabChangedEventSubscription =
+        Bus.instance.on<TabChangedEvent>().listen((event) {
+      if (event.source != TabChangedEventSource.tabBar) {
+        debugPrint('index changed to ${event.selectedIndex}');
+        if (mounted) {
+          setState(() {
+            currentTabIndex = event.selectedIndex;
+          });
+        }
+      }
+    });
+  }
+
+  @override
+  void dispose() {
+    _tabChangedEventSubscription.cancel();
+    super.dispose();
+  }
+
+  void _onTabChange(int index) {
+    Bus.instance.fire(
+      TabChangedEvent(
+        index,
+        TabChangedEventSource.tabBar,
+      ),
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final bool filesAreSelected = widget.selectedFiles.files.isNotEmpty;
+    final enteColorScheme = getEnteColorScheme(context);
+    final navBarBlur =
+        MediaQuery.of(context).platformBrightness == Brightness.light
+            ? blurBase
+            : blurMuted;
+
+    return AnimatedContainer(
+      duration: const Duration(milliseconds: 300),
+      curve: Curves.easeInOut,
+      height: filesAreSelected ? 0 : 56,
+      child: AnimatedOpacity(
+        duration: const Duration(milliseconds: 100),
+        opacity: filesAreSelected ? 0.0 : 1.0,
+        curve: Curves.easeIn,
+        child: IgnorePointer(
+          ignoring: filesAreSelected,
+          child: ListView(
+            physics: const NeverScrollableScrollPhysics(),
+            children: [
+              Row(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  ClipRRect(
+                    borderRadius: BorderRadius.circular(32),
+                    child: Container(
+                      alignment: Alignment.bottomCenter,
+                      height: 48,
+                      child: ClipRect(
+                        child: BackdropFilter(
+                          filter: ImageFilter.blur(
+                            sigmaX: navBarBlur,
+                            sigmaY: navBarBlur,
+                          ),
+                          child: GNav(
+                            curve: Curves.easeOutExpo,
+                            backgroundColor:
+                                getEnteColorScheme(context).fillMuted,
+                            mainAxisAlignment: MainAxisAlignment.center,
+                            rippleColor: Colors.white.withOpacity(0.1),
+                            activeColor: Theme.of(context)
+                                .colorScheme
+                                .gNavBarActiveColor,
+                            iconSize: 24,
+                            padding: const EdgeInsets.fromLTRB(16, 6, 16, 6),
+                            duration: const Duration(milliseconds: 200),
+                            gap: 0,
+                            tabBorderRadius: 32,
+                            tabBackgroundColor: Theme.of(context)
+                                .colorScheme
+                                .gNavBarActiveColor,
+                            haptic: false,
+                            tabs: [
+                              GButton(
+                                margin: const EdgeInsets.fromLTRB(8, 6, 10, 6),
+                                icon: Icons.home_rounded,
+                                iconColor: enteColorScheme.tabIcon,
+                                iconActiveColor: strokeBaseLight,
+                                text: '',
+                                onPressed: () {
+                                  _onTabChange(
+                                    0,
+                                  ); // To take care of occasional missing events
+                                },
+                              ),
+                              GButton(
+                                margin: const EdgeInsets.fromLTRB(10, 6, 10, 6),
+                                icon: Icons.collections_rounded,
+                                iconColor: enteColorScheme.tabIcon,
+                                iconActiveColor: strokeBaseLight,
+                                text: '',
+                                onPressed: () {
+                                  _onTabChange(
+                                    1,
+                                  ); // To take care of occasional missing events
+                                },
+                              ),
+                              GButton(
+                                margin: const EdgeInsets.fromLTRB(10, 6, 8, 6),
+                                icon: Icons.people_outlined,
+                                iconColor: enteColorScheme.tabIcon,
+                                iconActiveColor: strokeBaseLight,
+                                text: '',
+                                onPressed: () {
+                                  _onTabChange(
+                                    2,
+                                  ); // To take care of occasional missing events
+                                },
+                              ),
+                            ],
+                            selectedIndex: currentTabIndex,
+                            onTabChange: _onTabChange,
+                          ),
+                        ),
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 84 - 0
lib/ui/home/home_gallery_widget.dart

@@ -0,0 +1,84 @@
+// @dart=2.9
+import 'package:flutter/material.dart';
+import 'package:photos/core/configuration.dart';
+import 'package:photos/core/event_bus.dart';
+import 'package:photos/db/files_db.dart';
+import 'package:photos/events/backup_folders_updated_event.dart';
+import 'package:photos/events/files_updated_event.dart';
+import 'package:photos/events/force_reload_home_gallery_event.dart';
+import 'package:photos/events/local_photos_updated_event.dart';
+import 'package:photos/models/file_load_result.dart';
+import 'package:photos/models/selected_files.dart';
+import 'package:photos/services/collections_service.dart';
+import 'package:photos/services/ignored_files_service.dart';
+import 'package:photos/ui/viewer/gallery/gallery.dart';
+
+class HomeGalleryWidget extends StatelessWidget {
+  final Widget header;
+  final Widget footer;
+  final SelectedFiles selectedFiles;
+
+  const HomeGalleryWidget({
+    Key key,
+    this.header,
+    this.footer,
+    this.selectedFiles,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final gallery = Gallery(
+      asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) async {
+        final ownerID = Configuration.instance.getUserID();
+        final hasSelectedAllForBackup =
+            Configuration.instance.hasSelectedAllFoldersForBackup();
+        final archivedCollectionIds =
+            CollectionsService.instance.getArchivedCollections();
+        FileLoadResult result;
+        if (hasSelectedAllForBackup) {
+          result = await FilesDB.instance.getAllLocalAndUploadedFiles(
+            creationStartTime,
+            creationEndTime,
+            ownerID,
+            limit: limit,
+            asc: asc,
+            ignoredCollectionIDs: archivedCollectionIds,
+          );
+        } else {
+          result = await FilesDB.instance.getAllPendingOrUploadedFiles(
+            creationStartTime,
+            creationEndTime,
+            ownerID,
+            limit: limit,
+            asc: asc,
+            ignoredCollectionIDs: archivedCollectionIds,
+          );
+        }
+
+        // hide ignored files from home page UI
+        final ignoredIDs = await IgnoredFilesService.instance.ignoredIDs;
+        result.files.removeWhere(
+          (f) =>
+              f.uploadedFileID == null &&
+              IgnoredFilesService.instance.shouldSkipUpload(ignoredIDs, f),
+        );
+        return result;
+      },
+      reloadEvent: Bus.instance.on<LocalPhotosUpdatedEvent>(),
+      removalEventTypes: const {
+        EventType.deletedFromRemote,
+        EventType.deletedFromEverywhere,
+        EventType.archived,
+      },
+      forceReloadEvents: [
+        Bus.instance.on<BackupFoldersUpdatedEvent>(),
+        Bus.instance.on<ForceReloadHomeGalleryEvent>(),
+      ],
+      tagPrefix: "home_gallery",
+      selectedFiles: selectedFiles,
+      header: header,
+      footer: footer,
+    );
+    return gallery;
+  }
+}

+ 0 - 0
lib/ui/landing_page_widget.dart → lib/ui/home/landing_page_widget.dart


+ 0 - 0
lib/ui/memories_widget.dart → lib/ui/home/memories_widget.dart


+ 2 - 4
lib/ui/viewer/gallery/gallery_footer_widget.dart → lib/ui/home/preserve_footer_widget.dart

@@ -1,5 +1,3 @@
-// @dart=2.9
-
 import 'package:flutter/material.dart';
 import 'package:photo_manager/photo_manager.dart';
 import 'package:photos/services/local_sync_service.dart';
@@ -7,8 +5,8 @@ import 'package:photos/ui/backup_folder_selection_page.dart';
 import 'package:photos/ui/common/gradient_button.dart';
 import 'package:photos/utils/navigation_util.dart';
 
-class GalleryFooterWidget extends StatelessWidget {
-  const GalleryFooterWidget({Key key}) : super(key: key);
+class PreserveFooterWidget extends StatelessWidget {
+  const PreserveFooterWidget({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {

+ 63 - 0
lib/ui/home/start_backup_hook_widget.dart

@@ -0,0 +1,63 @@
+import 'package:flutter/material.dart';
+import 'package:photo_manager/photo_manager.dart';
+import 'package:photos/services/local_sync_service.dart';
+import 'package:photos/ui/backup_folder_selection_page.dart';
+import 'package:photos/ui/common/gradient_button.dart';
+import 'package:photos/utils/navigation_util.dart';
+
+class StartBackupHookWidget extends StatelessWidget {
+  final Widget headerWidget;
+
+  const StartBackupHookWidget({super.key, required this.headerWidget});
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+      children: [
+        headerWidget,
+        Padding(
+          padding: const EdgeInsets.only(top: 64),
+          child: Image.asset(
+            "assets/onboarding_safe.png",
+            height: 206,
+          ),
+        ),
+        Text(
+          'No photos are being backed up right now',
+          style: Theme.of(context)
+              .textTheme
+              .caption!
+              .copyWith(fontFamily: 'Inter-Medium', fontSize: 16),
+        ),
+        Center(
+          child: Material(
+            type: MaterialType.transparency,
+            child: Container(
+              width: double.infinity,
+              height: 64,
+              padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
+              child: GradientButton(
+                onTap: () async {
+                  if (LocalSyncService.instance
+                      .hasGrantedLimitedPermissions()) {
+                    PhotoManager.presentLimited();
+                  } else {
+                    routeToPage(
+                      context,
+                      const BackupFolderSelectionPage(
+                        buttonText: "Start backup",
+                      ),
+                    );
+                  }
+                },
+                text: "Start backup",
+              ),
+            ),
+          ),
+        ),
+        const Padding(padding: EdgeInsets.all(50)),
+      ],
+    );
+  }
+}

+ 3 - 3
lib/ui/status_bar_widget.dart → lib/ui/home/status_bar_widget.dart

@@ -13,7 +13,7 @@ import 'package:photos/theme/text_style.dart';
 import 'package:photos/ui/account/verify_recovery_page.dart';
 import 'package:photos/ui/components/home_header_widget.dart';
 import 'package:photos/ui/components/notification_warning_widget.dart';
-import 'package:photos/ui/header_error_widget.dart';
+import 'package:photos/ui/home/header_error_widget.dart';
 import 'package:photos/utils/navigation_util.dart';
 
 const double kContainerHeight = 36;
@@ -100,9 +100,9 @@ class _StatusBarWidgetState extends State<StatusBarWidget> {
             : const SizedBox.shrink(),
         UserRemoteFlagService.instance.shouldShowRecoveryVerification()
             ? NotificationWarningWidget(
-                warningIcon: Icons.gpp_maybe,
+                warningIcon: Icons.error_outline,
                 actionIcon: Icons.arrow_forward,
-                text: "Please ensure you have your 24 word recovery key",
+                text: "Confirm your recovery key",
                 onTap: () async => {
                   await routeToPage(
                     context,

+ 45 - 400
lib/ui/home_widget.dart

@@ -2,62 +2,48 @@
 
 import 'dart:async';
 import 'dart:io';
-import 'dart:ui';
 
 import 'package:flutter/material.dart';
 import 'package:flutter/scheduler.dart';
 import 'package:flutter/services.dart';
 import 'package:logging/logging.dart';
 import 'package:move_to_background/move_to_background.dart';
-import 'package:photo_manager/photo_manager.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/event_bus.dart';
-import 'package:photos/db/files_db.dart';
 import 'package:photos/ente_theme_data.dart';
 import 'package:photos/events/account_configured_event.dart';
 import 'package:photos/events/backup_folders_updated_event.dart';
-import 'package:photos/events/files_updated_event.dart';
-import 'package:photos/events/force_reload_home_gallery_event.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/sync_status_update_event.dart';
 import 'package:photos/events/tab_changed_event.dart';
 import 'package:photos/events/trigger_logout_event.dart';
 import 'package:photos/events/user_logged_out_event.dart';
-import 'package:photos/models/file_load_result.dart';
 import 'package:photos/models/gallery_type.dart';
 import 'package:photos/models/selected_files.dart';
 import 'package:photos/services/collections_service.dart';
-import 'package:photos/services/ignored_files_service.dart';
 import 'package:photos/services/local_sync_service.dart';
 import 'package:photos/services/update_service.dart';
 import 'package:photos/services/user_service.dart';
 import 'package:photos/states/user_details_state.dart';
-import 'package:photos/theme/colors.dart';
-import 'package:photos/theme/effects.dart';
 import 'package:photos/theme/ente_theme.dart';
-import 'package:photos/ui/backup_folder_selection_page.dart';
 import 'package:photos/ui/collections_gallery_widget.dart';
 import 'package:photos/ui/common/bottom_shadow.dart';
-import 'package:photos/ui/common/gradient_button.dart';
 import 'package:photos/ui/create_collection_page.dart';
 import 'package:photos/ui/extents_page_view.dart';
-import 'package:photos/ui/grant_permissions_widget.dart';
-import 'package:photos/ui/landing_page_widget.dart';
+import 'package:photos/ui/home/grant_permissions_widget.dart';
+import 'package:photos/ui/home/header_widget.dart';
+import 'package:photos/ui/home/home_bottom_nav_bar.dart';
+import 'package:photos/ui/home/home_gallery_widget.dart';
+import 'package:photos/ui/home/landing_page_widget.dart';
+import 'package:photos/ui/home/preserve_footer_widget.dart';
+import 'package:photos/ui/home/start_backup_hook_widget.dart';
 import 'package:photos/ui/loading_photos_widget.dart';
-import 'package:photos/ui/memories_widget.dart';
-import 'package:photos/ui/nav_bar.dart';
 import 'package:photos/ui/settings/app_update_dialog.dart';
 import 'package:photos/ui/settings_page.dart';
 import 'package:photos/ui/shared_collections_gallery.dart';
-import 'package:photos/ui/status_bar_widget.dart';
-import 'package:photos/ui/viewer/gallery/gallery.dart';
-import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart';
-import 'package:photos/ui/viewer/gallery/gallery_footer_widget.dart';
 import 'package:photos/ui/viewer/gallery/gallery_overlay_widget.dart';
 import 'package:photos/utils/dialog_util.dart';
-import 'package:photos/utils/navigation_util.dart';
 import 'package:receive_sharing_intent/receive_sharing_intent.dart';
 import 'package:uni_links/uni_links.dart';
 
@@ -81,7 +67,6 @@ class _HomeWidgetState extends State<HomeWidget> {
 
   final PageController _pageController = PageController();
   int _selectedTabIndex = 0;
-  Widget _headerWidgetWithSettingsButton;
 
   // for receiving media files
   // ignore: unused_field
@@ -100,11 +85,6 @@ class _HomeWidgetState extends State<HomeWidget> {
   @override
   void initState() {
     _logger.info("Building initstate");
-    _headerWidgetWithSettingsButton = Stack(
-      children: const [
-        _headerWidget,
-      ],
-    );
     _tabChangedEventSubscription =
         Bus.instance.on<TabChangedEvent>().listen((event) {
       if (event.source != TabChangedEventSource.pageView) {
@@ -126,34 +106,7 @@ class _HomeWidgetState extends State<HomeWidget> {
     });
     _triggerLogoutEvent =
         Bus.instance.on<TriggerLogoutEvent>().listen((event) async {
-      final AlertDialog alert = AlertDialog(
-        title: const Text("Session expired"),
-        content: const Text("Please login again"),
-        actions: [
-          TextButton(
-            child: Text(
-              "Ok",
-              style: TextStyle(
-                color: Theme.of(context).colorScheme.greenAlternative,
-              ),
-            ),
-            onPressed: () async {
-              Navigator.of(context, rootNavigator: true).pop('dialog');
-              final dialog = createProgressDialog(context, "Logging out...");
-              await dialog.show();
-              await Configuration.instance.logout();
-              await dialog.hide();
-            },
-          ),
-        ],
-      );
-
-      showDialog(
-        context: context,
-        builder: (BuildContext context) {
-          return alert;
-        },
-      );
+      await _autoLogoutAlert();
     });
     _loggedOutEvent = Bus.instance.on<UserLoggedOutEvent>().listen((event) {
       _logger.info('logged out, selectTab index to 0');
@@ -218,6 +171,37 @@ class _HomeWidgetState extends State<HomeWidget> {
     super.initState();
   }
 
+  Future<void> _autoLogoutAlert() async {
+    final AlertDialog alert = AlertDialog(
+      title: const Text("Session expired"),
+      content: const Text("Please login again"),
+      actions: [
+        TextButton(
+          child: Text(
+            "Ok",
+            style: TextStyle(
+              color: Theme.of(context).colorScheme.greenAlternative,
+            ),
+          ),
+          onPressed: () async {
+            Navigator.of(context, rootNavigator: true).pop('dialog');
+            final dialog = createProgressDialog(context, "Logging out...");
+            await dialog.show();
+            await Configuration.instance.logout();
+            await dialog.hide();
+          },
+        ),
+      ],
+    );
+
+    await showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return alert;
+      },
+    );
+  }
+
   @override
   void dispose() {
     _tabChangedEventSubscription.cancel();
@@ -345,8 +329,12 @@ class _HomeWidgetState extends State<HomeWidget> {
               physics: const BouncingScrollPhysics(),
               children: [
                 showBackupFolderHook
-                    ? _getBackupFolderSelectionHook()
-                    : _getMainGalleryWidget(),
+                    ? const StartBackupHookWidget(headerWidget: _headerWidget)
+                    : HomeGalleryWidget(
+                        header: _headerWidget,
+                        footer: const PreserveFooterWidget(),
+                        selectedFiles: _selectedFiles,
+                      ),
                 _deviceFolderGalleryWidget,
                 _sharedCollectionGallery,
               ],
@@ -422,347 +410,4 @@ class _HomeWidgetState extends State<HomeWidget> {
     final ott = Uri.parse(link).queryParameters["ott"];
     UserService.instance.verifyEmail(context, ott);
   }
-
-  Widget _getMainGalleryWidget() {
-    Widget header;
-    if (_selectedFiles.files.isEmpty) {
-      header = _headerWidgetWithSettingsButton;
-    } else {
-      header = _headerWidget;
-    }
-    final gallery = Gallery(
-      asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) async {
-        final ownerID = Configuration.instance.getUserID();
-        final hasSelectedAllForBackup =
-            Configuration.instance.hasSelectedAllFoldersForBackup();
-        final archivedCollectionIds =
-            CollectionsService.instance.getArchivedCollections();
-        FileLoadResult result;
-        if (hasSelectedAllForBackup) {
-          result = await FilesDB.instance.getAllLocalAndUploadedFiles(
-            creationStartTime,
-            creationEndTime,
-            ownerID,
-            limit: limit,
-            asc: asc,
-            ignoredCollectionIDs: archivedCollectionIds,
-          );
-        } else {
-          result = await FilesDB.instance.getAllPendingOrUploadedFiles(
-            creationStartTime,
-            creationEndTime,
-            ownerID,
-            limit: limit,
-            asc: asc,
-            ignoredCollectionIDs: archivedCollectionIds,
-          );
-        }
-
-        // hide ignored files from home page UI
-        final ignoredIDs = await IgnoredFilesService.instance.ignoredIDs;
-        result.files.removeWhere(
-          (f) =>
-              f.uploadedFileID == null &&
-              IgnoredFilesService.instance.shouldSkipUpload(ignoredIDs, f),
-        );
-        return result;
-      },
-      reloadEvent: Bus.instance.on<LocalPhotosUpdatedEvent>(),
-      removalEventTypes: const {
-        EventType.deletedFromRemote,
-        EventType.deletedFromEverywhere,
-        EventType.archived,
-      },
-      forceReloadEvents: [
-        Bus.instance.on<BackupFoldersUpdatedEvent>(),
-        Bus.instance.on<ForceReloadHomeGalleryEvent>(),
-      ],
-      tagPrefix: "home_gallery",
-      selectedFiles: _selectedFiles,
-      header: header,
-      footer: const GalleryFooterWidget(),
-    );
-    return Stack(
-      children: [
-        Container(
-          child: gallery,
-        ),
-        HomePageAppBar(_selectedFiles),
-      ],
-    );
-  }
-
-  Widget _getBackupFolderSelectionHook() {
-    return Column(
-      mainAxisAlignment: MainAxisAlignment.spaceBetween,
-      children: [
-        _headerWidgetWithSettingsButton,
-        Padding(
-          padding: const EdgeInsets.only(top: 64),
-          child: Image.asset(
-            "assets/onboarding_safe.png",
-            height: 206,
-          ),
-        ),
-        Text(
-          'No photos are being backed up right now',
-          style: Theme.of(context)
-              .textTheme
-              .caption
-              .copyWith(fontFamily: 'Inter-Medium', fontSize: 16),
-        ),
-        Center(
-          child: Material(
-            type: MaterialType.transparency,
-            child: Container(
-              width: double.infinity,
-              height: 64,
-              padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
-              child: GradientButton(
-                onTap: () async {
-                  if (LocalSyncService.instance
-                      .hasGrantedLimitedPermissions()) {
-                    PhotoManager.presentLimited();
-                  } else {
-                    routeToPage(
-                      context,
-                      const BackupFolderSelectionPage(
-                        buttonText: "Start backup",
-                      ),
-                    );
-                  }
-                },
-                text: "Start backup",
-              ),
-            ),
-          ),
-        ),
-        const Padding(padding: EdgeInsets.all(50)),
-      ],
-    );
-  }
-}
-
-class HomePageAppBar extends StatefulWidget {
-  const HomePageAppBar(
-    this.selectedFiles, {
-    Key key,
-  }) : super(key: key);
-
-  final SelectedFiles selectedFiles;
-
-  @override
-  State<HomePageAppBar> createState() => _HomePageAppBarState();
-}
-
-class _HomePageAppBarState extends State<HomePageAppBar> {
-  @override
-  void initState() {
-    super.initState();
-    widget.selectedFiles.addListener(() {
-      setState(() {});
-    });
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    final appBar = SizedBox(
-      height: 60,
-      child: GalleryAppBarWidget(
-        GalleryType.homepage,
-        null,
-        widget.selectedFiles,
-      ),
-    );
-    if (widget.selectedFiles.files.isEmpty) {
-      return IgnorePointer(child: appBar);
-    } else {
-      return appBar;
-    }
-  }
-}
-
-class HomeBottomNavigationBar extends StatefulWidget {
-  const HomeBottomNavigationBar(
-    this.selectedFiles, {
-    this.selectedTabIndex,
-    Key key,
-  }) : super(key: key);
-
-  final SelectedFiles selectedFiles;
-  final int selectedTabIndex;
-
-  @override
-  State<HomeBottomNavigationBar> createState() =>
-      _HomeBottomNavigationBarState();
-}
-
-class _HomeBottomNavigationBarState extends State<HomeBottomNavigationBar> {
-  StreamSubscription<TabChangedEvent> _tabChangedEventSubscription;
-  int currentTabIndex = 0;
-
-  @override
-  void initState() {
-    super.initState();
-    currentTabIndex = widget.selectedTabIndex;
-    widget.selectedFiles.addListener(() {
-      setState(() {});
-    });
-    _tabChangedEventSubscription =
-        Bus.instance.on<TabChangedEvent>().listen((event) {
-      if (event.source != TabChangedEventSource.tabBar) {
-        debugPrint('index changed to ${event.selectedIndex}');
-        if (mounted) {
-          setState(() {
-            currentTabIndex = event.selectedIndex;
-          });
-        }
-      }
-    });
-  }
-
-  @override
-  void dispose() {
-    _tabChangedEventSubscription.cancel();
-    super.dispose();
-  }
-
-  void _onTabChange(int index) {
-    Bus.instance.fire(
-      TabChangedEvent(
-        index,
-        TabChangedEventSource.tabBar,
-      ),
-    );
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    final bool filesAreSelected = widget.selectedFiles.files.isNotEmpty;
-    final enteColorScheme = getEnteColorScheme(context);
-    final navBarBlur =
-        MediaQuery.of(context).platformBrightness == Brightness.light
-            ? blurBase
-            : blurMuted;
-
-    return AnimatedContainer(
-      duration: const Duration(milliseconds: 300),
-      curve: Curves.easeInOut,
-      height: filesAreSelected ? 0 : 56,
-      child: AnimatedOpacity(
-        duration: const Duration(milliseconds: 100),
-        opacity: filesAreSelected ? 0.0 : 1.0,
-        curve: Curves.easeIn,
-        child: IgnorePointer(
-          ignoring: filesAreSelected,
-          child: ListView(
-            physics: const NeverScrollableScrollPhysics(),
-            children: [
-              Row(
-                mainAxisAlignment: MainAxisAlignment.center,
-                children: [
-                  ClipRRect(
-                    borderRadius: BorderRadius.circular(32),
-                    child: Container(
-                      alignment: Alignment.bottomCenter,
-                      height: 48,
-                      child: ClipRect(
-                        child: BackdropFilter(
-                          filter: ImageFilter.blur(
-                            sigmaX: navBarBlur,
-                            sigmaY: navBarBlur,
-                          ),
-                          child: GNav(
-                            curve: Curves.easeOutExpo,
-                            backgroundColor:
-                                getEnteColorScheme(context).fillMuted,
-                            mainAxisAlignment: MainAxisAlignment.center,
-                            rippleColor: Colors.white.withOpacity(0.1),
-                            activeColor: Theme.of(context)
-                                .colorScheme
-                                .gNavBarActiveColor,
-                            iconSize: 24,
-                            padding: const EdgeInsets.fromLTRB(16, 6, 16, 6),
-                            duration: const Duration(milliseconds: 200),
-                            gap: 0,
-                            tabBorderRadius: 32,
-                            tabBackgroundColor: Theme.of(context)
-                                .colorScheme
-                                .gNavBarActiveColor,
-                            haptic: false,
-                            tabs: [
-                              GButton(
-                                margin: const EdgeInsets.fromLTRB(8, 6, 10, 6),
-                                icon: Icons.home_rounded,
-                                iconColor: enteColorScheme.tabIcon,
-                                iconActiveColor: strokeBaseLight,
-                                text: '',
-                                onPressed: () {
-                                  _onTabChange(
-                                    0,
-                                  ); // To take care of occasional missing events
-                                },
-                              ),
-                              GButton(
-                                margin: const EdgeInsets.fromLTRB(10, 6, 10, 6),
-                                icon: Icons.collections_rounded,
-                                iconColor: enteColorScheme.tabIcon,
-                                iconActiveColor: strokeBaseLight,
-                                text: '',
-                                onPressed: () {
-                                  _onTabChange(
-                                    1,
-                                  ); // To take care of occasional missing events
-                                },
-                              ),
-                              GButton(
-                                margin: const EdgeInsets.fromLTRB(10, 6, 8, 6),
-                                icon: Icons.people_outlined,
-                                iconColor: enteColorScheme.tabIcon,
-                                iconActiveColor: strokeBaseLight,
-                                text: '',
-                                onPressed: () {
-                                  _onTabChange(
-                                    2,
-                                  ); // To take care of occasional missing events
-                                },
-                              ),
-                            ],
-                            selectedIndex: currentTabIndex,
-                            onTabChange: _onTabChange,
-                          ),
-                        ),
-                      ),
-                    ),
-                  ),
-                ],
-              ),
-            ],
-          ),
-        ),
-      ),
-    );
-  }
-}
-
-class HeaderWidget extends StatelessWidget {
-  static const _memoriesWidget = MemoriesWidget();
-  static const _statusBarWidget = StatusBarWidget();
-
-  const HeaderWidget({
-    Key key,
-  }) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    Logger("Header").info("Building header widget");
-    const list = [
-      _statusBarWidget,
-      _memoriesWidget,
-    ];
-    return Column(
-      crossAxisAlignment: CrossAxisAlignment.start,
-      children: list,
-    );
-  }
 }

+ 28 - 27
lib/ui/viewer/gallery/gallery_app_bar_widget.dart

@@ -255,33 +255,34 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
         ),
       );
     }
-
-    actions.add(
-      PopupMenuButton(
-        itemBuilder: (context) {
-          return items;
-        },
-        onSelected: (value) async {
-          if (value == 1) {
-            await _renameAlbum(context);
-          } else if (value == 2) {
-            await changeCollectionVisibility(
-              context,
-              widget.collection,
-              widget.collection.isArchived()
-                  ? visibilityVisible
-                  : visibilityArchive,
-            );
-          } else if (value == 3) {
-            await _trashCollection();
-          } else if (value == 4) {
-            await _leaveAlbum(context);
-          } else {
-            showToast(context, "Something went wrong");
-          }
-        },
-      ),
-    );
+    if (items.isNotEmpty) {
+      actions.add(
+        PopupMenuButton(
+          itemBuilder: (context) {
+            return items;
+          },
+          onSelected: (value) async {
+            if (value == 1) {
+              await _renameAlbum(context);
+            } else if (value == 2) {
+              await changeCollectionVisibility(
+                context,
+                widget.collection,
+                widget.collection.isArchived()
+                    ? visibilityVisible
+                    : visibilityArchive,
+              );
+            } else if (value == 3) {
+              await _trashCollection();
+            } else if (value == 4) {
+              await _leaveAlbum(context);
+            } else {
+              showToast(context, "Something went wrong");
+            }
+          },
+        ),
+      );
+    }
 
     return actions;
   }

+ 2 - 2
pubspec.lock

@@ -42,7 +42,7 @@ packages:
       name: background_fetch
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0"
+    version: "1.1.1"
   bip39:
     dependency: "direct main"
     description:
@@ -1373,7 +1373,7 @@ packages:
       name: visibility_detector
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.2"
+    version: "0.3.3"
   wakelock:
     dependency: "direct main"
     description:

+ 1 - 1
pubspec.yaml

@@ -114,7 +114,7 @@ dependencies:
   video_player:
     path: thirdparty/plugins/packages/video_player/video_player
   video_thumbnail: ^0.4.3
-  visibility_detector: ^0.2.2
+  visibility_detector: ^0.3.3
   wakelock: ^0.6.1+2
   wallpaper_manager_flutter: ^0.0.2