Skip to content

Commit

Permalink
Implemented load new image when navigating back from backup page (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
alextran1502 authored Feb 7, 2022
1 parent 1d3ee20 commit c24fb40
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 38 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/Build+push Immich.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ name: Build+push Immich
on:
# Triggers the workflow on push or pull request events but only for the main branch
#schedule:
# * is a special character in YAML so you have to quote this string
#- cron: '0 0 * * *'
# * is a special character in YAML so you have to quote this string
#- cron: '0 0 * * *'
workflow_dispatch:
#push:
#branches: [ main ]
pull_request:
branches: [ main ]
# push:
#branches: [ main ]
# pull_request:
# branches: [ main ]

jobs:
buildandpush:
Expand All @@ -18,7 +18,7 @@ jobs:
- name: Checkout
uses: actions/[email protected]
with:
ref: 'main' # branch
ref: "main" # branch
# https://github.com/docker/setup-qemu-action#usage
- name: Set up QEMU
uses: docker/[email protected]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
45 changes: 42 additions & 3 deletions mobile/lib/modules/home/providers/asset.provider.dart
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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 = [];
}
Expand Down
25 changes: 24 additions & 1 deletion mobile/lib/modules/home/services/asset.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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",
Expand All @@ -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 [];
}
}
}
13 changes: 3 additions & 10 deletions mobile/lib/modules/home/ui/immich_sliver_appbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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) {
Expand Down Expand Up @@ -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!();
}
},
),
Expand Down
4 changes: 1 addition & 3 deletions mobile/lib/modules/home/ui/profile_drawer.dart
Original file line number Diff line number Diff line change
@@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
30 changes: 24 additions & 6 deletions mobile/lib/modules/home/views/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -84,7 +99,10 @@ class HomePage extends HookConsumerWidget {
child: CustomScrollView(
controller: _scrollController,
slivers: [
ImmichSliverAppBar(imageGridGroup: imageGridGroup),
ImmichSliverAppBar(
imageGridGroup: imageGridGroup,
onPopBack: onPopBackFromBackupPage,
),
...imageGridGroup,
],
),
Expand Down
5 changes: 3 additions & 2 deletions mobile/lib/shared/views/video_viewer_page.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -17,14 +18,15 @@ class VideoViewerPage extends StatelessWidget {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
systemOverlayStyle: SystemUiOverlayStyle.light,
backgroundColor: Colors.black,
leading: IconButton(
onPressed: () {
AutoRouter.of(context).pop();
},
icon: const Icon(Icons.arrow_back_ios)),
),
body: Center(
body: SafeArea(
child: VideoThumbnailPlayer(
url: videoUrl,
jwtToken: jwtToken,
Expand Down Expand Up @@ -64,7 +66,6 @@ class _VideoThumbnailPlayerState extends State<VideoThumbnailPlayer> {
setState(() {});
} catch (e) {
debugPrint("ERROR initialize video player");
print(e);
}
}

Expand Down
6 changes: 6 additions & 0 deletions server/src/api-v1/asset/asset.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
19 changes: 15 additions & 4 deletions server/src/api-v1/asset/asset.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
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';
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 {
Expand Down Expand Up @@ -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')
Expand All @@ -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) {
Expand Down Expand Up @@ -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.
},
});
}
}
2 changes: 1 addition & 1 deletion server/src/api-v1/asset/dto/get-asset.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IsNotEmpty } from 'class-validator';

class GetAssetDto {
export class GetAssetDto {
@IsNotEmpty()
deviceId: string;
}
6 changes: 6 additions & 0 deletions server/src/api-v1/asset/dto/get-new-asset-query.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { IsNotEmpty } from 'class-validator';

export class GetNewAssetQueryDto {
@IsNotEmpty()
latestDate: string;
}

0 comments on commit c24fb40

Please sign in to comment.