mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
chore: add always_put_control_body_on_new_line lint (#28352)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
@@ -191,8 +191,12 @@ class _AssetPropertiesSectionState extends ConsumerState<_AssetPropertiesSection
|
||||
}
|
||||
|
||||
String _getAssetTypeTitle(BaseAsset asset) {
|
||||
if (asset is LocalAsset) return 'Local Asset';
|
||||
if (asset is RemoteAsset) return 'Remote Asset';
|
||||
if (asset is LocalAsset) {
|
||||
return 'Local Asset';
|
||||
}
|
||||
if (asset is RemoteAsset) {
|
||||
return 'Remote Asset';
|
||||
}
|
||||
return 'Base Asset';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,9 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> {
|
||||
}
|
||||
|
||||
Future<void> _handleSave() async {
|
||||
if (formKey.currentState?.validate() != true) return;
|
||||
if (formKey.currentState?.validate() != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final newTitle = titleController.text.trim();
|
||||
|
||||
@@ -95,7 +95,9 @@ class _DriftEditImagePageState extends ConsumerState<DriftEditImagePage> with Ti
|
||||
return PopScope(
|
||||
canPop: !hasUnsavedEdits,
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
if (didPop) return;
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
final shouldDiscard = await _showDiscardChangesDialog() ?? false;
|
||||
if (shouldDiscard && mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@@ -179,7 +179,9 @@ class EditorState {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other is EditorState &&
|
||||
other.isApplyingEdits == isApplyingEdits &&
|
||||
|
||||
@@ -58,7 +58,9 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
|
||||
}
|
||||
|
||||
Future<void> _handleDone() async {
|
||||
if (_isLoading) return;
|
||||
if (_isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
@@ -72,7 +74,9 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
|
||||
.read(uploadProfileImageProvider.notifier)
|
||||
.upload(xFile, fileName: 'profile-picture.png');
|
||||
|
||||
if (!context.mounted) return;
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
final profileImagePath = ref.read(uploadProfileImageProvider).profileImagePath;
|
||||
@@ -102,7 +106,9 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!context.mounted) return;
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
|
||||
@@ -708,7 +708,9 @@ class _SearchResultGrid extends ConsumerWidget {
|
||||
bool _onScrollUpdateNotification(ScrollNotification notification) {
|
||||
final metrics = notification.metrics;
|
||||
|
||||
if (metrics.axis != Axis.vertical) return false;
|
||||
if (metrics.axis != Axis.vertical) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final isBottomSheet = notification.context?.findAncestorWidgetOfExactType<DraggableScrollableSheet>() != null;
|
||||
final remaining = metrics.maxScrollExtent - metrics.pixels;
|
||||
@@ -735,7 +737,9 @@ class _SearchResultGrid extends ConsumerWidget {
|
||||
|
||||
final hasMore = ref.watch(paginatedSearchProvider.select((s) => s.nextPage != null));
|
||||
|
||||
if (hasMore) return null;
|
||||
if (hasMore) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
|
||||
@@ -44,7 +44,9 @@ class PaginatedSearchNotifier extends StateNotifier<SearchState> {
|
||||
Stream<int> get assetCount => _assetCountController.stream;
|
||||
|
||||
Future<void> search(SearchFilter filter) async {
|
||||
if (state.nextPage == null || state.isLoading) return;
|
||||
if (state.nextPage == null || state.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = SearchState(assets: state.assets, nextPage: state.nextPage, isLoading: true);
|
||||
|
||||
|
||||
@@ -50,7 +50,9 @@ class _AddActionButtonState extends ConsumerState<AddActionButton> {
|
||||
|
||||
List<Widget> _buildMenuChildren() {
|
||||
final asset = ref.read(assetViewerProvider).currentAsset;
|
||||
if (asset == null) return [];
|
||||
if (asset == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
final user = ref.read(currentUserProvider);
|
||||
final isOwner = asset is RemoteAsset && asset.ownerId == user?.id;
|
||||
|
||||
@@ -12,7 +12,9 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
|
||||
// used to allow performing archive action from different sources (without duplicating code)
|
||||
Future<void> performArchiveAction(BuildContext context, WidgetRef ref, {required ActionSource source}) async {
|
||||
if (!context.mounted) return;
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source == ActionSource.viewer) {
|
||||
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
||||
|
||||
@@ -54,7 +54,9 @@ class DeleteActionButton extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirm != true) return;
|
||||
if (confirm != true) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (source == ActionSource.viewer) {
|
||||
|
||||
+3
-1
@@ -33,7 +33,9 @@ class DeletePermanentActionButton extends ConsumerWidget {
|
||||
builder: (context) => PermanentDeleteDialog(count: count),
|
||||
) ??
|
||||
false;
|
||||
if (!confirm) return;
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source == ActionSource.viewer) {
|
||||
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
||||
|
||||
+3
-1
@@ -12,7 +12,9 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||
|
||||
// Reusable helper: move to locked folder from any source (e.g called from menu)
|
||||
Future<void> performMoveToLockFolderAction(BuildContext context, WidgetRef ref, {required ActionSource source}) async {
|
||||
if (!context.mounted) return;
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source == ActionSource.viewer) {
|
||||
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
||||
|
||||
@@ -14,7 +14,9 @@ import 'package:immich_mobile/domain/utils/event_stream.dart';
|
||||
|
||||
// used to allow performing unarchive action from different sources (without duplicating code)
|
||||
Future<void> performUnArchiveAction(BuildContext context, WidgetRef ref, {required ActionSource source}) async {
|
||||
if (!context.mounted) return;
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source == ActionSource.viewer) {
|
||||
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
||||
|
||||
+9
-3
@@ -21,21 +21,27 @@ class AppearsInDetails extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
if (!asset.hasRemote) return const SizedBox.shrink();
|
||||
if (!asset.hasRemote) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final remoteAssetId = switch (asset) {
|
||||
RemoteAsset(:final id) => id,
|
||||
LocalAsset(:final remoteAssetId) => remoteAssetId,
|
||||
};
|
||||
|
||||
if (remoteAssetId == null) return const SizedBox.shrink();
|
||||
if (remoteAssetId == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final userId = ref.watch(currentUserProvider)?.id;
|
||||
final assetAlbums = ref.watch(albumsContainingAssetProvider(remoteAssetId));
|
||||
|
||||
return assetAlbums.when(
|
||||
data: (albums) {
|
||||
if (albums.isEmpty) return const SizedBox.shrink();
|
||||
if (albums.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
albums.sortBy((a) => a.name);
|
||||
|
||||
|
||||
+3
-1
@@ -20,7 +20,9 @@ class RatingDetails extends ConsumerWidget {
|
||||
.watch(userMetadataPreferencesProvider)
|
||||
.maybeWhen(data: (prefs) => prefs?.ratingsEnabled ?? false, orElse: () => false);
|
||||
|
||||
if (!isRatingEnabled) return const SizedBox.shrink();
|
||||
if (!isRatingEnabled) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 16.0),
|
||||
|
||||
+12
-4
@@ -111,7 +111,9 @@ class TechnicalDetails extends ConsumerWidget {
|
||||
}
|
||||
|
||||
static String? _getCameraInfoTitle(ExifInfo? exifInfo) {
|
||||
if (exifInfo == null) return null;
|
||||
if (exifInfo == null) {
|
||||
return null;
|
||||
}
|
||||
return switch ((exifInfo.make, exifInfo.model)) {
|
||||
(null, null) => null,
|
||||
(String make, null) => make,
|
||||
@@ -121,17 +123,23 @@ class TechnicalDetails extends ConsumerWidget {
|
||||
}
|
||||
|
||||
static String? _getCameraInfoSubtitle(ExifInfo? exifInfo) {
|
||||
if (exifInfo == null) return null;
|
||||
if (exifInfo == null) {
|
||||
return null;
|
||||
}
|
||||
final exposureTime = exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null;
|
||||
final iso = exifInfo.iso != null ? 'ISO ${exifInfo.iso}' : null;
|
||||
return [exposureTime, iso].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator);
|
||||
}
|
||||
|
||||
static String? _getLensInfoSubtitle(ExifInfo? exifInfo) {
|
||||
if (exifInfo == null) return null;
|
||||
if (exifInfo == null) {
|
||||
return null;
|
||||
}
|
||||
final fNumber = exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null;
|
||||
final focalLength = exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null;
|
||||
if (fNumber == null && focalLength == null) return null;
|
||||
if (fNumber == null && focalLength == null) {
|
||||
return null;
|
||||
}
|
||||
return [fNumber, focalLength].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@ import 'package:immich_mobile/extensions/scroll_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_details.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.widget.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/widgets/photo_view/photo_view.dart';
|
||||
|
||||
@@ -62,7 +62,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
super.initState();
|
||||
_eventSubscription = EventStream.shared.listen(_onEvent);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted || !_scrollController.hasClients) return;
|
||||
if (!mounted || !_scrollController.hasClients) {
|
||||
return;
|
||||
}
|
||||
_scrollController.snapPosition.snapOffset = _snapOffset;
|
||||
if (_showingDetails && _snapOffset > 0) {
|
||||
_scrollController.jumpTo(_snapOffset);
|
||||
@@ -87,7 +89,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
}
|
||||
|
||||
void _showDetails() {
|
||||
if (!_scrollController.hasClients || _snapOffset <= 0) return;
|
||||
if (!_scrollController.hasClients || _snapOffset <= 0) {
|
||||
return;
|
||||
}
|
||||
_viewer.setShowingDetails(true);
|
||||
_scrollController.animateTo(_snapOffset, duration: Durations.medium2, curve: Curves.easeOutCubic);
|
||||
}
|
||||
@@ -128,7 +132,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
}
|
||||
|
||||
void _updateDrag(DragUpdateDetails details) {
|
||||
if (_dragStart == null) return;
|
||||
if (_dragStart == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_dragIntent == _DragIntent.none) {
|
||||
_dragIntent = switch ((details.globalPosition - _dragStart!.globalPosition).dy) {
|
||||
@@ -141,7 +147,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
switch (_dragIntent) {
|
||||
case _DragIntent.none:
|
||||
case _DragIntent.scroll:
|
||||
if (_drag == null) _startProxyDrag();
|
||||
if (_drag == null) {
|
||||
_startProxyDrag();
|
||||
}
|
||||
_drag?.update(details);
|
||||
|
||||
_syncShowingDetails();
|
||||
@@ -151,7 +159,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
}
|
||||
|
||||
void _endDrag(DragEndDetails details) {
|
||||
if (_dragStart == null) return;
|
||||
if (_dragStart == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final start = _dragStart;
|
||||
_dragStart = null;
|
||||
@@ -188,7 +198,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
PhotoViewControllerBase controller,
|
||||
PhotoViewScaleStateController scaleStateController,
|
||||
) {
|
||||
if (!_showingDetails && _isZoomed) return;
|
||||
if (!_showingDetails && _isZoomed) {
|
||||
return;
|
||||
}
|
||||
_beginDrag(details);
|
||||
}
|
||||
|
||||
@@ -215,7 +227,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
}
|
||||
|
||||
void _onTapUp(BuildContext context, TapUpDetails details, PhotoViewControllerValue controllerValue) {
|
||||
if (_showingDetails || _dragStart != null) return;
|
||||
if (_showingDetails || _dragStart != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final tapToNavigate = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.tapToNavigate);
|
||||
if (!tapToNavigate) {
|
||||
@@ -247,31 +261,43 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||
_viewer.setZoomed(_isZoomed);
|
||||
|
||||
if (scaleState != PhotoViewScaleState.initial) {
|
||||
if (_dragStart == null) _viewer.setControls(false);
|
||||
if (_dragStart == null) {
|
||||
_viewer.setControls(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_showingDetails) _viewer.setControls(true);
|
||||
if (!_showingDetails) {
|
||||
_viewer.setControls(true);
|
||||
}
|
||||
}
|
||||
|
||||
void _listenForScaleBoundaries(PhotoViewControllerBase? controller) {
|
||||
_scaleBoundarySub?.cancel();
|
||||
_scaleBoundarySub = null;
|
||||
if (controller == null || controller.scaleBoundaries != null) return;
|
||||
if (controller == null || controller.scaleBoundaries != null) {
|
||||
return;
|
||||
}
|
||||
_scaleBoundarySub = controller.outputStateStream.listen((_) {
|
||||
if (controller.scaleBoundaries != null) {
|
||||
_scaleBoundarySub?.cancel();
|
||||
_scaleBoundarySub = null;
|
||||
if (mounted) setState(() {});
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
double _getImageHeight(double maxWidth, double maxHeight, BaseAsset? asset) {
|
||||
final sb = _viewController?.scaleBoundaries;
|
||||
if (sb != null) return sb.childSize.height * sb.initialScale;
|
||||
if (sb != null) {
|
||||
return sb.childSize.height * sb.initialScale;
|
||||
}
|
||||
|
||||
if (asset == null || asset.width == null || asset.height == null) return maxHeight;
|
||||
if (asset == null || asset.width == null || asset.height == null) {
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
final r = asset.width! / asset.height!;
|
||||
return math.min(maxWidth / r, maxHeight);
|
||||
|
||||
@@ -21,12 +21,16 @@ class AssetPreloader {
|
||||
unawaited(timelineService.preloadAssets(index));
|
||||
_timer?.cancel();
|
||||
_timer = Timer(Durations.medium4, () async {
|
||||
if (!mounted()) return;
|
||||
if (!mounted()) {
|
||||
return;
|
||||
}
|
||||
final (prev, next) = await (
|
||||
timelineService.getAssetAsync(index - 1),
|
||||
timelineService.getAssetAsync(index + 1),
|
||||
).wait;
|
||||
if (!mounted()) return;
|
||||
if (!mounted()) {
|
||||
return;
|
||||
}
|
||||
_prevStream?.removeListener(_dummyListener);
|
||||
_nextStream?.removeListener(_dummyListener);
|
||||
_prevStream = prev != null ? _resolveImage(prev, size) : null;
|
||||
|
||||
@@ -17,9 +17,9 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/download_statu
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_page.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_preloader.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/viewer_top_app_bar.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/viewer_bottom_app_bar.widget.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/viewer_top_app_bar.widget.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/providers/cast.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||
@@ -67,7 +67,9 @@ class AssetViewer extends ConsumerStatefulWidget {
|
||||
ref.read(assetViewerProvider.notifier).reset();
|
||||
|
||||
// Hide controls by default for videos
|
||||
if (asset.isVideo) ref.read(assetViewerProvider.notifier).setControls(false);
|
||||
if (asset.isVideo) {
|
||||
ref.read(assetViewerProvider.notifier).setControls(false);
|
||||
}
|
||||
|
||||
_setAsset(ref, asset);
|
||||
}
|
||||
@@ -90,7 +92,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
|
||||
void _onTapNavigate(int direction) {
|
||||
final page = _pageController.page?.toInt();
|
||||
if (page == null) return;
|
||||
if (page == null) {
|
||||
return;
|
||||
}
|
||||
final target = page + direction;
|
||||
final maxPage = _totalAssets - 1;
|
||||
if (target >= 0 && target <= maxPage) {
|
||||
@@ -105,7 +109,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
|
||||
final asset = ref.read(assetViewerProvider).currentAsset;
|
||||
assert(asset != null, "Current asset should not be null when opening the AssetViewer");
|
||||
if (asset != null) _stackChildrenKeepAlive = ref.read(stackChildrenNotifier(asset).notifier).ref.keepAlive();
|
||||
if (asset != null) {
|
||||
_stackChildrenKeepAlive = ref.read(stackChildrenNotifier(asset).notifier).ref.keepAlive();
|
||||
}
|
||||
|
||||
_reloadSubscription = EventStream.shared.listen(_onEvent);
|
||||
|
||||
@@ -137,7 +143,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
// playing, and preventing the video on the next page from becoming ready
|
||||
// unnecessarily.
|
||||
bool _onScrollEnd(ScrollEndNotification notification) {
|
||||
if (notification.depth != 0) return false;
|
||||
if (notification.depth != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final page = _pageController.page?.round();
|
||||
if (page != null && page != _currentPage) {
|
||||
@@ -155,7 +163,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
_currentPage = index;
|
||||
|
||||
final asset = await ref.read(timelineServiceProvider).getAssetAsync(index);
|
||||
if (asset == null) return;
|
||||
if (asset == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AssetViewer._setAsset(ref, asset);
|
||||
_preloader.preload(index, context.sizeData);
|
||||
@@ -165,9 +175,13 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
}
|
||||
|
||||
void _handleCasting() {
|
||||
if (!ref.read(castProvider).isCasting) return;
|
||||
if (!ref.read(castProvider).isCasting) {
|
||||
return;
|
||||
}
|
||||
final asset = ref.read(assetViewerProvider).currentAsset;
|
||||
if (asset == null) return;
|
||||
if (asset == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (asset is RemoteAsset) {
|
||||
context.scaffoldMessenger.hideCurrentSnackBar();
|
||||
@@ -199,7 +213,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
}
|
||||
|
||||
void _onViewerReloadEvent() {
|
||||
if (_totalAssets <= 1) return;
|
||||
if (_totalAssets <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final index = _pageController.page?.round() ?? 0;
|
||||
final target = index >= _totalAssets - 1 ? index - 1 : index + 1;
|
||||
@@ -252,7 +268,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||
|
||||
// Listen for casting changes and send initial asset to the cast provider
|
||||
ref.listen(castProvider.select((value) => value.isCasting), (_, isCasting) {
|
||||
if (!isCasting) return;
|
||||
if (!isCasting) {
|
||||
return;
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_handleCasting();
|
||||
});
|
||||
|
||||
@@ -53,7 +53,9 @@ class _RatingBarState extends State<RatingBar> {
|
||||
final totalWidth = widget.itemCount * widget.itemSize + (widget.itemCount - 1) * widget.starPadding;
|
||||
double dx = localPosition.dx;
|
||||
|
||||
if (isRTL) dx = totalWidth - dx;
|
||||
if (isRTL) {
|
||||
dx = totalWidth - dx;
|
||||
}
|
||||
|
||||
double newRating;
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import 'package:immich_mobile/domain/services/setting.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart';
|
||||
import 'package:immich_mobile/providers/cast.provider.dart';
|
||||
@@ -61,7 +61,9 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
void didUpdateWidget(NativeVideoViewer oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
if (widget.isCurrent == oldWidget.isCurrent || _controller == null) return;
|
||||
if (widget.isCurrent == oldWidget.isCurrent || _controller == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!widget.isCurrent) {
|
||||
_loadTimer?.cancel();
|
||||
@@ -85,25 +87,35 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||
switch (state) {
|
||||
case AppLifecycleState.resumed:
|
||||
if (_shouldPlayOnForeground) await _notifier.play();
|
||||
if (_shouldPlayOnForeground) {
|
||||
await _notifier.play();
|
||||
}
|
||||
case AppLifecycleState.paused:
|
||||
_shouldPlayOnForeground = await _controller?.isPlaying() ?? true;
|
||||
if (_shouldPlayOnForeground) await _notifier.pause();
|
||||
if (_shouldPlayOnForeground) {
|
||||
await _notifier.pause();
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
Future<VideoSource?> _createSource() async {
|
||||
if (!mounted) return null;
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final videoAsset = await ref.read(assetServiceProvider).getAsset(widget.asset) ?? widget.asset;
|
||||
if (!mounted) return null;
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (videoAsset.hasLocal && videoAsset.livePhotoVideoId == null) {
|
||||
final id = videoAsset is LocalAsset ? videoAsset.id : (videoAsset as RemoteAsset).localId!;
|
||||
final file = await StorageRepository().getFileForAsset(id);
|
||||
if (!mounted) return null;
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (file == null) {
|
||||
throw Exception('No file found for the video');
|
||||
@@ -134,25 +146,35 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
}
|
||||
|
||||
void _onPlaybackReady() async {
|
||||
if (!mounted || !widget.isCurrent) return;
|
||||
if (!mounted || !widget.isCurrent) {
|
||||
return;
|
||||
}
|
||||
|
||||
_notifier.onNativePlaybackReady();
|
||||
|
||||
// onPlaybackReady may be called multiple times, usually when more data
|
||||
// loads. If this is not the first time that the player has become ready, we
|
||||
// should not autoplay.
|
||||
if (_isVideoReady) return;
|
||||
if (_isVideoReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isVideoReady = true);
|
||||
|
||||
if (ref.read(assetViewerProvider).showingDetails) return;
|
||||
if (ref.read(assetViewerProvider).showingDetails) {
|
||||
return;
|
||||
}
|
||||
|
||||
final autoPlayVideo = AppSetting.get(Setting.autoPlayVideo);
|
||||
if (autoPlayVideo || widget.asset.isMotionPhoto) await _notifier.play();
|
||||
if (autoPlayVideo || widget.asset.isMotionPhoto) {
|
||||
await _notifier.play();
|
||||
}
|
||||
}
|
||||
|
||||
void _onPlaybackEnded() {
|
||||
if (!mounted) return;
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
_notifier.onNativePlaybackEnded();
|
||||
|
||||
@@ -162,12 +184,16 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
}
|
||||
|
||||
void _onPlaybackPositionChanged() {
|
||||
if (!mounted) return;
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_notifier.onNativePositionChanged();
|
||||
}
|
||||
|
||||
void _onPlaybackStatusChanged() {
|
||||
if (!mounted) return;
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_notifier.onNativeStatusChanged();
|
||||
}
|
||||
|
||||
@@ -180,10 +206,14 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
|
||||
void _loadVideo() async {
|
||||
final nc = _controller;
|
||||
if (nc == null || nc.videoSource != null || !mounted) return;
|
||||
if (nc == null || nc.videoSource != null || !mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
final source = await _videoSource;
|
||||
if (source == null || !mounted) return;
|
||||
if (source == null || !mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
await _notifier.load(source);
|
||||
final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo);
|
||||
@@ -192,7 +222,9 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
}
|
||||
|
||||
void _initController(NativeVideoPlayerController nc) {
|
||||
if (_controller != null || !mounted) return;
|
||||
if (_controller != null || !mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
_notifier.attachController(nc);
|
||||
|
||||
@@ -203,7 +235,9 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||
|
||||
_controller = nc;
|
||||
|
||||
if (widget.isCurrent) _loadVideo();
|
||||
if (widget.isCurrent) {
|
||||
_loadVideo();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -41,7 +41,9 @@ class LocalThumbProvider extends CancellableImageProvider<LocalThumbProvider>
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other is LocalThumbProvider) {
|
||||
return id == other.id;
|
||||
}
|
||||
@@ -148,7 +150,9 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
||||
final originalRequest = request = LocalImageRequest(localId: key.id, size: Size.zero, assetType: key.assetType);
|
||||
final codec = await loadCodecRequest(originalRequest, isFinal: true);
|
||||
if (codec == null) {
|
||||
if (isCancelled) return;
|
||||
if (isCancelled) {
|
||||
return;
|
||||
}
|
||||
throw StateError('Failed to load animated codec for local asset ${key.id}');
|
||||
}
|
||||
yield codec;
|
||||
@@ -156,7 +160,9 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other is LocalFullImageProvider) {
|
||||
return id == other.id && size == other.size && isAnimated == other.isAnimated;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ class RemoteImageProvider extends CancellableImageProvider<RemoteImageProvider>
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other is RemoteImageProvider) {
|
||||
return url == other.url && edited == other.edited;
|
||||
}
|
||||
@@ -175,7 +177,9 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other is RemoteFullImageProvider) {
|
||||
return assetId == other.assetId &&
|
||||
thumbhash == other.thumbhash &&
|
||||
|
||||
@@ -27,7 +27,9 @@ class ThumbHashProvider extends CancellableImageProvider<ThumbHashProvider>
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other is ThumbHashProvider) {
|
||||
return thumbHash == other.thumbHash;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,9 @@ class _ThumbnailState extends State<Thumbnail> with SingleTickerProviderStateMix
|
||||
void _loadFromThumbhashProvider() {
|
||||
_stopListeningToThumbhashStream();
|
||||
final thumbhashProvider = widget.thumbhashProvider;
|
||||
if (thumbhashProvider == null || _providerImage != null) return;
|
||||
if (thumbhashProvider == null || _providerImage != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final thumbhashStream = _thumbhashStream = thumbhashProvider.resolve(ImageConfiguration.empty);
|
||||
final thumbhashStreamListener = _thumbhashStreamListener = ImageStreamListener(
|
||||
@@ -108,7 +110,9 @@ class _ThumbnailState extends State<Thumbnail> with SingleTickerProviderStateMix
|
||||
void _loadFromImageProvider() {
|
||||
_stopListeningToImageStream();
|
||||
final imageProvider = widget.imageProvider;
|
||||
if (imageProvider == null) return;
|
||||
if (imageProvider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final imageStream = _imageStream = imageProvider.resolve(ImageConfiguration.empty);
|
||||
final imageStreamListener = _imageStreamListener = ImageStreamListener(
|
||||
@@ -201,7 +205,9 @@ class _ThumbnailState extends State<Thumbnail> with SingleTickerProviderStateMix
|
||||
|
||||
bool _isVisible() {
|
||||
final renderObject = context.findRenderObject() as RenderBox?;
|
||||
if (renderObject == null || !renderObject.attached) return false;
|
||||
if (renderObject == null || !renderObject.attached) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final topLeft = renderObject.localToGlobal(Offset.zero);
|
||||
final bottomRight = renderObject.localToGlobal(Offset(renderObject.size.width, renderObject.size.height));
|
||||
|
||||
@@ -54,7 +54,9 @@ class DriftMemoryCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
if (asset.isImage) return FullImage(asset, fit: fit, size: const Size(double.infinity, double.infinity));
|
||||
if (asset.isImage) {
|
||||
return FullImage(asset, fit: fit, size: const Size(double.infinity, double.infinity));
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: AspectRatio(
|
||||
|
||||
@@ -136,7 +136,9 @@ class QuickDatePicker extends HookWidget {
|
||||
menuStyle: MenuStyle(maximumSize: WidgetStateProperty.all(Size(size.width, size.height * 0.5))),
|
||||
dropdownMenuEntries: _recentYears.map((e) => DropdownMenuEntry(value: e, label: e.toString())).toList(),
|
||||
onSelected: (year) {
|
||||
if (year == null) return;
|
||||
if (year == null) {
|
||||
return;
|
||||
}
|
||||
onSelect(YearFilter(year));
|
||||
},
|
||||
),
|
||||
@@ -179,7 +181,9 @@ class QuickDatePicker extends HookWidget {
|
||||
child: SingleChildScrollView(
|
||||
child: RadioGroup(
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
final _ = switch (value) {
|
||||
_QuickPickerType.custom => onRequestPicker(),
|
||||
_QuickPickerType.last1Month => onSelect(RecentMonthRangeFilter(1)),
|
||||
|
||||
@@ -77,7 +77,9 @@ class RenderFixedRow extends RenderBox
|
||||
double _height;
|
||||
|
||||
set height(double value) {
|
||||
if (_height == value) return;
|
||||
if (_height == value) {
|
||||
return;
|
||||
}
|
||||
_height = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
@@ -86,7 +88,9 @@ class RenderFixedRow extends RenderBox
|
||||
List<double> _widths;
|
||||
|
||||
set widths(List<double> value) {
|
||||
if (listEquals(_widths, value)) return;
|
||||
if (listEquals(_widths, value)) {
|
||||
return;
|
||||
}
|
||||
_widths = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
@@ -95,7 +99,9 @@ class RenderFixedRow extends RenderBox
|
||||
double _spacing;
|
||||
|
||||
set spacing(double value) {
|
||||
if (_spacing == value) return;
|
||||
if (_spacing == value) {
|
||||
return;
|
||||
}
|
||||
_spacing = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
@@ -104,7 +110,9 @@ class RenderFixedRow extends RenderBox
|
||||
TextDirection _textDirection;
|
||||
|
||||
set textDirection(TextDirection value) {
|
||||
if (_textDirection == value) return;
|
||||
if (_textDirection == value) {
|
||||
return;
|
||||
}
|
||||
_textDirection = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
@@ -53,14 +53,18 @@ class FixedSegment extends Segment {
|
||||
@override
|
||||
int getMinChildIndexForScrollOffset(double scrollOffset) {
|
||||
final adjustedOffset = scrollOffset - gridOffset;
|
||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) return firstIndex;
|
||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) {
|
||||
return firstIndex;
|
||||
}
|
||||
return gridIndex + (adjustedOffset / mainAxisExtend).floor();
|
||||
}
|
||||
|
||||
@override
|
||||
int getMaxChildIndexForScrollOffset(double scrollOffset) {
|
||||
final adjustedOffset = scrollOffset - gridOffset;
|
||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) return firstIndex;
|
||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) {
|
||||
return firstIndex;
|
||||
}
|
||||
return gridIndex + (adjustedOffset / mainAxisExtend).ceil() - 1;
|
||||
}
|
||||
|
||||
@@ -162,8 +166,12 @@ class _FixedSegmentRow extends ConsumerWidget {
|
||||
// 0.5: width < mean - threshold
|
||||
// 1.5: width > mean + threshold
|
||||
final arConfiguration = aspectRatios.map((e) {
|
||||
if (e - meanAspectRatio > 0.3) return 1.5;
|
||||
if (e - meanAspectRatio < -0.3) return 0.5;
|
||||
if (e - meanAspectRatio > 0.3) {
|
||||
return 1.5;
|
||||
}
|
||||
if (e - meanAspectRatio < -0.3) {
|
||||
return 0.5;
|
||||
}
|
||||
return 1.0;
|
||||
});
|
||||
|
||||
|
||||
@@ -104,7 +104,9 @@ class ScrubberState extends ConsumerState<Scrubber> with TickerProviderStateMixi
|
||||
late ScrollController _scrollController;
|
||||
|
||||
double get _currentOffset {
|
||||
if (_scrollController.hasClients != true) return 0.0;
|
||||
if (_scrollController.hasClients != true) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return _scrollController.offset * _scrubberHeight / _scrollController.position.maxScrollExtent;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,9 @@ abstract class Segment {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other is Segment &&
|
||||
other.firstIndex == firstIndex &&
|
||||
|
||||
@@ -201,7 +201,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
}
|
||||
|
||||
void _restoreAssetPosition(_) {
|
||||
if (_restoreAssetIndex == null) return;
|
||||
if (_restoreAssetIndex == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final asyncSegments = ref.read(timelineSegmentProvider);
|
||||
asyncSegments.whenData((segments) {
|
||||
@@ -329,7 +331,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
}
|
||||
|
||||
void _handleDragAssetEnter(TimelineAssetIndex index) {
|
||||
if (_dragAnchorIndex == null || !_dragging) return;
|
||||
if (_dragAnchorIndex == null || !_dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
final timelineService = ref.read(timelineServiceProvider);
|
||||
final dragAnchorIndex = _dragAnchorIndex!;
|
||||
@@ -399,7 +403,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||
segments: segments,
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(ctx, index) {
|
||||
if (index >= childCount) return null;
|
||||
if (index >= childCount) {
|
||||
return null;
|
||||
}
|
||||
final segment = segments.findByIndex(index);
|
||||
return segment?.builder(ctx, index) ?? const SizedBox.shrink();
|
||||
},
|
||||
|
||||
@@ -81,11 +81,15 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||
|
||||
TimelineAssetIndex? _getValueKeyAtPosition(Offset position) {
|
||||
final box = context.findAncestorRenderObjectOfType<RenderBox>();
|
||||
if (box == null) return null;
|
||||
if (box == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final hitTestResult = BoxHitTestResult();
|
||||
final local = box.globalToLocal(position);
|
||||
if (!box.hitTest(hitTestResult, position: local)) return null;
|
||||
if (!box.hitTest(hitTestResult, position: local)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (hitTestResult.path.firstWhereOrNull((hit) => hit.target is _TimelineAssetIndexProxy)?.target
|
||||
as _TimelineAssetIndexProxy?)
|
||||
@@ -103,7 +107,9 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||
|
||||
final initialHit = _getValueKeyAtPosition(event.globalPosition);
|
||||
anchorAsset = initialHit;
|
||||
if (initialHit == null) return;
|
||||
if (initialHit == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (anchorAsset != null) {
|
||||
widget.onStart?.call(anchorAsset!);
|
||||
@@ -117,8 +123,12 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||
}
|
||||
|
||||
void _onLongPressMove(LongPressMoveUpdateDetails event) {
|
||||
if (anchorAsset == null) return;
|
||||
if (topScrollOffset == null || bottomScrollOffset == null) return;
|
||||
if (anchorAsset == null) {
|
||||
return;
|
||||
}
|
||||
if (topScrollOffset == null || bottomScrollOffset == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final currentDy = event.localPosition.dy;
|
||||
|
||||
@@ -138,7 +148,9 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||
}
|
||||
|
||||
final currentlyTouchingAsset = _getValueKeyAtPosition(event.globalPosition);
|
||||
if (currentlyTouchingAsset == null) return;
|
||||
if (currentlyTouchingAsset == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (assetUnderPointer != currentlyTouchingAsset) {
|
||||
if (!scrollNotified) {
|
||||
@@ -202,7 +214,9 @@ class TimelineAssetIndex {
|
||||
|
||||
@override
|
||||
bool operator ==(covariant TimelineAssetIndex other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other.assetIndex == assetIndex && other.segmentIndex == segmentIndex;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user