Browse Source

Implemented reload when pop back from backup controller page

Alex Tran 3 years ago
parent
commit
fe6f01cc0c

+ 1 - 1
README.md

@@ -79,7 +79,7 @@ flutter run --release
 
 # Known Issue
 
-TensorFlow doesn't run with older CPU architecture, it requires CPU with AVX and AVX2 instruction set. If you encounter error `illegal instruction core dump` when running the docker-compose command above, check for your CPU flags with the command ad make sure you see `AVX` and `AVX2`. Otherwise, switch to a different VM/desktop with different architecture.
+TensorFlow doesn't run with older CPU architecture, it requires CPU with AVX and AVX2 instruction set. If you encounter the error `illegal instruction core dump` when running the docker-compose command above, check for your CPU flags with the command and make sure you see `AVX` and `AVX2`. Otherwise, switch to a different VM/desktop with different architecture.
 
 ```bash
 more /proc/cpuinfo | grep flags

+ 42 - 3
mobile/lib/modules/home/providers/asset.provider.dart

@@ -1,15 +1,19 @@
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/home/models/get_all_asset_respose.model.dart';
 import 'package:immich_mobile/modules/home/services/asset.service.dart';
+import 'package:immich_mobile/shared/models/immich_asset.model.dart';
+import 'package:intl/intl.dart';
+import 'package:collection/collection.dart';
 
 class AssetNotifier extends StateNotifier<List<ImmichAssetGroupByDate>> {
-  final imagePerPage = 100;
   final AssetService _assetService = AssetService();
 
   AssetNotifier() : super([]);
+
   late String? nextPageKey = "";
   bool isFetching = false;
 
+  // Get All assets
   getImmichAssets() async {
     GetAllAssetResponse? res = await _assetService.getAllAsset();
     nextPageKey = res?.nextPageKey;
@@ -21,10 +25,11 @@ class AssetNotifier extends StateNotifier<List<ImmichAssetGroupByDate>> {
     }
   }
 
-  getMoreAsset() async {
+  // Get Asset From The Past
+  getOlderAsset() async {
     if (nextPageKey != null && !isFetching) {
       isFetching = true;
-      GetAllAssetResponse? res = await _assetService.getMoreAsset(nextPageKey);
+      GetAllAssetResponse? res = await _assetService.getOlderAsset(nextPageKey);
 
       if (res != null) {
         nextPageKey = res.nextPageKey;
@@ -48,6 +53,40 @@ class AssetNotifier extends StateNotifier<List<ImmichAssetGroupByDate>> {
     }
   }
 
+  // Get newer asset from the current time
+  getNewAsset() async {
+    if (state.isNotEmpty) {
+      var latestGroup = state.first;
+
+      // Sort the last asset group and put the lastest asset in front.
+      latestGroup.assets.sortByCompare<DateTime>((e) => DateTime.parse(e.createdAt), (a, b) => b.compareTo(a));
+      var latestAsset = latestGroup.assets.first;
+      var formatDateTemplate = 'y-MM-dd';
+      var latestAssetDateText = DateFormat(formatDateTemplate).format(DateTime.parse(latestAsset.createdAt));
+
+      List<ImmichAsset> newAssets = await _assetService.getNewAsset(latestAsset.createdAt);
+
+      if (newAssets.isEmpty) {
+        return;
+      }
+
+      // Grouping by data
+      var groupByDateList = groupBy<ImmichAsset, String>(
+          newAssets, (asset) => DateFormat(formatDateTemplate).format(DateTime.parse(asset.createdAt)));
+
+      groupByDateList.forEach((groupDateInFormattedText, assets) {
+        if (groupDateInFormattedText != latestAssetDateText) {
+          ImmichAssetGroupByDate newGroup = ImmichAssetGroupByDate(assets: assets, date: groupDateInFormattedText);
+          state = [newGroup, ...state];
+        } else {
+          latestGroup.assets.insertAll(0, assets);
+
+          state = [latestGroup, ...state.sublist(1)];
+        }
+      });
+    }
+  }
+
   clearAllAsset() {
     state = [];
   }

+ 24 - 1
mobile/lib/modules/home/services/asset.service.dart

@@ -2,6 +2,7 @@ import 'dart:convert';
 
 import 'package:flutter/material.dart';
 import 'package:immich_mobile/modules/home/models/get_all_asset_respose.model.dart';
+import 'package:immich_mobile/shared/models/immich_asset.model.dart';
 import 'package:immich_mobile/shared/services/network.service.dart';
 
 class AssetService {
@@ -17,9 +18,10 @@ class AssetService {
     } catch (e) {
       debugPrint("Error getAllAsset  ${e.toString()}");
     }
+    return null;
   }
 
-  Future<GetAllAssetResponse?> getMoreAsset(String? nextPageKey) async {
+  Future<GetAllAssetResponse?> getOlderAsset(String? nextPageKey) async {
     try {
       var res = await _networkService.getRequest(
         url: "asset/all?nextPageKey=$nextPageKey",
@@ -34,5 +36,26 @@ class AssetService {
     } catch (e) {
       debugPrint("Error getAllAsset  ${e.toString()}");
     }
+    return null;
+  }
+
+  Future<List<ImmichAsset>> getNewAsset(String latestDate) async {
+    try {
+      var res = await _networkService.getRequest(
+        url: "asset/new?latestDate=$latestDate",
+      );
+
+      List<dynamic> decodedData = jsonDecode(res.toString());
+
+      List<ImmichAsset> result = List.from(decodedData.map((a) => ImmichAsset.fromMap(a)));
+      if (result.isNotEmpty) {
+        return result;
+      }
+
+      return [];
+    } catch (e) {
+      debugPrint("Error getAllAsset  ${e.toString()}");
+      return [];
+    }
   }
 }

+ 3 - 10
mobile/lib/modules/home/ui/immich_sliver_appbar.dart

@@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
 import 'package:google_fonts/google_fonts.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
-import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
 
 import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/shared/models/backup_state.model.dart';
@@ -12,9 +11,11 @@ class ImmichSliverAppBar extends ConsumerWidget {
   const ImmichSliverAppBar({
     Key? key,
     required this.imageGridGroup,
+    this.onPopBack,
   }) : super(key: key);
 
   final List<Widget> imageGridGroup;
+  final Function? onPopBack;
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
@@ -75,15 +76,7 @@ class ImmichSliverAppBar extends ConsumerWidget {
                   var onPop = await AutoRouter.of(context).push(const BackupControllerRoute());
 
                   if (onPop == true) {
-                    // Remove and force getting new widget again if there is not many widget on screen.
-                    // Otherwise do nothing.
-                    if (imageGridGroup.isNotEmpty && imageGridGroup.length < 20) {
-                      print("Get more access");
-                      ref.read(assetProvider.notifier).getMoreAsset();
-                    } else if (imageGridGroup.isEmpty) {
-                      print("get immich asset");
-                      ref.read(assetProvider.notifier).getImmichAssets();
-                    }
+                    onPopBack!();
                   }
                 },
               ),

+ 1 - 3
mobile/lib/modules/home/ui/profile_drawer.dart

@@ -1,12 +1,9 @@
-import 'package:auto_route/annotations.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter/src/widgets/framework.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/modules/home/providers/asset.provider.dart';
 import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
 import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
-import 'package:immich_mobile/routing/router.dart';
 
 class ProfileDrawer extends ConsumerWidget {
   const ProfileDrawer({Key? key}) : super(key: key);
@@ -58,6 +55,7 @@ class ProfileDrawer extends ConsumerWidget {
             ),
             onTap: () async {
               bool res = await ref.read(authenticationProvider.notifier).logout();
+
               ref.read(assetProvider.notifier).clearAllAsset();
 
               if (res) {

+ 24 - 6
mobile/lib/modules/home/views/home_page.dart

@@ -24,7 +24,7 @@ class HomePage extends HookConsumerWidget {
       var endOfPage = _scrollController.position.maxScrollExtent;
 
       if (_scrollController.offset >= endOfPage - (endOfPage * 0.1) && !_scrollController.position.outOfRange) {
-        ref.read(assetProvider.notifier).getMoreAsset();
+        ref.read(assetProvider.notifier).getOlderAsset();
       }
 
       if (_scrollController.offset >= 400) {
@@ -44,6 +44,18 @@ class HomePage extends HookConsumerWidget {
       };
     }, []);
 
+    onPopBackFromBackupPage() {
+      ref.read(assetProvider.notifier).getNewAsset();
+      // Remove and force getting new widget again if there is not many widget on screen.
+      // Otherwise do nothing.
+
+      if (imageGridGroup.isNotEmpty && imageGridGroup.length < 20) {
+        ref.read(assetProvider.notifier).getOlderAsset();
+      } else if (imageGridGroup.isEmpty) {
+        ref.read(assetProvider.notifier).getImmichAssets();
+      }
+    }
+
     Widget _buildBody() {
       if (assetGroup.isNotEmpty) {
         String lastGroupDate = assetGroup[0].date;
@@ -56,10 +68,13 @@ class HomePage extends HookConsumerWidget {
           int? previousMonth = DateTime.tryParse(lastGroupDate)?.month;
 
           // Add Monthly Title Group if started at the beginning of the month
-          if ((currentMonth! - previousMonth!) != 0) {
-            imageGridGroup.add(
-              MonthlyTitleText(isoDate: dateTitle),
-            );
+
+          if (currentMonth != null && previousMonth != null) {
+            if ((currentMonth - previousMonth) != 0) {
+              imageGridGroup.add(
+                MonthlyTitleText(isoDate: dateTitle),
+              );
+            }
           }
 
           // Add Daily Title Group
@@ -84,7 +99,10 @@ class HomePage extends HookConsumerWidget {
           child: CustomScrollView(
             controller: _scrollController,
             slivers: [
-              ImmichSliverAppBar(imageGridGroup: imageGridGroup),
+              ImmichSliverAppBar(
+                imageGridGroup: imageGridGroup,
+                onPopBack: onPopBackFromBackupPage,
+              ),
               ...imageGridGroup,
             ],
           ),

+ 3 - 2
mobile/lib/shared/views/video_viewer_page.dart

@@ -1,5 +1,6 @@
 import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:hive/hive.dart';
 import 'package:immich_mobile/constants/hive_box.dart';
 import 'package:chewie/chewie.dart';
@@ -17,6 +18,7 @@ class VideoViewerPage extends StatelessWidget {
     return Scaffold(
       backgroundColor: Colors.black,
       appBar: AppBar(
+        systemOverlayStyle: SystemUiOverlayStyle.light,
         backgroundColor: Colors.black,
         leading: IconButton(
             onPressed: () {
@@ -24,7 +26,7 @@ class VideoViewerPage extends StatelessWidget {
             },
             icon: const Icon(Icons.arrow_back_ios)),
       ),
-      body: Center(
+      body: SafeArea(
         child: VideoThumbnailPlayer(
           url: videoUrl,
           jwtToken: jwtToken,
@@ -64,7 +66,6 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
       setState(() {});
     } catch (e) {
       debugPrint("ERROR initialize video player");
-      print(e);
     }
   }
 

+ 6 - 0
server/src/api-v1/asset/asset.controller.ts

@@ -29,6 +29,7 @@ import { Response as Res } from 'express';
 import { promisify } from 'util';
 import { stat } from 'fs';
 import { pipeline } from 'stream';
+import { GetNewAssetQueryDto } from './dto/get-new-asset-query.dto';
 
 const fileInfo = promisify(stat);
 
@@ -141,6 +142,11 @@ export class AssetController {
     console.log('SHOULD NOT BE HERE');
   }
 
+  @Get('/new')
+  async getNewAssets(@GetAuthUser() authUser: AuthUserDto, @Query(ValidationPipe) query: GetNewAssetQueryDto) {
+    return await this.assetService.getNewAssets(authUser, query.latestDate);
+  }
+
   @Get('/all')
   async getAllAssets(@GetAuthUser() authUser: AuthUserDto, @Query(ValidationPipe) query: GetAllAssetQueryDto) {
     return await this.assetService.getAllAssets(authUser, query);

+ 15 - 4
server/src/api-v1/asset/asset.service.ts

@@ -1,6 +1,6 @@
 import { BadRequestException, Injectable, Logger } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
-import { Repository } from 'typeorm';
+import { MoreThan, Repository } from 'typeorm';
 import { AuthUserDto } from '../../decorators/auth-user.decorator';
 import { CreateAssetDto } from './dto/create-asset.dto';
 import { UpdateAssetDto } from './dto/update-asset.dto';
@@ -8,6 +8,7 @@ import { AssetEntity, AssetType } from './entities/asset.entity';
 import _ from 'lodash';
 import { GetAllAssetQueryDto } from './dto/get-all-asset-query.dto';
 import { GetAllAssetReponseDto } from './dto/get-all-asset-response.dto';
+import { Greater } from '@tensorflow/tfjs-core';
 
 @Injectable()
 export class AssetService {
@@ -53,8 +54,6 @@ export class AssetService {
   }
 
   public async getAllAssets(authUser: AuthUserDto, query: GetAllAssetQueryDto): Promise<GetAllAssetReponseDto> {
-    // Each page will take 100 images.
-
     try {
       const assets = await this.assetRepository
         .createQueryBuilder('a')
@@ -63,7 +62,7 @@ export class AssetService {
           lastQueryCreatedAt: query.nextPageKey || new Date().toISOString(),
         })
         .orderBy('a."createdAt"::date', 'DESC')
-        // .take(500)
+        .take(5000)
         .getMany();
 
       if (assets.length > 0) {
@@ -102,4 +101,16 @@ export class AssetService {
 
     return rows[0] as AssetEntity;
   }
+
+  public async getNewAssets(authUser: AuthUserDto, latestDate: string) {
+    return await this.assetRepository.find({
+      where: {
+        userId: authUser.id,
+        createdAt: MoreThan(latestDate),
+      },
+      order: {
+        createdAt: 'ASC', // ASC order to add existed asset the latest group first before creating a new date group.
+      },
+    });
+  }
 }

+ 1 - 1
server/src/api-v1/asset/dto/get-asset.dto.ts

@@ -1,6 +1,6 @@
 import { IsNotEmpty } from 'class-validator';
 
-class GetAssetDto {
+export class GetAssetDto {
   @IsNotEmpty()
   deviceId: string;
 }

+ 6 - 0
server/src/api-v1/asset/dto/get-new-asset-query.dto.ts

@@ -0,0 +1,6 @@
+import { IsNotEmpty } from 'class-validator';
+
+export class GetNewAssetQueryDto {
+  @IsNotEmpty()
+  latestDate: string;
+}