mirror of
https://github.com/immich-app/immich.git
synced 2026-05-26 09:32:29 -04:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fc40c223f4 | |||
| c2779258b4 | |||
| 76e9aa012e | |||
| 915d865ce2 | |||
| c28e5f90b6 | |||
| 4383473ed6 | |||
| 77701dd5a3 | |||
| cb328c63cf | |||
| afc470e8f4 | |||
| b156ae46b6 | |||
| 67b1a9d99e | |||
| d4f7c2ae0a |
@@ -159,14 +159,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Comment APK download link on PR
|
- name: Comment APK download link on PR
|
||||||
if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
|
if: ${{ github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork }}
|
||||||
uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
env:
|
env:
|
||||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
APK_URL: ${{ steps.upload-apk.outputs.artifact-url }}
|
APK_URL: ${{ steps.upload-apk.outputs.artifact-url }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: mobile-android-apk
|
||||||
message-id: 'mobile-android-apk'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: |
|
body: |
|
||||||
📱 **Android release APK (universal)** — `${{ env.HEAD_SHA }}`
|
📱 **Android release APK (universal)** — `${{ env.HEAD_SHA }}`
|
||||||
|
|
||||||
Download: ${{ env.APK_URL }}
|
Download: ${{ env.APK_URL }}
|
||||||
|
|||||||
@@ -213,12 +213,11 @@ jobs:
|
|||||||
run: 'mise run //deployment:tf apply'
|
run: 'mise run //deployment:tf apply'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
|
uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
if: ${{ steps.parameters.outputs.event == 'pr' }}
|
||||||
with:
|
with:
|
||||||
|
id: docs-pr-url
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
|
||||||
body: |
|
body: |
|
||||||
📖 Documentation deployed to [${{ steps.docs-output.outputs.subdomain }}](https://${{ steps.docs-output.outputs.subdomain }})
|
📖 Documentation deployed to [${{ steps.docs-output.outputs.subdomain }}](https://${{ steps.docs-output.outputs.subdomain }})
|
||||||
emojis: 'rocket'
|
|
||||||
body-include: '<!-- Docs PR URL -->'
|
|
||||||
|
|||||||
@@ -44,9 +44,8 @@ jobs:
|
|||||||
run: 'mise run //deployment:tf destroy -- -refresh=false'
|
run: 'mise run //deployment:tf destroy -- -refresh=false'
|
||||||
|
|
||||||
- name: Comment
|
- name: Comment
|
||||||
uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
|
uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
with:
|
with:
|
||||||
|
id: docs-pr-url
|
||||||
token: ${{ steps.token.outputs.token }}
|
token: ${{ steps.token.outputs.token }}
|
||||||
number: ${{ github.event.number }}
|
|
||||||
delete: true
|
delete: true
|
||||||
body-include: '<!-- Docs PR URL -->'
|
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ jobs:
|
|||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
security-events: write
|
security-events: write
|
||||||
|
secrets: inherit
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ jobs:
|
|||||||
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
client-id: ${{ secrets.PUSH_O_MATIC_APP_CLIENT_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: preview-status
|
||||||
message-id: 'preview-status'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.build/'
|
body: 'Deploying preview environment to https://pr-${{ github.event.pull_request.number }}.preview.internal.immich.build/'
|
||||||
|
|
||||||
remove-label:
|
remove-label:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -48,16 +48,16 @@ jobs:
|
|||||||
name: 'preview'
|
name: 'preview'
|
||||||
})
|
})
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
if: ${{ github.event.pull_request.head.repo.fork }}
|
if: ${{ github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: preview-status
|
||||||
message-id: 'preview-status'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: 'PRs from forks cannot have preview environments.'
|
body: 'PRs from forks cannot have preview environments.'
|
||||||
|
|
||||||
- uses: mshick/add-pr-comment@8e4927817251f1ff60c001f04568532b38e0b4a0 # v3.11.0
|
- uses: immich-app/devtools/actions/sticky-comment@0135acd12ad9f3369b94a2aa3c0ae8c835a4e926 # sticky-comment-action-v1.0.0
|
||||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.token.outputs.token }}
|
id: preview-status
|
||||||
message-id: 'preview-status'
|
token: ${{ steps.token.outputs.token }}
|
||||||
message: 'Preview environment has been removed.'
|
body: 'Preview environment has been removed.'
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ flutter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
constraints {
|
||||||
|
implementation("androidx.glance:glance-appwidget") {
|
||||||
|
version { strictly libs.versions.glance.get() }
|
||||||
|
because 'home_widget requests 1.+ which can resolve to pre-releases incompatible with our compileSdk/AGP'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
implementation libs.okhttp
|
implementation libs.okhttp
|
||||||
implementation libs.cronet.embedded
|
implementation libs.cronet.embedded
|
||||||
implementation libs.media3.datasource.okhttp
|
implementation libs.media3.datasource.okhttp
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:immich_mobile/domain/models/config/album_config.dart';
|
import 'package:immich_mobile/domain/models/config/album_config.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/config/backup_config.dart';
|
||||||
import 'package:immich_mobile/domain/models/config/cleanup_config.dart';
|
import 'package:immich_mobile/domain/models/config/cleanup_config.dart';
|
||||||
import 'package:immich_mobile/domain/models/config/image_config.dart';
|
import 'package:immich_mobile/domain/models/config/image_config.dart';
|
||||||
import 'package:immich_mobile/domain/models/config/map_config.dart';
|
import 'package:immich_mobile/domain/models/config/map_config.dart';
|
||||||
@@ -16,6 +17,7 @@ class AppConfig {
|
|||||||
final ViewerConfig viewer;
|
final ViewerConfig viewer;
|
||||||
final SlideshowConfig slideshow;
|
final SlideshowConfig slideshow;
|
||||||
final AlbumConfig album;
|
final AlbumConfig album;
|
||||||
|
final BackupConfig backup;
|
||||||
|
|
||||||
const AppConfig({
|
const AppConfig({
|
||||||
this.theme = const .new(),
|
this.theme = const .new(),
|
||||||
@@ -26,6 +28,7 @@ class AppConfig {
|
|||||||
this.viewer = const .new(),
|
this.viewer = const .new(),
|
||||||
this.slideshow = const .new(),
|
this.slideshow = const .new(),
|
||||||
this.album = const .new(),
|
this.album = const .new(),
|
||||||
|
this.backup = const .new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
AppConfig copyWith({
|
AppConfig copyWith({
|
||||||
@@ -37,6 +40,7 @@ class AppConfig {
|
|||||||
ViewerConfig? viewer,
|
ViewerConfig? viewer,
|
||||||
SlideshowConfig? slideshow,
|
SlideshowConfig? slideshow,
|
||||||
AlbumConfig? album,
|
AlbumConfig? album,
|
||||||
|
BackupConfig? backup,
|
||||||
}) => .new(
|
}) => .new(
|
||||||
theme: theme ?? this.theme,
|
theme: theme ?? this.theme,
|
||||||
cleanup: cleanup ?? this.cleanup,
|
cleanup: cleanup ?? this.cleanup,
|
||||||
@@ -46,6 +50,7 @@ class AppConfig {
|
|||||||
viewer: viewer ?? this.viewer,
|
viewer: viewer ?? this.viewer,
|
||||||
slideshow: slideshow ?? this.slideshow,
|
slideshow: slideshow ?? this.slideshow,
|
||||||
album: album ?? this.album,
|
album: album ?? this.album,
|
||||||
|
backup: backup ?? this.backup,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -59,12 +64,13 @@ class AppConfig {
|
|||||||
other.image == image &&
|
other.image == image &&
|
||||||
other.viewer == viewer &&
|
other.viewer == viewer &&
|
||||||
other.slideshow == slideshow &&
|
other.slideshow == slideshow &&
|
||||||
other.album == album);
|
other.album == album &&
|
||||||
|
other.backup == backup);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(theme, cleanup, map, timeline, image, viewer, slideshow, album);
|
int get hashCode => Object.hash(theme, cleanup, map, timeline, image, viewer, slideshow, album, backup);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() =>
|
String toString() =>
|
||||||
'AppConfig(theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image, viewer: $viewer, slideshow: $slideshow, album: $album)';
|
'AppConfig(theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image, viewer: $viewer, slideshow: $slideshow, album: $album, backup: $backup)';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
class BackupConfig {
|
||||||
|
final bool enabled;
|
||||||
|
final bool useCellularForVideos;
|
||||||
|
final bool useCellularForPhotos;
|
||||||
|
final bool requireCharging;
|
||||||
|
final int triggerDelay;
|
||||||
|
final bool syncAlbums;
|
||||||
|
|
||||||
|
const BackupConfig({
|
||||||
|
this.enabled = false,
|
||||||
|
this.useCellularForVideos = false,
|
||||||
|
this.useCellularForPhotos = false,
|
||||||
|
this.requireCharging = false,
|
||||||
|
this.triggerDelay = 30,
|
||||||
|
this.syncAlbums = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
BackupConfig copyWith({
|
||||||
|
bool? enabled,
|
||||||
|
bool? useCellularForVideos,
|
||||||
|
bool? useCellularForPhotos,
|
||||||
|
bool? requireCharging,
|
||||||
|
int? triggerDelay,
|
||||||
|
bool? syncAlbums,
|
||||||
|
}) => BackupConfig(
|
||||||
|
enabled: enabled ?? this.enabled,
|
||||||
|
useCellularForVideos: useCellularForVideos ?? this.useCellularForVideos,
|
||||||
|
useCellularForPhotos: useCellularForPhotos ?? this.useCellularForPhotos,
|
||||||
|
requireCharging: requireCharging ?? this.requireCharging,
|
||||||
|
triggerDelay: triggerDelay ?? this.triggerDelay,
|
||||||
|
syncAlbums: syncAlbums ?? this.syncAlbums,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is BackupConfig &&
|
||||||
|
other.enabled == enabled &&
|
||||||
|
other.useCellularForVideos == useCellularForVideos &&
|
||||||
|
other.useCellularForPhotos == useCellularForPhotos &&
|
||||||
|
other.requireCharging == requireCharging &&
|
||||||
|
other.triggerDelay == triggerDelay &&
|
||||||
|
other.syncAlbums == syncAlbums);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(enabled, useCellularForVideos, useCellularForPhotos, requireCharging, triggerDelay, syncAlbums);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() =>
|
||||||
|
'BackupConfig(enabled: $enabled, useCellularForVideos: $useCellularForVideos, useCellularForPhotos: $useCellularForPhotos, requireCharging: $requireCharging, triggerDelay: $triggerDelay, syncAlbums: $syncAlbums)';
|
||||||
|
}
|
||||||
@@ -1,20 +1,28 @@
|
|||||||
class ImageConfig {
|
class ImageConfig {
|
||||||
final bool preferRemote;
|
final bool preferRemote;
|
||||||
|
final bool loadPreview;
|
||||||
final bool loadOriginal;
|
final bool loadOriginal;
|
||||||
|
|
||||||
const ImageConfig({this.preferRemote = false, this.loadOriginal = false});
|
const ImageConfig({this.preferRemote = false, this.loadPreview = true, this.loadOriginal = false});
|
||||||
|
|
||||||
ImageConfig copyWith({bool? preferRemote, bool? loadOriginal}) =>
|
ImageConfig copyWith({bool? preferRemote, bool? loadPreview, bool? loadOriginal}) => ImageConfig(
|
||||||
ImageConfig(preferRemote: preferRemote ?? this.preferRemote, loadOriginal: loadOriginal ?? this.loadOriginal);
|
preferRemote: preferRemote ?? this.preferRemote,
|
||||||
|
loadPreview: loadPreview ?? this.loadPreview,
|
||||||
|
loadOriginal: loadOriginal ?? this.loadOriginal,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is ImageConfig && other.preferRemote == preferRemote && other.loadOriginal == loadOriginal);
|
(other is ImageConfig &&
|
||||||
|
other.preferRemote == preferRemote &&
|
||||||
|
other.loadPreview == loadPreview &&
|
||||||
|
other.loadOriginal == loadOriginal);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(preferRemote, loadOriginal);
|
int get hashCode => Object.hash(preferRemote, loadPreview, loadOriginal);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'ImageConfig(preferRemoteImage: $preferRemote, loadOriginal: $loadOriginal)';
|
String toString() =>
|
||||||
|
'ImageConfig(preferRemoteImage: $preferRemote, loadPreview: $loadPreview, loadOriginal: $loadOriginal)';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ enum MetadataKey<T extends Object> {
|
|||||||
|
|
||||||
// Image
|
// Image
|
||||||
imagePreferRemote<bool>(.appConfig, 'image.preferRemote', false),
|
imagePreferRemote<bool>(.appConfig, 'image.preferRemote', false),
|
||||||
|
imageLoadPreview<bool>(.appConfig, 'image.loadPreview', true),
|
||||||
imageLoadOriginal<bool>(.appConfig, 'image.loadOriginal', false),
|
imageLoadOriginal<bool>(.appConfig, 'image.loadOriginal', false),
|
||||||
|
|
||||||
// Viewer
|
// Viewer
|
||||||
@@ -62,6 +63,14 @@ enum MetadataKey<T extends Object> {
|
|||||||
albumIsReverse<bool>(.appConfig, 'album.isReverse', true),
|
albumIsReverse<bool>(.appConfig, 'album.isReverse', true),
|
||||||
albumIsGrid<bool>(.appConfig, 'album.isGrid', false),
|
albumIsGrid<bool>(.appConfig, 'album.isGrid', false),
|
||||||
|
|
||||||
|
// Backup
|
||||||
|
backupEnabled<bool>(.appConfig, 'backup.enabled', false),
|
||||||
|
backupUseCellularForVideos<bool>(.appConfig, 'backup.useCellularForVideos', false),
|
||||||
|
backupUseCellularForPhotos<bool>(.appConfig, 'backup.useCellularForPhotos', false),
|
||||||
|
backupRequireCharging<bool>(.appConfig, 'backup.requireCharging', false),
|
||||||
|
backupTriggerDelay<int>(.appConfig, 'backup.triggerDelay', 30),
|
||||||
|
backupSyncAlbums<bool>(.appConfig, 'backup.syncAlbums', false),
|
||||||
|
|
||||||
// Timeline
|
// Timeline
|
||||||
timelineTilesPerRow<int>(.appConfig, 'timeline.tilesPerRow', 4),
|
timelineTilesPerRow<int>(.appConfig, 'timeline.tilesPerRow', 4),
|
||||||
timelineGroupAssetsBy<GroupAssetsBy>(
|
timelineGroupAssetsBy<GroupAssetsBy>(
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
|
|
||||||
enum Setting<T> {
|
enum Setting<T> {
|
||||||
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false),
|
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false);
|
||||||
enableBackup<bool>(StoreKey.enableBackup, false);
|
|
||||||
|
|
||||||
const Setting(this.storeKey, this.defaultValue);
|
const Setting(this.storeKey, this.defaultValue);
|
||||||
|
|
||||||
|
|||||||
@@ -6,26 +6,25 @@ enum StoreKey<T> {
|
|||||||
version<int>._(0),
|
version<int>._(0),
|
||||||
currentUser<UserDto>._(2),
|
currentUser<UserDto>._(2),
|
||||||
deviceId<String>._(4),
|
deviceId<String>._(4),
|
||||||
backupRequireCharging<bool>._(7),
|
|
||||||
backupTriggerDelay<int>._(8),
|
|
||||||
serverUrl<String>._(10),
|
serverUrl<String>._(10),
|
||||||
accessToken<String>._(11),
|
accessToken<String>._(11),
|
||||||
serverEndpoint<String>._(12),
|
serverEndpoint<String>._(12),
|
||||||
advancedTroubleshooting<bool>._(114),
|
advancedTroubleshooting<bool>._(114),
|
||||||
enableHapticFeedback<bool>._(126),
|
enableHapticFeedback<bool>._(126),
|
||||||
syncAlbums<bool>._(131),
|
|
||||||
|
|
||||||
manageLocalMediaAndroid<bool>._(137),
|
manageLocalMediaAndroid<bool>._(137),
|
||||||
// Read-only Mode settings
|
// Read-only Mode settings
|
||||||
readonlyModeEnabled<bool>._(138),
|
readonlyModeEnabled<bool>._(138),
|
||||||
|
|
||||||
// Experimental stuff
|
|
||||||
enableBackup<bool>._(1003),
|
|
||||||
useWifiForUploadVideos<bool>._(1004),
|
|
||||||
useWifiForUploadPhotos<bool>._(1005),
|
|
||||||
syncMigrationStatus<String>._(1013),
|
syncMigrationStatus<String>._(1013),
|
||||||
|
|
||||||
// Legacy keys that have been migrated to the new metadata store
|
// Legacy keys that have been migrated to the new metadata store
|
||||||
|
legacyBackupRequireCharging<bool>._(7),
|
||||||
|
legacyBackupTriggerDelay<int>._(8),
|
||||||
|
legacySyncAlbums<bool>._(131),
|
||||||
|
legacyEnableBackup<bool>._(1003),
|
||||||
|
legacyUseWifiForUploadVideos<bool>._(1004),
|
||||||
|
legacyUseWifiForUploadPhotos<bool>._(1005),
|
||||||
legacySelectedAlbumSortOrder<int>._(113),
|
legacySelectedAlbumSortOrder<int>._(113),
|
||||||
legacySelectedAlbumSortReverse<bool>._(123),
|
legacySelectedAlbumSortReverse<bool>._(123),
|
||||||
legacyAlbumGridView<bool>._(140),
|
legacyAlbumGridView<bool>._(140),
|
||||||
|
|||||||
@@ -11,15 +11,14 @@ 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/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/platform/background_worker_api.g.dart';
|
import 'package:immich_mobile/platform/background_worker_api.g.dart';
|
||||||
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
|
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart' show nativeSyncApiProvider;
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart' show nativeSyncApiProvider;
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/services/auth.service.dart';
|
import 'package:immich_mobile/services/auth.service.dart';
|
||||||
import 'package:immich_mobile/services/foreground_upload.service.dart';
|
import 'package:immich_mobile/services/foreground_upload.service.dart';
|
||||||
import 'package:immich_mobile/services/localization.service.dart';
|
import 'package:immich_mobile/services/localization.service.dart';
|
||||||
@@ -39,16 +38,15 @@ class BackgroundWorkerFgService {
|
|||||||
Future<void> saveNotificationMessage(String title, String body) =>
|
Future<void> saveNotificationMessage(String title, String body) =>
|
||||||
_foregroundHostApi.saveNotificationMessage(title, body);
|
_foregroundHostApi.saveNotificationMessage(title, body);
|
||||||
|
|
||||||
Future<void> configure({int? minimumDelaySeconds, bool? requireCharging}) => _foregroundHostApi.configure(
|
Future<void> configure({int? minimumDelaySeconds, bool? requireCharging}) {
|
||||||
BackgroundWorkerSettings(
|
final backup = MetadataRepository.instance.appConfig.backup;
|
||||||
minimumDelaySeconds:
|
return _foregroundHostApi.configure(
|
||||||
minimumDelaySeconds ??
|
BackgroundWorkerSettings(
|
||||||
Store.get(AppSettingsEnum.backupTriggerDelay.storeKey, AppSettingsEnum.backupTriggerDelay.defaultValue),
|
minimumDelaySeconds: minimumDelaySeconds ?? backup.triggerDelay,
|
||||||
requiresCharging:
|
requiresCharging: requireCharging ?? backup.requireCharging,
|
||||||
requireCharging ??
|
),
|
||||||
Store.get(AppSettingsEnum.backupRequireCharging.storeKey, AppSettingsEnum.backupRequireCharging.defaultValue),
|
);
|
||||||
),
|
}
|
||||||
);
|
|
||||||
|
|
||||||
Future<void> disable() => _foregroundHostApi.disable();
|
Future<void> disable() => _foregroundHostApi.disable();
|
||||||
}
|
}
|
||||||
@@ -71,7 +69,7 @@ class BackgroundWorkerBgService extends BackgroundWorkerFlutterApi {
|
|||||||
BackgroundWorkerFlutterApi.setUp(this);
|
BackgroundWorkerFlutterApi.setUp(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _isBackupEnabled => _ref?.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup) ?? false;
|
bool get _isBackupEnabled => MetadataRepository.instance.appConfig.backup.enabled;
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -133,7 +133,11 @@ extension<T extends Object> on MetadataDomain<T> {
|
|||||||
groupAssetsBy: repo._read(.timelineGroupAssetsBy),
|
groupAssetsBy: repo._read(.timelineGroupAssetsBy),
|
||||||
storageIndicator: repo._read(.timelineStorageIndicator),
|
storageIndicator: repo._read(.timelineStorageIndicator),
|
||||||
),
|
),
|
||||||
image: .new(preferRemote: repo._read(.imagePreferRemote), loadOriginal: repo._read(.imageLoadOriginal)),
|
image: .new(
|
||||||
|
preferRemote: repo._read(.imagePreferRemote),
|
||||||
|
loadPreview: repo._read(.imageLoadPreview),
|
||||||
|
loadOriginal: repo._read(.imageLoadOriginal),
|
||||||
|
),
|
||||||
viewer: .new(
|
viewer: .new(
|
||||||
loopVideo: repo._read(.viewerLoopVideo),
|
loopVideo: repo._read(.viewerLoopVideo),
|
||||||
loadOriginalVideo: repo._read(.viewerLoadOriginalVideo),
|
loadOriginalVideo: repo._read(.viewerLoadOriginalVideo),
|
||||||
@@ -152,6 +156,14 @@ extension<T extends Object> on MetadataDomain<T> {
|
|||||||
isReverse: repo._read(.albumIsReverse),
|
isReverse: repo._read(.albumIsReverse),
|
||||||
isGrid: repo._read(.albumIsGrid),
|
isGrid: repo._read(.albumIsGrid),
|
||||||
),
|
),
|
||||||
|
backup: .new(
|
||||||
|
enabled: repo._read(.backupEnabled),
|
||||||
|
useCellularForVideos: repo._read(.backupUseCellularForVideos),
|
||||||
|
useCellularForPhotos: repo._read(.backupUseCellularForPhotos),
|
||||||
|
requireCharging: repo._read(.backupRequireCharging),
|
||||||
|
triggerDelay: repo._read(.backupTriggerDelay),
|
||||||
|
syncAlbums: repo._read(.backupSyncAlbums),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
case .systemConfig:
|
case .systemConfig:
|
||||||
repo._systemConfig = .new(
|
repo._systemConfig = .new(
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
|||||||
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
|
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
|
import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
|
||||||
import 'package:immich_mobile/widgets/common/search_field.dart';
|
import 'package:immich_mobile/widgets/common/search_field.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@@ -43,7 +43,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
_searchController = TextEditingController();
|
_searchController = TextEditingController();
|
||||||
_searchFocusNode = FocusNode();
|
_searchFocusNode = FocusNode();
|
||||||
|
|
||||||
_enableSyncUploadAlbum.value = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
_enableSyncUploadAlbum.value = ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
ref.read(backupAlbumProvider.notifier).getAll();
|
ref.read(backupAlbumProvider.notifier).getAll();
|
||||||
|
|
||||||
_initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
_initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||||
@@ -55,7 +55,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final enableSyncUploadAlbum = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
final enableSyncUploadAlbum = ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
final selectedAlbums = ref
|
final selectedAlbums = ref
|
||||||
.read(backupAlbumProvider)
|
.read(backupAlbumProvider)
|
||||||
.where((a) => a.backupSelection == BackupSelection.selected)
|
.where((a) => a.backupSelection == BackupSelection.selected)
|
||||||
@@ -103,7 +103,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
final isBackupEnabled = MetadataRepository.instance.appConfig.backup.enabled;
|
||||||
await ref.read(driftBackupProvider.notifier).getBackupStatus(user.id);
|
await ref.read(driftBackupProvider.notifier).getBackupStatus(user.id);
|
||||||
final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||||
final totalChanged = currentTotalAssetCount != _initialTotalAssetCount;
|
final totalChanged = currentTotalAssetCount != _initialTotalAssetCount;
|
||||||
|
|||||||
@@ -3,14 +3,12 @@ import 'dart:async';
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart';
|
import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
@@ -21,18 +19,20 @@ class DriftBackupOptionsPage extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
bool hasPopped = false;
|
bool hasPopped = false;
|
||||||
final previousWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false;
|
final previousBackup = ref.read(metadataProvider).appConfig.backup;
|
||||||
final previousWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false;
|
final previousCellularForVideos = previousBackup.useCellularForVideos;
|
||||||
|
final previousCellularForPhotos = previousBackup.useCellularForPhotos;
|
||||||
return PopScope(
|
return PopScope(
|
||||||
onPopInvokedWithResult: (didPop, result) async {
|
onPopInvokedWithResult: (didPop, result) async {
|
||||||
// There is an issue with Flutter where the pop event
|
// There is an issue with Flutter where the pop event
|
||||||
// can be triggered multiple times, so we guard it with _hasPopped
|
// can be triggered multiple times, so we guard it with _hasPopped
|
||||||
|
|
||||||
final currentWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false;
|
final currentBackup = ref.read(metadataProvider).appConfig.backup;
|
||||||
final currentWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false;
|
final currentCellularForVideos = currentBackup.useCellularForVideos;
|
||||||
|
final currentCellularForPhotos = currentBackup.useCellularForPhotos;
|
||||||
|
|
||||||
if (currentWifiReqForVideos == previousWifiReqForVideos &&
|
if (currentCellularForVideos == previousCellularForVideos &&
|
||||||
currentWifiReqForPhotos == previousWifiReqForPhotos) {
|
currentCellularForPhotos == previousCellularForPhotos) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ class DriftBackupOptionsPage extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
|
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
|
||||||
final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
final isBackupEnabled = MetadataRepository.instance.appConfig.backup.enabled;
|
||||||
if (!isBackupEnabled) {
|
if (!isBackupEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/generated/codegen_loader.g.dart';
|
import 'package:immich_mobile/generated/codegen_loader.g.dart';
|
||||||
import 'package:immich_mobile/generated/translations.g.dart';
|
import 'package:immich_mobile/generated/translations.g.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
@@ -340,7 +341,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
|||||||
await backgroundManager.hashAssets();
|
await backgroundManager.hashAssets();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Store.get(StoreKey.syncAlbums, false)) {
|
if (MetadataRepository.instance.appConfig.backup.syncAlbums) {
|
||||||
await backgroundManager.syncLinkedAlbum();
|
await backgroundManager.syncLinkedAlbum();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -369,7 +370,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _resumeBackup(DriftBackupNotifier notifier) async {
|
Future<void> _resumeBackup(DriftBackupNotifier notifier) async {
|
||||||
final isEnableBackup = Store.get(StoreKey.enableBackup, false);
|
final isEnableBackup = MetadataRepository.instance.appConfig.backup.enabled;
|
||||||
|
|
||||||
if (isEnableBackup) {
|
if (isEnableBackup) {
|
||||||
final currentUser = Store.tryGet(StoreKey.currentUser);
|
final currentUser = Store.tryGet(StoreKey.currentUser);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
|
|
||||||
class BackupToggleButton extends ConsumerStatefulWidget {
|
class BackupToggleButton extends ConsumerStatefulWidget {
|
||||||
final VoidCallback onStart;
|
final VoidCallback onStart;
|
||||||
@@ -31,7 +31,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with Sin
|
|||||||
end: 1,
|
end: 1,
|
||||||
).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut));
|
).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut));
|
||||||
|
|
||||||
_isEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
_isEnabled = ref.read(metadataProvider).appConfig.backup.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -41,7 +41,7 @@ class BackupToggleButtonState extends ConsumerState<BackupToggleButton> with Sin
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onToggle(bool value) async {
|
Future<void> _onToggle(bool value) async {
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.enableBackup, value);
|
await ref.read(metadataProvider).write(MetadataKey.backupEnabled, value);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isEnabled = value;
|
_isEnabled = value;
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
|||||||
|
|
||||||
completer.operation.valueOrCancellation().whenComplete(() {
|
completer.operation.valueOrCancellation().whenComplete(() {
|
||||||
cachedStream.removeListener(listener);
|
cachedStream.removeListener(listener);
|
||||||
cachedOperation = null;
|
|
||||||
});
|
});
|
||||||
cachedOperation = completer.operation;
|
cachedOperation = completer.operation;
|
||||||
return null;
|
return null;
|
||||||
@@ -94,6 +93,9 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
|||||||
isFinished = isFinal;
|
isFinished = isFinal;
|
||||||
return codec;
|
return codec;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (isCancelled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (isFinal) {
|
if (isFinal) {
|
||||||
isFinished = true;
|
isFinished = true;
|
||||||
PaintingBinding.instance.imageCache.evict(this);
|
PaintingBinding.instance.imageCache.evict(this);
|
||||||
@@ -105,18 +107,33 @@ mixin CancellableImageProviderMixin<T extends Object> on CancellableImageProvide
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<ImageInfo> initialImageStream() async* {
|
Stream<ImageInfo> initialImageStream({required bool isFinal}) async* {
|
||||||
final cachedOperation = this.cachedOperation;
|
final cachedOperation = this.cachedOperation;
|
||||||
|
if (isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (cachedOperation == null) {
|
if (cachedOperation == null) {
|
||||||
|
// image resolved synchronously
|
||||||
|
isFinished = isFinal;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final cachedImage = await cachedOperation.valueOrCancellation();
|
final cachedImage = await cachedOperation.valueOrCancellation();
|
||||||
if (cachedImage != null && !isCancelled) {
|
if (isCancelled || cachedImage == null) {
|
||||||
yield cachedImage;
|
return;
|
||||||
}
|
}
|
||||||
|
isFinished = isFinal;
|
||||||
|
yield cachedImage;
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
|
if (isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isFinal) {
|
||||||
|
isFinished = true;
|
||||||
|
PaintingBinding.instance.imageCache.evict(this);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
_log.severe('Error loading initial image', e, stack);
|
_log.severe('Error loading initial image', e, stack);
|
||||||
} finally {
|
} finally {
|
||||||
this.cachedOperation = null;
|
this.cachedOperation = null;
|
||||||
|
|||||||
@@ -98,51 +98,55 @@ class LocalFullImageProvider extends CancellableImageProvider<LocalFullImageProv
|
|||||||
}
|
}
|
||||||
|
|
||||||
Stream<ImageInfo> _codec(LocalFullImageProvider key, ImageDecoderCallback decode) async* {
|
Stream<ImageInfo> _codec(LocalFullImageProvider key, ImageDecoderCallback decode) async* {
|
||||||
yield* initialImageStream();
|
final loadOriginal = MetadataRepository.instance.appConfig.image.loadOriginal;
|
||||||
|
final loadPreview = MetadataRepository.instance.appConfig.image.loadPreview;
|
||||||
|
yield* initialImageStream(isFinal: !loadOriginal && !loadPreview);
|
||||||
|
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final loadOriginal = MetadataRepository.instance.appConfig.image.loadOriginal;
|
if (loadPreview) {
|
||||||
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
|
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
|
||||||
var request = this.request = LocalImageRequest(
|
final previewRequest = request = LocalImageRequest(
|
||||||
localId: key.id,
|
localId: key.id,
|
||||||
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
|
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
|
||||||
assetType: key.assetType,
|
assetType: key.assetType,
|
||||||
);
|
);
|
||||||
yield* loadRequest(request, decode, isFinal: !loadOriginal);
|
yield* loadRequest(previewRequest, decode, isFinal: !loadOriginal);
|
||||||
|
|
||||||
|
if (isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!loadOriginal) {
|
if (!loadOriginal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCancelled) {
|
final originalRequest = request = LocalImageRequest(localId: key.id, assetType: key.assetType, size: Size.zero);
|
||||||
return;
|
yield* loadRequest(originalRequest, decode, isFinal: true);
|
||||||
}
|
|
||||||
|
|
||||||
request = this.request = LocalImageRequest(localId: key.id, assetType: key.assetType, size: Size.zero);
|
|
||||||
|
|
||||||
yield* loadRequest(request, decode, isFinal: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Object> _animatedCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async* {
|
Stream<Object> _animatedCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async* {
|
||||||
yield* initialImageStream();
|
yield* initialImageStream(isFinal: false);
|
||||||
|
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
|
if (MetadataRepository.instance.appConfig.image.loadPreview) {
|
||||||
final previewRequest = request = LocalImageRequest(
|
final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio;
|
||||||
localId: key.id,
|
final previewRequest = request = LocalImageRequest(
|
||||||
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
|
localId: key.id,
|
||||||
assetType: key.assetType,
|
size: Size(size.width * devicePixelRatio, size.height * devicePixelRatio),
|
||||||
);
|
assetType: key.assetType,
|
||||||
yield* loadRequest(previewRequest, decode, isFinal: false);
|
);
|
||||||
|
yield* loadRequest(previewRequest, decode, isFinal: false);
|
||||||
|
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// always try original for animated, since previews don't support animation
|
// always try original for animated, since previews don't support animation
|
||||||
|
|||||||
@@ -108,31 +108,35 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
|||||||
}
|
}
|
||||||
|
|
||||||
Stream<ImageInfo> _codec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* {
|
Stream<ImageInfo> _codec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* {
|
||||||
yield* initialImageStream();
|
final isImage = assetType == AssetType.image;
|
||||||
|
final loadOriginal = isImage && MetadataRepository.instance.appConfig.image.loadOriginal;
|
||||||
|
final loadPreview = isImage && MetadataRepository.instance.appConfig.image.loadPreview;
|
||||||
|
yield* initialImageStream(isFinal: !loadOriginal && !loadPreview);
|
||||||
|
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final previewRequest = request = RemoteImageRequest(
|
if (loadPreview) {
|
||||||
uri: getThumbnailUrlForRemoteId(
|
final previewRequest = request = RemoteImageRequest(
|
||||||
key.assetId,
|
uri: getThumbnailUrlForRemoteId(
|
||||||
type: AssetMediaSize.preview,
|
key.assetId,
|
||||||
thumbhash: key.thumbhash,
|
type: AssetMediaSize.preview,
|
||||||
edited: key.edited,
|
thumbhash: key.thumbhash,
|
||||||
),
|
edited: key.edited,
|
||||||
);
|
),
|
||||||
final loadOriginal = assetType == AssetType.image && MetadataRepository.instance.appConfig.image.loadOriginal;
|
);
|
||||||
yield* loadRequest(previewRequest, decode, isFinal: !loadOriginal);
|
yield* loadRequest(previewRequest, decode, isFinal: !loadOriginal);
|
||||||
|
|
||||||
|
if (isCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!loadOriginal) {
|
if (!loadOriginal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCancelled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final originalRequest = request = RemoteImageRequest(
|
final originalRequest = request = RemoteImageRequest(
|
||||||
uri: getOriginalUrlForRemoteId(key.assetId, edited: key.edited),
|
uri: getOriginalUrlForRemoteId(key.assetId, edited: key.edited),
|
||||||
);
|
);
|
||||||
@@ -140,24 +144,26 @@ class RemoteFullImageProvider extends CancellableImageProvider<RemoteFullImagePr
|
|||||||
}
|
}
|
||||||
|
|
||||||
Stream<Object> _animatedCodec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* {
|
Stream<Object> _animatedCodec(RemoteFullImageProvider key, ImageDecoderCallback decode) async* {
|
||||||
yield* initialImageStream();
|
yield* initialImageStream(isFinal: false);
|
||||||
|
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final previewRequest = request = RemoteImageRequest(
|
if (MetadataRepository.instance.appConfig.image.loadPreview) {
|
||||||
uri: getThumbnailUrlForRemoteId(
|
final previewRequest = request = RemoteImageRequest(
|
||||||
key.assetId,
|
uri: getThumbnailUrlForRemoteId(
|
||||||
type: AssetMediaSize.preview,
|
key.assetId,
|
||||||
thumbhash: key.thumbhash,
|
type: AssetMediaSize.preview,
|
||||||
edited: key.edited,
|
thumbhash: key.thumbhash,
|
||||||
),
|
edited: key.edited,
|
||||||
);
|
),
|
||||||
yield* loadRequest(previewRequest, decode, isFinal: false);
|
);
|
||||||
|
yield* loadRequest(previewRequest, decode, isFinal: false);
|
||||||
|
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// always try original for animated, since previews don't support animation
|
// always try original for animated, since previews don't support animation
|
||||||
|
|||||||
@@ -5,16 +5,15 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/domain/services/log.service.dart';
|
import 'package:immich_mobile/domain/services/log.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/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
import 'package:immich_mobile/providers/notification_permission.provider.dart';
|
import 'package:immich_mobile/providers/notification_permission.provider.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/providers/websocket.provider.dart';
|
import 'package:immich_mobile/providers/websocket.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
enum AppLifeCycleEnum { active, inactive, paused, resumed, detached, hidden }
|
enum AppLifeCycleEnum { active, inactive, paused, resumed, detached, hidden }
|
||||||
@@ -108,7 +107,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
final backgroundManager = _ref.read(backgroundSyncProvider);
|
final backgroundManager = _ref.read(backgroundSyncProvider);
|
||||||
final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
final isAlbumLinkedSyncEnable = _ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bool syncSuccess = false;
|
bool syncSuccess = false;
|
||||||
@@ -138,7 +137,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _resumeBackup() async {
|
Future<void> _resumeBackup() async {
|
||||||
final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
final isEnableBackup = _ref.read(metadataProvider).appConfig.backup.enabled;
|
||||||
|
|
||||||
if (isEnableBackup) {
|
if (isEnableBackup) {
|
||||||
final currentUser = Store.tryGet(StoreKey.currentUser);
|
final currentUser = Store.tryGet(StoreKey.currentUser);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:immich_mobile/infrastructure/repositories/network.repository.dar
|
|||||||
import 'package:immich_mobile/models/server_info/server_version.model.dart';
|
import 'package:immich_mobile/models/server_info/server_version.model.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/utils/debounce.dart';
|
import 'package:immich_mobile/utils/debounce.dart';
|
||||||
import 'package:immich_mobile/utils/debug_print.dart';
|
import 'package:immich_mobile/utils/debug_print.dart';
|
||||||
@@ -192,7 +193,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isSyncAlbumEnabled = Store.get(StoreKey.syncAlbums, false);
|
final isSyncAlbumEnabled = _ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
try {
|
try {
|
||||||
unawaited(
|
unawaited(
|
||||||
_ref.read(backgroundSyncProvider).syncWebsocketBatchV1(_batchedAssetUploadReady.toList()).then((_) {
|
_ref.read(backgroundSyncProvider).syncWebsocketBatchV1(_batchedAssetUploadReady.toList()).then((_) {
|
||||||
@@ -213,7 +214,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isSyncAlbumEnabled = Store.get(StoreKey.syncAlbums, false);
|
final isSyncAlbumEnabled = _ref.read(metadataProvider).appConfig.backup.syncAlbums;
|
||||||
try {
|
try {
|
||||||
unawaited(
|
unawaited(
|
||||||
_ref.read(backgroundSyncProvider).syncWebsocketBatchV2(_batchedAssetUploadReady.toList()).then((_) {
|
_ref.read(backgroundSyncProvider).syncWebsocketBatchV2(_batchedAssetUploadReady.toList()).then((_) {
|
||||||
|
|||||||
@@ -5,13 +5,7 @@ enum AppSettingsEnum<T> {
|
|||||||
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, null, false),
|
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, null, false),
|
||||||
manageLocalMediaAndroid<bool>(StoreKey.manageLocalMediaAndroid, null, false),
|
manageLocalMediaAndroid<bool>(StoreKey.manageLocalMediaAndroid, null, false),
|
||||||
enableHapticFeedback<bool>(StoreKey.enableHapticFeedback, null, true),
|
enableHapticFeedback<bool>(StoreKey.enableHapticFeedback, null, true),
|
||||||
syncAlbums<bool>(StoreKey.syncAlbums, null, false),
|
readonlyModeEnabled<bool>(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false);
|
||||||
enableBackup<bool>(StoreKey.enableBackup, null, false),
|
|
||||||
useCellularForUploadVideos<bool>(StoreKey.useWifiForUploadVideos, null, false),
|
|
||||||
useCellularForUploadPhotos<bool>(StoreKey.useWifiForUploadPhotos, null, false),
|
|
||||||
readonlyModeEnabled<bool>(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false),
|
|
||||||
backupRequireCharging<bool>(StoreKey.backupRequireCharging, null, false),
|
|
||||||
backupTriggerDelay<int>(StoreKey.backupTriggerDelay, null, 30);
|
|
||||||
|
|
||||||
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
|
||||||
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
||||||
import 'package:immich_mobile/models/auth/login_response.model.dart';
|
import 'package:immich_mobile/models/auth/login_response.model.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/auth.repository.dart';
|
import 'package:immich_mobile/repositories/auth.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/auth_api.repository.dart';
|
import 'package:immich_mobile/repositories/auth_api.repository.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/services/network.service.dart';
|
import 'package:immich_mobile/services/network.service.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:openapi/api.dart';
|
import 'package:openapi/api.dart';
|
||||||
@@ -25,7 +25,6 @@ final authServiceProvider = Provider(
|
|||||||
ref.watch(apiServiceProvider),
|
ref.watch(apiServiceProvider),
|
||||||
ref.watch(networkServiceProvider),
|
ref.watch(networkServiceProvider),
|
||||||
ref.watch(backgroundSyncProvider),
|
ref.watch(backgroundSyncProvider),
|
||||||
ref.watch(appSettingsServiceProvider),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -35,7 +34,6 @@ class AuthService {
|
|||||||
final ApiService _apiService;
|
final ApiService _apiService;
|
||||||
final NetworkService _networkService;
|
final NetworkService _networkService;
|
||||||
final BackgroundSyncManager _backgroundSyncManager;
|
final BackgroundSyncManager _backgroundSyncManager;
|
||||||
final AppSettingsService _appSettingsService;
|
|
||||||
final _log = Logger("AuthService");
|
final _log = Logger("AuthService");
|
||||||
|
|
||||||
AuthService(
|
AuthService(
|
||||||
@@ -44,7 +42,6 @@ class AuthService {
|
|||||||
this._apiService,
|
this._apiService,
|
||||||
this._networkService,
|
this._networkService,
|
||||||
this._backgroundSyncManager,
|
this._backgroundSyncManager,
|
||||||
this._appSettingsService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Validates the provided server URL by resolving and setting the endpoint.
|
/// Validates the provided server URL by resolving and setting the endpoint.
|
||||||
@@ -103,7 +100,7 @@ class AuthService {
|
|||||||
_log.severe("Error clearing local data", error, stackTrace);
|
_log.severe("Error clearing local data", error, stackTrace);
|
||||||
});
|
});
|
||||||
|
|
||||||
await _appSettingsService.setSetting(AppSettingsEnum.enableBackup, false);
|
await MetadataRepository.instance.write(MetadataKey.backupEnabled, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ 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/backup.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/upload.repository.dart';
|
import 'package:immich_mobile/repositories/upload.repository.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/utils/debug_print.dart';
|
import 'package:immich_mobile/utils/debug_print.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
@@ -31,7 +30,6 @@ final backgroundUploadServiceProvider = Provider((ref) {
|
|||||||
ref.watch(storageRepositoryProvider),
|
ref.watch(storageRepositoryProvider),
|
||||||
ref.watch(localAssetRepository),
|
ref.watch(localAssetRepository),
|
||||||
ref.watch(backupRepositoryProvider),
|
ref.watch(backupRepositoryProvider),
|
||||||
ref.watch(appSettingsServiceProvider),
|
|
||||||
ref.watch(assetMediaRepositoryProvider),
|
ref.watch(assetMediaRepositoryProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -105,7 +103,6 @@ class BackgroundUploadService {
|
|||||||
this._storageRepository,
|
this._storageRepository,
|
||||||
this._localAssetRepository,
|
this._localAssetRepository,
|
||||||
this._backupRepository,
|
this._backupRepository,
|
||||||
this._appSettingsService,
|
|
||||||
this._assetMediaRepository,
|
this._assetMediaRepository,
|
||||||
) {
|
) {
|
||||||
_uploadRepository.onUploadStatus = _onUploadCallback;
|
_uploadRepository.onUploadStatus = _onUploadCallback;
|
||||||
@@ -116,7 +113,6 @@ class BackgroundUploadService {
|
|||||||
final StorageRepository _storageRepository;
|
final StorageRepository _storageRepository;
|
||||||
final DriftLocalAssetRepository _localAssetRepository;
|
final DriftLocalAssetRepository _localAssetRepository;
|
||||||
final DriftBackupRepository _backupRepository;
|
final DriftBackupRepository _backupRepository;
|
||||||
final AppSettingsService _appSettingsService;
|
|
||||||
final AssetMediaRepository _assetMediaRepository;
|
final AssetMediaRepository _assetMediaRepository;
|
||||||
final Logger _logger = Logger('BackgroundUploadService');
|
final Logger _logger = Logger('BackgroundUploadService');
|
||||||
|
|
||||||
@@ -363,15 +359,14 @@ class BackgroundUploadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldRequireWiFi(LocalAsset asset) {
|
bool _shouldRequireWiFi(LocalAsset asset) {
|
||||||
bool requiresWiFi = true;
|
final backup = MetadataRepository.instance.appConfig.backup;
|
||||||
|
if (asset.isVideo && backup.useCellularForVideos) {
|
||||||
if (asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)) {
|
return false;
|
||||||
requiresWiFi = false;
|
|
||||||
} else if (!asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)) {
|
|
||||||
requiresWiFi = false;
|
|
||||||
}
|
}
|
||||||
|
if (!asset.isVideo && backup.useCellularForPhotos) {
|
||||||
return requiresWiFi;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<UploadTask> buildUploadTask(
|
Future<UploadTask> buildUploadTask(
|
||||||
|
|||||||
@@ -7,18 +7,17 @@ import 'package:immich_mobile/domain/models/asset/asset_metadata.model.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/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.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/network_capability_extensions.dart';
|
import 'package:immich_mobile/extensions/network_capability_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
import 'package:immich_mobile/platform/connectivity_api.g.dart';
|
import 'package:immich_mobile/platform/connectivity_api.g.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/upload.repository.dart';
|
import 'package:immich_mobile/repositories/upload.repository.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:photo_manager/photo_manager.dart' show PMProgressHandler;
|
import 'package:photo_manager/photo_manager.dart' show PMProgressHandler;
|
||||||
@@ -39,7 +38,6 @@ final foregroundUploadServiceProvider = Provider((ref) {
|
|||||||
ref.watch(storageRepositoryProvider),
|
ref.watch(storageRepositoryProvider),
|
||||||
ref.watch(backupRepositoryProvider),
|
ref.watch(backupRepositoryProvider),
|
||||||
ref.watch(connectivityApiProvider),
|
ref.watch(connectivityApiProvider),
|
||||||
ref.watch(appSettingsServiceProvider),
|
|
||||||
ref.watch(assetMediaRepositoryProvider),
|
ref.watch(assetMediaRepositoryProvider),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -55,7 +53,6 @@ class ForegroundUploadService {
|
|||||||
this._storageRepository,
|
this._storageRepository,
|
||||||
this._backupRepository,
|
this._backupRepository,
|
||||||
this._connectivityApi,
|
this._connectivityApi,
|
||||||
this._appSettingsService,
|
|
||||||
this._assetMediaRepository,
|
this._assetMediaRepository,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -63,7 +60,6 @@ class ForegroundUploadService {
|
|||||||
final StorageRepository _storageRepository;
|
final StorageRepository _storageRepository;
|
||||||
final DriftBackupRepository _backupRepository;
|
final DriftBackupRepository _backupRepository;
|
||||||
final ConnectivityApi _connectivityApi;
|
final ConnectivityApi _connectivityApi;
|
||||||
final AppSettingsService _appSettingsService;
|
|
||||||
final AssetMediaRepository _assetMediaRepository;
|
final AssetMediaRepository _assetMediaRepository;
|
||||||
final Logger _logger = Logger('ForegroundUploadService');
|
final Logger _logger = Logger('ForegroundUploadService');
|
||||||
|
|
||||||
@@ -455,14 +451,13 @@ class ForegroundUploadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldRequireWiFi(LocalAsset asset) {
|
bool _shouldRequireWiFi(LocalAsset asset) {
|
||||||
bool requiresWiFi = true;
|
final backup = MetadataRepository.instance.appConfig.backup;
|
||||||
|
if (asset.isVideo && backup.useCellularForVideos) {
|
||||||
if (asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)) {
|
return false;
|
||||||
requiresWiFi = false;
|
|
||||||
} else if (!asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)) {
|
|
||||||
requiresWiFi = false;
|
|
||||||
}
|
}
|
||||||
|
if (!asset.isVideo && backup.useCellularForPhotos) {
|
||||||
return requiresWiFi;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,13 @@ Future<void> _migrateTo26(Drift drift) async {
|
|||||||
await _migrateAlbumSortMode(migrator);
|
await _migrateAlbumSortMode(migrator);
|
||||||
await migrator.migrateBool(StoreKey.legacySelectedAlbumSortReverse, MetadataKey.albumIsReverse);
|
await migrator.migrateBool(StoreKey.legacySelectedAlbumSortReverse, MetadataKey.albumIsReverse);
|
||||||
await migrator.migrateBool(StoreKey.legacyAlbumGridView, MetadataKey.albumIsGrid);
|
await migrator.migrateBool(StoreKey.legacyAlbumGridView, MetadataKey.albumIsGrid);
|
||||||
|
// Backup
|
||||||
|
await migrator.migrateBool(StoreKey.legacyEnableBackup, MetadataKey.backupEnabled);
|
||||||
|
await migrator.migrateBool(StoreKey.legacyUseWifiForUploadVideos, MetadataKey.backupUseCellularForVideos);
|
||||||
|
await migrator.migrateBool(StoreKey.legacyUseWifiForUploadPhotos, MetadataKey.backupUseCellularForPhotos);
|
||||||
|
await migrator.migrateBool(StoreKey.legacyBackupRequireCharging, MetadataKey.backupRequireCharging);
|
||||||
|
await migrator.migrateInt(StoreKey.legacyBackupTriggerDelay, MetadataKey.backupTriggerDelay);
|
||||||
|
await migrator.migrateBool(StoreKey.legacySyncAlbums, MetadataKey.backupSyncAlbums);
|
||||||
await migrator.complete();
|
await migrator.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,12 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/cast.provider.dart';
|
import 'package:immich_mobile/providers/cast.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/providers/sync_status.provider.dart';
|
import 'package:immich_mobile/providers/sync_status.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
@@ -193,64 +192,51 @@ class _BackupIndicator extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget? _getBackupBadgeIcon(BuildContext context, WidgetRef ref) {
|
Widget? _getBackupBadgeIcon(BuildContext context, WidgetRef ref) {
|
||||||
final backupStateStream = ref.watch(settingsProvider).watch(Setting.enableBackup);
|
final backupEnabled = ref.watch(appConfigProvider.select((c) => c.backup.enabled));
|
||||||
final hasError = ref.watch(driftBackupProvider.select((state) => state.error != BackupError.none));
|
final hasError = ref.watch(driftBackupProvider.select((state) => state.error != BackupError.none));
|
||||||
final isDarkTheme = context.isDarkTheme;
|
final isDarkTheme = context.isDarkTheme;
|
||||||
final iconColor = isDarkTheme ? Colors.white : Colors.black;
|
final iconColor = isDarkTheme ? Colors.white : Colors.black;
|
||||||
final isUploading = ref.watch(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty));
|
final isUploading = ref.watch(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty));
|
||||||
|
|
||||||
return StreamBuilder(
|
if (!backupEnabled) {
|
||||||
stream: backupStateStream,
|
return _BadgeLabel(
|
||||||
initialData: false,
|
Icon(Icons.cloud_off_rounded, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()),
|
||||||
builder: (ctx, snapshot) {
|
);
|
||||||
final backupEnabled = snapshot.data ?? false;
|
}
|
||||||
|
|
||||||
if (!backupEnabled) {
|
if (hasError) {
|
||||||
return _BadgeLabel(
|
return _BadgeLabel(
|
||||||
Icon(
|
Icon(
|
||||||
Icons.cloud_off_rounded,
|
Icons.warning_rounded,
|
||||||
size: 9,
|
size: 12,
|
||||||
color: iconColor,
|
color: context.colorScheme.error,
|
||||||
semanticLabel: 'backup_controller_page_backup'.tr(),
|
semanticLabel: 'backup_controller_page_backup'.tr(),
|
||||||
|
),
|
||||||
|
backgroundColor: context.colorScheme.errorContainer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUploading) {
|
||||||
|
return _BadgeLabel(
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(3.5),
|
||||||
|
child: Theme(
|
||||||
|
data: context.themeData.copyWith(
|
||||||
|
progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true),
|
||||||
),
|
),
|
||||||
);
|
child: CircularProgressIndicator(
|
||||||
}
|
strokeWidth: 2,
|
||||||
|
strokeCap: StrokeCap.round,
|
||||||
if (hasError) {
|
valueColor: AlwaysStoppedAnimation<Color>(iconColor),
|
||||||
return _BadgeLabel(
|
semanticsLabel: 'backup_controller_page_backup'.tr(),
|
||||||
Icon(
|
|
||||||
Icons.warning_rounded,
|
|
||||||
size: 12,
|
|
||||||
color: context.colorScheme.error,
|
|
||||||
semanticLabel: 'backup_controller_page_backup'.tr(),
|
|
||||||
),
|
),
|
||||||
backgroundColor: context.colorScheme.errorContainer,
|
),
|
||||||
);
|
),
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isUploading) {
|
return _BadgeLabel(
|
||||||
return _BadgeLabel(
|
Icon(Icons.check_outlined, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()),
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(3.5),
|
|
||||||
child: Theme(
|
|
||||||
data: context.themeData.copyWith(
|
|
||||||
progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true),
|
|
||||||
),
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 2,
|
|
||||||
strokeCap: StrokeCap.round,
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(iconColor),
|
|
||||||
semanticsLabel: 'backup_controller_page_backup'.tr(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _BadgeLabel(
|
|
||||||
Icon(Icons.check_outlined, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr()),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
||||||
@@ -186,7 +187,7 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
await backgroundManager.syncRemote();
|
await backgroundManager.syncRemote();
|
||||||
await backgroundManager.hashAssets();
|
await backgroundManager.hashAssets();
|
||||||
|
|
||||||
if (Store.get(StoreKey.syncAlbums, false)) {
|
if (MetadataRepository.instance.appConfig.backup.syncAlbums) {
|
||||||
await backgroundManager.syncLinkedAlbum();
|
await backgroundManager.syncLinkedAlbum();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ class ImageViewerQualitySetting extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final isPreview = useState(ref.read(appConfigProvider).image.loadPreview);
|
||||||
final isOriginal = useState(ref.read(appConfigProvider).image.loadOriginal);
|
final isOriginal = useState(ref.read(appConfigProvider).image.loadOriginal);
|
||||||
|
useValueChanged<bool, void>(isPreview.value, (_, __) {
|
||||||
|
ref.read(metadataProvider).write(.imageLoadPreview, isPreview.value);
|
||||||
|
});
|
||||||
useValueChanged<bool, void>(isOriginal.value, (_, __) {
|
useValueChanged<bool, void>(isOriginal.value, (_, __) {
|
||||||
ref.read(metadataProvider).write(.imageLoadOriginal, isOriginal.value);
|
ref.read(metadataProvider).write(.imageLoadOriginal, isOriginal.value);
|
||||||
});
|
});
|
||||||
@@ -25,6 +29,12 @@ class ImageViewerQualitySetting extends HookConsumerWidget {
|
|||||||
icon: Icons.image_outlined,
|
icon: Icons.image_outlined,
|
||||||
subtitle: "setting_image_viewer_help".t(context: context),
|
subtitle: "setting_image_viewer_help".t(context: context),
|
||||||
),
|
),
|
||||||
|
SettingsSwitchListTile(
|
||||||
|
valueNotifier: isPreview,
|
||||||
|
title: "setting_image_viewer_preview_title".t(context: context),
|
||||||
|
subtitle: "setting_image_viewer_preview_subtitle".t(context: context),
|
||||||
|
onChanged: (_) => ref.invalidate(appSettingsServiceProvider),
|
||||||
|
),
|
||||||
SettingsSwitchListTile(
|
SettingsSwitchListTile(
|
||||||
valueNotifier: isOriginal,
|
valueNotifier: isOriginal,
|
||||||
title: "setting_image_viewer_original_title".t(context: context),
|
title: "setting_image_viewer_original_title".t(context: context),
|
||||||
|
|||||||
@@ -4,18 +4,17 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/config/app_config.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
||||||
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
|
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
|
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/setting_list_tile.dart';
|
import 'package:immich_mobile/widgets/settings/setting_list_tile.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
|
import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
|
||||||
@@ -31,8 +30,8 @@ class DriftBackupSettings extends ConsumerWidget {
|
|||||||
title: "network_requirements".t(context: context),
|
title: "network_requirements".t(context: context),
|
||||||
icon: Icons.cell_tower,
|
icon: Icons.cell_tower,
|
||||||
),
|
),
|
||||||
const _UseWifiForUploadVideosButton(),
|
const _UseCellularForVideosButton(),
|
||||||
const _UseWifiForUploadPhotosButton(),
|
const _UseCellularForPhotosButton(),
|
||||||
if (CurrentPlatform.isAndroid) ...[
|
if (CurrentPlatform.isAndroid) ...[
|
||||||
const Divider(),
|
const Divider(),
|
||||||
SettingGroupTitle(
|
SettingGroupTitle(
|
||||||
@@ -99,64 +98,58 @@ class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final albumSyncEnable = ref.watch(appConfigProvider.select((c) => c.backup.syncAlbums));
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
children: [
|
children: [
|
||||||
StreamBuilder(
|
Column(
|
||||||
stream: Store.watch(StoreKey.syncAlbums),
|
children: [
|
||||||
initialData: Store.tryGet(StoreKey.syncAlbums) ?? false,
|
SettingListTile(
|
||||||
builder: (context, snapshot) {
|
title: "sync_albums".t(context: context),
|
||||||
final albumSyncEnable = snapshot.data ?? false;
|
subtitle: "sync_upload_album_setting_subtitle".t(context: context),
|
||||||
return Column(
|
trailing: Switch(
|
||||||
children: [
|
value: albumSyncEnable,
|
||||||
SettingListTile(
|
onChanged: (bool newValue) async {
|
||||||
title: "sync_albums".t(context: context),
|
await ref.read(metadataProvider).write(MetadataKey.backupSyncAlbums, newValue);
|
||||||
subtitle: "sync_upload_album_setting_subtitle".t(context: context),
|
|
||||||
trailing: Switch(
|
|
||||||
value: albumSyncEnable,
|
|
||||||
onChanged: (bool newValue) async {
|
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.syncAlbums, newValue);
|
|
||||||
|
|
||||||
if (newValue == true) {
|
if (newValue == true) {
|
||||||
await _manageLinkedAlbums();
|
await _manageLinkedAlbums();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
AnimatedSize(
|
AnimatedSize(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
opacity: albumSyncEnable ? 1.0 : 0.0,
|
opacity: albumSyncEnable ? 1.0 : 0.0,
|
||||||
child: albumSyncEnable
|
child: albumSyncEnable
|
||||||
? SettingListTile(
|
? SettingListTile(
|
||||||
onTap: _manualSyncAlbums,
|
onTap: _manualSyncAlbums,
|
||||||
contentPadding: const EdgeInsets.only(left: 32, right: 16),
|
contentPadding: const EdgeInsets.only(left: 32, right: 16),
|
||||||
title: "organize_into_albums".t(context: context),
|
title: "organize_into_albums".t(context: context),
|
||||||
subtitle: "organize_into_albums_description".t(context: context),
|
subtitle: "organize_into_albums_description".t(context: context),
|
||||||
trailing: isAlbumSyncInProgress
|
trailing: isAlbumSyncInProgress
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
||||||
)
|
)
|
||||||
: IconButton(
|
: IconButton(
|
||||||
onPressed: _manualSyncAlbums,
|
onPressed: _manualSyncAlbums,
|
||||||
icon: const Icon(Icons.sync_rounded),
|
icon: const Icon(Icons.sync_rounded),
|
||||||
color: context.colorScheme.onSurface.withValues(alpha: 0.7),
|
color: context.colorScheme.onSurface.withValues(alpha: 0.7),
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
|
constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -164,60 +157,34 @@ class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SettingsSwitchTile extends ConsumerStatefulWidget {
|
class _BackupSwitchTile extends ConsumerWidget {
|
||||||
final AppSettingsEnum<bool> appSettingsEnum;
|
final MetadataKey<bool> metadataKey;
|
||||||
|
final bool Function(AppConfig) selector;
|
||||||
final String titleKey;
|
final String titleKey;
|
||||||
final String subtitleKey;
|
final String subtitleKey;
|
||||||
final void Function(bool?)? onChanged;
|
final void Function(bool)? onChanged;
|
||||||
|
|
||||||
const _SettingsSwitchTile({
|
const _BackupSwitchTile({
|
||||||
required this.appSettingsEnum,
|
required this.metadataKey,
|
||||||
|
required this.selector,
|
||||||
required this.titleKey,
|
required this.titleKey,
|
||||||
required this.subtitleKey,
|
required this.subtitleKey,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState createState() => _SettingsSwitchTileState();
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
}
|
final value = ref.watch(appConfigProvider.select(selector));
|
||||||
|
|
||||||
class _SettingsSwitchTileState extends ConsumerState<_SettingsSwitchTile> {
|
|
||||||
late final Stream<bool?> valueStream;
|
|
||||||
late final StreamSubscription<bool?> subscription;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
valueStream = Store.watch(widget.appSettingsEnum.storeKey).asBroadcastStream();
|
|
||||||
subscription = valueStream.listen((value) {
|
|
||||||
widget.onChanged?.call(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
subscription.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
child: SettingListTile(
|
child: SettingListTile(
|
||||||
title: widget.titleKey.t(context: context),
|
title: titleKey.t(context: context),
|
||||||
subtitle: widget.subtitleKey.t(context: context),
|
subtitle: subtitleKey.t(context: context),
|
||||||
trailing: StreamBuilder(
|
trailing: Switch(
|
||||||
stream: valueStream,
|
value: value,
|
||||||
initialData: Store.tryGet(widget.appSettingsEnum.storeKey) ?? widget.appSettingsEnum.defaultValue,
|
onChanged: (bool newValue) async {
|
||||||
builder: (context, snapshot) {
|
await ref.read(metadataProvider).write(metadataKey, newValue);
|
||||||
final value = snapshot.data ?? false;
|
onChanged?.call(newValue);
|
||||||
return Switch(
|
|
||||||
value: value,
|
|
||||||
onChanged: (bool newValue) async {
|
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(widget.appSettingsEnum, newValue);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -225,26 +192,28 @@ class _SettingsSwitchTileState extends ConsumerState<_SettingsSwitchTile> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UseWifiForUploadVideosButton extends ConsumerWidget {
|
class _UseCellularForVideosButton extends StatelessWidget {
|
||||||
const _UseWifiForUploadVideosButton();
|
const _UseCellularForVideosButton();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context) {
|
||||||
return const _SettingsSwitchTile(
|
return _BackupSwitchTile(
|
||||||
appSettingsEnum: AppSettingsEnum.useCellularForUploadVideos,
|
metadataKey: MetadataKey.backupUseCellularForVideos,
|
||||||
|
selector: (c) => c.backup.useCellularForVideos,
|
||||||
titleKey: "videos",
|
titleKey: "videos",
|
||||||
subtitleKey: "network_requirement_videos_upload",
|
subtitleKey: "network_requirement_videos_upload",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UseWifiForUploadPhotosButton extends ConsumerWidget {
|
class _UseCellularForPhotosButton extends StatelessWidget {
|
||||||
const _UseWifiForUploadPhotosButton();
|
const _UseCellularForPhotosButton();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context) {
|
||||||
return const _SettingsSwitchTile(
|
return _BackupSwitchTile(
|
||||||
appSettingsEnum: AppSettingsEnum.useCellularForUploadPhotos,
|
metadataKey: MetadataKey.backupUseCellularForPhotos,
|
||||||
|
selector: (c) => c.backup.useCellularForPhotos,
|
||||||
titleKey: "photos",
|
titleKey: "photos",
|
||||||
subtitleKey: "network_requirement_photos_upload",
|
subtitleKey: "network_requirement_photos_upload",
|
||||||
);
|
);
|
||||||
@@ -256,29 +225,22 @@ class _BackupOnlyWhenChargingButton extends ConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
return _SettingsSwitchTile(
|
final fgService = ref.read(backgroundWorkerFgServiceProvider);
|
||||||
appSettingsEnum: AppSettingsEnum.backupRequireCharging,
|
return _BackupSwitchTile(
|
||||||
|
metadataKey: MetadataKey.backupRequireCharging,
|
||||||
|
selector: (c) => c.backup.requireCharging,
|
||||||
titleKey: "charging",
|
titleKey: "charging",
|
||||||
subtitleKey: "charging_requirement_mobile_backup",
|
subtitleKey: "charging_requirement_mobile_backup",
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
ref.read(backgroundWorkerFgServiceProvider).configure(requireCharging: value ?? false);
|
fgService.configure(requireCharging: value);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BackupDelaySlider extends ConsumerStatefulWidget {
|
class _BackupDelaySlider extends ConsumerWidget {
|
||||||
const _BackupDelaySlider();
|
const _BackupDelaySlider();
|
||||||
|
|
||||||
@override
|
|
||||||
ConsumerState<_BackupDelaySlider> createState() => _BackupDelaySliderState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BackupDelaySliderState extends ConsumerState<_BackupDelaySlider> {
|
|
||||||
late final Stream<int?> valueStream;
|
|
||||||
late final StreamSubscription<int?> subscription;
|
|
||||||
late int currentValue;
|
|
||||||
|
|
||||||
static int backupDelayToSliderValue(int ms) => switch (ms) {
|
static int backupDelayToSliderValue(int ms) => switch (ms) {
|
||||||
5 => 0,
|
5 => 0,
|
||||||
30 => 1,
|
30 => 1,
|
||||||
@@ -301,30 +263,9 @@ class _BackupDelaySliderState extends ConsumerState<_BackupDelaySlider> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
super.initState();
|
final triggerDelay = ref.watch(appConfigProvider.select((c) => c.backup.triggerDelay));
|
||||||
final initialValue =
|
final currentValue = backupDelayToSliderValue(triggerDelay);
|
||||||
Store.tryGet(AppSettingsEnum.backupTriggerDelay.storeKey) ?? AppSettingsEnum.backupTriggerDelay.defaultValue;
|
|
||||||
currentValue = backupDelayToSliderValue(initialValue);
|
|
||||||
|
|
||||||
valueStream = Store.watch(AppSettingsEnum.backupTriggerDelay.storeKey).asBroadcastStream();
|
|
||||||
subscription = valueStream.listen((value) {
|
|
||||||
if (mounted && value != null) {
|
|
||||||
setState(() {
|
|
||||||
currentValue = backupDelayToSliderValue(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
subscription.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -339,14 +280,13 @@ class _BackupDelaySliderState extends ConsumerState<_BackupDelaySlider> {
|
|||||||
),
|
),
|
||||||
Slider(
|
Slider(
|
||||||
value: currentValue.toDouble(),
|
value: currentValue.toDouble(),
|
||||||
onChanged: (double v) {
|
onChanged: (double v) async {
|
||||||
setState(() {
|
final seconds = backupDelayToSeconds(v.toInt());
|
||||||
currentValue = v.toInt();
|
await ref.read(metadataProvider).write(MetadataKey.backupTriggerDelay, seconds);
|
||||||
});
|
|
||||||
},
|
},
|
||||||
onChangeEnd: (double v) async {
|
onChangeEnd: (double v) async {
|
||||||
final milliseconds = backupDelayToSeconds(v.toInt());
|
final seconds = backupDelayToSeconds(v.toInt());
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.backupTriggerDelay, milliseconds);
|
await ref.read(metadataProvider).write(MetadataKey.backupTriggerDelay, seconds);
|
||||||
},
|
},
|
||||||
max: 3.0,
|
max: 3.0,
|
||||||
min: 0.0,
|
min: 0.0,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import 'package:mocktail/mocktail.dart';
|
|||||||
import '../../infrastructure/repository.mock.dart';
|
import '../../infrastructure/repository.mock.dart';
|
||||||
|
|
||||||
const _kAccessToken = '#ThisIsAToken';
|
const _kAccessToken = '#ThisIsAToken';
|
||||||
const _kEnableBackup = false;
|
const _kAdvancedTroubleshooting = false;
|
||||||
const _kVersion = 2;
|
const _kVersion = 2;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@@ -22,13 +22,13 @@ void main() {
|
|||||||
mockDriftStoreRepo = MockDriftStoreRepository();
|
mockDriftStoreRepo = MockDriftStoreRepository();
|
||||||
// For generics, we need to provide fallback to each concrete type to avoid runtime errors
|
// For generics, we need to provide fallback to each concrete type to avoid runtime errors
|
||||||
registerFallbackValue(StoreKey.accessToken);
|
registerFallbackValue(StoreKey.accessToken);
|
||||||
registerFallbackValue(StoreKey.backupTriggerDelay);
|
registerFallbackValue(StoreKey.version);
|
||||||
registerFallbackValue(StoreKey.enableBackup);
|
registerFallbackValue(StoreKey.advancedTroubleshooting);
|
||||||
|
|
||||||
when(() => mockDriftStoreRepo.getAll()).thenAnswer(
|
when(() => mockDriftStoreRepo.getAll()).thenAnswer(
|
||||||
(_) async => [
|
(_) async => [
|
||||||
const StoreDto(StoreKey.accessToken, _kAccessToken),
|
const StoreDto(StoreKey.accessToken, _kAccessToken),
|
||||||
const StoreDto(StoreKey.enableBackup, _kEnableBackup),
|
const StoreDto(StoreKey.advancedTroubleshooting, _kAdvancedTroubleshooting),
|
||||||
const StoreDto(StoreKey.version, _kVersion),
|
const StoreDto(StoreKey.version, _kVersion),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -46,7 +46,7 @@ void main() {
|
|||||||
test('Populates the internal cache on init', () {
|
test('Populates the internal cache on init', () {
|
||||||
verify(() => mockDriftStoreRepo.getAll()).called(1);
|
verify(() => mockDriftStoreRepo.getAll()).called(1);
|
||||||
expect(sut.tryGet(StoreKey.accessToken), _kAccessToken);
|
expect(sut.tryGet(StoreKey.accessToken), _kAccessToken);
|
||||||
expect(sut.tryGet(StoreKey.enableBackup), _kEnableBackup);
|
expect(sut.tryGet(StoreKey.advancedTroubleshooting), _kAdvancedTroubleshooting);
|
||||||
expect(sut.tryGet(StoreKey.version), _kVersion);
|
expect(sut.tryGet(StoreKey.version), _kVersion);
|
||||||
// Other keys should be null
|
// Other keys should be null
|
||||||
expect(sut.tryGet(StoreKey.currentUser), isNull);
|
expect(sut.tryGet(StoreKey.currentUser), isNull);
|
||||||
@@ -147,7 +147,7 @@ void main() {
|
|||||||
await sut.clear();
|
await sut.clear();
|
||||||
verify(() => mockDriftStoreRepo.deleteAll()).called(1);
|
verify(() => mockDriftStoreRepo.deleteAll()).called(1);
|
||||||
expect(sut.tryGet(StoreKey.accessToken), isNull);
|
expect(sut.tryGet(StoreKey.accessToken), isNull);
|
||||||
expect(sut.tryGet(StoreKey.enableBackup), isNull);
|
expect(sut.tryGet(StoreKey.advancedTroubleshooting), isNull);
|
||||||
expect(sut.tryGet(StoreKey.version), isNull);
|
expect(sut.tryGet(StoreKey.version), isNull);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import '../../fixtures/user.stub.dart';
|
|||||||
|
|
||||||
const _kTestAccessToken = "#TestToken";
|
const _kTestAccessToken = "#TestToken";
|
||||||
const _kTestVersion = 10;
|
const _kTestVersion = 10;
|
||||||
const _kTestBackupRequireCharging = false;
|
const _kTestAdvancedTroubleshooting = false;
|
||||||
final _kTestUser = UserStub.admin;
|
final _kTestUser = UserStub.admin;
|
||||||
|
|
||||||
Future<void> _populateStore(Drift db) async {
|
Future<void> _populateStore(Drift db) async {
|
||||||
@@ -21,8 +21,8 @@ Future<void> _populateStore(Drift db) async {
|
|||||||
batch.insert(
|
batch.insert(
|
||||||
db.storeEntity,
|
db.storeEntity,
|
||||||
StoreEntityCompanion(
|
StoreEntityCompanion(
|
||||||
id: Value(StoreKey.backupRequireCharging.id),
|
id: Value(StoreKey.advancedTroubleshooting.id),
|
||||||
intValue: const Value(_kTestBackupRequireCharging ? 1 : 0),
|
intValue: const Value(_kTestAdvancedTroubleshooting ? 1 : 0),
|
||||||
stringValue: const Value(null),
|
stringValue: const Value(null),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -76,11 +76,11 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('converts bool', () async {
|
test('converts bool', () async {
|
||||||
bool? backupRequireCharging = await sut.tryGet(StoreKey.backupRequireCharging);
|
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
|
||||||
expect(backupRequireCharging, isNull);
|
expect(advancedTroubleshooting, isNull);
|
||||||
await sut.upsert(StoreKey.backupRequireCharging, _kTestBackupRequireCharging);
|
await sut.upsert(StoreKey.advancedTroubleshooting, _kTestAdvancedTroubleshooting);
|
||||||
backupRequireCharging = await sut.tryGet(StoreKey.backupRequireCharging);
|
advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
|
||||||
expect(backupRequireCharging, _kTestBackupRequireCharging);
|
expect(advancedTroubleshooting, _kTestAdvancedTroubleshooting);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('converts user', () async {
|
test('converts user', () async {
|
||||||
@@ -98,11 +98,11 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('delete()', () async {
|
test('delete()', () async {
|
||||||
bool? backupRequireCharging = await sut.tryGet(StoreKey.backupRequireCharging);
|
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
|
||||||
expect(backupRequireCharging, isFalse);
|
expect(advancedTroubleshooting, isFalse);
|
||||||
await sut.delete(StoreKey.backupRequireCharging);
|
await sut.delete(StoreKey.advancedTroubleshooting);
|
||||||
backupRequireCharging = await sut.tryGet(StoreKey.backupRequireCharging);
|
advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
|
||||||
expect(backupRequireCharging, isNull);
|
expect(advancedTroubleshooting, isNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('deleteAll()', () async {
|
test('deleteAll()', () async {
|
||||||
@@ -147,13 +147,13 @@ void main() {
|
|||||||
emitsInOrder([
|
emitsInOrder([
|
||||||
[
|
[
|
||||||
const StoreDto<Object>(StoreKey.version, _kTestVersion),
|
const StoreDto<Object>(StoreKey.version, _kTestVersion),
|
||||||
const StoreDto<Object>(StoreKey.backupRequireCharging, _kTestBackupRequireCharging),
|
|
||||||
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
|
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
|
||||||
|
const StoreDto<Object>(StoreKey.advancedTroubleshooting, _kTestAdvancedTroubleshooting),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
const StoreDto<Object>(StoreKey.version, _kTestVersion + 10),
|
const StoreDto<Object>(StoreKey.version, _kTestVersion + 10),
|
||||||
const StoreDto<Object>(StoreKey.backupRequireCharging, _kTestBackupRequireCharging),
|
|
||||||
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
|
const StoreDto<Object>(StoreKey.accessToken, _kTestAccessToken),
|
||||||
|
const StoreDto<Object>(StoreKey.advancedTroubleshooting, _kTestAdvancedTroubleshooting),
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ void main() {
|
|||||||
late MockApiService apiService;
|
late MockApiService apiService;
|
||||||
late MockNetworkService networkService;
|
late MockNetworkService networkService;
|
||||||
late MockBackgroundSyncManager backgroundSyncManager;
|
late MockBackgroundSyncManager backgroundSyncManager;
|
||||||
late MockAppSettingService appSettingsService;
|
|
||||||
late Drift db;
|
late Drift db;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
@@ -30,15 +29,12 @@ void main() {
|
|||||||
apiService = MockApiService();
|
apiService = MockApiService();
|
||||||
networkService = MockNetworkService();
|
networkService = MockNetworkService();
|
||||||
backgroundSyncManager = MockBackgroundSyncManager();
|
backgroundSyncManager = MockBackgroundSyncManager();
|
||||||
appSettingsService = MockAppSettingService();
|
|
||||||
|
|
||||||
sut = AuthService(
|
sut = AuthService(
|
||||||
authApiRepository,
|
authApiRepository,
|
||||||
authRepository,
|
authRepository,
|
||||||
apiService,
|
apiService,
|
||||||
networkService,
|
networkService,
|
||||||
backgroundSyncManager,
|
backgroundSyncManager,
|
||||||
appSettingsService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
registerFallbackValue(Uri());
|
registerFallbackValue(Uri());
|
||||||
|
|||||||
@@ -13,11 +13,9 @@ import 'package:immich_mobile/entities/store.entity.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/services/background_upload.service.dart';
|
import 'package:immich_mobile/services/background_upload.service.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
|
||||||
import '../domain/service.mock.dart';
|
|
||||||
import '../fixtures/asset.stub.dart';
|
import '../fixtures/asset.stub.dart';
|
||||||
import '../infrastructure/repository.mock.dart';
|
import '../infrastructure/repository.mock.dart';
|
||||||
import '../mocks/asset_entity.mock.dart';
|
import '../mocks/asset_entity.mock.dart';
|
||||||
@@ -29,13 +27,10 @@ void main() {
|
|||||||
late MockStorageRepository mockStorageRepository;
|
late MockStorageRepository mockStorageRepository;
|
||||||
late MockDriftLocalAssetRepository mockLocalAssetRepository;
|
late MockDriftLocalAssetRepository mockLocalAssetRepository;
|
||||||
late MockDriftBackupRepository mockBackupRepository;
|
late MockDriftBackupRepository mockBackupRepository;
|
||||||
late MockAppSettingsService mockAppSettingsService;
|
|
||||||
late MockAssetMediaRepository mockAssetMediaRepository;
|
late MockAssetMediaRepository mockAssetMediaRepository;
|
||||||
late Drift db;
|
late Drift db;
|
||||||
|
|
||||||
setUpAll(() async {
|
setUpAll(() async {
|
||||||
registerFallbackValue(AppSettingsEnum.useCellularForUploadPhotos);
|
|
||||||
|
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||||
const MethodChannel('plugins.flutter.io/path_provider'),
|
const MethodChannel('plugins.flutter.io/path_provider'),
|
||||||
@@ -54,18 +49,13 @@ void main() {
|
|||||||
mockStorageRepository = MockStorageRepository();
|
mockStorageRepository = MockStorageRepository();
|
||||||
mockLocalAssetRepository = MockDriftLocalAssetRepository();
|
mockLocalAssetRepository = MockDriftLocalAssetRepository();
|
||||||
mockBackupRepository = MockDriftBackupRepository();
|
mockBackupRepository = MockDriftBackupRepository();
|
||||||
mockAppSettingsService = MockAppSettingsService();
|
|
||||||
mockAssetMediaRepository = MockAssetMediaRepository();
|
mockAssetMediaRepository = MockAssetMediaRepository();
|
||||||
|
|
||||||
when(() => mockAppSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)).thenReturn(false);
|
|
||||||
when(() => mockAppSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)).thenReturn(false);
|
|
||||||
|
|
||||||
sut = BackgroundUploadService(
|
sut = BackgroundUploadService(
|
||||||
mockUploadRepository,
|
mockUploadRepository,
|
||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -181,7 +171,6 @@ void main() {
|
|||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
addTearDown(() => sutWithV24.dispose());
|
addTearDown(() => sutWithV24.dispose());
|
||||||
@@ -232,7 +221,6 @@ void main() {
|
|||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
addTearDown(() => sutAndroid.dispose());
|
addTearDown(() => sutAndroid.dispose());
|
||||||
@@ -273,7 +261,6 @@ void main() {
|
|||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
addTearDown(() => sutWithV24.dispose());
|
addTearDown(() => sutWithV24.dispose());
|
||||||
@@ -314,7 +301,6 @@ void main() {
|
|||||||
mockStorageRepository,
|
mockStorageRepository,
|
||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockBackupRepository,
|
mockBackupRepository,
|
||||||
mockAppSettingsService,
|
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
);
|
);
|
||||||
addTearDown(() => sutWithV24.dispose());
|
addTearDown(() => sutWithV24.dispose());
|
||||||
|
|||||||
@@ -11,9 +11,6 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Album ID",
|
"description": "Album ID",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
@@ -25,9 +22,6 @@
|
|||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Asset ID (if activity is for an asset)",
|
"description": "Asset ID (if activity is for an asset)",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
@@ -38,9 +32,6 @@
|
|||||||
"name": "level",
|
"name": "level",
|
||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/components/schemas/ReactionLevel"
|
"$ref": "#/components/schemas/ReactionLevel"
|
||||||
}
|
}
|
||||||
@@ -49,9 +40,6 @@
|
|||||||
"name": "type",
|
"name": "type",
|
||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/components/schemas/ReactionType"
|
"$ref": "#/components/schemas/ReactionType"
|
||||||
}
|
}
|
||||||
@@ -61,9 +49,6 @@
|
|||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Filter by user ID",
|
"description": "Filter by user ID",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity search"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
@@ -187,9 +172,6 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Album ID",
|
"description": "Album ID",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
@@ -201,9 +183,6 @@
|
|||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"description": "Asset ID (if activity is for an asset)",
|
"description": "Asset ID (if activity is for an asset)",
|
||||||
"x-nestjs_zod-parent-metadata": {
|
|
||||||
"description": "Activity"
|
|
||||||
},
|
|
||||||
"schema": {
|
"schema": {
|
||||||
"format": "uuid",
|
"format": "uuid",
|
||||||
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$",
|
||||||
|
|||||||
@@ -36,18 +36,16 @@ const ActivityStatisticsResponseSchema = z
|
|||||||
})
|
})
|
||||||
.meta({ id: 'ActivityStatisticsResponseDto' });
|
.meta({ id: 'ActivityStatisticsResponseDto' });
|
||||||
|
|
||||||
const ActivitySchema = z
|
const ActivitySchema = z.object({
|
||||||
.object({
|
albumId: z.uuidv4().describe('Album ID'),
|
||||||
albumId: z.uuidv4().describe('Album ID'),
|
assetId: z.uuidv4().optional().describe('Asset ID (if activity is for an asset)'),
|
||||||
assetId: z.uuidv4().optional().describe('Asset ID (if activity is for an asset)'),
|
});
|
||||||
})
|
|
||||||
.describe('Activity');
|
|
||||||
|
|
||||||
const ActivitySearchSchema = ActivitySchema.extend({
|
const ActivitySearchSchema = ActivitySchema.extend({
|
||||||
type: ReactionTypeSchema.optional(),
|
type: ReactionTypeSchema.optional(),
|
||||||
level: ReactionLevelSchema.optional(),
|
level: ReactionLevelSchema.optional(),
|
||||||
userId: z.uuidv4().optional().describe('Filter by user ID'),
|
userId: z.uuidv4().optional().describe('Filter by user ID'),
|
||||||
}).describe('Activity search');
|
});
|
||||||
|
|
||||||
const ActivityCreateSchema = ActivitySchema.extend({
|
const ActivityCreateSchema = ActivitySchema.extend({
|
||||||
type: ReactionTypeSchema,
|
type: ReactionTypeSchema,
|
||||||
|
|||||||
Reference in New Issue
Block a user