mirror of
https://github.com/immich-app/immich.git
synced 2025-12-09 22:30:59 -08:00
Compare commits
1 Commits
release/ne
...
more-user-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3af54c6c9 |
@@ -639,6 +639,8 @@
|
|||||||
"cannot_update_the_description": "Cannot update the description",
|
"cannot_update_the_description": "Cannot update the description",
|
||||||
"cast": "Cast",
|
"cast": "Cast",
|
||||||
"cast_description": "Configure available cast destinations",
|
"cast_description": "Configure available cast destinations",
|
||||||
|
"cellular_data_for_photos": "Cellular data for photos",
|
||||||
|
"cellular_data_for_videos": "Cellular data for videos",
|
||||||
"change_date": "Change date",
|
"change_date": "Change date",
|
||||||
"change_description": "Change description",
|
"change_description": "Change description",
|
||||||
"change_display_order": "Change display order",
|
"change_display_order": "Change display order",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 77;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@@ -129,6 +129,8 @@
|
|||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
|
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
|
||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
);
|
||||||
path = Sync;
|
path = Sync;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -505,14 +507,10 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
name = "[CP] Copy Pods Resources";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
@@ -541,14 +539,10 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ enum StoreKey<T> {
|
|||||||
betaPromptShown<bool>._(1001),
|
betaPromptShown<bool>._(1001),
|
||||||
betaTimeline<bool>._(1002),
|
betaTimeline<bool>._(1002),
|
||||||
enableBackup<bool>._(1003),
|
enableBackup<bool>._(1003),
|
||||||
useWifiForUploadVideos<bool>._(1004),
|
useCellularForUploadVideos<bool>._(1004),
|
||||||
useWifiForUploadPhotos<bool>._(1005);
|
useCellularForUploadPhotos<bool>._(1005);
|
||||||
|
|
||||||
const StoreKey._(this.id);
|
const StoreKey._(this.id);
|
||||||
final int id;
|
final int id;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ 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/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/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
@@ -93,7 +95,11 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
|
|||||||
const _BackupCard(),
|
const _BackupCard(),
|
||||||
const _RemainderCard(),
|
const _RemainderCard(),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
const _CellularBackupStatus(),
|
||||||
|
const SizedBox(height: 4),
|
||||||
BackupToggleButton(onStart: () async => await startBackup(), onStop: () async => await stopBackup()),
|
BackupToggleButton(onStart: () async => await startBackup(), onStop: () async => await stopBackup()),
|
||||||
|
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
icon: const Icon(Icons.info_outline_rounded),
|
icon: const Icon(Icons.info_outline_rounded),
|
||||||
onPressed: () => context.pushRoute(const DriftUploadDetailRoute()),
|
onPressed: () => context.pushRoute(const DriftUploadDetailRoute()),
|
||||||
@@ -109,6 +115,64 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _CellularBackupStatus extends ConsumerWidget {
|
||||||
|
const _CellularBackupStatus();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final cellularReqForVideos = Store.watch(StoreKey.useCellularForUploadVideos);
|
||||||
|
final cellularReqForPhotos = Store.watch(StoreKey.useCellularForUploadPhotos);
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => context.pushRoute(const DriftBackupOptionsRoute()),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
StreamBuilder(
|
||||||
|
stream: cellularReqForVideos,
|
||||||
|
initialData: Store.tryGet(StoreKey.useCellularForUploadVideos) ?? false,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
return Expanded(
|
||||||
|
child: ListTile(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
leading: Icon(
|
||||||
|
snapshot.data ?? false ? Icons.check_circle : Icons.cancel_outlined,
|
||||||
|
size: 16,
|
||||||
|
color: snapshot.data ?? false ? Colors.green : context.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"cellular_data_for_videos".t(context: context),
|
||||||
|
style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceVariant),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
StreamBuilder(
|
||||||
|
stream: cellularReqForPhotos,
|
||||||
|
initialData: Store.tryGet(StoreKey.useCellularForUploadPhotos) ?? false,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
return Expanded(
|
||||||
|
child: ListTile(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
leading: Icon(
|
||||||
|
snapshot.data ?? false ? Icons.check_circle : Icons.cancel_outlined,
|
||||||
|
size: 16,
|
||||||
|
color: snapshot.data ?? false ? Colors.green : context.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
"cellular_data_for_photos".t(context: context),
|
||||||
|
style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceVariant),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _BackupAlbumSelectionCard extends ConsumerWidget {
|
class _BackupAlbumSelectionCard extends ConsumerWidget {
|
||||||
const _BackupAlbumSelectionCard();
|
const _BackupAlbumSelectionCard();
|
||||||
|
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ 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 previousWifiReqForVideos = Store.tryGet(StoreKey.useCellularForUploadVideos) ?? false;
|
||||||
final previousWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false;
|
final previousWifiReqForPhotos = Store.tryGet(StoreKey.useCellularForUploadPhotos) ?? false;
|
||||||
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 currentWifiReqForVideos = Store.tryGet(StoreKey.useCellularForUploadVideos) ?? false;
|
||||||
final currentWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false;
|
final currentWifiReqForPhotos = Store.tryGet(StoreKey.useCellularForUploadPhotos) ?? false;
|
||||||
|
|
||||||
if (currentWifiReqForVideos == previousWifiReqForVideos &&
|
if (currentWifiReqForVideos == previousWifiReqForVideos &&
|
||||||
currentWifiReqForPhotos == previousWifiReqForPhotos) {
|
currentWifiReqForPhotos == previousWifiReqForPhotos) {
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ enum AppSettingsEnum<T> {
|
|||||||
photoManagerCustomFilter<bool>(StoreKey.photoManagerCustomFilter, null, true),
|
photoManagerCustomFilter<bool>(StoreKey.photoManagerCustomFilter, null, true),
|
||||||
betaTimeline<bool>(StoreKey.betaTimeline, null, false),
|
betaTimeline<bool>(StoreKey.betaTimeline, null, false),
|
||||||
enableBackup<bool>(StoreKey.enableBackup, null, false),
|
enableBackup<bool>(StoreKey.enableBackup, null, false),
|
||||||
useCellularForUploadVideos<bool>(StoreKey.useWifiForUploadVideos, null, false),
|
useCellularForUploadVideos<bool>(StoreKey.useCellularForUploadVideos, null, false),
|
||||||
useCellularForUploadPhotos<bool>(StoreKey.useWifiForUploadPhotos, null, false),
|
useCellularForUploadPhotos<bool>(StoreKey.useCellularForUploadPhotos, null, false),
|
||||||
readonlyModeEnabled<bool>(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false);
|
readonlyModeEnabled<bool>(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false);
|
||||||
|
|
||||||
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import 'package:immich_mobile/providers/infrastructure/storage.provider.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/services/app_settings.service.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
final uploadServiceProvider = Provider((ref) {
|
final uploadServiceProvider = Provider((ref) {
|
||||||
@@ -57,6 +58,7 @@ class UploadService {
|
|||||||
|
|
||||||
Stream<TaskStatusUpdate> get taskStatusStream => _taskStatusController.stream;
|
Stream<TaskStatusUpdate> get taskStatusStream => _taskStatusController.stream;
|
||||||
Stream<TaskProgressUpdate> get taskProgressStream => _taskProgressController.stream;
|
Stream<TaskProgressUpdate> get taskProgressStream => _taskProgressController.stream;
|
||||||
|
final Logger _log = Logger('UploadService');
|
||||||
|
|
||||||
bool shouldAbortQueuingTasks = false;
|
bool shouldAbortQueuingTasks = false;
|
||||||
|
|
||||||
@@ -127,11 +129,16 @@ class UploadService {
|
|||||||
|
|
||||||
final candidates = await _backupRepository.getCandidates(userId);
|
final candidates = await _backupRepository.getCandidates(userId);
|
||||||
if (candidates.isEmpty) {
|
if (candidates.isEmpty) {
|
||||||
|
_log.info("No backup candidates found for user $userId");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_log.info("Starting backup for ${candidates.length} candidates");
|
||||||
|
onEnqueueTasks(EnqueueStatus(enqueueCount: 0, totalCount: candidates.length));
|
||||||
|
|
||||||
const batchSize = 100;
|
const batchSize = 100;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
int skippedAssets = 0;
|
||||||
for (int i = 0; i < candidates.length; i += batchSize) {
|
for (int i = 0; i < candidates.length; i += batchSize) {
|
||||||
if (shouldAbortQueuingTasks) {
|
if (shouldAbortQueuingTasks) {
|
||||||
break;
|
break;
|
||||||
@@ -144,16 +151,22 @@ class UploadService {
|
|||||||
final task = await _getUploadTask(asset);
|
final task = await _getUploadTask(asset);
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
tasks.add(task);
|
tasks.add(task);
|
||||||
|
} else {
|
||||||
|
skippedAssets++;
|
||||||
|
_log.warning("Skipped asset ${asset.id} (${asset.name}) - unable to create upload task");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tasks.isNotEmpty && !shouldAbortQueuingTasks) {
|
count += tasks.length;
|
||||||
count += tasks.length;
|
onEnqueueTasks(EnqueueStatus(enqueueCount: count, totalCount: candidates.length));
|
||||||
await enqueueTasks(tasks);
|
|
||||||
|
|
||||||
onEnqueueTasks(EnqueueStatus(enqueueCount: count, totalCount: candidates.length));
|
if (tasks.isNotEmpty && !shouldAbortQueuingTasks) {
|
||||||
|
_log.info("Enqueuing ${tasks.length} upload tasks");
|
||||||
|
await enqueueTasks(tasks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_log.info("Upload queueing completed: $count tasks enqueued, $skippedAssets assets skipped");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enqueue All does not work from the background on Android yet. This method is a temporary workaround
|
// Enqueue All does not work from the background on Android yet. This method is a temporary workaround
|
||||||
@@ -165,9 +178,14 @@ class UploadService {
|
|||||||
|
|
||||||
final candidates = await _backupRepository.getCandidates(userId);
|
final candidates = await _backupRepository.getCandidates(userId);
|
||||||
if (candidates.isEmpty) {
|
if (candidates.isEmpty) {
|
||||||
|
debugPrint("No backup candidates found for serial backup");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugPrint("Starting serial backup for ${candidates.length} candidates");
|
||||||
|
int skippedAssets = 0;
|
||||||
|
int enqueuedTasks = 0;
|
||||||
|
|
||||||
for (final asset in candidates) {
|
for (final asset in candidates) {
|
||||||
if (shouldAbortQueuingTasks) {
|
if (shouldAbortQueuingTasks) {
|
||||||
break;
|
break;
|
||||||
@@ -176,8 +194,13 @@ class UploadService {
|
|||||||
final task = await _getUploadTask(asset);
|
final task = await _getUploadTask(asset);
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
await _uploadRepository.enqueueBackground(task);
|
await _uploadRepository.enqueueBackground(task);
|
||||||
|
enqueuedTasks++;
|
||||||
|
} else {
|
||||||
|
skippedAssets++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugPrint("Serial backup completed: $enqueuedTasks tasks enqueued, $skippedAssets assets skipped");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cancel all ongoing uploads and reset the upload queue
|
/// Cancel all ongoing uploads and reset the upload queue
|
||||||
@@ -245,6 +268,7 @@ class UploadService {
|
|||||||
Future<UploadTask?> _getUploadTask(LocalAsset asset, {String group = kBackupGroup, int? priority}) async {
|
Future<UploadTask?> _getUploadTask(LocalAsset asset, {String group = kBackupGroup, int? priority}) async {
|
||||||
final entity = await _storageRepository.getAssetEntityForAsset(asset);
|
final entity = await _storageRepository.getAssetEntityForAsset(asset);
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
|
_log.warning("Cannot get AssetEntity for asset ${asset.id} (${asset.name}) created on ${asset.createdAt}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,6 +291,9 @@ class UploadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
|
_log.warning(
|
||||||
|
"Cannot get file for asset ${asset.id} (${asset.name}) created on ${asset.createdAt} - file may have been deleted or moved",
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class _UseWifiForUploadVideosButton extends ConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final valueStream = Store.watch(StoreKey.useWifiForUploadVideos);
|
final valueStream = Store.watch(StoreKey.useCellularForUploadVideos);
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
@@ -32,7 +32,7 @@ class _UseWifiForUploadVideosButton extends ConsumerWidget {
|
|||||||
subtitle: Text("network_requirement_videos_upload".t(context: context), style: context.textTheme.labelLarge),
|
subtitle: Text("network_requirement_videos_upload".t(context: context), style: context.textTheme.labelLarge),
|
||||||
trailing: StreamBuilder(
|
trailing: StreamBuilder(
|
||||||
stream: valueStream,
|
stream: valueStream,
|
||||||
initialData: Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false,
|
initialData: Store.tryGet(StoreKey.useCellularForUploadVideos) ?? false,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final value = snapshot.data ?? false;
|
final value = snapshot.data ?? false;
|
||||||
return Switch(
|
return Switch(
|
||||||
@@ -54,7 +54,7 @@ class _UseWifiForUploadPhotosButton extends ConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final valueStream = Store.watch(StoreKey.useWifiForUploadPhotos);
|
final valueStream = Store.watch(StoreKey.useCellularForUploadPhotos);
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
@@ -64,7 +64,7 @@ class _UseWifiForUploadPhotosButton extends ConsumerWidget {
|
|||||||
subtitle: Text("network_requirement_photos_upload".t(context: context), style: context.textTheme.labelLarge),
|
subtitle: Text("network_requirement_photos_upload".t(context: context), style: context.textTheme.labelLarge),
|
||||||
trailing: StreamBuilder(
|
trailing: StreamBuilder(
|
||||||
stream: valueStream,
|
stream: valueStream,
|
||||||
initialData: Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false,
|
initialData: Store.tryGet(StoreKey.useCellularForUploadPhotos) ?? false,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final value = snapshot.data ?? false;
|
final value = snapshot.data ?? false;
|
||||||
return Switch(
|
return Switch(
|
||||||
|
|||||||
Reference in New Issue
Block a user