feat: redesign Gallery as photo timeline with sync state, deduplication, and detail view
- GalleryItem: unified model with phone/nas/synced source, sync state enum - GalleryViewModel: off-actor PHFetchResult + manifest merge, Phone/NAS/All tab filtering, date section grouping, Combine-driven reactivity - GalleryView: sticky date section headers, 3-col grid, shimmer placeholders, sync dots (green/orange/blue), video duration badge, search bar, context menu (Share, Save to Photos, Delete from Device) - GalleryDetailView: full-screen swipeable photo viewer (TabView page mode), pinch-to-zoom, double-tap zoom toggle, bottom metadata sheet with file info and actions - BackupStatusService: expose lastManifest for gallery deduplication without extra NAS connection - ThumbnailCache: add decodeOffThread static helper for off-actor JPEG decode - BackupManifest: Sendable conformance for safe Task.detached capture - ThumbnailCache: synced items load from PHImageManager (instant) instead of NAS Co-Authored-By: Kutesir <kutesir@provoc.ug> Co-Authored-By: Sentry <sentry@provoc.ug>
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
19CB72B2DCA08EC8C8EB3103 /* UGreenCompatibilityBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B4A0DF226AB4C5EE9E0D16 /* UGreenCompatibilityBadge.swift */; };
|
||||
1AEF3190A6F5D3AB43CC521D /* BackupEngineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35A5F6ABBD052F931949B2FA /* BackupEngineTests.swift */; };
|
||||
20F9E76FDA60BA806980E44A /* ToggleRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674029116718AB4525AD9450 /* ToggleRow.swift */; };
|
||||
218313D06E536CC541620B1A /* GalleryDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18ADB8B4D428026420CCA46B /* GalleryDetailView.swift */; };
|
||||
24B0F2FC039AF5898BF3ADAE /* Date+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57448AD7D80B10985B8A222B /* Date+.swift */; };
|
||||
280D31F6A61529FB13A06EA4 /* SMBClient in Frameworks */ = {isa = PBXBuildFile; productRef = BD0A57EC048DFE706E440152 /* SMBClient */; };
|
||||
28C259C9EB7F49F4C2043375 /* SMBService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E4F11D9683F64A06B855649 /* SMBService.swift */; };
|
||||
@@ -34,6 +35,7 @@
|
||||
48A1C445346B6E84BB4955D3 /* BackupResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = E987653F4F7129568656EFDE /* BackupResult.swift */; };
|
||||
526A705B348CD4C8AC96576D /* KeychainStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AA5C090CF766ED8E3AF77D1 /* KeychainStore.swift */; };
|
||||
52D01C4B07CBCEB957E6A48D /* ThumbnailCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AF29317285580DD2246F54F /* ThumbnailCache.swift */; };
|
||||
54744C00729FAAB9715B06B5 /* GalleryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409404E5F90E8FECE829E43D /* GalleryViewModel.swift */; };
|
||||
5861495375343105B577CFEF /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371184164F77DD3B847A75C7 /* SettingsView.swift */; };
|
||||
600B0282353CDE47AA5B9069 /* NASProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8635AB32FC433B6ECA98B2D /* NASProtocolTests.swift */; };
|
||||
6328A1448CA2F792C509AFC6 /* BackupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A63232F3A6456E16E36215 /* BackupView.swift */; };
|
||||
@@ -59,6 +61,7 @@
|
||||
D18F0F1DCFEB945F2AD6DC20 /* ConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C29AB7B029DBC37E189C81D6 /* ConnectView.swift */; };
|
||||
D2EB2844309ECB331955F2A9 /* SFTPService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF029B56DE72F2D5D7977D95 /* SFTPService.swift */; };
|
||||
D7C3444EF261573E590725E9 /* MockNASService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B1138B91494A6B3BF4515C0 /* MockNASService.swift */; };
|
||||
D8247C4721601698EB006C86 /* GalleryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE62B5210F46415531D531D /* GalleryItem.swift */; };
|
||||
E6E8A98506D79BB36771FA16 /* GalleryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB509370FEEE38EA8732FDE /* GalleryView.swift */; };
|
||||
EAD70575AAC91030698676E1 /* KisaniLogoMark.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0987D44494F1385E8D4876F /* KisaniLogoMark.swift */; };
|
||||
EE8AE9228AC7BA87E0985BDF /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A1DAA26CE0A4DFFDD90370F2 /* PrivacyInfo.xcprivacy */; };
|
||||
@@ -81,6 +84,7 @@
|
||||
0B68D1C6752070B3B79B20F9 /* NASBackupApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NASBackupApp.swift; sourceTree = "<group>"; };
|
||||
101678CDEDFF343DCC880C2F /* ConnectionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionStore.swift; sourceTree = "<group>"; };
|
||||
1088EF7C38547B982CA1F220 /* LANMonitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LANMonitorTests.swift; sourceTree = "<group>"; };
|
||||
18ADB8B4D428026420CCA46B /* GalleryDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryDetailView.swift; sourceTree = "<group>"; };
|
||||
1AB509370FEEE38EA8732FDE /* GalleryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryView.swift; sourceTree = "<group>"; };
|
||||
1AF29317285580DD2246F54F /* ThumbnailCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailCache.swift; sourceTree = "<group>"; };
|
||||
22BF6D1FB493F75096805690 /* FolderSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderSetupView.swift; sourceTree = "<group>"; };
|
||||
@@ -95,6 +99,7 @@
|
||||
3CD95417C6B22E6FB7BECC4D /* BackupViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupViewModel.swift; sourceTree = "<group>"; };
|
||||
3E4F11D9683F64A06B855649 /* SMBService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMBService.swift; sourceTree = "<group>"; };
|
||||
3F26ACA8ED4D03984F5A9C7D /* LoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModel.swift; sourceTree = "<group>"; };
|
||||
409404E5F90E8FECE829E43D /* GalleryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryViewModel.swift; sourceTree = "<group>"; };
|
||||
4512EE2A2DE276DB46690E06 /* ButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStyles.swift; sourceTree = "<group>"; };
|
||||
57448AD7D80B10985B8A222B /* Date+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+.swift"; sourceTree = "<group>"; };
|
||||
5AA5C090CF766ED8E3AF77D1 /* KeychainStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStore.swift; sourceTree = "<group>"; };
|
||||
@@ -118,6 +123,7 @@
|
||||
C29AB7B029DBC37E189C81D6 /* ConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectView.swift; sourceTree = "<group>"; };
|
||||
C2FABB55A4E874EE203CE585 /* URL+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+.swift"; sourceTree = "<group>"; };
|
||||
C5FFBBC0D0A57B7476BAFBCF /* PhotoPermissionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoPermissionView.swift; sourceTree = "<group>"; };
|
||||
CEE62B5210F46415531D531D /* GalleryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryItem.swift; sourceTree = "<group>"; };
|
||||
D008AD85FA9B690DAFECCC34 /* LANMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LANMonitor.swift; sourceTree = "<group>"; };
|
||||
D8635AB32FC433B6ECA98B2D /* NASProtocolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NASProtocolTests.swift; sourceTree = "<group>"; };
|
||||
DA38966CDE15FCE36BEA5DC5 /* ProgressRing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressRing.swift; sourceTree = "<group>"; };
|
||||
@@ -240,6 +246,7 @@
|
||||
966927456571CE45600BEF75 /* BackupManifest.swift */,
|
||||
E987653F4F7129568656EFDE /* BackupResult.swift */,
|
||||
2EDEAE39E0AFC06D0C169589 /* BackupStatusSnapshot.swift */,
|
||||
CEE62B5210F46415531D531D /* GalleryItem.swift */,
|
||||
DAF503BCE3AEDC25F4D35083 /* NASConnection.swift */,
|
||||
);
|
||||
path = Models;
|
||||
@@ -323,7 +330,9 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6004FE7B107CC60894EDC434 /* BrowseView.swift */,
|
||||
18ADB8B4D428026420CCA46B /* GalleryDetailView.swift */,
|
||||
1AB509370FEEE38EA8732FDE /* GalleryView.swift */,
|
||||
409404E5F90E8FECE829E43D /* GalleryViewModel.swift */,
|
||||
);
|
||||
path = Browse;
|
||||
sourceTree = "<group>";
|
||||
@@ -513,7 +522,10 @@
|
||||
24B0F2FC039AF5898BF3ADAE /* Date+.swift in Sources */,
|
||||
3165724C02C31367688DC9A1 /* FolderRow.swift in Sources */,
|
||||
FFC6FD2A10B12C5536CC8BFF /* FolderSetupView.swift in Sources */,
|
||||
218313D06E536CC541620B1A /* GalleryDetailView.swift in Sources */,
|
||||
D8247C4721601698EB006C86 /* GalleryItem.swift in Sources */,
|
||||
E6E8A98506D79BB36771FA16 /* GalleryView.swift in Sources */,
|
||||
54744C00729FAAB9715B06B5 /* GalleryViewModel.swift in Sources */,
|
||||
0FCDDB2EAE2937413C4290BC /* HistoryView.swift in Sources */,
|
||||
526A705B348CD4C8AC96576D /* KeychainStore.swift in Sources */,
|
||||
EAD70575AAC91030698676E1 /* KisaniLogoMark.swift in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user