Skip to content

Commit

Permalink
feature(mobile): Hardening synchronization mechanism + Pull to refresh (
Browse files Browse the repository at this point in the history
immich-app#2085)

* fix(mobile): allow syncing duplicate local IDs

* enable to run isar unit tests on CI

* serialize sync operations, add pull to refresh on timeline

---------

Co-authored-by: Fynn Petersen-Frey <[email protected]>
  • Loading branch information
fyfrey and Fynn Petersen-Frey authored Mar 27, 2023
1 parent 1a94530 commit cae3765
Show file tree
Hide file tree
Showing 21 changed files with 654 additions and 250 deletions.
1 change: 1 addition & 0 deletions mobile/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ void main() async {
await initApp();
await migrateHiveToStoreIfNecessary();
await migrateJsonCacheIfNecessary();
await migrateDatabaseIfNeeded(db);
runApp(getMainWidget(db));
}

Expand Down
2 changes: 1 addition & 1 deletion mobile/lib/modules/album/ui/album_thumbnail_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class AlbumThumbnailCard extends StatelessWidget {
// Add the owner name to the subtitle
String? owner;
if (showOwner) {
if (album.ownerId == Store.get(StoreKey.userRemoteId)) {
if (album.ownerId == Store.get(StoreKey.currentUser).id) {
owner = 'album_thumbnail_owned'.tr();
} else if (album.ownerName != null) {
owner = 'album_thumbnail_shared_by'.tr(args: [album.ownerName!]);
Expand Down
2 changes: 1 addition & 1 deletion mobile/lib/modules/album/views/sharing_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class SharingPage extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final List<Album> sharedAlbums = ref.watch(sharedAlbumProvider);
final userId = store.Store.get(store.StoreKey.userRemoteId);
final userId = store.Store.get(store.StoreKey.currentUser).id;
var isDarkMode = Theme.of(context).brightness == Brightness.dark;

useEffect(
Expand Down
46 changes: 24 additions & 22 deletions mobile/lib/modules/home/ui/asset_grid/immich_asset_grid.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ class ImmichAssetGrid extends HookConsumerWidget {
final bool selectionActive;
final List<Asset> assets;
final RenderList? renderList;
final Future<void> Function()? onRefresh;

const ImmichAssetGrid({
super.key,
required this.assets,
this.onRefresh,
this.renderList,
this.assetsPerRow,
this.showStorageIndicator,
Expand Down Expand Up @@ -62,11 +64,12 @@ class ImmichAssetGrid extends HookConsumerWidget {
enabled: enableHeroAnimations.value,
child: ImmichAssetGridView(
allAssets: assets,
assetsPerRow: assetsPerRow
?? settings.getSetting(AppSettingsEnum.tilesPerRow),
onRefresh: onRefresh,
assetsPerRow: assetsPerRow ??
settings.getSetting(AppSettingsEnum.tilesPerRow),
listener: listener,
showStorageIndicator: showStorageIndicator
?? settings.getSetting(AppSettingsEnum.storageIndicator),
showStorageIndicator: showStorageIndicator ??
settings.getSetting(AppSettingsEnum.storageIndicator),
renderList: renderList!,
margin: margin,
selectionActive: selectionActive,
Expand All @@ -76,26 +79,25 @@ class ImmichAssetGrid extends HookConsumerWidget {
}

return renderListFuture.when(
data: (renderList) =>
WillPopScope(
onWillPop: onWillPop,
child: HeroMode(
enabled: enableHeroAnimations.value,
child: ImmichAssetGridView(
allAssets: assets,
assetsPerRow: assetsPerRow
?? settings.getSetting(AppSettingsEnum.tilesPerRow),
listener: listener,
showStorageIndicator: showStorageIndicator
?? settings.getSetting(AppSettingsEnum.storageIndicator),
renderList: renderList,
margin: margin,
selectionActive: selectionActive,
),
data: (renderList) => WillPopScope(
onWillPop: onWillPop,
child: HeroMode(
enabled: enableHeroAnimations.value,
child: ImmichAssetGridView(
allAssets: assets,
onRefresh: onRefresh,
assetsPerRow: assetsPerRow ??
settings.getSetting(AppSettingsEnum.tilesPerRow),
listener: listener,
showStorageIndicator: showStorageIndicator ??
settings.getSetting(AppSettingsEnum.storageIndicator),
renderList: renderList,
margin: margin,
selectionActive: selectionActive,
),
),
error: (err, stack) =>
Center(child: Text("$err")),
),
error: (err, stack) => Center(child: Text("$err")),
loading: () => const Center(
child: ImmichLoadingIndicator(),
),
Expand Down
36 changes: 20 additions & 16 deletions mobile/lib/modules/home/ui/asset_grid/immich_asset_grid_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -199,21 +199,23 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
addRepaintBoundaries: true,
);

if (!useDragScrolling) {
return listWidget;
}

return DraggableScrollbar.semicircle(
scrollStateListener: dragScrolling,
itemPositionsListener: _itemPositionsListener,
controller: _itemScrollController,
backgroundColor: Theme.of(context).hintColor,
labelTextBuilder: _labelBuilder,
labelConstraints: const BoxConstraints(maxHeight: 28),
scrollbarAnimationDuration: const Duration(seconds: 1),
scrollbarTimeToFade: const Duration(seconds: 4),
child: listWidget,
);
final child = useDragScrolling
? DraggableScrollbar.semicircle(
scrollStateListener: dragScrolling,
itemPositionsListener: _itemPositionsListener,
controller: _itemScrollController,
backgroundColor: Theme.of(context).hintColor,
labelTextBuilder: _labelBuilder,
labelConstraints: const BoxConstraints(maxHeight: 28),
scrollbarAnimationDuration: const Duration(seconds: 1),
scrollbarTimeToFade: const Duration(seconds: 4),
child: listWidget,
)
: listWidget;

return widget.onRefresh == null
? child
: RefreshIndicator(onRefresh: widget.onRefresh!, child: child);
}

@override
Expand Down Expand Up @@ -248,7 +250,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
}

void _scrollToTop() {
// for some reason, this is necessary as well in order
// for some reason, this is necessary as well in order
// to correctly reposition the drag thumb scroll bar
_itemScrollController.jumpTo(
index: 0,
Expand Down Expand Up @@ -281,6 +283,7 @@ class ImmichAssetGridView extends StatefulWidget {
final ImmichAssetGridSelectionListener? listener;
final bool selectionActive;
final List<Asset> allAssets;
final Future<void> Function()? onRefresh;

const ImmichAssetGridView({
super.key,
Expand All @@ -291,6 +294,7 @@ class ImmichAssetGridView extends StatefulWidget {
this.listener,
this.margin = 5.0,
this.selectionActive = false,
this.onRefresh,
});

@override
Expand Down
18 changes: 18 additions & 0 deletions mobile/lib/modules/home/views/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class HomePage extends HookConsumerWidget {
final albumService = ref.watch(albumServiceProvider);

final tipOneOpacity = useState(0.0);
final refreshCount = useState(0);

useEffect(
() {
Expand Down Expand Up @@ -182,6 +183,22 @@ class HomePage extends HookConsumerWidget {
}
}

Future<void> refreshAssets() async {
debugPrint("refreshCount.value ${refreshCount.value}");
final fullRefresh = refreshCount.value > 0;
await ref.read(assetProvider.notifier).getAllAsset(clear: fullRefresh);
if (fullRefresh) {
// refresh was forced: user requested another refresh within 2 seconds
refreshCount.value = 0;
} else {
refreshCount.value++;
// set counter back to 0 if user does not request refresh again
Timer(const Duration(seconds: 2), () {
refreshCount.value = 0;
});
}
}

buildLoadingIndicator() {
Timer(const Duration(seconds: 2), () {
tipOneOpacity.value = 1;
Expand Down Expand Up @@ -241,6 +258,7 @@ class HomePage extends HookConsumerWidget {
.getSetting(AppSettingsEnum.storageIndicator),
listener: selectionListener,
selectionActive: selectionEnabledHook.value,
onRefresh: refreshAssets,
),
if (selectionEnabledHook.value)
SafeArea(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
await Future.wait([
_apiService.authenticationApi.logout(),
Store.delete(StoreKey.assetETag),
Store.delete(StoreKey.userRemoteId),
Store.delete(StoreKey.currentUser),
Store.delete(StoreKey.accessToken),
]);
Expand Down Expand Up @@ -133,7 +132,6 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
var deviceInfo = await _deviceInfoService.getDeviceInfo();
Store.put(StoreKey.deviceId, deviceInfo["deviceId"]);
Store.put(StoreKey.deviceIdHash, fastHash(deviceInfo["deviceId"]));
Store.put(StoreKey.userRemoteId, userResponseDto.id);
Store.put(StoreKey.currentUser, User.fromDto(userResponseDto));
Store.put(StoreKey.serverUrl, serverUrl);
Store.put(StoreKey.accessToken, accessToken);
Expand Down
Loading

0 comments on commit cae3765

Please sign in to comment.