1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-28 22:51:59 +00:00

fix(mobile): asset description is not shown on the sheet when opened for the first time (#10377)

* fix: invalidate asset's description when asset details changed

* refactor(exif-sheet): use description from exif instead

* refactor(asset-description): remove asset_description.provider

* fix(asset-description): set is empty based on exifInfo.description

* chore: rename service to provider
This commit is contained in:
RanKKI 2024-06-18 03:01:02 +10:00 committed by GitHub
parent 7ce87abc95
commit 29e4666dfa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 32 additions and 123 deletions

View file

@ -1,87 +0,0 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/services/asset_description.service.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/exif_info.entity.dart';
import 'package:immich_mobile/providers/db.provider.dart';
import 'package:isar/isar.dart';
class AssetDescriptionNotifier extends StateNotifier<String> {
final Isar _db;
final AssetDescriptionService _service;
final Asset _asset;
AssetDescriptionNotifier(
this._db,
this._service,
this._asset,
) : super('') {
_fetchLocalDescription();
_fetchRemoteDescription();
}
String get description => state;
/// Fetches the local database value for description
/// and writes it to [state]
void _fetchLocalDescription() async {
final localExifId = _asset.exifInfo?.id;
// Guard [localExifId] null
if (localExifId == null) {
return;
}
// Subscribe to local changes
final exifInfo = await _db.exifInfos.get(localExifId);
// Guard
if (exifInfo?.description == null) {
return;
}
state = exifInfo!.description!;
}
/// Fetches the remote value and sets the state
void _fetchRemoteDescription() async {
final remoteAssetId = _asset.remoteId;
final localExifId = _asset.exifInfo?.id;
// Guard [remoteAssetId] and [localExifId] null
if (remoteAssetId == null || localExifId == null) {
return;
}
// Reads the latest from the remote and writes it to DB in the service
final latest = await _service.readLatest(remoteAssetId, localExifId);
state = latest;
}
/// Sets the description to [description]
/// Uses the service to set the asset value
Future<void> setDescription(String description) async {
state = description;
final remoteAssetId = _asset.remoteId;
final localExifId = _asset.exifInfo?.id;
// Guard [remoteAssetId] and [localExifId] null
if (remoteAssetId == null || localExifId == null) {
return;
}
return _service.setDescription(description, remoteAssetId, localExifId);
}
}
final assetDescriptionProvider = StateNotifierProvider.autoDispose
.family<AssetDescriptionNotifier, String, Asset>(
(ref, asset) => AssetDescriptionNotifier(
ref.watch(dbProvider),
ref.watch(assetDescriptionServiceProvider),
asset,
),
);

View file

@ -1,4 +1,5 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/exif_info.entity.dart';
import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/db.provider.dart';
@ -12,46 +13,36 @@ class AssetDescriptionService {
final Isar _db;
final ApiService _api;
setDescription(
String description,
String remoteAssetId,
int localExifId,
Future<void> setDescription(
Asset asset,
String newDescription,
) async {
final remoteAssetId = asset.remoteId;
final localExifId = asset.exifInfo?.id;
// Guard [remoteAssetId] and [localExifId] null
if (remoteAssetId == null || localExifId == null) {
return;
}
final result = await _api.assetsApi.updateAsset(
remoteAssetId,
UpdateAssetDto(description: description),
UpdateAssetDto(description: newDescription),
);
if (result?.exifInfo?.description != null) {
final description = result?.exifInfo?.description;
if (description != null) {
var exifInfo = await _db.exifInfos.get(localExifId);
if (exifInfo != null) {
exifInfo.description = result!.exifInfo!.description;
exifInfo.description = description;
await _db.writeTxn(
() => _db.exifInfos.put(exifInfo),
);
}
}
}
Future<String> readLatest(String assetRemoteId, int localExifId) async {
final latestAssetFromServer =
await _api.assetsApi.getAssetInfo(assetRemoteId);
final localExifInfo = await _db.exifInfos.get(localExifId);
if (latestAssetFromServer != null && localExifInfo != null) {
localExifInfo.description =
latestAssetFromServer.exifInfo?.description ?? '';
await _db.writeTxn(
() => _db.exifInfos.put(localExifInfo),
);
return localExifInfo.description!;
}
return "";
}
}
final assetDescriptionServiceProvider = Provider(

View file

@ -2,10 +2,11 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/entities/exif_info.entity.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_description.provider.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/services/asset_description.service.dart';
import 'package:immich_mobile/widgets/common/immich_toast.dart';
import 'package:logging/logging.dart';
@ -13,9 +14,11 @@ class DescriptionInput extends HookConsumerWidget {
DescriptionInput({
super.key,
required this.asset,
this.exifInfo,
});
final Asset asset;
final ExifInfo? exifInfo;
final Logger _log = Logger('DescriptionInput');
@override
@ -25,25 +28,25 @@ class DescriptionInput extends HookConsumerWidget {
final focusNode = useFocusNode();
final isFocus = useState(false);
final isTextEmpty = useState(controller.text.isEmpty);
final descriptionProvider =
ref.watch(assetDescriptionProvider(asset).notifier);
final description = ref.watch(assetDescriptionProvider(asset));
final descriptionProvider = ref.watch(assetDescriptionServiceProvider);
final owner = ref.watch(currentUserProvider);
final hasError = useState(false);
useEffect(
() {
controller.text = description;
isTextEmpty.value = description.isEmpty;
controller.text = exifInfo?.description ?? '';
isTextEmpty.value = exifInfo?.description?.isEmpty ?? true;
return null;
},
[description],
[exifInfo?.description],
);
submitDescription(String description) async {
hasError.value = false;
try {
await descriptionProvider.setDescription(
asset,
description,
);
} catch (error, stack) {
@ -85,7 +88,7 @@ class DescriptionInput extends HookConsumerWidget {
isFocus.value = false;
focusNode.unfocus();
if (description != controller.text) {
if (exifInfo?.description != controller.text) {
await submitDescription(controller.text);
}
},

View file

@ -73,7 +73,8 @@ class ExifBottomSheet extends HookConsumerWidget {
child: Column(
children: [
dateWidget,
if (asset.isRemote) DescriptionInput(asset: asset),
if (asset.isRemote)
DescriptionInput(asset: asset, exifInfo: exifInfo),
],
),
),
@ -132,7 +133,8 @@ class ExifBottomSheet extends HookConsumerWidget {
child: Column(
children: [
dateWidget,
if (asset.isRemote) DescriptionInput(asset: asset),
if (asset.isRemote)
DescriptionInput(asset: asset, exifInfo: exifInfo),
Padding(
padding: EdgeInsets.only(top: asset.isRemote ? 0 : 16.0),
child: ExifLocation(