From 56c92cd83bfd155de68052ef371226ad68a1e38f Mon Sep 17 00:00:00 2001 From: Alex Tran Date: Thu, 3 Feb 2022 21:01:14 -0600 Subject: [PATCH] Implementing scroll bar like Google Photos --- Makefile | 5 + README.md | 4 +- mobile/lib/main.dart | 4 - .../modules/home/ui/immich_sliver_appbar.dart | 10 +- mobile/lib/modules/home/views/home_page.dart | 167 ++++++++++-------- mobile/lib/modules/login/ui/login_form.dart | 2 +- mobile/pubspec.lock | 7 + mobile/pubspec.yaml | 2 + server/src/api-v1/asset/asset.service.ts | 2 +- 9 files changed, 115 insertions(+), 88 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..e3f4b1994 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +run_server_dev: + docker-compose -f ./server/docker-compose.yml up + +run_server_update: + docker-compose -f ./server/docker-compose.yml up --build -V \ No newline at end of file diff --git a/README.md b/README.md index ab9701ca5..1e5db5296 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,13 @@ Then populate the value in there. To start, run ```bash -docker-compose up ./server +docker-compose -f ./server/docker-compose.yml up ``` To force rebuild node modules after installing new packages ```bash -docker-compose up --build -V ./server +docker-compose -f ./server/docker-compose.yml up --build -V ``` # Known Issue diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 753dba5db..47222e579 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -46,10 +46,6 @@ class _ImmichAppState extends ConsumerState with WidgetsBindingObserv } Future initApp() async { - // ! TOBE DELETE - // Simulate Sign In And Register/Get Device ID - // await ref.read(authenticationProvider.notifier).login(); - // ref.read(backupProvider.notifier).getBackupInfo(); // WidgetsBinding.instance?.addObserver(this); } diff --git a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart index ed54d1f93..b26c7c2c1 100644 --- a/mobile/lib/modules/home/ui/immich_sliver_appbar.dart +++ b/mobile/lib/modules/home/ui/immich_sliver_appbar.dart @@ -75,12 +75,14 @@ class ImmichSliverAppBar extends ConsumerWidget { onPressed: () async { var onPop = await AutoRouter.of(context).push(const BackupControllerRoute()); - // Fetch new image if (onPop == true) { - // Remove and force getting new widget again - if (imageGridGroup.isNotEmpty) { + // 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 { + } else if (imageGridGroup.isEmpty) { + print("get immich asset"); ref.read(assetProvider.notifier).getImmichAssets(); } } diff --git a/mobile/lib/modules/home/views/home_page.dart b/mobile/lib/modules/home/views/home_page.dart index e34e07b20..569b97d25 100644 --- a/mobile/lib/modules/home/views/home_page.dart +++ b/mobile/lib/modules/home/views/home_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/modules/home/ui/immich_sliver_appbar.dart'; @@ -8,6 +9,7 @@ import 'package:immich_mobile/modules/home/models/get_all_asset_respose.model.da import 'package:immich_mobile/modules/home/ui/image_grid.dart'; import 'package:immich_mobile/modules/home/providers/asset.provider.dart'; import 'package:immich_mobile/shared/providers/backup.provider.dart'; +import 'package:visibility_detector/visibility_detector.dart'; import 'package:intl/intl.dart'; class HomePage extends HookConsumerWidget { @@ -18,10 +20,9 @@ class HomePage extends HookConsumerWidget { final ValueNotifier _showBackToTopBtn = useState(false); ScrollController _scrollController = useScrollController(); List assetGroup = ref.watch(assetProvider); - BackUpState _backupState = ref.watch(backupProvider); List imageGridGroup = []; List monthGroupKey = []; - + final monthInView = useState(""); _scrollControllerCallback() { var endOfPage = _scrollController.position.maxScrollExtent; @@ -34,6 +35,13 @@ class HomePage extends HookConsumerWidget { } else { _showBackToTopBtn.value = false; } + + // Quick Scroll For Jumping to Month + if (_scrollController.position.userScrollDirection == ScrollDirection.forward) { + // Scroll UP + } else if (_scrollController.position.userScrollDirection == ScrollDirection.reverse) { + // SCroll Down + } } useEffect(() { @@ -41,8 +49,63 @@ class HomePage extends HookConsumerWidget { _scrollController.addListener(_scrollControllerCallback); - return () => _scrollController.removeListener(_scrollControllerCallback); - }, [_scrollController, key]); + return () { + debugPrint("Remove scroll listener"); + _scrollController.removeListener(_scrollControllerCallback); + }; + }, []); + + SliverToBoxAdapter _buildMonthGroupTitle(String dateTitle, BuildContext context) { + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(left: 10.0, top: 32), + child: Text( + DateFormat('MMMM, y').format( + DateTime.parse(dateTitle), + ), + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColor, + ), + ), + ), + ); + } + + SliverToBoxAdapter _buildDateGroupTitle(String dateTitle) { + var currentYear = DateTime.now().year; + var groupYear = DateTime.parse(dateTitle).year; + var formatDateTemplate = currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy'; + var dateText = DateFormat(formatDateTemplate).format(DateTime.parse(dateTitle)); + var monthText = DateFormat('MMMM, y').format(DateTime.parse(dateTitle)); + return SliverToBoxAdapter( + child: VisibilityDetector( + key: Key(dateText), + onVisibilityChanged: (visibilityInfo) { + monthInView.value = monthText; + }, + child: Padding( + padding: const EdgeInsets.only(top: 24.0, bottom: 24.0, left: 3.0), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 8.0, bottom: 5.0, top: 5.0), + child: Text( + dateText, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + ), + ], + ), + ), + ), + ); + } Widget _buildBody() { if (assetGroup.isNotEmpty) { @@ -56,28 +119,9 @@ class HomePage extends HookConsumerWidget { int? previousMonth = DateTime.tryParse(lastGroupDate)?.month; if ((currentMonth! - previousMonth!) != 0) { - var myKey = GlobalKey(); - monthGroupKey.add(myKey); - // debugPrint("Group Key $myKey"); + var monthTitleText = DateFormat('MMMM, y').format(DateTime.parse(dateTitle)); - imageGridGroup.add( - SliverToBoxAdapter( - key: myKey, - child: Padding( - padding: const EdgeInsets.only(left: 10.0, top: 32), - child: Text( - DateFormat('MMMM, y').format( - DateTime.parse(dateTitle), - ), - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Theme.of(context).primaryColor, - ), - ), - ), - ), - ); + imageGridGroup.add(_buildMonthGroupTitle(monthTitleText, context)); } imageGridGroup.add( @@ -88,39 +132,36 @@ class HomePage extends HookConsumerWidget { lastGroupDate = dateTitle; } - } + } - return SafeArea( - child: CustomScrollView( + return SafeArea( + child: Stack(children: [ + RawScrollbar( + minThumbLength: 50, + isAlwaysShown: false, + interactive: true, controller: _scrollController, - slivers: [ - ImmichSliverAppBar(imageGridGroup: imageGridGroup), - ...imageGridGroup, - ], + thickness: 50, + crossAxisMargin: -20, + mainAxisMargin: 70, + timeToFade: const Duration(seconds: 2), + thumbColor: Colors.blueGrey, + radius: const Radius.circular(30), + child: CustomScrollView( + controller: _scrollController, + slivers: [ + ImmichSliverAppBar(imageGridGroup: imageGridGroup), + ...imageGridGroup, + ], + ), ), - ); + ]), + ); } return Scaffold( drawer: const ProfileDrawer(), body: _buildBody(), - bottomNavigationBar: BottomAppBar( - child: IconButton( - onPressed: () { - if (monthGroupKey.isNotEmpty) { - var targetContext = monthGroupKey.last.currentContext; - if (targetContext != null) { - Scrollable.ensureVisible( - targetContext, - duration: const Duration(milliseconds: 400), - curve: Curves.easeInOut, - ); - } - } - }, - icon: const Icon(Icons.ac_unit_outlined), - ), - ), floatingActionButton: _showBackToTopBtn.value ? FloatingActionButton.small( enableFeedback: true, @@ -134,30 +175,4 @@ class HomePage extends HookConsumerWidget { : null, ); } - - SliverToBoxAdapter _buildDateGroupTitle(String dateTitle) { - var currentYear = DateTime.now().year; - var groupYear = DateTime.parse(dateTitle).year; - var formatDateTemplate = currentYear == groupYear ? 'E, MMM dd' : 'E, MMM dd, yyyy'; - return SliverToBoxAdapter( - child: Padding( - padding: const EdgeInsets.only(top: 24.0, bottom: 24.0, left: 3.0), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(left: 8.0, bottom: 5.0, top: 5.0), - child: Text( - DateFormat(formatDateTemplate).format(DateTime.parse(dateTitle)), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ), - ], - ), - ), - ); - } } diff --git a/mobile/lib/modules/login/ui/login_form.dart b/mobile/lib/modules/login/ui/login_form.dart index 058f6bd63..0b6b13a71 100644 --- a/mobile/lib/modules/login/ui/login_form.dart +++ b/mobile/lib/modules/login/ui/login_form.dart @@ -12,7 +12,7 @@ class LoginForm extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final usernameController = useTextEditingController(text: 'testuser@email.com'); final passwordController = useTextEditingController(text: 'password'); - final serverEndpointController = useTextEditingController(text: 'http://192.168.1.216'); + final serverEndpointController = useTextEditingController(text: 'http://192.168.1.103:3000'); return Center( child: ConstrainedBox( diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index b4603ab63..b00996822 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -805,6 +805,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + visibility_detector: + dependency: "direct main" + description: + name: visibility_detector + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.2" watcher: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 23b9c9db7..2585dd0dd 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -25,6 +25,8 @@ dependencies: auto_route: ^3.2.2 exif: ^3.1.1 transparent_image: ^2.0.0 + visibility_detector: ^0.2.2 + dev_dependencies: flutter_test: sdk: flutter diff --git a/server/src/api-v1/asset/asset.service.ts b/server/src/api-v1/asset/asset.service.ts index 8eb885825..8c8532c3c 100644 --- a/server/src/api-v1/asset/asset.service.ts +++ b/server/src/api-v1/asset/asset.service.ts @@ -63,7 +63,7 @@ export class AssetService { lastQueryCreatedAt: query.nextPageKey || new Date().toISOString(), }) .orderBy('a."createdAt"::date', 'DESC') - .take(200) + .take(10000) .getMany(); if (assets.length > 0) {