chore: add always_put_control_body_on_new_line lint (#28352)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>pull/28351/head
parent
7837d40f57
commit
12f7b2a005
|
|
@ -34,6 +34,7 @@ linter:
|
||||||
unrelated_type_equality_checks: true
|
unrelated_type_equality_checks: true
|
||||||
prefer_const_constructors: true
|
prefer_const_constructors: true
|
||||||
always_use_package_imports: true
|
always_use_package_imports: true
|
||||||
|
always_put_control_body_on_new_line: true
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
|
@ -50,6 +51,7 @@ analyzer:
|
||||||
# - custom_lint
|
# - custom_lint
|
||||||
errors:
|
errors:
|
||||||
unawaited_futures: warning
|
unawaited_futures: warning
|
||||||
|
always_put_control_body_on_new_line: warning
|
||||||
|
|
||||||
custom_lint:
|
custom_lint:
|
||||||
rules:
|
rules:
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,9 @@ List<TranslationParam> _extractParams(String value) {
|
||||||
final icuType = match.group(2)!;
|
final icuType = match.group(2)!;
|
||||||
final icuContent = match.group(3) ?? '';
|
final icuContent = match.group(3) ?? '';
|
||||||
|
|
||||||
if (params.containsKey(name)) continue;
|
if (params.containsKey(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
String type;
|
String type;
|
||||||
if (icuType == 'plural' || icuType == 'number') {
|
if (icuType == 'plural' || icuType == 'number') {
|
||||||
|
|
@ -238,7 +240,9 @@ List<TranslationParam> _extractParams(String value) {
|
||||||
|
|
||||||
for (var i = 0; i < value.length; i++) {
|
for (var i = 0; i < value.length; i++) {
|
||||||
if (value[i] == '{') {
|
if (value[i] == '{') {
|
||||||
if (depth == 0) icuStart = i;
|
if (depth == 0) {
|
||||||
|
icuStart = i;
|
||||||
|
}
|
||||||
depth++;
|
depth++;
|
||||||
} else if (value[i] == '}') {
|
} else if (value[i] == '}') {
|
||||||
depth--;
|
depth--;
|
||||||
|
|
@ -256,7 +260,9 @@ List<TranslationParam> _extractParams(String value) {
|
||||||
for (final match in simpleRegex.allMatches(cleanedValue)) {
|
for (final match in simpleRegex.allMatches(cleanedValue)) {
|
||||||
final name = match.group(1)!;
|
final name = match.group(1)!;
|
||||||
|
|
||||||
if (params.containsKey(name)) continue;
|
if (params.containsKey(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
String type;
|
String type;
|
||||||
if (_kIntParamNames.contains(name.toLowerCase())) {
|
if (_kIntParamNames.contains(name.toLowerCase())) {
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,12 @@ class RemoteAlbum {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (other is! RemoteAlbum) return false;
|
if (other is! RemoteAlbum) {
|
||||||
if (identical(this, other)) return true;
|
return false;
|
||||||
|
}
|
||||||
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return id == other.id &&
|
return id == other.id &&
|
||||||
name == other.name &&
|
name == other.name &&
|
||||||
ownerId == other.ownerId &&
|
ownerId == other.ownerId &&
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,12 @@ class LocalAlbum {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (other is! LocalAlbum) return false;
|
if (other is! LocalAlbum) {
|
||||||
if (identical(this, other)) return true;
|
return false;
|
||||||
|
}
|
||||||
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id &&
|
return other.id == id &&
|
||||||
other.name == name &&
|
other.name == name &&
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,18 @@ sealed class BaseAsset {
|
||||||
bool get isAnimatedImage => playbackStyle == AssetPlaybackStyle.imageAnimated;
|
bool get isAnimatedImage => playbackStyle == AssetPlaybackStyle.imageAnimated;
|
||||||
|
|
||||||
AssetPlaybackStyle get playbackStyle {
|
AssetPlaybackStyle get playbackStyle {
|
||||||
if (isVideo) return AssetPlaybackStyle.video;
|
if (isVideo) {
|
||||||
if (isMotionPhoto) return AssetPlaybackStyle.livePhoto;
|
return AssetPlaybackStyle.video;
|
||||||
|
}
|
||||||
|
if (isMotionPhoto) {
|
||||||
|
return AssetPlaybackStyle.livePhoto;
|
||||||
|
}
|
||||||
if (isImage && durationMs != null && durationMs! > 0) {
|
if (isImage && durationMs != null && durationMs! > 0) {
|
||||||
return AssetPlaybackStyle.imageAnimated;
|
return AssetPlaybackStyle.imageAnimated;
|
||||||
}
|
}
|
||||||
if (isImage) return AssetPlaybackStyle.image;
|
if (isImage) {
|
||||||
|
return AssetPlaybackStyle.image;
|
||||||
|
}
|
||||||
return AssetPlaybackStyle.unknown;
|
return AssetPlaybackStyle.unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,7 +104,9 @@ sealed class BaseAsset {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (other is BaseAsset) {
|
if (other is BaseAsset) {
|
||||||
return name == other.name &&
|
return name == other.name &&
|
||||||
type == other.type &&
|
type == other.type &&
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,12 @@ class LocalAsset extends BaseAsset {
|
||||||
// Not checking for remoteId here
|
// Not checking for remoteId here
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (other is! LocalAsset) return false;
|
if (other is! LocalAsset) {
|
||||||
if (identical(this, other)) return true;
|
return false;
|
||||||
|
}
|
||||||
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return super == other &&
|
return super == other &&
|
||||||
id == other.id &&
|
id == other.id &&
|
||||||
cloudId == other.cloudId &&
|
cloudId == other.cloudId &&
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,12 @@ class RemoteAsset extends BaseAsset {
|
||||||
// Not checking for localId here
|
// Not checking for localId here
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (other is! RemoteAsset) return false;
|
if (other is! RemoteAsset) {
|
||||||
if (identical(this, other)) return true;
|
return false;
|
||||||
|
}
|
||||||
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return super == other &&
|
return super == other &&
|
||||||
id == other.id &&
|
id == other.id &&
|
||||||
ownerId == other.ownerId &&
|
ownerId == other.ownerId &&
|
||||||
|
|
@ -158,8 +162,12 @@ class RemoteAssetExif extends RemoteAsset {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (other is! RemoteAssetExif) return false;
|
if (other is! RemoteAssetExif) {
|
||||||
if (identical(this, other)) return true;
|
return false;
|
||||||
|
}
|
||||||
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return super == other && exifInfo == other.exifInfo;
|
return super == other && exifInfo == other.exifInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,9 @@ class AssetFace {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant AssetFace other) {
|
bool operator ==(covariant AssetFace other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id &&
|
return other.id == id &&
|
||||||
other.assetId == assetId &&
|
other.assetId == assetId &&
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,9 @@ class ExifInfo {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant ExifInfo other) {
|
bool operator ==(covariant ExifInfo other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.fileSize == fileSize &&
|
return other.fileSize == fileSize &&
|
||||||
other.description == description &&
|
other.description == description &&
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ class LogMessage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant LogMessage other) {
|
bool operator ==(covariant LogMessage other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.message == message &&
|
return other.message == message &&
|
||||||
other.level == level &&
|
other.level == level &&
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@ class Marker {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant Marker other) {
|
bool operator ==(covariant Marker other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.location == location && other.assetId == assetId;
|
return other.location == location && other.assetId == assetId;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
|
||||||
enum MemoryTypeEnum {
|
enum MemoryTypeEnum {
|
||||||
|
|
@ -36,7 +35,9 @@ class MemoryData {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant MemoryData other) {
|
bool operator ==(covariant MemoryData other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.year == year;
|
return other.year == year;
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +133,9 @@ class DriftMemory {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant DriftMemory other) {
|
bool operator ==(covariant DriftMemory other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
final listEquals = const DeepCollectionEquality().equals;
|
final listEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
return other.id == id &&
|
return other.id == id &&
|
||||||
|
|
|
||||||
|
|
@ -115,12 +115,18 @@ final class _ListCodec<T extends Object> extends _MetadataCodec<List<T>> {
|
||||||
List<T>? decode(String raw) {
|
List<T>? decode(String raw) {
|
||||||
try {
|
try {
|
||||||
final decoded = jsonDecode(raw);
|
final decoded = jsonDecode(raw);
|
||||||
if (decoded is! List) return null;
|
if (decoded is! List) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final result = <T>[];
|
final result = <T>[];
|
||||||
for (final item in decoded) {
|
for (final item in decoded) {
|
||||||
if (item is! String) return null;
|
if (item is! String) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final element = _elementCodec.decode(item);
|
final element = _elementCodec.decode(item);
|
||||||
if (element == null) return null;
|
if (element == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
result.add(element);
|
result.add(element);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,9 @@ class PersonDto {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant PersonDto other) {
|
bool operator ==(covariant PersonDto other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id &&
|
return other.id == id &&
|
||||||
other.birthDate == birthDate &&
|
other.birthDate == birthDate &&
|
||||||
|
|
@ -160,7 +162,9 @@ class DriftPerson {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant DriftPerson other) {
|
bool operator ==(covariant DriftPerson other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id &&
|
return other.id == id &&
|
||||||
other.createdAt == createdAt &&
|
other.createdAt == createdAt &&
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ class SearchResult {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant SearchResult other) {
|
bool operator ==(covariant SearchResult other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
final listEquals = const DeepCollectionEquality().equals;
|
final listEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
return listEquals(other.assets, assets) && other.nextPage == nextPage;
|
return listEquals(other.assets, assets) && other.nextPage == nextPage;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@ class Stack {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant Stack other) {
|
bool operator ==(covariant Stack other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id &&
|
return other.id == id &&
|
||||||
other.createdAt == createdAt &&
|
other.createdAt == createdAt &&
|
||||||
|
|
@ -61,7 +63,9 @@ class StackResponse {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant StackResponse other) {
|
bool operator ==(covariant StackResponse other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id && other.primaryAssetId == primaryAssetId && other.assetIds == assetIds;
|
return other.id == id && other.primaryAssetId == primaryAssetId && other.assetIds == assetIds;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,9 @@ StoreDto: {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant StoreDto<T> other) {
|
bool operator ==(covariant StoreDto<T> other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.key == key && other.value == value;
|
return other.key == key && other.value == value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ class Tag {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant Tag other) {
|
bool operator ==(covariant Tag other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id && other.value == value;
|
return other.id == id && other.value == value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,9 @@ profileChangedAt: $profileChangedAt
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant UserDto other) {
|
bool operator ==(covariant UserDto other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id &&
|
return other.id == id &&
|
||||||
((updatedAt == null && other.updatedAt == null) ||
|
((updatedAt == null && other.updatedAt == null) ||
|
||||||
|
|
@ -219,7 +221,9 @@ class PartnerUserDto {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant PartnerUserDto other) {
|
bool operator ==(covariant PartnerUserDto other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id &&
|
return other.id == id &&
|
||||||
other.email == email &&
|
other.email == email &&
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@ isOnboarded: $isOnboarded,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant Onboarding other) {
|
bool operator ==(covariant Onboarding other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return isOnboarded == other.isOnboarded;
|
return isOnboarded == other.isOnboarded;
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +134,9 @@ showSupportBadge: $showSupportBadge,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant Preferences other) {
|
bool operator ==(covariant Preferences other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.foldersEnabled == foldersEnabled &&
|
return other.foldersEnabled == foldersEnabled &&
|
||||||
other.memoriesEnabled == memoriesEnabled &&
|
other.memoriesEnabled == memoriesEnabled &&
|
||||||
|
|
@ -199,7 +203,9 @@ licenseKey: $licenseKey,
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant License other) {
|
bool operator ==(covariant License other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return activatedAt == other.activatedAt && activationKey == other.activationKey && licenseKey == other.licenseKey;
|
return activatedAt == other.activatedAt && activationKey == other.activationKey && licenseKey == other.licenseKey;
|
||||||
}
|
}
|
||||||
|
|
@ -251,7 +257,9 @@ license: ${license ?? "<NA>"},
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant UserMetadata other) {
|
bool operator ==(covariant UserMetadata other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.userId == userId &&
|
return other.userId == userId &&
|
||||||
other.key == key &&
|
other.key == key &&
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,9 @@ class RemoteAlbumService {
|
||||||
List<RemoteAlbum> albums, {
|
List<RemoteAlbum> albums, {
|
||||||
required AssetDateAggregation aggregation,
|
required AssetDateAggregation aggregation,
|
||||||
}) async {
|
}) async {
|
||||||
if (albums.isEmpty) return [];
|
if (albums.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
final albumIds = albums.map((e) => e.id).toList();
|
final albumIds = albums.map((e) => e.id).toList();
|
||||||
final sortedIds = await _repository.getSortedAlbumIds(albumIds, aggregation: aggregation);
|
final sortedIds = await _repository.getSortedAlbumIds(albumIds, aggregation: aggregation);
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ class StoreService {
|
||||||
|
|
||||||
/// Stores the [value] for the [key]. Skips write if value hasn't changed.
|
/// Stores the [value] for the [key]. Skips write if value hasn't changed.
|
||||||
Future<void> put<U extends StoreKey<T>, T>(U key, T value) async {
|
Future<void> put<U extends StoreKey<T>, T>(U key, T value) async {
|
||||||
if (_cache[key.id] == value) return;
|
if (_cache[key.id] == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await _storeRepository.upsert(key, value);
|
await _storeRepository.upsert(key, value);
|
||||||
_cache[key.id] = value;
|
_cache[key.id] = value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,9 @@ class SyncStreamService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) async {
|
Future<void> handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) async {
|
||||||
if (batchData.isEmpty) return;
|
if (batchData.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_logger.info('Processing batch of ${batchData.length} AssetUploadReadyV1 events');
|
_logger.info('Processing batch of ${batchData.length} AssetUploadReadyV1 events');
|
||||||
|
|
||||||
|
|
@ -359,7 +361,9 @@ class SyncStreamService {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleWsAssetUploadReadyV2Batch(List<dynamic> batchData) async {
|
Future<void> handleWsAssetUploadReadyV2Batch(List<dynamic> batchData) async {
|
||||||
if (batchData.isEmpty) return;
|
if (batchData.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_logger.info('Processing batch of ${batchData.length} AssetUploadReadyV2 events');
|
_logger.info('Processing batch of ${batchData.length} AssetUploadReadyV2 events');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,9 @@ class UserService {
|
||||||
|
|
||||||
Future<UserDto?> refreshMyUser() async {
|
Future<UserDto?> refreshMyUser() async {
|
||||||
final user = await _userApiRepository.getMyUser();
|
final user = await _userApiRepository.getMyUser();
|
||||||
if (user == null) return null;
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
await _storeService.put(StoreKey.currentUser, user);
|
await _storeService.put(StoreKey.currentUser, user);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,8 +94,12 @@ class SnapScrollPhysics extends ScrollPhysics {
|
||||||
bool get allowUserScrolling => false;
|
bool get allowUserScrolling => false;
|
||||||
|
|
||||||
static double target(ScrollMetrics position, double velocity, double snapOffset) {
|
static double target(ScrollMetrics position, double velocity, double snapOffset) {
|
||||||
if (velocity > _minFlingVelocity) return snapOffset;
|
if (velocity > _minFlingVelocity) {
|
||||||
if (velocity < -_minFlingVelocity) return position.pixels < snapOffset ? 0.0 : snapOffset;
|
return snapOffset;
|
||||||
|
}
|
||||||
|
if (velocity < -_minFlingVelocity) {
|
||||||
|
return position.pixels < snapOffset ? 0.0 : snapOffset;
|
||||||
|
}
|
||||||
return position.pixels < minSnapDistance ? 0.0 : snapOffset;
|
return position.pixels < minSnapDistance ? 0.0 : snapOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@ import 'dart:async';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:ffi/ffi.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
|
|
||||||
part 'local_image_request.dart';
|
part 'local_image_request.dart';
|
||||||
part 'thumbhash_image_request.dart';
|
|
||||||
part 'remote_image_request.dart';
|
part 'remote_image_request.dart';
|
||||||
|
part 'thumbhash_image_request.dart';
|
||||||
|
|
||||||
abstract class ImageRequest {
|
abstract class ImageRequest {
|
||||||
static int _nextRequestId = 0;
|
static int _nextRequestId = 0;
|
||||||
|
|
@ -74,7 +74,9 @@ abstract class ImageRequest {
|
||||||
|
|
||||||
Future<ui.FrameInfo?> _fromEncodedPlatformImage(int address, int length) async {
|
Future<ui.FrameInfo?> _fromEncodedPlatformImage(int address, int length) async {
|
||||||
final result = await _codecFromEncodedPlatformImage(address, length);
|
final result = await _codecFromEncodedPlatformImage(address, length);
|
||||||
if (result == null) return null;
|
if (result == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final (codec, descriptor) = result;
|
final (codec, descriptor) = result;
|
||||||
if (_isCancelled) {
|
if (_isCancelled) {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,9 @@ class LocalImageRequest extends ImageRequest {
|
||||||
isVideo: assetType == AssetType.video,
|
isVideo: assetType == AssetType.video,
|
||||||
preferEncoded: true,
|
preferEncoded: true,
|
||||||
);
|
);
|
||||||
if (info == null) return null;
|
if (info == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final (codec, _) = await _codecFromEncodedPlatformImage(info['pointer']!, info['length']!) ?? (null, null);
|
final (codec, _) = await _codecFromEncodedPlatformImage(info['pointer']!, info['length']!) ?? (null, null);
|
||||||
return codec;
|
return codec;
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ class RemoteImageRequest extends ImageRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
final info = await remoteImageApi.requestImage(uri, requestId: requestId, preferEncoded: true);
|
final info = await remoteImageApi.requestImage(uri, requestId: requestId, preferEncoded: true);
|
||||||
if (info == null) return null;
|
if (info == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final (codec, _) = await _codecFromEncodedPlatformImage(info['pointer']!, info['length']!) ?? (null, null);
|
final (codec, _) = await _codecFromEncodedPlatformImage(info['pointer']!, info['length']!) ?? (null, null);
|
||||||
return codec;
|
return codec;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ class ApiRepository {
|
||||||
|
|
||||||
Future<T> checkNull<T>(Future<T?> future) async {
|
Future<T> checkNull<T>(Future<T?> future) async {
|
||||||
final response = await future;
|
final response = await future;
|
||||||
if (response == null) throw const NoResponseDtoError();
|
if (response == null) {
|
||||||
|
throw const NoResponseDtoError();
|
||||||
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,9 @@ class MetadataRepository extends DriftDatabaseRepository {
|
||||||
T _read<T extends Object>(MetadataKey<T> key) => (_cache[key] as T?) ?? key.defaultValue;
|
T _read<T extends Object>(MetadataKey<T> key) => (_cache[key] as T?) ?? key.defaultValue;
|
||||||
|
|
||||||
Future<void> write<T extends Object, U extends T>(MetadataKey<T> key, U value) async {
|
Future<void> write<T extends Object, U extends T>(MetadataKey<T> key, U value) async {
|
||||||
if (_read(key) == value) return;
|
if (_read(key) == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await _db
|
await _db
|
||||||
.into(_db.metadataEntity)
|
.into(_db.metadataEntity)
|
||||||
|
|
@ -79,13 +81,17 @@ class MetadataRepository extends DriftDatabaseRepository {
|
||||||
final keyMap = MetadataKey.asKeyMap();
|
final keyMap = MetadataKey.asKeyMap();
|
||||||
for (final row in rows) {
|
for (final row in rows) {
|
||||||
final key = keyMap[row.key];
|
final key = keyMap[row.key];
|
||||||
if (key == null) continue;
|
if (key == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
_updateCache(key, key.decode(row.value));
|
_updateCache(key, key.decode(row.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateCache<T extends Object>(MetadataKey<T> key, T value) {
|
void _updateCache<T extends Object>(MetadataKey<T> key, T value) {
|
||||||
if (_cache[key] == value) return;
|
if (_cache[key] == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_cache[key] = value;
|
_cache[key] = value;
|
||||||
key.domain.rebuild(this);
|
key.domain.rebuild(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -360,7 +360,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> getSortedAlbumIds(List<String> albumIds, {required AssetDateAggregation aggregation}) async {
|
Future<List<String>> getSortedAlbumIds(List<String> albumIds, {required AssetDateAggregation aggregation}) async {
|
||||||
if (albumIds.isEmpty) return [];
|
if (albumIds.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
final jsonIds = jsonEncode(albumIds);
|
final jsonIds = jsonEncode(albumIds);
|
||||||
final sqlAgg = aggregation == AssetDateAggregation.start ? 'MIN' : 'MAX';
|
final sqlAgg = aggregation == AssetDateAggregation.start ? 'MIN' : 'MAX';
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ class DriftAuthUserRepository extends DriftDatabaseRepository {
|
||||||
Future<UserDto?> get(String id) async {
|
Future<UserDto?> get(String id) async {
|
||||||
final user = await _db.managers.authUserEntity.filter((user) => user.id.equals(id)).getSingleOrNull();
|
final user = await _db.managers.authUserEntity.filter((user) => user.id.equals(id)).getSingleOrNull();
|
||||||
|
|
||||||
if (user == null) return null;
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(id));
|
final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(id));
|
||||||
final metadata = await query.map((row) => row.toDto()).get();
|
final metadata = await query.map((row) => row.toDto()).get();
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ class UserApiRepository extends ApiRepository {
|
||||||
|
|
||||||
Future<UserDto?> getMyUser() async {
|
Future<UserDto?> getMyUser() async {
|
||||||
final (adminDto, preferenceDto) = await (_api.getMyUser(), _api.getMyPreferences()).wait;
|
final (adminDto, preferenceDto) = await (_api.getMyUser(), _api.getMyPreferences()).wait;
|
||||||
if (adminDto == null) return null;
|
if (adminDto == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
return UserConverter.fromAdminDto(adminDto, preferenceDto);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,9 @@ class Activity {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant Activity other) {
|
bool operator ==(covariant Activity other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.id == id &&
|
return other.id == id &&
|
||||||
other.assetId == assetId &&
|
other.assetId == assetId &&
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,9 @@ class AuthState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other is AuthState &&
|
return other is AuthState &&
|
||||||
other.deviceId == deviceId &&
|
other.deviceId == deviceId &&
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@ class AuxilaryEndpoint {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant AuxilaryEndpoint other) {
|
bool operator ==(covariant AuxilaryEndpoint other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.url == url && other.status == status;
|
return other.url == url && other.status == status;
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +55,9 @@ class AuxCheckStatus {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant AuxCheckStatus other) {
|
bool operator ==(covariant AuxCheckStatus other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.name == name;
|
return other.name == name;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@ class BiometricStatus {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant BiometricStatus other) {
|
bool operator ==(covariant BiometricStatus other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
final listEquals = const DeepCollectionEquality().equals;
|
final listEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
return listEquals(other.availableBiometrics, availableBiometrics) && other.canAuthenticate == canAuthenticate;
|
return listEquals(other.availableBiometrics, availableBiometrics) && other.canAuthenticate == canAuthenticate;
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,9 @@ class CastManagerState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other is CastManagerState &&
|
return other is CastManagerState &&
|
||||||
other.isCasting == isCasting &&
|
other.isCasting == isCasting &&
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,9 @@ class DownloadInfo {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant DownloadInfo other) {
|
bool operator ==(covariant DownloadInfo other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.fileName == fileName && other.progress == progress && other.status == status;
|
return other.fileName == fileName && other.progress == progress && other.status == status;
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +73,9 @@ class DownloadState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant DownloadState other) {
|
bool operator ==(covariant DownloadState other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
final mapEquals = const DeepCollectionEquality().equals;
|
final mapEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
return other.downloadStatus == downloadStatus &&
|
return other.downloadStatus == downloadStatus &&
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@ class LivePhotosMetadata {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant LivePhotosMetadata other) {
|
bool operator ==(covariant LivePhotosMetadata other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.part == part && other.id == id;
|
return other.part == part && other.id == id;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ class MapMarker {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant MapMarker other) {
|
bool operator ==(covariant MapMarker other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.latLng == latLng && other.assetRemoteId == assetRemoteId;
|
return other.latLng == latLng && other.assetRemoteId == assetRemoteId;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,9 @@ class MapState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant MapState other) {
|
bool operator ==(covariant MapState other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.themeMode == themeMode &&
|
return other.themeMode == themeMode &&
|
||||||
other.showFavoriteOnly == showFavoriteOnly &&
|
other.showFavoriteOnly == showFavoriteOnly &&
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ class SearchCuratedContent {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant SearchCuratedContent other) {
|
bool operator ==(covariant SearchCuratedContent other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.label == label && other.subtitle == subtitle && other.id == id;
|
return other.label == label && other.subtitle == subtitle && other.id == id;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,9 @@ class SearchLocationFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant SearchLocationFilter other) {
|
bool operator ==(covariant SearchLocationFilter other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.country == country && other.state == state && other.city == city;
|
return other.country == country && other.state == state && other.city == city;
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +77,9 @@ class SearchCameraFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant SearchCameraFilter other) {
|
bool operator ==(covariant SearchCameraFilter other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.make == make && other.model == model;
|
return other.make == make && other.model == model;
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +121,9 @@ class SearchDateFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant SearchDateFilter other) {
|
bool operator ==(covariant SearchDateFilter other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.takenBefore == takenBefore && other.takenAfter == takenAfter;
|
return other.takenBefore == takenBefore && other.takenAfter == takenAfter;
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +158,9 @@ class SearchRatingFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant SearchRatingFilter other) {
|
bool operator ==(covariant SearchRatingFilter other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.rating == rating;
|
return other.rating == rating;
|
||||||
}
|
}
|
||||||
|
|
@ -198,7 +206,9 @@ class SearchDisplayFilters {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant SearchDisplayFilters other) {
|
bool operator ==(covariant SearchDisplayFilters other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.isNotInAlbum == isNotInAlbum && other.isArchive == isArchive && other.isFavorite == isFavorite;
|
return other.isNotInAlbum == isNotInAlbum && other.isArchive == isArchive && other.isFavorite == isFavorite;
|
||||||
}
|
}
|
||||||
|
|
@ -305,7 +315,9 @@ class SearchFilter {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant SearchFilter other) {
|
bool operator ==(covariant SearchFilter other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.context == context &&
|
return other.context == context &&
|
||||||
other.filename == filename &&
|
other.filename == filename &&
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,9 @@ class ServerConfig {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant ServerConfig other) {
|
bool operator ==(covariant ServerConfig other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.trashDays == trashDays &&
|
return other.trashDays == trashDays &&
|
||||||
other.oauthButtonText == oauthButtonText &&
|
other.oauthButtonText == oauthButtonText &&
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@ class ServerDiskInfo {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other is ServerDiskInfo &&
|
return other is ServerDiskInfo &&
|
||||||
other.diskAvailable == diskAvailable &&
|
other.diskAvailable == diskAvailable &&
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,9 @@ class ServerFeatures {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant ServerFeatures other) {
|
bool operator ==(covariant ServerFeatures other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.trash == trash &&
|
return other.trash == trash &&
|
||||||
other.map == map &&
|
other.map == map &&
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,9 @@ class ServerInfo {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other is ServerInfo &&
|
return other is ServerInfo &&
|
||||||
other.serverVersion == serverVersion &&
|
other.serverVersion == serverVersion &&
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,9 @@ class ShareIntentAttachment {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant ShareIntentAttachment other) {
|
bool operator ==(covariant ShareIntentAttachment other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.path == path && other.type == type;
|
return other.path == path && other.type == type;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -418,7 +418,9 @@ class _PreparingStatusState extends ConsumerState {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startPollingIfNeeded() {
|
void _startPollingIfNeeded() {
|
||||||
if (_pollingTimer != null) return;
|
if (_pollingTimer != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_pollingTimer = Timer.periodic(const Duration(seconds: 3), (timer) async {
|
_pollingTimer = Timer.periodic(const Duration(seconds: 3), (timer) async {
|
||||||
final currentUser = ref.read(currentUserProvider);
|
final currentUser = ref.read(currentUserProvider);
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,9 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
||||||
final albumCount = albums.length;
|
final albumCount = albums.length;
|
||||||
// Filter albums based on search query
|
// Filter albums based on search query
|
||||||
final filteredAlbums = albums.where((album) {
|
final filteredAlbums = albums.where((album) {
|
||||||
if (_searchQuery.isEmpty) return true;
|
if (_searchQuery.isEmpty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return album.name.toLowerCase().contains(_searchQuery.toLowerCase());
|
return album.name.toLowerCase().contains(_searchQuery.toLowerCase());
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,9 @@ class _DriftUploadDetailPageState extends ConsumerState<DriftUploadDetailPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final item in uploadingItems) {
|
for (final item in uploadingItems) {
|
||||||
if (_taskSlotAssignments.containsKey(item.taskId)) continue;
|
if (_taskSlotAssignments.containsKey(item.taskId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _maxSlots; i++) {
|
for (int i = 0; i < _maxSlots; i++) {
|
||||||
if (slots[i] == null) {
|
if (slots[i] == null) {
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,9 @@ class HeaderSettingsPage extends HookConsumerWidget {
|
||||||
final key = header.key.trim();
|
final key = header.key.trim();
|
||||||
final value = header.value.trim();
|
final value = header.value.trim();
|
||||||
|
|
||||||
if (key.isEmpty || value.isEmpty) continue;
|
if (key.isEmpty || value.isEmpty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
headersMap[key] = value;
|
headersMap[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,9 @@ RecursiveFolder? _findFolderInStructure(RootFolder rootFolder, RecursiveFolder t
|
||||||
|
|
||||||
if (folder.subfolders.isNotEmpty) {
|
if (folder.subfolders.isNotEmpty) {
|
||||||
final found = _findFolderInStructure(folder, targetFolder);
|
final found = _findFolderInStructure(folder, targetFolder);
|
||||||
if (found != null) return found;
|
if (found != null) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -113,7 +115,9 @@ class FolderContent extends HookConsumerWidget {
|
||||||
|
|
||||||
// Initial asset fetch
|
// Initial asset fetch
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (folder == null) return;
|
if (folder == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ref.read(folderRenderListProvider(folder!).notifier).fetchAssets(sortOrder);
|
ref.read(folderRenderListProvider(folder!).notifier).fetchAssets(sortOrder);
|
||||||
return null;
|
return null;
|
||||||
}, [folder]);
|
}, [folder]);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ class SharedLinkPage extends HookConsumerWidget {
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
ref.read(sharedLinksStateProvider.notifier).fetchLinks();
|
ref.read(sharedLinksStateProvider.notifier).fetchLinks();
|
||||||
return () {
|
return () {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ref.invalidate(sharedLinksStateProvider);
|
ref.invalidate(sharedLinksStateProvider);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
||||||
|
|
@ -191,8 +191,12 @@ class _AssetPropertiesSectionState extends ConsumerState<_AssetPropertiesSection
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getAssetTypeTitle(BaseAsset asset) {
|
String _getAssetTypeTitle(BaseAsset asset) {
|
||||||
if (asset is LocalAsset) return 'Local Asset';
|
if (asset is LocalAsset) {
|
||||||
if (asset is RemoteAsset) return 'Remote Asset';
|
return 'Local Asset';
|
||||||
|
}
|
||||||
|
if (asset is RemoteAsset) {
|
||||||
|
return 'Remote Asset';
|
||||||
|
}
|
||||||
return 'Base Asset';
|
return 'Base Asset';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,9 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleSave() async {
|
Future<void> _handleSave() async {
|
||||||
if (formKey.currentState?.validate() != true) return;
|
if (formKey.currentState?.validate() != true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final newTitle = titleController.text.trim();
|
final newTitle = titleController.text.trim();
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,9 @@ class _DriftEditImagePageState extends ConsumerState<DriftEditImagePage> with Ti
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: !hasUnsavedEdits,
|
canPop: !hasUnsavedEdits,
|
||||||
onPopInvokedWithResult: (didPop, result) async {
|
onPopInvokedWithResult: (didPop, result) async {
|
||||||
if (didPop) return;
|
if (didPop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final shouldDiscard = await _showDiscardChangesDialog() ?? false;
|
final shouldDiscard = await _showDiscardChangesDialog() ?? false;
|
||||||
if (shouldDiscard && mounted) {
|
if (shouldDiscard && mounted) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,9 @@ class EditorState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other is EditorState &&
|
return other is EditorState &&
|
||||||
other.isApplyingEdits == isApplyingEdits &&
|
other.isApplyingEdits == isApplyingEdits &&
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,9 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleDone() async {
|
Future<void> _handleDone() async {
|
||||||
if (_isLoading) return;
|
if (_isLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
|
|
@ -72,7 +74,9 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
|
||||||
.read(uploadProfileImageProvider.notifier)
|
.read(uploadProfileImageProvider.notifier)
|
||||||
.upload(xFile, fileName: 'profile-picture.png');
|
.upload(xFile, fileName: 'profile-picture.png');
|
||||||
|
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
final profileImagePath = ref.read(uploadProfileImageProvider).profileImagePath;
|
final profileImagePath = ref.read(uploadProfileImageProvider).profileImagePath;
|
||||||
|
|
@ -102,7 +106,9 @@ class _ProfilePictureCropPageState extends ConsumerState<ProfilePictureCropPage>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ImmichToast.show(
|
ImmichToast.show(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
||||||
|
|
@ -708,7 +708,9 @@ class _SearchResultGrid extends ConsumerWidget {
|
||||||
bool _onScrollUpdateNotification(ScrollNotification notification) {
|
bool _onScrollUpdateNotification(ScrollNotification notification) {
|
||||||
final metrics = notification.metrics;
|
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 isBottomSheet = notification.context?.findAncestorWidgetOfExactType<DraggableScrollableSheet>() != null;
|
||||||
final remaining = metrics.maxScrollExtent - metrics.pixels;
|
final remaining = metrics.maxScrollExtent - metrics.pixels;
|
||||||
|
|
@ -735,7 +737,9 @@ class _SearchResultGrid extends ConsumerWidget {
|
||||||
|
|
||||||
final hasMore = ref.watch(paginatedSearchProvider.select((s) => s.nextPage != null));
|
final hasMore = ref.watch(paginatedSearchProvider.select((s) => s.nextPage != null));
|
||||||
|
|
||||||
if (hasMore) return null;
|
if (hasMore) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,9 @@ class PaginatedSearchNotifier extends StateNotifier<SearchState> {
|
||||||
Stream<int> get assetCount => _assetCountController.stream;
|
Stream<int> get assetCount => _assetCountController.stream;
|
||||||
|
|
||||||
Future<void> search(SearchFilter filter) async {
|
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);
|
state = SearchState(assets: state.assets, nextPage: state.nextPage, isLoading: true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,9 @@ class _AddActionButtonState extends ConsumerState<AddActionButton> {
|
||||||
|
|
||||||
List<Widget> _buildMenuChildren() {
|
List<Widget> _buildMenuChildren() {
|
||||||
final asset = ref.read(assetViewerProvider).currentAsset;
|
final asset = ref.read(assetViewerProvider).currentAsset;
|
||||||
if (asset == null) return [];
|
if (asset == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
final user = ref.read(currentUserProvider);
|
final user = ref.read(currentUserProvider);
|
||||||
final isOwner = asset is RemoteAsset && asset.ownerId == user?.id;
|
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)
|
// used to allow performing archive action from different sources (without duplicating code)
|
||||||
Future<void> performArchiveAction(BuildContext context, WidgetRef ref, {required ActionSource source}) async {
|
Future<void> performArchiveAction(BuildContext context, WidgetRef ref, {required ActionSource source}) async {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (source == ActionSource.viewer) {
|
if (source == ActionSource.viewer) {
|
||||||
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
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) {
|
if (source == ActionSource.viewer) {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,9 @@ class DeletePermanentActionButton extends ConsumerWidget {
|
||||||
builder: (context) => PermanentDeleteDialog(count: count),
|
builder: (context) => PermanentDeleteDialog(count: count),
|
||||||
) ??
|
) ??
|
||||||
false;
|
false;
|
||||||
if (!confirm) return;
|
if (!confirm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (source == ActionSource.viewer) {
|
if (source == ActionSource.viewer) {
|
||||||
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
||||||
|
|
|
||||||
|
|
@ -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)
|
// 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 {
|
Future<void> performMoveToLockFolderAction(BuildContext context, WidgetRef ref, {required ActionSource source}) async {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (source == ActionSource.viewer) {
|
if (source == ActionSource.viewer) {
|
||||||
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
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)
|
// used to allow performing unarchive action from different sources (without duplicating code)
|
||||||
Future<void> performUnArchiveAction(BuildContext context, WidgetRef ref, {required ActionSource source}) async {
|
Future<void> performUnArchiveAction(BuildContext context, WidgetRef ref, {required ActionSource source}) async {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (source == ActionSource.viewer) {
|
if (source == ActionSource.viewer) {
|
||||||
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
EventStream.shared.emit(const ViewerReloadAssetEvent());
|
||||||
|
|
|
||||||
|
|
@ -21,21 +21,27 @@ class AppearsInDetails extends ConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
if (!asset.hasRemote) return const SizedBox.shrink();
|
if (!asset.hasRemote) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
final remoteAssetId = switch (asset) {
|
final remoteAssetId = switch (asset) {
|
||||||
RemoteAsset(:final id) => id,
|
RemoteAsset(:final id) => id,
|
||||||
LocalAsset(:final remoteAssetId) => remoteAssetId,
|
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 userId = ref.watch(currentUserProvider)?.id;
|
||||||
final assetAlbums = ref.watch(albumsContainingAssetProvider(remoteAssetId));
|
final assetAlbums = ref.watch(albumsContainingAssetProvider(remoteAssetId));
|
||||||
|
|
||||||
return assetAlbums.when(
|
return assetAlbums.when(
|
||||||
data: (albums) {
|
data: (albums) {
|
||||||
if (albums.isEmpty) return const SizedBox.shrink();
|
if (albums.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
albums.sortBy((a) => a.name);
|
albums.sortBy((a) => a.name);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ class RatingDetails extends ConsumerWidget {
|
||||||
.watch(userMetadataPreferencesProvider)
|
.watch(userMetadataPreferencesProvider)
|
||||||
.maybeWhen(data: (prefs) => prefs?.ratingsEnabled ?? false, orElse: () => false);
|
.maybeWhen(data: (prefs) => prefs?.ratingsEnabled ?? false, orElse: () => false);
|
||||||
|
|
||||||
if (!isRatingEnabled) return const SizedBox.shrink();
|
if (!isRatingEnabled) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 16.0, top: 16.0),
|
padding: const EdgeInsets.only(left: 16.0, top: 16.0),
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,9 @@ class TechnicalDetails extends ConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
static String? _getCameraInfoTitle(ExifInfo? exifInfo) {
|
static String? _getCameraInfoTitle(ExifInfo? exifInfo) {
|
||||||
if (exifInfo == null) return null;
|
if (exifInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return switch ((exifInfo.make, exifInfo.model)) {
|
return switch ((exifInfo.make, exifInfo.model)) {
|
||||||
(null, null) => null,
|
(null, null) => null,
|
||||||
(String make, null) => make,
|
(String make, null) => make,
|
||||||
|
|
@ -121,17 +123,23 @@ class TechnicalDetails extends ConsumerWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
static String? _getCameraInfoSubtitle(ExifInfo? exifInfo) {
|
static String? _getCameraInfoSubtitle(ExifInfo? exifInfo) {
|
||||||
if (exifInfo == null) return null;
|
if (exifInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final exposureTime = exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null;
|
final exposureTime = exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null;
|
||||||
final iso = exifInfo.iso != null ? 'ISO ${exifInfo.iso}' : null;
|
final iso = exifInfo.iso != null ? 'ISO ${exifInfo.iso}' : null;
|
||||||
return [exposureTime, iso].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator);
|
return [exposureTime, iso].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String? _getLensInfoSubtitle(ExifInfo? exifInfo) {
|
static String? _getLensInfoSubtitle(ExifInfo? exifInfo) {
|
||||||
if (exifInfo == null) return null;
|
if (exifInfo == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final fNumber = exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null;
|
final fNumber = exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null;
|
||||||
final focalLength = exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : 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);
|
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_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.provider.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.widget.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/asset_viewer/video_viewer.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.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/presentation/widgets/images/thumbnail.widget.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.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/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/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/common/immich_loading_indicator.dart';
|
||||||
import 'package:immich_mobile/widgets/photo_view/photo_view.dart';
|
import 'package:immich_mobile/widgets/photo_view/photo_view.dart';
|
||||||
|
|
||||||
|
|
@ -62,7 +62,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||||
super.initState();
|
super.initState();
|
||||||
_eventSubscription = EventStream.shared.listen(_onEvent);
|
_eventSubscription = EventStream.shared.listen(_onEvent);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (!mounted || !_scrollController.hasClients) return;
|
if (!mounted || !_scrollController.hasClients) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_scrollController.snapPosition.snapOffset = _snapOffset;
|
_scrollController.snapPosition.snapOffset = _snapOffset;
|
||||||
if (_showingDetails && _snapOffset > 0) {
|
if (_showingDetails && _snapOffset > 0) {
|
||||||
_scrollController.jumpTo(_snapOffset);
|
_scrollController.jumpTo(_snapOffset);
|
||||||
|
|
@ -87,7 +89,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showDetails() {
|
void _showDetails() {
|
||||||
if (!_scrollController.hasClients || _snapOffset <= 0) return;
|
if (!_scrollController.hasClients || _snapOffset <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_viewer.setShowingDetails(true);
|
_viewer.setShowingDetails(true);
|
||||||
_scrollController.animateTo(_snapOffset, duration: Durations.medium2, curve: Curves.easeOutCubic);
|
_scrollController.animateTo(_snapOffset, duration: Durations.medium2, curve: Curves.easeOutCubic);
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +132,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateDrag(DragUpdateDetails details) {
|
void _updateDrag(DragUpdateDetails details) {
|
||||||
if (_dragStart == null) return;
|
if (_dragStart == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_dragIntent == _DragIntent.none) {
|
if (_dragIntent == _DragIntent.none) {
|
||||||
_dragIntent = switch ((details.globalPosition - _dragStart!.globalPosition).dy) {
|
_dragIntent = switch ((details.globalPosition - _dragStart!.globalPosition).dy) {
|
||||||
|
|
@ -141,7 +147,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||||
switch (_dragIntent) {
|
switch (_dragIntent) {
|
||||||
case _DragIntent.none:
|
case _DragIntent.none:
|
||||||
case _DragIntent.scroll:
|
case _DragIntent.scroll:
|
||||||
if (_drag == null) _startProxyDrag();
|
if (_drag == null) {
|
||||||
|
_startProxyDrag();
|
||||||
|
}
|
||||||
_drag?.update(details);
|
_drag?.update(details);
|
||||||
|
|
||||||
_syncShowingDetails();
|
_syncShowingDetails();
|
||||||
|
|
@ -151,7 +159,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _endDrag(DragEndDetails details) {
|
void _endDrag(DragEndDetails details) {
|
||||||
if (_dragStart == null) return;
|
if (_dragStart == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final start = _dragStart;
|
final start = _dragStart;
|
||||||
_dragStart = null;
|
_dragStart = null;
|
||||||
|
|
@ -188,7 +198,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||||
PhotoViewControllerBase controller,
|
PhotoViewControllerBase controller,
|
||||||
PhotoViewScaleStateController scaleStateController,
|
PhotoViewScaleStateController scaleStateController,
|
||||||
) {
|
) {
|
||||||
if (!_showingDetails && _isZoomed) return;
|
if (!_showingDetails && _isZoomed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_beginDrag(details);
|
_beginDrag(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,7 +227,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTapUp(BuildContext context, TapUpDetails details, PhotoViewControllerValue controllerValue) {
|
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);
|
final tapToNavigate = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.tapToNavigate);
|
||||||
if (!tapToNavigate) {
|
if (!tapToNavigate) {
|
||||||
|
|
@ -247,31 +261,43 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
||||||
_viewer.setZoomed(_isZoomed);
|
_viewer.setZoomed(_isZoomed);
|
||||||
|
|
||||||
if (scaleState != PhotoViewScaleState.initial) {
|
if (scaleState != PhotoViewScaleState.initial) {
|
||||||
if (_dragStart == null) _viewer.setControls(false);
|
if (_dragStart == null) {
|
||||||
|
_viewer.setControls(false);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_showingDetails) _viewer.setControls(true);
|
if (!_showingDetails) {
|
||||||
|
_viewer.setControls(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _listenForScaleBoundaries(PhotoViewControllerBase? controller) {
|
void _listenForScaleBoundaries(PhotoViewControllerBase? controller) {
|
||||||
_scaleBoundarySub?.cancel();
|
_scaleBoundarySub?.cancel();
|
||||||
_scaleBoundarySub = null;
|
_scaleBoundarySub = null;
|
||||||
if (controller == null || controller.scaleBoundaries != null) return;
|
if (controller == null || controller.scaleBoundaries != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_scaleBoundarySub = controller.outputStateStream.listen((_) {
|
_scaleBoundarySub = controller.outputStateStream.listen((_) {
|
||||||
if (controller.scaleBoundaries != null) {
|
if (controller.scaleBoundaries != null) {
|
||||||
_scaleBoundarySub?.cancel();
|
_scaleBoundarySub?.cancel();
|
||||||
_scaleBoundarySub = null;
|
_scaleBoundarySub = null;
|
||||||
if (mounted) setState(() {});
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
double _getImageHeight(double maxWidth, double maxHeight, BaseAsset? asset) {
|
double _getImageHeight(double maxWidth, double maxHeight, BaseAsset? asset) {
|
||||||
final sb = _viewController?.scaleBoundaries;
|
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!;
|
final r = asset.width! / asset.height!;
|
||||||
return math.min(maxWidth / r, maxHeight);
|
return math.min(maxWidth / r, maxHeight);
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,16 @@ class AssetPreloader {
|
||||||
unawaited(timelineService.preloadAssets(index));
|
unawaited(timelineService.preloadAssets(index));
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
_timer = Timer(Durations.medium4, () async {
|
_timer = Timer(Durations.medium4, () async {
|
||||||
if (!mounted()) return;
|
if (!mounted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final (prev, next) = await (
|
final (prev, next) = await (
|
||||||
timelineService.getAssetAsync(index - 1),
|
timelineService.getAssetAsync(index - 1),
|
||||||
timelineService.getAssetAsync(index + 1),
|
timelineService.getAssetAsync(index + 1),
|
||||||
).wait;
|
).wait;
|
||||||
if (!mounted()) return;
|
if (!mounted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_prevStream?.removeListener(_dummyListener);
|
_prevStream?.removeListener(_dummyListener);
|
||||||
_nextStream?.removeListener(_dummyListener);
|
_nextStream?.removeListener(_dummyListener);
|
||||||
_prevStream = prev != null ? _resolveImage(prev, size) : null;
|
_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_page.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_preloader.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/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_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/cast.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
|
@ -67,7 +67,9 @@ class AssetViewer extends ConsumerStatefulWidget {
|
||||||
ref.read(assetViewerProvider.notifier).reset();
|
ref.read(assetViewerProvider.notifier).reset();
|
||||||
|
|
||||||
// Hide controls by default for videos
|
// 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);
|
_setAsset(ref, asset);
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +92,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||||
|
|
||||||
void _onTapNavigate(int direction) {
|
void _onTapNavigate(int direction) {
|
||||||
final page = _pageController.page?.toInt();
|
final page = _pageController.page?.toInt();
|
||||||
if (page == null) return;
|
if (page == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final target = page + direction;
|
final target = page + direction;
|
||||||
final maxPage = _totalAssets - 1;
|
final maxPage = _totalAssets - 1;
|
||||||
if (target >= 0 && target <= maxPage) {
|
if (target >= 0 && target <= maxPage) {
|
||||||
|
|
@ -105,7 +109,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||||
|
|
||||||
final asset = ref.read(assetViewerProvider).currentAsset;
|
final asset = ref.read(assetViewerProvider).currentAsset;
|
||||||
assert(asset != null, "Current asset should not be null when opening the AssetViewer");
|
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);
|
_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
|
// playing, and preventing the video on the next page from becoming ready
|
||||||
// unnecessarily.
|
// unnecessarily.
|
||||||
bool _onScrollEnd(ScrollEndNotification notification) {
|
bool _onScrollEnd(ScrollEndNotification notification) {
|
||||||
if (notification.depth != 0) return false;
|
if (notification.depth != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
final page = _pageController.page?.round();
|
final page = _pageController.page?.round();
|
||||||
if (page != null && page != _currentPage) {
|
if (page != null && page != _currentPage) {
|
||||||
|
|
@ -155,7 +163,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||||
_currentPage = index;
|
_currentPage = index;
|
||||||
|
|
||||||
final asset = await ref.read(timelineServiceProvider).getAssetAsync(index);
|
final asset = await ref.read(timelineServiceProvider).getAssetAsync(index);
|
||||||
if (asset == null) return;
|
if (asset == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AssetViewer._setAsset(ref, asset);
|
AssetViewer._setAsset(ref, asset);
|
||||||
_preloader.preload(index, context.sizeData);
|
_preloader.preload(index, context.sizeData);
|
||||||
|
|
@ -165,9 +175,13 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleCasting() {
|
void _handleCasting() {
|
||||||
if (!ref.read(castProvider).isCasting) return;
|
if (!ref.read(castProvider).isCasting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final asset = ref.read(assetViewerProvider).currentAsset;
|
final asset = ref.read(assetViewerProvider).currentAsset;
|
||||||
if (asset == null) return;
|
if (asset == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (asset is RemoteAsset) {
|
if (asset is RemoteAsset) {
|
||||||
context.scaffoldMessenger.hideCurrentSnackBar();
|
context.scaffoldMessenger.hideCurrentSnackBar();
|
||||||
|
|
@ -199,7 +213,9 @@ class _AssetViewerState extends ConsumerState<AssetViewer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onViewerReloadEvent() {
|
void _onViewerReloadEvent() {
|
||||||
if (_totalAssets <= 1) return;
|
if (_totalAssets <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final index = _pageController.page?.round() ?? 0;
|
final index = _pageController.page?.round() ?? 0;
|
||||||
final target = index >= _totalAssets - 1 ? index - 1 : index + 1;
|
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
|
// Listen for casting changes and send initial asset to the cast provider
|
||||||
ref.listen(castProvider.select((value) => value.isCasting), (_, isCasting) {
|
ref.listen(castProvider.select((value) => value.isCasting), (_, isCasting) {
|
||||||
if (!isCasting) return;
|
if (!isCasting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_handleCasting();
|
_handleCasting();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,9 @@ class _RatingBarState extends State<RatingBar> {
|
||||||
final totalWidth = widget.itemCount * widget.itemSize + (widget.itemCount - 1) * widget.starPadding;
|
final totalWidth = widget.itemCount * widget.itemSize + (widget.itemCount - 1) * widget.starPadding;
|
||||||
double dx = localPosition.dx;
|
double dx = localPosition.dx;
|
||||||
|
|
||||||
if (isRTL) dx = totalWidth - dx;
|
if (isRTL) {
|
||||||
|
dx = totalWidth - dx;
|
||||||
|
}
|
||||||
|
|
||||||
double newRating;
|
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/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.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/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/is_motion_video_playing.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/video_player_provider.dart';
|
||||||
import 'package:immich_mobile/providers/cast.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) {
|
void didUpdateWidget(NativeVideoViewer oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
if (widget.isCurrent == oldWidget.isCurrent || _controller == null) return;
|
if (widget.isCurrent == oldWidget.isCurrent || _controller == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!widget.isCurrent) {
|
if (!widget.isCurrent) {
|
||||||
_loadTimer?.cancel();
|
_loadTimer?.cancel();
|
||||||
|
|
@ -85,25 +87,35 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) async {
|
void didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case AppLifecycleState.resumed:
|
case AppLifecycleState.resumed:
|
||||||
if (_shouldPlayOnForeground) await _notifier.play();
|
if (_shouldPlayOnForeground) {
|
||||||
|
await _notifier.play();
|
||||||
|
}
|
||||||
case AppLifecycleState.paused:
|
case AppLifecycleState.paused:
|
||||||
_shouldPlayOnForeground = await _controller?.isPlaying() ?? true;
|
_shouldPlayOnForeground = await _controller?.isPlaying() ?? true;
|
||||||
if (_shouldPlayOnForeground) await _notifier.pause();
|
if (_shouldPlayOnForeground) {
|
||||||
|
await _notifier.pause();
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<VideoSource?> _createSource() async {
|
Future<VideoSource?> _createSource() async {
|
||||||
if (!mounted) return null;
|
if (!mounted) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final videoAsset = await ref.read(assetServiceProvider).getAsset(widget.asset) ?? widget.asset;
|
final videoAsset = await ref.read(assetServiceProvider).getAsset(widget.asset) ?? widget.asset;
|
||||||
if (!mounted) return null;
|
if (!mounted) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (videoAsset.hasLocal && videoAsset.livePhotoVideoId == null) {
|
if (videoAsset.hasLocal && videoAsset.livePhotoVideoId == null) {
|
||||||
final id = videoAsset is LocalAsset ? videoAsset.id : (videoAsset as RemoteAsset).localId!;
|
final id = videoAsset is LocalAsset ? videoAsset.id : (videoAsset as RemoteAsset).localId!;
|
||||||
final file = await StorageRepository().getFileForAsset(id);
|
final file = await StorageRepository().getFileForAsset(id);
|
||||||
if (!mounted) return null;
|
if (!mounted) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
throw Exception('No file found for the video');
|
throw Exception('No file found for the video');
|
||||||
|
|
@ -134,25 +146,35 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onPlaybackReady() async {
|
void _onPlaybackReady() async {
|
||||||
if (!mounted || !widget.isCurrent) return;
|
if (!mounted || !widget.isCurrent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_notifier.onNativePlaybackReady();
|
_notifier.onNativePlaybackReady();
|
||||||
|
|
||||||
// onPlaybackReady may be called multiple times, usually when more data
|
// 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
|
// loads. If this is not the first time that the player has become ready, we
|
||||||
// should not autoplay.
|
// should not autoplay.
|
||||||
if (_isVideoReady) return;
|
if (_isVideoReady) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setState(() => _isVideoReady = true);
|
setState(() => _isVideoReady = true);
|
||||||
|
|
||||||
if (ref.read(assetViewerProvider).showingDetails) return;
|
if (ref.read(assetViewerProvider).showingDetails) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final autoPlayVideo = AppSetting.get(Setting.autoPlayVideo);
|
final autoPlayVideo = AppSetting.get(Setting.autoPlayVideo);
|
||||||
if (autoPlayVideo || widget.asset.isMotionPhoto) await _notifier.play();
|
if (autoPlayVideo || widget.asset.isMotionPhoto) {
|
||||||
|
await _notifier.play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onPlaybackEnded() {
|
void _onPlaybackEnded() {
|
||||||
if (!mounted) return;
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_notifier.onNativePlaybackEnded();
|
_notifier.onNativePlaybackEnded();
|
||||||
|
|
||||||
|
|
@ -162,12 +184,16 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onPlaybackPositionChanged() {
|
void _onPlaybackPositionChanged() {
|
||||||
if (!mounted) return;
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_notifier.onNativePositionChanged();
|
_notifier.onNativePositionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onPlaybackStatusChanged() {
|
void _onPlaybackStatusChanged() {
|
||||||
if (!mounted) return;
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_notifier.onNativeStatusChanged();
|
_notifier.onNativeStatusChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,10 +206,14 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||||
|
|
||||||
void _loadVideo() async {
|
void _loadVideo() async {
|
||||||
final nc = _controller;
|
final nc = _controller;
|
||||||
if (nc == null || nc.videoSource != null || !mounted) return;
|
if (nc == null || nc.videoSource != null || !mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final source = await _videoSource;
|
final source = await _videoSource;
|
||||||
if (source == null || !mounted) return;
|
if (source == null || !mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await _notifier.load(source);
|
await _notifier.load(source);
|
||||||
final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo);
|
final loopVideo = ref.read(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.loopVideo);
|
||||||
|
|
@ -192,7 +222,9 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initController(NativeVideoPlayerController nc) {
|
void _initController(NativeVideoPlayerController nc) {
|
||||||
if (_controller != null || !mounted) return;
|
if (_controller != null || !mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_notifier.attachController(nc);
|
_notifier.attachController(nc);
|
||||||
|
|
||||||
|
|
@ -203,7 +235,9 @@ class _NativeVideoViewerState extends ConsumerState<NativeVideoViewer> with Widg
|
||||||
|
|
||||||
_controller = nc;
|
_controller = nc;
|
||||||
|
|
||||||
if (widget.isCurrent) _loadVideo();
|
if (widget.isCurrent) {
|
||||||
|
_loadVideo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,9 @@ class LocalThumbProvider extends CancellableImageProvider<LocalThumbProvider>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (other is LocalThumbProvider) {
|
if (other is LocalThumbProvider) {
|
||||||
return id == other.id;
|
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 originalRequest = request = LocalImageRequest(localId: key.id, size: Size.zero, assetType: key.assetType);
|
||||||
final codec = await loadCodecRequest(originalRequest, isFinal: true);
|
final codec = await loadCodecRequest(originalRequest, isFinal: true);
|
||||||
if (codec == null) {
|
if (codec == null) {
|
||||||
if (isCancelled) return;
|
if (isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
throw StateError('Failed to load animated codec for local asset ${key.id}');
|
throw StateError('Failed to load animated codec for local asset ${key.id}');
|
||||||
}
|
}
|
||||||
yield codec;
|
yield codec;
|
||||||
|
|
@ -156,7 +160,9 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (other is LocalFullImageProvider) {
|
if (other is LocalFullImageProvider) {
|
||||||
return id == other.id && size == other.size && isAnimated == other.isAnimated;
|
return id == other.id && size == other.size && isAnimated == other.isAnimated;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,9 @@ class RemoteImageProvider extends CancellableImageProvider<RemoteImageProvider>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (other is RemoteImageProvider) {
|
if (other is RemoteImageProvider) {
|
||||||
return url == other.url && edited == other.edited;
|
return url == other.url && edited == other.edited;
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +177,9 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (other is RemoteFullImageProvider) {
|
if (other is RemoteFullImageProvider) {
|
||||||
return assetId == other.assetId &&
|
return assetId == other.assetId &&
|
||||||
thumbhash == other.thumbhash &&
|
thumbhash == other.thumbhash &&
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ class ThumbHashProvider extends CancellableImageProvider<ThumbHashProvider>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (other is ThumbHashProvider) {
|
if (other is ThumbHashProvider) {
|
||||||
return thumbHash == other.thumbHash;
|
return thumbHash == other.thumbHash;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,9 @@ class _ThumbnailState extends State<Thumbnail> with SingleTickerProviderStateMix
|
||||||
void _loadFromThumbhashProvider() {
|
void _loadFromThumbhashProvider() {
|
||||||
_stopListeningToThumbhashStream();
|
_stopListeningToThumbhashStream();
|
||||||
final thumbhashProvider = widget.thumbhashProvider;
|
final thumbhashProvider = widget.thumbhashProvider;
|
||||||
if (thumbhashProvider == null || _providerImage != null) return;
|
if (thumbhashProvider == null || _providerImage != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final thumbhashStream = _thumbhashStream = thumbhashProvider.resolve(ImageConfiguration.empty);
|
final thumbhashStream = _thumbhashStream = thumbhashProvider.resolve(ImageConfiguration.empty);
|
||||||
final thumbhashStreamListener = _thumbhashStreamListener = ImageStreamListener(
|
final thumbhashStreamListener = _thumbhashStreamListener = ImageStreamListener(
|
||||||
|
|
@ -108,7 +110,9 @@ class _ThumbnailState extends State<Thumbnail> with SingleTickerProviderStateMix
|
||||||
void _loadFromImageProvider() {
|
void _loadFromImageProvider() {
|
||||||
_stopListeningToImageStream();
|
_stopListeningToImageStream();
|
||||||
final imageProvider = widget.imageProvider;
|
final imageProvider = widget.imageProvider;
|
||||||
if (imageProvider == null) return;
|
if (imageProvider == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final imageStream = _imageStream = imageProvider.resolve(ImageConfiguration.empty);
|
final imageStream = _imageStream = imageProvider.resolve(ImageConfiguration.empty);
|
||||||
final imageStreamListener = _imageStreamListener = ImageStreamListener(
|
final imageStreamListener = _imageStreamListener = ImageStreamListener(
|
||||||
|
|
@ -201,7 +205,9 @@ class _ThumbnailState extends State<Thumbnail> with SingleTickerProviderStateMix
|
||||||
|
|
||||||
bool _isVisible() {
|
bool _isVisible() {
|
||||||
final renderObject = context.findRenderObject() as RenderBox?;
|
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 topLeft = renderObject.localToGlobal(Offset.zero);
|
||||||
final bottomRight = renderObject.localToGlobal(Offset(renderObject.size.width, renderObject.size.height));
|
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(
|
return Center(
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,9 @@ class QuickDatePicker extends HookWidget {
|
||||||
menuStyle: MenuStyle(maximumSize: WidgetStateProperty.all(Size(size.width, size.height * 0.5))),
|
menuStyle: MenuStyle(maximumSize: WidgetStateProperty.all(Size(size.width, size.height * 0.5))),
|
||||||
dropdownMenuEntries: _recentYears.map((e) => DropdownMenuEntry(value: e, label: e.toString())).toList(),
|
dropdownMenuEntries: _recentYears.map((e) => DropdownMenuEntry(value: e, label: e.toString())).toList(),
|
||||||
onSelected: (year) {
|
onSelected: (year) {
|
||||||
if (year == null) return;
|
if (year == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
onSelect(YearFilter(year));
|
onSelect(YearFilter(year));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -179,7 +181,9 @@ class QuickDatePicker extends HookWidget {
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: RadioGroup(
|
child: RadioGroup(
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value == null) return;
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final _ = switch (value) {
|
final _ = switch (value) {
|
||||||
_QuickPickerType.custom => onRequestPicker(),
|
_QuickPickerType.custom => onRequestPicker(),
|
||||||
_QuickPickerType.last1Month => onSelect(RecentMonthRangeFilter(1)),
|
_QuickPickerType.last1Month => onSelect(RecentMonthRangeFilter(1)),
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,9 @@ class RenderFixedRow extends RenderBox
|
||||||
double _height;
|
double _height;
|
||||||
|
|
||||||
set height(double value) {
|
set height(double value) {
|
||||||
if (_height == value) return;
|
if (_height == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_height = value;
|
_height = value;
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +88,9 @@ class RenderFixedRow extends RenderBox
|
||||||
List<double> _widths;
|
List<double> _widths;
|
||||||
|
|
||||||
set widths(List<double> value) {
|
set widths(List<double> value) {
|
||||||
if (listEquals(_widths, value)) return;
|
if (listEquals(_widths, value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_widths = value;
|
_widths = value;
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +99,9 @@ class RenderFixedRow extends RenderBox
|
||||||
double _spacing;
|
double _spacing;
|
||||||
|
|
||||||
set spacing(double value) {
|
set spacing(double value) {
|
||||||
if (_spacing == value) return;
|
if (_spacing == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_spacing = value;
|
_spacing = value;
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +110,9 @@ class RenderFixedRow extends RenderBox
|
||||||
TextDirection _textDirection;
|
TextDirection _textDirection;
|
||||||
|
|
||||||
set textDirection(TextDirection value) {
|
set textDirection(TextDirection value) {
|
||||||
if (_textDirection == value) return;
|
if (_textDirection == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_textDirection = value;
|
_textDirection = value;
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,14 +53,18 @@ class FixedSegment extends Segment {
|
||||||
@override
|
@override
|
||||||
int getMinChildIndexForScrollOffset(double scrollOffset) {
|
int getMinChildIndexForScrollOffset(double scrollOffset) {
|
||||||
final adjustedOffset = scrollOffset - gridOffset;
|
final adjustedOffset = scrollOffset - gridOffset;
|
||||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) return firstIndex;
|
if (!adjustedOffset.isFinite || adjustedOffset < 0) {
|
||||||
|
return firstIndex;
|
||||||
|
}
|
||||||
return gridIndex + (adjustedOffset / mainAxisExtend).floor();
|
return gridIndex + (adjustedOffset / mainAxisExtend).floor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int getMaxChildIndexForScrollOffset(double scrollOffset) {
|
int getMaxChildIndexForScrollOffset(double scrollOffset) {
|
||||||
final adjustedOffset = scrollOffset - gridOffset;
|
final adjustedOffset = scrollOffset - gridOffset;
|
||||||
if (!adjustedOffset.isFinite || adjustedOffset < 0) return firstIndex;
|
if (!adjustedOffset.isFinite || adjustedOffset < 0) {
|
||||||
|
return firstIndex;
|
||||||
|
}
|
||||||
return gridIndex + (adjustedOffset / mainAxisExtend).ceil() - 1;
|
return gridIndex + (adjustedOffset / mainAxisExtend).ceil() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,8 +166,12 @@ class _FixedSegmentRow extends ConsumerWidget {
|
||||||
// 0.5: width < mean - threshold
|
// 0.5: width < mean - threshold
|
||||||
// 1.5: width > mean + threshold
|
// 1.5: width > mean + threshold
|
||||||
final arConfiguration = aspectRatios.map((e) {
|
final arConfiguration = aspectRatios.map((e) {
|
||||||
if (e - meanAspectRatio > 0.3) return 1.5;
|
if (e - meanAspectRatio > 0.3) {
|
||||||
if (e - meanAspectRatio < -0.3) return 0.5;
|
return 1.5;
|
||||||
|
}
|
||||||
|
if (e - meanAspectRatio < -0.3) {
|
||||||
|
return 0.5;
|
||||||
|
}
|
||||||
return 1.0;
|
return 1.0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,9 @@ class ScrubberState extends ConsumerState<Scrubber> with TickerProviderStateMixi
|
||||||
late ScrollController _scrollController;
|
late ScrollController _scrollController;
|
||||||
|
|
||||||
double get _currentOffset {
|
double get _currentOffset {
|
||||||
if (_scrollController.hasClients != true) return 0.0;
|
if (_scrollController.hasClients != true) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
return _scrollController.offset * _scrubberHeight / _scrollController.position.maxScrollExtent;
|
return _scrollController.offset * _scrubberHeight / _scrollController.position.maxScrollExtent;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,9 @@ abstract class Segment {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other is Segment &&
|
return other is Segment &&
|
||||||
other.firstIndex == firstIndex &&
|
other.firstIndex == firstIndex &&
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _restoreAssetPosition(_) {
|
void _restoreAssetPosition(_) {
|
||||||
if (_restoreAssetIndex == null) return;
|
if (_restoreAssetIndex == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final asyncSegments = ref.read(timelineSegmentProvider);
|
final asyncSegments = ref.read(timelineSegmentProvider);
|
||||||
asyncSegments.whenData((segments) {
|
asyncSegments.whenData((segments) {
|
||||||
|
|
@ -329,7 +331,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragAssetEnter(TimelineAssetIndex index) {
|
void _handleDragAssetEnter(TimelineAssetIndex index) {
|
||||||
if (_dragAnchorIndex == null || !_dragging) return;
|
if (_dragAnchorIndex == null || !_dragging) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final timelineService = ref.read(timelineServiceProvider);
|
final timelineService = ref.read(timelineServiceProvider);
|
||||||
final dragAnchorIndex = _dragAnchorIndex!;
|
final dragAnchorIndex = _dragAnchorIndex!;
|
||||||
|
|
@ -399,7 +403,9 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
|
||||||
segments: segments,
|
segments: segments,
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(ctx, index) {
|
(ctx, index) {
|
||||||
if (index >= childCount) return null;
|
if (index >= childCount) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final segment = segments.findByIndex(index);
|
final segment = segments.findByIndex(index);
|
||||||
return segment?.builder(ctx, index) ?? const SizedBox.shrink();
|
return segment?.builder(ctx, index) ?? const SizedBox.shrink();
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -81,11 +81,15 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||||
|
|
||||||
TimelineAssetIndex? _getValueKeyAtPosition(Offset position) {
|
TimelineAssetIndex? _getValueKeyAtPosition(Offset position) {
|
||||||
final box = context.findAncestorRenderObjectOfType<RenderBox>();
|
final box = context.findAncestorRenderObjectOfType<RenderBox>();
|
||||||
if (box == null) return null;
|
if (box == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final hitTestResult = BoxHitTestResult();
|
final hitTestResult = BoxHitTestResult();
|
||||||
final local = box.globalToLocal(position);
|
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
|
return (hitTestResult.path.firstWhereOrNull((hit) => hit.target is _TimelineAssetIndexProxy)?.target
|
||||||
as _TimelineAssetIndexProxy?)
|
as _TimelineAssetIndexProxy?)
|
||||||
|
|
@ -103,7 +107,9 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||||
|
|
||||||
final initialHit = _getValueKeyAtPosition(event.globalPosition);
|
final initialHit = _getValueKeyAtPosition(event.globalPosition);
|
||||||
anchorAsset = initialHit;
|
anchorAsset = initialHit;
|
||||||
if (initialHit == null) return;
|
if (initialHit == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (anchorAsset != null) {
|
if (anchorAsset != null) {
|
||||||
widget.onStart?.call(anchorAsset!);
|
widget.onStart?.call(anchorAsset!);
|
||||||
|
|
@ -117,8 +123,12 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onLongPressMove(LongPressMoveUpdateDetails event) {
|
void _onLongPressMove(LongPressMoveUpdateDetails event) {
|
||||||
if (anchorAsset == null) return;
|
if (anchorAsset == null) {
|
||||||
if (topScrollOffset == null || bottomScrollOffset == null) return;
|
return;
|
||||||
|
}
|
||||||
|
if (topScrollOffset == null || bottomScrollOffset == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final currentDy = event.localPosition.dy;
|
final currentDy = event.localPosition.dy;
|
||||||
|
|
||||||
|
|
@ -138,7 +148,9 @@ class _TimelineDragRegionState extends State<TimelineDragRegion> {
|
||||||
}
|
}
|
||||||
|
|
||||||
final currentlyTouchingAsset = _getValueKeyAtPosition(event.globalPosition);
|
final currentlyTouchingAsset = _getValueKeyAtPosition(event.globalPosition);
|
||||||
if (currentlyTouchingAsset == null) return;
|
if (currentlyTouchingAsset == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (assetUnderPointer != currentlyTouchingAsset) {
|
if (assetUnderPointer != currentlyTouchingAsset) {
|
||||||
if (!scrollNotified) {
|
if (!scrollNotified) {
|
||||||
|
|
@ -202,7 +214,9 @@ class TimelineAssetIndex {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant TimelineAssetIndex other) {
|
bool operator ==(covariant TimelineAssetIndex other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.assetIndex == assetIndex && other.segmentIndex == segmentIndex;
|
return other.assetIndex == assetIndex && other.segmentIndex == segmentIndex;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,9 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
||||||
|
|
||||||
Future<void> _performResume() async {
|
Future<void> _performResume() async {
|
||||||
// no need to resume because app was never really paused
|
// no need to resume because app was never really paused
|
||||||
if (!_wasPaused) return;
|
if (!_wasPaused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_wasPaused = false;
|
_wasPaused = false;
|
||||||
|
|
||||||
final isAuthenticated = _ref.read(authProvider).isAuthenticated;
|
final isAuthenticated = _ref.read(authProvider).isAuthenticated;
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,12 @@ class AssetViewerState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
if (other.runtimeType != runtimeType) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (other.runtimeType != runtimeType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return other is AssetViewerState &&
|
return other is AssetViewerState &&
|
||||||
other.backgroundOpacity == backgroundOpacity &&
|
other.backgroundOpacity == backgroundOpacity &&
|
||||||
other.showingDetails == showingDetails &&
|
other.showingDetails == showingDetails &&
|
||||||
|
|
@ -83,7 +87,9 @@ class AssetViewerStateNotifier extends Notifier<AssetViewerState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAsset(BaseAsset asset) {
|
void setAsset(BaseAsset asset) {
|
||||||
if (asset == state.currentAsset) return;
|
if (asset == state.currentAsset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
state = state.copyWith(currentAsset: asset, stackIndex: 0);
|
state = state.copyWith(currentAsset: asset, stackIndex: 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,6 +144,8 @@ final assetViewerProvider = NotifierProvider<AssetViewerStateNotifier, AssetView
|
||||||
final _watchedCurrentAssetProvider = StreamProvider<BaseAsset?>((ref) {
|
final _watchedCurrentAssetProvider = StreamProvider<BaseAsset?>((ref) {
|
||||||
ref.watch(assetViewerProvider.select((s) => s.currentAsset?.heroTag));
|
ref.watch(assetViewerProvider.select((s) => s.currentAsset?.heroTag));
|
||||||
final asset = ref.read(assetViewerProvider).currentAsset;
|
final asset = ref.read(assetViewerProvider).currentAsset;
|
||||||
if (asset == null) return const Stream.empty();
|
if (asset == null) {
|
||||||
|
return const Stream.empty();
|
||||||
|
}
|
||||||
return ref.read(assetServiceProvider).watchAsset(asset);
|
return ref.read(assetServiceProvider).watchAsset(asset);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,9 @@ class VideoPlayerNotifier extends StateNotifier<VideoPlayerState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pause() async {
|
Future<void> pause() async {
|
||||||
if (_controller == null) return;
|
if (_controller == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_bufferingTimer?.cancel();
|
_bufferingTimer?.cancel();
|
||||||
|
|
||||||
|
|
@ -83,7 +85,9 @@ class VideoPlayerNotifier extends StateNotifier<VideoPlayerState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> play() async {
|
Future<void> play() async {
|
||||||
if (_controller == null) return;
|
if (_controller == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await _flushSeek();
|
await _flushSeek();
|
||||||
|
|
@ -97,18 +101,24 @@ class VideoPlayerNotifier extends StateNotifier<VideoPlayerState> {
|
||||||
|
|
||||||
Future<void> _flushSeek() async {
|
Future<void> _flushSeek() async {
|
||||||
final timer = _seekTimer;
|
final timer = _seekTimer;
|
||||||
if (timer == null || !timer.isActive) return;
|
if (timer == null || !timer.isActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
await _controller?.seekTo(state.position.inMilliseconds);
|
await _controller?.seekTo(state.position.inMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void seekTo(Duration position) {
|
void seekTo(Duration position) {
|
||||||
if (_controller == null || state.position == position) return;
|
if (_controller == null || state.position == position) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
state = state.copyWith(position: position);
|
state = state.copyWith(position: position);
|
||||||
|
|
||||||
if (_seekTimer?.isActive ?? false) return;
|
if (_seekTimer?.isActive ?? false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_seekTimer = Timer(const Duration(milliseconds: 150), () {
|
_seekTimer = Timer(const Duration(milliseconds: 150), () {
|
||||||
_controller?.seekTo(state.position.inMilliseconds);
|
_controller?.seekTo(state.position.inMilliseconds);
|
||||||
|
|
@ -130,7 +140,9 @@ class VideoPlayerNotifier extends StateNotifier<VideoPlayerState> {
|
||||||
|
|
||||||
/// Pauses playback and preserves the current status for later restoration.
|
/// Pauses playback and preserves the current status for later restoration.
|
||||||
void hold() {
|
void hold() {
|
||||||
if (_holdStatus != null) return;
|
if (_holdStatus != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_holdStatus = state.status;
|
_holdStatus = state.status;
|
||||||
pause();
|
pause();
|
||||||
|
|
@ -170,12 +182,16 @@ class VideoPlayerNotifier extends StateNotifier<VideoPlayerState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNativePlaybackReady() {
|
void onNativePlaybackReady() {
|
||||||
if (!mounted) return;
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final playbackInfo = _controller?.playbackInfo;
|
final playbackInfo = _controller?.playbackInfo;
|
||||||
final videoInfo = _controller?.videoInfo;
|
final videoInfo = _controller?.videoInfo;
|
||||||
|
|
||||||
if (playbackInfo == null || videoInfo == null) return;
|
if (playbackInfo == null || videoInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
position: Duration(milliseconds: playbackInfo.position),
|
position: Duration(milliseconds: playbackInfo.position),
|
||||||
|
|
@ -185,15 +201,23 @@ class VideoPlayerNotifier extends StateNotifier<VideoPlayerState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNativePositionChanged() {
|
void onNativePositionChanged() {
|
||||||
if (!mounted || (_seekTimer?.isActive ?? false)) return;
|
if (!mounted || (_seekTimer?.isActive ?? false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final playbackInfo = _controller?.playbackInfo;
|
final playbackInfo = _controller?.playbackInfo;
|
||||||
if (playbackInfo == null) return;
|
if (playbackInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final position = Duration(milliseconds: playbackInfo.position);
|
final position = Duration(milliseconds: playbackInfo.position);
|
||||||
if (state.position == position) return;
|
if (state.position == position) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (state.status == VideoPlaybackStatus.playing) _startBufferingTimer();
|
if (state.status == VideoPlaybackStatus.playing) {
|
||||||
|
_startBufferingTimer();
|
||||||
|
}
|
||||||
|
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
position: position,
|
position: position,
|
||||||
|
|
@ -202,10 +226,14 @@ class VideoPlayerNotifier extends StateNotifier<VideoPlayerState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNativeStatusChanged() {
|
void onNativeStatusChanged() {
|
||||||
if (!mounted) return;
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final playbackInfo = _controller?.playbackInfo;
|
final playbackInfo = _controller?.playbackInfo;
|
||||||
if (playbackInfo == null) return;
|
if (playbackInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final newStatus = _mapStatus(playbackInfo.status);
|
final newStatus = _mapStatus(playbackInfo.status);
|
||||||
switch (newStatus) {
|
switch (newStatus) {
|
||||||
|
|
@ -216,7 +244,9 @@ class VideoPlayerNotifier extends StateNotifier<VideoPlayerState> {
|
||||||
onNativePlaybackEnded();
|
onNativePlaybackEnded();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.status != newStatus) state = state.copyWith(status: newStatus);
|
if (state.status != newStatus) {
|
||||||
|
state = state.copyWith(status: newStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNativePlaybackEnded() {
|
void onNativePlaybackEnded() {
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,9 @@ class DriftUploadStatus {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant DriftUploadStatus other) {
|
bool operator ==(covariant DriftUploadStatus other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other.taskId == taskId &&
|
return other.taskId == taskId &&
|
||||||
other.filename == filename &&
|
other.filename == filename &&
|
||||||
|
|
@ -153,7 +155,9 @@ class DriftBackupState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant DriftBackupState other) {
|
bool operator ==(covariant DriftBackupState other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
final mapEquals = const DeepCollectionEquality().equals;
|
final mapEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
return other.totalCount == totalCount &&
|
return other.totalCount == totalCount &&
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,9 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
|
||||||
|
|
||||||
void applyDefaultAlbumSelections(List<(String id, String name)> albums) {
|
void applyDefaultAlbumSelections(List<(String id, String name)> albums) {
|
||||||
final isInitialized = _metadataRepository.appConfig.cleanup.defaultsInitialized;
|
final isInitialized = _metadataRepository.appConfig.cleanup.defaultsInitialized;
|
||||||
if (isInitialized) return;
|
if (isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final toKeep = _cleanupService.getDefaultKeepAlbumIds(albums);
|
final toKeep = _cleanupService.getDefaultKeepAlbumIds(albums);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -518,7 +518,9 @@ extension on Iterable<RemoteAsset> {
|
||||||
Iterable<String> toIds() => map((e) => e.id);
|
Iterable<String> toIds() => map((e) => e.id);
|
||||||
|
|
||||||
Iterable<RemoteAsset> ownedAssets(String? ownerId) {
|
Iterable<RemoteAsset> ownedAssets(String? ownerId) {
|
||||||
if (ownerId == null) return const [];
|
if (ownerId == null) {
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
return whereType<RemoteAsset>().where((a) => a.ownerId == ownerId);
|
return whereType<RemoteAsset>().where((a) => a.ownerId == ownerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ class RemoteAlbumState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant RemoteAlbumState other) {
|
bool operator ==(covariant RemoteAlbumState other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
final listEquals = const DeepCollectionEquality().equals;
|
final listEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
return listEquals(other.albums, albums);
|
return listEquals(other.albums, albums);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ final userMetadataRepository = Provider<DriftUserMetadataRepository>(
|
||||||
final userMetadataProvider = FutureProvider<List<UserMetadata>>((ref) async {
|
final userMetadataProvider = FutureProvider<List<UserMetadata>>((ref) async {
|
||||||
final repository = ref.watch(userMetadataRepository);
|
final repository = ref.watch(userMetadataRepository);
|
||||||
final user = ref.watch(currentUserProvider);
|
final user = ref.watch(currentUserProvider);
|
||||||
if (user == null) return [];
|
if (user == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
return repository.getUserMetadata(user.id);
|
return repository.getUserMetadata(user.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ class SearchSuggestionArgs {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other is SearchSuggestionArgs &&
|
return other is SearchSuggestionArgs &&
|
||||||
other.type == type &&
|
other.type == type &&
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,9 @@ class SyncStatusState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return other is SyncStatusState &&
|
return other is SyncStatusState &&
|
||||||
other.remoteSyncStatus == remoteSyncStatus &&
|
other.remoteSyncStatus == remoteSyncStatus &&
|
||||||
other.localSyncStatus == localSyncStatus &&
|
other.localSyncStatus == localSyncStatus &&
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,9 @@ class MultiSelectState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant MultiSelectState other) {
|
bool operator ==(covariant MultiSelectState other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
final setEquals = const DeepCollectionEquality().equals;
|
final setEquals = const DeepCollectionEquality().equals;
|
||||||
|
|
||||||
return setEquals(other.selectedAssets, selectedAssets) &&
|
return setEquals(other.selectedAssets, selectedAssets) &&
|
||||||
|
|
@ -124,7 +126,9 @@ class MultiSelectNotifier extends Notifier<MultiSelectState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggleBucketSelectionByAssets(List<BaseAsset> bucketAssets) {
|
void toggleBucketSelectionByAssets(List<BaseAsset> bucketAssets) {
|
||||||
if (bucketAssets.isEmpty) return;
|
if (bucketAssets.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if all assets in this bucket are currently selected
|
// Check if all assets in this bucket are currently selected
|
||||||
final allSelected = bucketAssets.every((asset) => state.selectedAssets.contains(asset));
|
final allSelected = bucketAssets.every((asset) => state.selectedAssets.contains(asset));
|
||||||
|
|
@ -150,7 +154,9 @@ class MultiSelectNotifier extends Notifier<MultiSelectState> {
|
||||||
final bucketSelectionProvider = Provider.family<bool, List<BaseAsset>>((ref, bucketAssets) {
|
final bucketSelectionProvider = Provider.family<bool, List<BaseAsset>>((ref, bucketAssets) {
|
||||||
final selectedAssets = ref.watch(multiSelectProvider.select((s) => s.selectedAssets));
|
final selectedAssets = ref.watch(multiSelectProvider.select((s) => s.selectedAssets));
|
||||||
|
|
||||||
if (bucketAssets.isEmpty) return false;
|
if (bucketAssets.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if all assets in the bucket are selected
|
// Check if all assets in the bucket are selected
|
||||||
return bucketAssets.every((asset) => selectedAssets.contains(asset));
|
return bucketAssets.every((asset) => selectedAssets.contains(asset));
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,9 @@ class UploadProfileImageState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return other is UploadProfileImageState && other.status == status && other.profileImagePath == profileImagePath;
|
return other is UploadProfileImageState && other.status == status && other.profileImagePath == profileImagePath;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue