mirror of
https://github.com/immich-app/immich.git
synced 2026-05-23 08:02:29 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 98c7347496 |
@@ -0,0 +1,13 @@
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'memory_auto_play.provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
class MemoryAutoPlay extends _$MemoryAutoPlay {
|
||||
@override
|
||||
bool build() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void toggleAutoPlay() => state = !state;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'memory_auto_play.provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$memoryAutoPlayHash() => r'62b2133258680a0f842a58232bf43a4d3c83e26d';
|
||||
|
||||
/// See also [MemoryAutoPlay].
|
||||
@ProviderFor(MemoryAutoPlay)
|
||||
final memoryAutoPlayProvider =
|
||||
AutoDisposeNotifierProvider<MemoryAutoPlay, bool>.internal(
|
||||
MemoryAutoPlay.new,
|
||||
name: r'memoryAutoPlayProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$memoryAutoPlayHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$MemoryAutoPlay = AutoDisposeNotifier<bool>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/memories/models/memory.dart';
|
||||
import 'package:immich_mobile/modules/memories/providers/memory_auto_play.provider.dart';
|
||||
|
||||
class MemoryBottomInfo extends StatelessWidget {
|
||||
final Memory memory;
|
||||
@@ -13,6 +15,7 @@ class MemoryBottomInfo extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -37,6 +40,22 @@ class MemoryBottomInfo extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
Consumer(
|
||||
builder: (_, ref, __) => MaterialButton(
|
||||
minWidth: 0,
|
||||
onPressed: () =>
|
||||
ref.read(memoryAutoPlayProvider.notifier).toggleAutoPlay(),
|
||||
shape: const CircleBorder(),
|
||||
color: Colors.white.withOpacity(0.2),
|
||||
elevation: 0,
|
||||
child: Icon(
|
||||
ref.watch(memoryAutoPlayProvider)
|
||||
? Icons.pause_circle_outline_rounded
|
||||
: Icons.play_circle_outline_rounded,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/asset_viewer/views/video_viewer_page.dart';
|
||||
import 'package:immich_mobile/modules/memories/providers/memory_auto_play.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/ui/hooks/blurhash_hook.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_image.dart';
|
||||
|
||||
class MemoryCard extends StatelessWidget {
|
||||
class MemoryCard extends HookConsumerWidget {
|
||||
final Asset asset;
|
||||
final String title;
|
||||
final bool showTitle;
|
||||
@@ -23,12 +28,48 @@ class MemoryCard extends StatelessWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final animationDuration = useRef(
|
||||
ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting(AppSettingsEnum.memoryAutoPlayDuration),
|
||||
);
|
||||
|
||||
final animation = useAnimationController(
|
||||
duration: Duration(seconds: animationDuration.value + 1),
|
||||
);
|
||||
|
||||
const scale = 1.2;
|
||||
final shouldZoom = Random().nextBool();
|
||||
final identity = Matrix4.identity();
|
||||
final scaled = Matrix4.identity()..scale(scale);
|
||||
final beginTransform = shouldZoom ? identity : scaled;
|
||||
final endTransform = shouldZoom ? scaled : identity;
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
if (ref.read(memoryAutoPlayProvider)) {
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => animation.forward());
|
||||
}
|
||||
return null;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
ref.listen(memoryAutoPlayProvider, (_, value) {
|
||||
if (!value) {
|
||||
animation.stop();
|
||||
} else {
|
||||
animation.forward();
|
||||
}
|
||||
});
|
||||
|
||||
return Card(
|
||||
color: Colors.black,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(25.0),
|
||||
side: const BorderSide(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25.0)),
|
||||
side: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 1.0,
|
||||
),
|
||||
@@ -58,11 +99,24 @@ class MemoryCard extends StatelessWidget {
|
||||
if (asset.isImage) {
|
||||
return Hero(
|
||||
tag: 'memory-${asset.id}',
|
||||
child: ImmichImage(
|
||||
asset,
|
||||
fit: fit,
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
child: AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (_, child) => Container(
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
transform: Matrix4Tween(
|
||||
begin: beginTransform,
|
||||
end: endTransform,
|
||||
).evaluate(animation),
|
||||
transformAlignment: Alignment.center,
|
||||
child: child,
|
||||
),
|
||||
child: ImmichImage(
|
||||
asset,
|
||||
fit: fit,
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||
import 'package:immich_mobile/modules/memories/providers/memory_auto_play.provider.dart';
|
||||
|
||||
class MemoryProgressIndicator extends StatelessWidget {
|
||||
/// The number of ticks in the progress indicator
|
||||
final int ticks;
|
||||
|
||||
/// The current value of the indicator
|
||||
final double value;
|
||||
/// The current index of memory
|
||||
final int value;
|
||||
|
||||
/// The duration to animate the current tick
|
||||
final int animationDuration;
|
||||
|
||||
const MemoryProgressIndicator({
|
||||
super.key,
|
||||
required this.ticks,
|
||||
required this.value,
|
||||
required this.animationDuration,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -19,36 +26,107 @@ class MemoryProgressIndicator extends StatelessWidget {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final tickWidth = constraints.maxWidth / ticks;
|
||||
return ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(2.0)),
|
||||
child: Stack(
|
||||
children: [
|
||||
LinearProgressIndicator(
|
||||
value: value,
|
||||
backgroundColor: Colors.grey[600],
|
||||
color: immichDarkThemePrimaryColor,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(
|
||||
ticks,
|
||||
(i) => Container(
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(
|
||||
ticks,
|
||||
(i) => i > value
|
||||
? _NonAnimatingTick(
|
||||
width: tickWidth,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
border: i == 0
|
||||
? null
|
||||
: const Border(
|
||||
left: BorderSide(
|
||||
color: Colors.black,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
filled: false,
|
||||
)
|
||||
: i < value
|
||||
? _NonAnimatingTick(
|
||||
width: tickWidth,
|
||||
filled: true,
|
||||
)
|
||||
: _AnimatingTick(
|
||||
width: tickWidth,
|
||||
duration: animationDuration,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NonAnimatingTick extends StatelessWidget {
|
||||
final double width;
|
||||
final bool filled;
|
||||
|
||||
const _NonAnimatingTick({required this.width, required this.filled});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: width,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: filled ? immichDarkThemePrimaryColor : Colors.grey,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
border: const Border(
|
||||
left: BorderSide(color: Colors.black, width: 1),
|
||||
right: BorderSide(color: Colors.black, width: 1),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AnimatingTick extends HookConsumerWidget {
|
||||
final double width;
|
||||
final int duration;
|
||||
|
||||
const _AnimatingTick({
|
||||
required this.width,
|
||||
required this.duration,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final animationController =
|
||||
useAnimationController(duration: Duration(seconds: duration));
|
||||
|
||||
useEffect(
|
||||
() {
|
||||
if (ref.read(memoryAutoPlayProvider)) {
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => animationController.forward());
|
||||
}
|
||||
return null;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
ref.listen(memoryAutoPlayProvider, (_, value) {
|
||||
if (!value) {
|
||||
animationController.stop();
|
||||
} else {
|
||||
animationController.forward();
|
||||
}
|
||||
});
|
||||
|
||||
return AnimatedBuilder(
|
||||
animation: animationController,
|
||||
builder: (_, __) {
|
||||
final filledWidth =
|
||||
Tween(begin: 0.0, end: width).evaluate(animationController);
|
||||
return Container(
|
||||
width: width,
|
||||
height: 4,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
border: Border(
|
||||
left: BorderSide(color: Colors.black, width: 1),
|
||||
right: BorderSide(color: Colors.black, width: 1),
|
||||
),
|
||||
),
|
||||
child: LinearProgressIndicator(
|
||||
value: filledWidth / width,
|
||||
backgroundColor: Colors.grey,
|
||||
color: immichDarkThemePrimaryColor,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/memories/models/memory.dart';
|
||||
import 'package:immich_mobile/modules/memories/providers/memory_auto_play.provider.dart';
|
||||
import 'package:immich_mobile/modules/memories/ui/memory_bottom_info.dart';
|
||||
import 'package:immich_mobile/modules/memories/ui/memory_card.dart';
|
||||
import 'package:immich_mobile/modules/memories/ui/memory_epilogue.dart';
|
||||
import 'package:immich_mobile/modules/memories/ui/memory_progress_indicator.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_image.dart';
|
||||
|
||||
@@ -24,13 +30,17 @@ class MemoryPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final currentMemory = useState(memories[memoryIndex]);
|
||||
final currentAssetPage = useState(0);
|
||||
final currentMemory = useRef(memories[memoryIndex]);
|
||||
final currentAssetPage = useRef(0);
|
||||
final currentMemoryIndex = useState(memoryIndex);
|
||||
final assetProgress = useState(
|
||||
"${currentAssetPage.value + 1}|${currentMemory.value.assets.length}",
|
||||
);
|
||||
final memoryTimer = useRef<Timer?>(null);
|
||||
final memoryStopWatch = useRef<Stopwatch?>(null);
|
||||
const bgColor = Colors.black;
|
||||
final animationDuration = useRef(
|
||||
ref
|
||||
.read(appSettingsServiceProvider)
|
||||
.getSetting(AppSettingsEnum.memoryAutoPlayDuration),
|
||||
);
|
||||
|
||||
/// The list of all of the asset page controllers
|
||||
final memoryAssetPageControllers =
|
||||
@@ -39,13 +49,6 @@ class MemoryPage extends HookConsumerWidget {
|
||||
/// The main vertically scrolling page controller with each list of memories
|
||||
final memoryPageController = usePageController(initialPage: memoryIndex);
|
||||
|
||||
// The Page Controller that scrolls horizontally with all of the assets
|
||||
useEffect(() {
|
||||
// Memories is an immersive activity
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
||||
return null;
|
||||
});
|
||||
|
||||
toNextMemory() {
|
||||
memoryPageController.nextPage(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
@@ -69,11 +72,6 @@ class MemoryPage extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
updateProgressText() {
|
||||
assetProgress.value =
|
||||
"${currentAssetPage.value + 1}|${currentMemory.value.assets.length}";
|
||||
}
|
||||
|
||||
/// Downloads and caches the image for the asset at this [currentMemory]'s index
|
||||
precacheAsset(int index) async {
|
||||
// Guard index out of range
|
||||
@@ -124,16 +122,74 @@ class MemoryPage extends HookConsumerWidget {
|
||||
.then((_) => precacheAsset(1));
|
||||
}
|
||||
|
||||
Future<void> onAssetChanged(int otherIndex) async {
|
||||
int getAutoPlayDuration() {
|
||||
final currentAsset = currentMemory.value.assets[currentAssetPage.value];
|
||||
return currentAsset.isImage
|
||||
? animationDuration.value
|
||||
: math.max(
|
||||
currentAsset.durationInSeconds + 2,
|
||||
animationDuration.value,
|
||||
);
|
||||
}
|
||||
|
||||
void resetTimer([int? remainingTime]) {
|
||||
final isEpiloguePage =
|
||||
(memoryPageController.page?.floor() ?? 0) >= memories.length;
|
||||
|
||||
memoryTimer.value?.cancel();
|
||||
memoryStopWatch.value?.reset();
|
||||
if (isEpiloguePage) {
|
||||
memoryTimer.value = null;
|
||||
memoryStopWatch.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
memoryTimer.value = Timer(
|
||||
Duration(
|
||||
seconds: remainingTime ?? getAutoPlayDuration(),
|
||||
),
|
||||
() => toNextAsset(currentAssetPage.value),
|
||||
);
|
||||
if (ref.read(memoryAutoPlayProvider)) {
|
||||
memoryStopWatch.value = Stopwatch()..start();
|
||||
}
|
||||
}
|
||||
|
||||
onAssetChanged(int otherIndex) async {
|
||||
HapticFeedback.selectionClick();
|
||||
currentAssetPage.value = otherIndex;
|
||||
updateProgressText();
|
||||
// Wait for page change animation to finish
|
||||
await Future.delayed(const Duration(milliseconds: 400));
|
||||
// And then precache the next asset
|
||||
await precacheAsset(otherIndex + 1);
|
||||
precacheAsset(otherIndex + 1);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => resetTimer());
|
||||
}
|
||||
|
||||
ref.listen(memoryAutoPlayProvider, (_, value) {
|
||||
if (!value) {
|
||||
memoryTimer.value?.cancel();
|
||||
memoryStopWatch.value?.stop();
|
||||
} else {
|
||||
final elapsedSeconds = memoryStopWatch.value?.elapsed.inSeconds;
|
||||
final remaining = getAutoPlayDuration() - (elapsedSeconds ?? 0);
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => resetTimer(remaining));
|
||||
}
|
||||
});
|
||||
|
||||
// The Page Controller that scrolls horizontally with all of the assets
|
||||
useEffect(
|
||||
() {
|
||||
// Memories is an immersive activity
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => resetTimer());
|
||||
return () {
|
||||
memoryTimer.value?.cancel();
|
||||
memoryStopWatch.value?.stop();
|
||||
};
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
/* Notification listener is used instead of OnPageChanged callback since OnPageChanged is called
|
||||
* when the page in the **center** of the viewer changes. We want to reset currentAssetPage only when the final
|
||||
* page during the end of scroll is different than the current page
|
||||
@@ -160,10 +216,9 @@ class MemoryPage extends HookConsumerWidget {
|
||||
child: Scaffold(
|
||||
backgroundColor: bgColor,
|
||||
body: PopScope(
|
||||
onPopInvoked: (didPop) {
|
||||
// Remove immersive mode and go back to normal mode
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
},
|
||||
onPopInvoked: (_) =>
|
||||
// Remove immersive mode and go back to normal mode
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge),
|
||||
child: SafeArea(
|
||||
child: PageView.builder(
|
||||
physics: const BouncingScrollPhysics(
|
||||
@@ -173,14 +228,14 @@ class MemoryPage extends HookConsumerWidget {
|
||||
controller: memoryPageController,
|
||||
onPageChanged: (pageNumber) {
|
||||
HapticFeedback.mediumImpact();
|
||||
currentAssetPage.value = 0;
|
||||
|
||||
if (pageNumber < memories.length) {
|
||||
currentMemoryIndex.value = pageNumber;
|
||||
currentMemory.value = memories[pageNumber];
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => resetTimer());
|
||||
}
|
||||
|
||||
currentAssetPage.value = 0;
|
||||
|
||||
updateProgressText();
|
||||
},
|
||||
itemCount: memories.length + 1,
|
||||
itemBuilder: (context, mIndex) {
|
||||
@@ -208,14 +263,10 @@ class MemoryPage extends HookConsumerWidget {
|
||||
child: AnimatedBuilder(
|
||||
animation: assetController,
|
||||
builder: (context, child) {
|
||||
double value = 0.0;
|
||||
if (assetController.hasClients) {
|
||||
// We can only access [page] if this has clients
|
||||
value = assetController.page ?? 0;
|
||||
}
|
||||
return MemoryProgressIndicator(
|
||||
ticks: memories[mIndex].assets.length,
|
||||
value: (value + 1) / memories[mIndex].assets.length,
|
||||
value: currentAssetPage.value,
|
||||
animationDuration: getAutoPlayDuration(),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -235,9 +286,7 @@ class MemoryPage extends HookConsumerWidget {
|
||||
final asset = memories[mIndex].assets[index];
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () {
|
||||
toNextAsset(index);
|
||||
},
|
||||
onTap: () => toNextAsset(index),
|
||||
child: Container(
|
||||
color: Colors.black,
|
||||
child: MemoryCard(
|
||||
|
||||
@@ -57,6 +57,7 @@ enum AppSettingsEnum<T> {
|
||||
null,
|
||||
false,
|
||||
),
|
||||
memoryAutoPlayDuration<int>(StoreKey.memoryAutoPlayDuration, null, 5),
|
||||
;
|
||||
|
||||
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
||||
|
||||
@@ -21,6 +21,8 @@ class AdvancedSettings extends HookConsumerWidget {
|
||||
final isEnabled =
|
||||
useState(AppSettingsEnum.advancedTroubleshooting.defaultValue);
|
||||
final levelId = useState(AppSettingsEnum.logLevel.defaultValue);
|
||||
final memoryAutoPlayDuration =
|
||||
useState(AppSettingsEnum.memoryAutoPlayDuration.defaultValue);
|
||||
final preferRemote =
|
||||
useState(AppSettingsEnum.preferRemoteImage.defaultValue);
|
||||
final allowSelfSignedSSLCert =
|
||||
@@ -34,6 +36,8 @@ class AdvancedSettings extends HookConsumerWidget {
|
||||
levelId.value = appSettingService.getSetting(AppSettingsEnum.logLevel);
|
||||
preferRemote.value =
|
||||
appSettingService.getSetting(AppSettingsEnum.preferRemoteImage);
|
||||
memoryAutoPlayDuration.value = appSettingService
|
||||
.getSetting(AppSettingsEnum.memoryAutoPlayDuration);
|
||||
allowSelfSignedSSLCert.value = appSettingService
|
||||
.getSetting(AppSettingsEnum.allowSelfSignedSSLCert);
|
||||
return null;
|
||||
@@ -84,6 +88,26 @@ class AdvancedSettings extends HookConsumerWidget {
|
||||
activeColor: context.primaryColor,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
title: Text(
|
||||
"Memory auto play duration: ${memoryAutoPlayDuration.value}s",
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
).tr(),
|
||||
subtitle: Slider(
|
||||
value: memoryAutoPlayDuration.value.toDouble(),
|
||||
onChanged: (double v) => memoryAutoPlayDuration.value = v.toInt(),
|
||||
onChangeEnd: (double v) => appSettingService.setSetting(
|
||||
AppSettingsEnum.memoryAutoPlayDuration,
|
||||
v.toInt(),
|
||||
),
|
||||
max: 5,
|
||||
min: 1.0,
|
||||
divisions: 5,
|
||||
label: "${memoryAutoPlayDuration.value}",
|
||||
activeColor: context.primaryColor,
|
||||
),
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
appSettingService: appSettingService,
|
||||
valueNotifier: preferRemote,
|
||||
|
||||
@@ -190,6 +190,7 @@ enum StoreKey<T> {
|
||||
ignoreIcloudAssets<bool>(122, type: bool),
|
||||
selectedAlbumSortReverse<bool>(123, type: bool),
|
||||
mapThemeMode<int>(124, type: int),
|
||||
memoryAutoPlayDuration<int>(125, type: int),
|
||||
;
|
||||
|
||||
const StoreKey(
|
||||
|
||||
Reference in New Issue
Block a user