Skip to content

Commit 8a16096

Browse files
authored
Added slideshows (#5)
* Added slideshows - Ken Burns zoom/pan animation with toggle in settings - Play (ordered) and Shuffle modes with consistent pause/resume - Countdown progress bar - Arrow keys skip to next/prev photo during slideshow - Slideshow menu with Play and Shuffle options - Face-aware centering via AssetResponseDto+FaceCenter extension - SlideshowMode, SlideshowSettings, and SlideshowLaunch types * Address PR review comments, auto-hide toolbar in full screen
1 parent 7c9af18 commit 8a16096

File tree

9 files changed

+762
-73
lines changed

9 files changed

+762
-73
lines changed

ImmichLens/Assets/AssetCollectionView.swift

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct AssetCollectionView<Source: AssetSource>: View {
7979
@State private var visibleRange: Range<Int> = 0..<0
8080
@State private var initialNavigationDone = false
8181
@FocusState private var focusedIndex: Int?
82+
@State private var slideshowLaunch: SlideshowLaunch?
8283

8384
var body: some View {
8485
gridContent
@@ -106,6 +107,31 @@ struct AssetCollectionView<Source: AssetSource>: View {
106107
AssetDetailView(assets: assets, initialAsset: asset)
107108
.environment(apiService)
108109
}
110+
.navigationDestination(item: $slideshowLaunch) { launch in
111+
AssetDetailView(
112+
assets: assets,
113+
initialAsset: launch.asset,
114+
slideshowMode: launch.mode
115+
)
116+
.environment(apiService)
117+
}
118+
#if os(macOS)
119+
.toolbar {
120+
ToolbarItem {
121+
Menu {
122+
Button("Play", systemImage: "play.circle") {
123+
launchSlideshow(mode: .ordered)
124+
}
125+
Button("Shuffle", systemImage: "shuffle.circle") {
126+
launchSlideshow(mode: .shuffle)
127+
}
128+
} label: {
129+
Label("Slideshow", systemImage: "play.square.stack")
130+
}
131+
.disabled(photoAssets.isEmpty)
132+
}
133+
}
134+
#endif
109135
}
110136

111137
@State private var navigationToInitialAsset: Asset?
@@ -134,7 +160,13 @@ struct AssetCollectionView<Source: AssetSource>: View {
134160
title: tvOSTitle,
135161
subtitle: tvOSSubtitle,
136162
focusedIndex: $focusedIndex,
137-
visibleRange: $visibleRange
163+
visibleRange: $visibleRange,
164+
onSlideshow: photoAssets.isEmpty ? nil : {
165+
launchSlideshow(mode: .ordered)
166+
},
167+
onShuffle: photoAssets.isEmpty ? nil : {
168+
launchSlideshow(mode: .shuffle)
169+
}
138170
)
139171
#endif
140172
}
@@ -192,4 +224,13 @@ struct AssetCollectionView<Source: AssetSource>: View {
192224
private var tvOSSubtitle: String? {
193225
source.title.isEmpty ? nil : visibleDateRange
194226
}
227+
228+
private var photoAssets: [Asset] {
229+
assets.filter { $0.type == .photo }
230+
}
231+
232+
private func launchSlideshow(mode: SlideshowMode) {
233+
guard let startAsset = (mode == .shuffle ? photoAssets.randomElement() : photoAssets.first) else { return }
234+
slideshowLaunch = SlideshowLaunch(asset: startAsset, mode: mode)
235+
}
195236
}

0 commit comments

Comments
 (0)