Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| edba462faf | |||
| d21964c0cc | |||
| bfab33f9d4 | |||
| 0c36eb1f8b | |||
| aa90229c84 |
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "machine-learning"
|
name = "machine-learning"
|
||||||
version = "1.94.1"
|
version = "1.94.0"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
authors = ["Hau Tran <alex.tran1502@gmail.com>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ platform :android do
|
|||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 120,
|
"android.injected.version.code" => 120,
|
||||||
"android.injected.version.name" => "1.94.1",
|
"android.injected.version.name" => "1.94.0",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000307">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000253">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="94.411929">
|
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="74.926686">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="29.826675">
|
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="28.933555">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
@@ -180,4 +180,4 @@ SPEC CHECKSUMS:
|
|||||||
|
|
||||||
PODFILE CHECKSUM: 64c9b5291666c0ca3caabdfe9865c141ac40321d
|
PODFILE CHECKSUM: 64c9b5291666c0ca3caabdfe9865c141ac40321d
|
||||||
|
|
||||||
COCOAPODS: 1.12.1
|
COCOAPODS: 1.11.3
|
||||||
|
|||||||
@@ -379,7 +379,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 136;
|
CURRENT_PROJECT_VERSION = 134;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -515,7 +515,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 136;
|
CURRENT_PROJECT_VERSION = 134;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -543,7 +543,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 136;
|
CURRENT_PROJECT_VERSION = 134;
|
||||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
|||||||
@@ -55,11 +55,11 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.94.0</string>
|
<string>1.93.0</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>136</string>
|
<string>134</string>
|
||||||
<key>FLTEnableImpeller</key>
|
<key>FLTEnableImpeller</key>
|
||||||
<true />
|
<true />
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ platform :ios do
|
|||||||
desc "iOS Beta"
|
desc "iOS Beta"
|
||||||
lane :beta do
|
lane :beta do
|
||||||
increment_version_number(
|
increment_version_number(
|
||||||
version_number: "1.94.1"
|
version_number: "1.94.0"
|
||||||
)
|
)
|
||||||
increment_build_number(
|
increment_build_number(
|
||||||
build_number: latest_testflight_build_number + 1,
|
build_number: latest_testflight_build_number + 1,
|
||||||
|
|||||||
@@ -5,32 +5,32 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000255">
|
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.00026">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="1.040605">
|
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="0.172341">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="5.393894">
|
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="6.491104">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="0.185907">
|
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="0.217405">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="4: build_app" time="147.537526">
|
<testcase classname="fastlane.lanes" name="4: build_app" time="138.23059">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|
||||||
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="87.643366">
|
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="70.463048">
|
||||||
|
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
|||||||
@@ -12,18 +12,14 @@ import 'package:openapi/api.dart';
|
|||||||
class MemoryCard extends StatelessWidget {
|
class MemoryCard extends StatelessWidget {
|
||||||
final Asset asset;
|
final Asset asset;
|
||||||
final void Function() onTap;
|
final void Function() onTap;
|
||||||
final void Function() onClose;
|
|
||||||
final String title;
|
final String title;
|
||||||
final String? rightCornerText;
|
|
||||||
final bool showTitle;
|
final bool showTitle;
|
||||||
|
|
||||||
const MemoryCard({
|
const MemoryCard({
|
||||||
required this.asset,
|
required this.asset,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
required this.onClose,
|
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.showTitle,
|
required this.showTitle,
|
||||||
this.rightCornerText,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -65,34 +61,31 @@ class MemoryCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: ImmichImage(
|
child: LayoutBuilder(
|
||||||
asset,
|
builder: (context, constraints) {
|
||||||
fit: BoxFit.fitWidth,
|
// Determine the fit using the aspect ratio
|
||||||
height: double.infinity,
|
BoxFit fit = BoxFit.fitWidth;
|
||||||
width: double.infinity,
|
if (asset.width != null && asset.height != null) {
|
||||||
type: ThumbnailFormat.JPEG,
|
final aspectRatio = asset.height! / asset.width!;
|
||||||
preferredLocalAssetSize: 2048,
|
final phoneAspectRatio =
|
||||||
),
|
constraints.maxWidth / constraints.maxHeight;
|
||||||
),
|
// Look for a 25% difference in either direction
|
||||||
Positioned(
|
if (phoneAspectRatio * .75 < aspectRatio &&
|
||||||
top: 2.0,
|
phoneAspectRatio * 1.25 > aspectRatio) {
|
||||||
left: 2.0,
|
// Cover to look nice if we have nearly the same aspect ratio
|
||||||
child: IconButton(
|
fit = BoxFit.cover;
|
||||||
onPressed: onClose,
|
}
|
||||||
icon: const Icon(Icons.close_rounded),
|
}
|
||||||
color: Colors.grey[400],
|
|
||||||
),
|
return ImmichImage(
|
||||||
),
|
asset,
|
||||||
Positioned(
|
fit: fit,
|
||||||
right: 18.0,
|
height: double.infinity,
|
||||||
top: 18.0,
|
width: double.infinity,
|
||||||
child: Text(
|
type: ThumbnailFormat.JPEG,
|
||||||
rightCornerText ?? "",
|
preferredLocalAssetSize: 2048,
|
||||||
style: TextStyle(
|
);
|
||||||
color: Colors.grey[200],
|
},
|
||||||
fontSize: 12.0,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (showTitle)
|
if (showTitle)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class _MemoryEpilogueState extends State<MemoryEpilogue>
|
|||||||
late final _animationController = AnimationController(
|
late final _animationController = AnimationController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
duration: const Duration(
|
duration: const Duration(
|
||||||
seconds: 3,
|
seconds: 2,
|
||||||
),
|
),
|
||||||
)..repeat(
|
)..repeat(
|
||||||
reverse: true,
|
reverse: true,
|
||||||
@@ -29,7 +29,7 @@ class _MemoryEpilogueState extends State<MemoryEpilogue>
|
|||||||
super.initState();
|
super.initState();
|
||||||
_animation = CurvedAnimation(
|
_animation = CurvedAnimation(
|
||||||
parent: _animationController,
|
parent: _animationController,
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeIn,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,74 +41,82 @@ class _MemoryEpilogueState extends State<MemoryEpilogue>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return SafeArea(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
child: Stack(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: [
|
||||||
children: [
|
Positioned.fill(
|
||||||
Expanded(
|
child: Column(
|
||||||
child: Column(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: [
|
||||||
children: [
|
const Icon(
|
||||||
const Icon(
|
Icons.check_circle_outline_sharp,
|
||||||
Icons.check_circle_outline_sharp,
|
color: immichDarkThemePrimaryColor,
|
||||||
color: immichDarkThemePrimaryColor,
|
size: 64.0,
|
||||||
size: 64.0,
|
),
|
||||||
),
|
const SizedBox(height: 16.0),
|
||||||
const SizedBox(height: 16.0),
|
Text(
|
||||||
Text(
|
'All caught up',
|
||||||
'All caught up',
|
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
color: Colors.white,
|
||||||
color: Colors.white,
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16.0),
|
||||||
|
Text(
|
||||||
|
'Check back tomorrow for more memories',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16.0),
|
||||||
|
TextButton(
|
||||||
|
onPressed: widget.onStartOver,
|
||||||
|
child: Text(
|
||||||
|
'Start Over',
|
||||||
|
style: context.textTheme.displayMedium?.copyWith(
|
||||||
|
color: immichDarkThemePrimaryColor,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
const SizedBox(height: 16.0),
|
|
||||||
Text(
|
|
||||||
'Check back tomorrow for more memories',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16.0),
|
|
||||||
TextButton(
|
|
||||||
onPressed: widget.onStartOver,
|
|
||||||
child: Text(
|
|
||||||
'Start Over',
|
|
||||||
style: context.textTheme.displayMedium?.copyWith(
|
|
||||||
color: immichDarkThemePrimaryColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
Positioned(
|
||||||
Column(
|
bottom: 0,
|
||||||
children: [
|
left: 0,
|
||||||
SizedBox(
|
right: 0,
|
||||||
height: 48,
|
child: Padding(
|
||||||
child: AnimatedBuilder(
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
animation: _animation,
|
child: Column(
|
||||||
builder: (context, child) {
|
children: [
|
||||||
return Transform.translate(
|
SizedBox(
|
||||||
offset: Offset(0, 5 * _animationController.value),
|
height: 48,
|
||||||
child: child,
|
child: AnimatedBuilder(
|
||||||
);
|
animation: _animation,
|
||||||
},
|
builder: (context, child) {
|
||||||
child: const Icon(
|
return Transform.translate(
|
||||||
size: 32,
|
offset: Offset(0, 8 * _animationController.value),
|
||||||
Icons.expand_less_sharp,
|
child: child,
|
||||||
color: Colors.white,
|
);
|
||||||
),
|
},
|
||||||
|
child: const Icon(
|
||||||
|
size: 32,
|
||||||
|
Icons.expand_less_sharp,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Swipe up to close',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
'Swipe up to close',
|
],
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
),
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import 'package:flutter/material.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;
|
||||||
|
|
||||||
|
const MemoryProgressIndicator(
|
||||||
|
{super.key, required this.ticks, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final tickWidth = constraints.maxWidth / ticks;
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: List.generate(
|
||||||
|
ticks,
|
||||||
|
(i) => Container(
|
||||||
|
width: tickWidth,
|
||||||
|
height: 4,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: i == 0
|
||||||
|
? null
|
||||||
|
: Border(
|
||||||
|
left: BorderSide(
|
||||||
|
color: Theme.of(context).scaffoldBackgroundColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import 'package:immich_mobile/modules/memories/models/memory.dart';
|
|||||||
import 'package:immich_mobile/modules/memories/ui/memory_bottom_info.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_card.dart';
|
||||||
import 'package:immich_mobile/modules/memories/ui/memory_epilogue.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/shared/models/asset.dart';
|
import 'package:immich_mobile/shared/models/asset.dart';
|
||||||
import 'package:immich_mobile/shared/ui/immich_image.dart';
|
import 'package:immich_mobile/shared/ui/immich_image.dart';
|
||||||
import 'package:openapi/api.dart' as api;
|
import 'package:openapi/api.dart' as api;
|
||||||
@@ -24,15 +25,31 @@ class MemoryPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final memoryPageController = usePageController(initialPage: memoryIndex);
|
|
||||||
final memoryAssetPageController = usePageController();
|
|
||||||
final currentMemory = useState(memories[memoryIndex]);
|
final currentMemory = useState(memories[memoryIndex]);
|
||||||
final currentAssetPage = useState(0);
|
final currentAssetPage = useState(0);
|
||||||
|
final currentMemoryIndex = useState(0);
|
||||||
final assetProgress = useState(
|
final assetProgress = useState(
|
||||||
"${currentAssetPage.value + 1}|${currentMemory.value.assets.length}",
|
"${currentAssetPage.value + 1}|${currentMemory.value.assets.length}",
|
||||||
);
|
);
|
||||||
const bgColor = Colors.black;
|
const bgColor = Colors.black;
|
||||||
|
|
||||||
|
/// The list of all of the asset page controllers
|
||||||
|
final memoryAssetPageControllers =
|
||||||
|
List.generate(memories.length, (i) => usePageController());
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
PageController currentMemoryAssetPageController =
|
||||||
|
memoryAssetPageControllers[currentMemoryIndex.value];
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
// Memories is an immersive activity
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
toNextMemory() {
|
toNextMemory() {
|
||||||
memoryPageController.nextPage(
|
memoryPageController.nextPage(
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
@@ -43,7 +60,7 @@ class MemoryPage extends HookConsumerWidget {
|
|||||||
toNextAsset(int currentAssetIndex) {
|
toNextAsset(int currentAssetIndex) {
|
||||||
if (currentAssetIndex + 1 < currentMemory.value.assets.length) {
|
if (currentAssetIndex + 1 < currentMemory.value.assets.length) {
|
||||||
// Go to the next asset
|
// Go to the next asset
|
||||||
memoryAssetPageController.nextPage(
|
currentMemoryAssetPageController.nextPage(
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
);
|
);
|
||||||
@@ -154,67 +171,121 @@ class MemoryPage extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: bgColor,
|
backgroundColor: bgColor,
|
||||||
body: SafeArea(
|
body: PopScope(
|
||||||
child: PageView.builder(
|
onPopInvoked: (didPop) {
|
||||||
physics: const BouncingScrollPhysics(
|
// Remove immersive mode and go back to normal mode
|
||||||
parent: AlwaysScrollableScrollPhysics(),
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
),
|
},
|
||||||
scrollDirection: Axis.vertical,
|
child: SafeArea(
|
||||||
controller: memoryPageController,
|
child: PageView.builder(
|
||||||
onPageChanged: (pageNumber) {
|
physics: const BouncingScrollPhysics(
|
||||||
HapticFeedback.mediumImpact();
|
parent: AlwaysScrollableScrollPhysics(),
|
||||||
if (pageNumber < memories.length) {
|
),
|
||||||
currentMemory.value = memories[pageNumber];
|
scrollDirection: Axis.vertical,
|
||||||
}
|
controller: memoryPageController,
|
||||||
|
onPageChanged: (pageNumber) {
|
||||||
|
HapticFeedback.mediumImpact();
|
||||||
|
if (pageNumber < memories.length) {
|
||||||
|
currentMemoryIndex.value = pageNumber;
|
||||||
|
currentMemory.value = memories[pageNumber];
|
||||||
|
}
|
||||||
|
|
||||||
currentAssetPage.value = 0;
|
currentAssetPage.value = 0;
|
||||||
|
|
||||||
updateProgressText();
|
updateProgressText();
|
||||||
},
|
},
|
||||||
itemCount: memories.length + 1,
|
itemCount: memories.length + 1,
|
||||||
itemBuilder: (context, mIndex) {
|
itemBuilder: (context, mIndex) {
|
||||||
// Build last page
|
// Build last page
|
||||||
if (mIndex == memories.length) {
|
if (mIndex == memories.length) {
|
||||||
return MemoryEpilogue(
|
return MemoryEpilogue(
|
||||||
onStartOver: () => memoryPageController.animateToPage(
|
onStartOver: () => memoryPageController.animateToPage(
|
||||||
0,
|
0,
|
||||||
duration: const Duration(seconds: 1),
|
duration: const Duration(seconds: 1),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Build horizontal page
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: PageView.builder(
|
|
||||||
physics: const BouncingScrollPhysics(
|
|
||||||
parent: AlwaysScrollableScrollPhysics(),
|
|
||||||
),
|
|
||||||
controller: memoryAssetPageController,
|
|
||||||
onPageChanged: onAssetChanged,
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
itemCount: memories[mIndex].assets.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final asset = memories[mIndex].assets[index];
|
|
||||||
return Container(
|
|
||||||
color: Colors.black,
|
|
||||||
child: MemoryCard(
|
|
||||||
asset: asset,
|
|
||||||
onTap: () => toNextAsset(index),
|
|
||||||
onClose: () => context.popRoute(),
|
|
||||||
rightCornerText: assetProgress.value,
|
|
||||||
title: memories[mIndex].title,
|
|
||||||
showTitle: index == 0,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
MemoryBottomInfo(memory: memories[mIndex]),
|
}
|
||||||
],
|
// Build horizontal page
|
||||||
);
|
return Column(
|
||||||
},
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 24.0,
|
||||||
|
right: 24.0,
|
||||||
|
top: 8.0,
|
||||||
|
bottom: 2.0,
|
||||||
|
),
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: currentMemoryAssetPageController,
|
||||||
|
builder: (context, child) {
|
||||||
|
double value = 0.0;
|
||||||
|
if (currentMemoryAssetPageController.hasClients) {
|
||||||
|
// We can only access [page] if this has clients
|
||||||
|
value = currentMemoryAssetPageController.page ?? 0;
|
||||||
|
}
|
||||||
|
return MemoryProgressIndicator(
|
||||||
|
ticks: memories[mIndex].assets.length,
|
||||||
|
value: (value + 1) / memories[mIndex].assets.length,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
PageView.builder(
|
||||||
|
physics: const BouncingScrollPhysics(
|
||||||
|
parent: AlwaysScrollableScrollPhysics(),
|
||||||
|
),
|
||||||
|
controller: memoryAssetPageControllers[mIndex],
|
||||||
|
onPageChanged: onAssetChanged,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: memories[mIndex].assets.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final asset = memories[mIndex].assets[index];
|
||||||
|
return Container(
|
||||||
|
color: Colors.black,
|
||||||
|
child: MemoryCard(
|
||||||
|
asset: asset,
|
||||||
|
onTap: () => toNextAsset(index),
|
||||||
|
title: memories[mIndex].title,
|
||||||
|
showTitle: index == 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 8,
|
||||||
|
left: 8,
|
||||||
|
child: MaterialButton(
|
||||||
|
minWidth: 0,
|
||||||
|
onPressed: () {
|
||||||
|
// auto_route doesn't invoke pop scope, so
|
||||||
|
// turn off full screen mode here
|
||||||
|
// https://github.com/Milad-Akarie/auto_route_library/issues/1799
|
||||||
|
context.popRoute();
|
||||||
|
SystemChrome.setEnabledSystemUIMode(
|
||||||
|
SystemUiMode.edgeToEdge,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
color: Colors.white.withOpacity(0.2),
|
||||||
|
elevation: 0,
|
||||||
|
child: const Icon(
|
||||||
|
Icons.close_rounded,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
MemoryBottomInfo(memory: memories[mIndex]),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Generated
+1
-1
@@ -3,7 +3,7 @@ Immich API
|
|||||||
|
|
||||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||||
|
|
||||||
- API version: 1.94.1
|
- API version: 1.94.0
|
||||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ name: immich_mobile
|
|||||||
description: Immich - selfhosted backup media file on mobile phone
|
description: Immich - selfhosted backup media file on mobile phone
|
||||||
|
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
version: 1.94.1+120
|
version: 1.94.0+120
|
||||||
isar_version: &isar_version 3.1.0+1
|
isar_version: &isar_version 3.1.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -6473,7 +6473,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Immich",
|
"title": "Immich",
|
||||||
"description": "Immich API",
|
"description": "Immich API",
|
||||||
"version": "1.94.1",
|
"version": "1.94.0",
|
||||||
"contact": {}
|
"contact": {}
|
||||||
},
|
},
|
||||||
"tags": [],
|
"tags": [],
|
||||||
|
|||||||
Generated
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* Immich API
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 1.94.1
|
* The version of the OpenAPI document: 1.94.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
Generated
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* Immich API
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 1.94.1
|
* The version of the OpenAPI document: 1.94.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
Generated
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* Immich API
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 1.94.1
|
* The version of the OpenAPI document: 1.94.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* Immich API
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 1.94.1
|
* The version of the OpenAPI document: 1.94.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
Generated
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* Immich API
|
||||||
*
|
*
|
||||||
* The version of the OpenAPI document: 1.94.1
|
* The version of the OpenAPI document: 1.94.0
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.94.1",
|
"version": "1.94.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.94.1",
|
"version": "1.94.0",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.22.11",
|
"@babel/runtime": "^7.22.11",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.94.1",
|
"version": "1.94.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { goto } from '$app/navigation';
|
||||||
import type { AxiosError, AxiosPromise } from 'axios';
|
import type { AxiosError, AxiosPromise } from 'axios';
|
||||||
import {
|
import {
|
||||||
notificationController,
|
notificationController,
|
||||||
@@ -44,7 +45,7 @@ export const oauth = {
|
|||||||
try {
|
try {
|
||||||
const redirectUri = location.href.split('?')[0];
|
const redirectUri = location.href.split('?')[0];
|
||||||
const { data } = await api.oauthApi.startOAuth({ oAuthConfigDto: { redirectUri } });
|
const { data } = await api.oauthApi.startOAuth({ oAuthConfigDto: { redirectUri } });
|
||||||
window.location.href = data.url;
|
goto(data.url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, 'Unable to login with OAuth');
|
handleError(error, 'Unable to login with OAuth');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
afterNavigate(async ({ from }) => {
|
afterNavigate(async ({ from }) => {
|
||||||
if (from?.url?.pathname === AppRoute.AUTH_CHANGE_PASSWORD) {
|
if (from?.url.pathname === AppRoute.AUTH_CHANGE_PASSWORD) {
|
||||||
resetSavedUser();
|
resetSavedUser();
|
||||||
await api.authenticationApi.logout();
|
await api.authenticationApi.logout();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user