Skip to content

Commit e01b17c

Browse files
authored
Merge pull request #28 from mensadilabs/dev
Dev
2 parents db27836 + d659888 commit e01b17c

18 files changed

+769
-305
lines changed

CHNAGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,35 @@
1+
version 1.0.7 Build 1
2+
3+
🎬 Animated Thumbnails
4+
5+
- **Dynamic Grid Previews**: Albums, People, and Tags now show animated slideshow previews of their content
6+
- **Smooth Fade Transitions**: Gentle 1.5-second crossfades between thumbnails every 4 seconds
7+
- **Smart Animation**: Pauses when focused, resumes when unfocused for better navigation
8+
- **User Control**: New "Enable Thumbnail Animation" toggle in Settings (enabled by default)
9+
- **Performance Optimized**: Uses thumbnail cache and loads maximum 10 images per animation
10+
11+
✨ Enhanced Visual Experience
12+
13+
- **Album Previews**: See actual photos from each album cycling in the thumbnail
14+
- **People Previews**: View photos containing each person rotating through their thumbnail
15+
- **Tag Previews**: Discover content in each tag through animated previews
16+
- **Overlay Labels**: Subtle name overlays on animated thumbnails for better identification
17+
18+
⚙️ Settings Integration
19+
20+
- Added "Enable Thumbnail Animation" setting under Slideshow & Display settings
21+
- Real-time setting changes - no restart required
22+
- Descriptive subtitle: "Animate thumbnails in Albums, People, and Tags views"
23+
24+
🔧 Technical Improvements
25+
26+
- Consistent animation timing across all views
27+
- Proper memory management and timer cleanup
28+
- Graceful fallbacks when no images are available
29+
- UserDefaults integration for persistent settings
30+
31+
---
32+
133
version 1.0.4 Build 1
234

335
📊 EXIF Photo Information Display

Immich Gallery.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@
408408
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
409409
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
410410
CODE_SIGN_STYLE = Automatic;
411-
CURRENT_PROJECT_VERSION = 2;
411+
CURRENT_PROJECT_VERSION = 1;
412412
DEVELOPMENT_ASSET_PATHS = "\"Immich Gallery/Preview Content\"";
413413
DEVELOPMENT_TEAM = 2F58933V55;
414414
ENABLE_PREVIEWS = YES;
@@ -420,7 +420,7 @@
420420
"$(inherited)",
421421
"@executable_path/Frameworks",
422422
);
423-
MARKETING_VERSION = 1.0.6;
423+
MARKETING_VERSION = 1.0.8;
424424
PRODUCT_BUNDLE_IDENTIFIER = "com.sanketh.dev.Immich-Gallery";
425425
PRODUCT_NAME = "$(TARGET_NAME)";
426426
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -436,7 +436,7 @@
436436
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
437437
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
438438
CODE_SIGN_STYLE = Automatic;
439-
CURRENT_PROJECT_VERSION = 2;
439+
CURRENT_PROJECT_VERSION = 1;
440440
DEVELOPMENT_ASSET_PATHS = "\"Immich Gallery/Preview Content\"";
441441
DEVELOPMENT_TEAM = 2F58933V55;
442442
ENABLE_PREVIEWS = YES;
@@ -448,7 +448,7 @@
448448
"$(inherited)",
449449
"@executable_path/Frameworks",
450450
);
451-
MARKETING_VERSION = 1.0.6;
451+
MARKETING_VERSION = 1.0.8;
452452
PRODUCT_BUNDLE_IDENTIFIER = "com.sanketh.dev.Immich-Gallery";
453453
PRODUCT_NAME = "$(TARGET_NAME)";
454454
SWIFT_EMIT_LOC_STRINGS = YES;

Immich Gallery.xcodeproj/xcuserdata/sanketkumar.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
endingColumnNumber = "9223372036854775807"
3333
startingLineNumber = "17"
3434
endingLineNumber = "17"
35-
landmarkName = "fetchAssets(page:limit:albumId:personId:tagId:)"
35+
landmarkName = "fetchAssets(page:limit:albumId:personId:tagId:isAllPhotos:)"
3636
landmarkType = "7">
3737
</BreakpointContent>
3838
</BreakpointProxy>

Immich Gallery/ContentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ struct ContentView: View {
4545
} else {
4646
// Main app interface
4747
TabView(selection: $selectedTab) {
48-
AssetGridView(assetService: assetService, authService: authService, albumId: nil, personId: nil, tagId: nil, onAssetsLoaded: nil)
48+
AssetGridView(assetService: assetService, authService: authService, albumId: nil, personId: nil, tagId: nil, isAllPhotos: true, onAssetsLoaded: nil)
4949
.errorBoundary(context: "Photos Tab")
5050
.tabItem {
5151
Image(systemName: "photo.on.rectangle")

Immich Gallery/Services/AlbumService.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,13 @@ class AlbumService: ObservableObject {
2020
endpoint: "/api/albums",
2121
responseType: [ImmichAlbum].self
2222
)
23+
24+
let sharedAlbums = try await networkService.makeRequest(
25+
endpoint: "/api/albums?shared=true",
26+
responseType: [ImmichAlbum].self
27+
)
2328
print("AlbumService: Received \(albums.count) albums")
24-
return albums
29+
return [albums, sharedAlbums].flatMap { $0 }
2530
}
2631

2732
func getAlbumInfo(albumId: String, withoutAssets: Bool = false) async throws -> ImmichAlbum {

Immich Gallery/Services/AssetService.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ class AssetService: ObservableObject {
1414
self.networkService = networkService
1515
}
1616

17-
func fetchAssets(page: Int = 1, limit: Int = 50, albumId: String? = nil, personId: String? = nil, tagId: String? = nil) async throws -> SearchResult {
18-
let sortOrder = UserDefaults.standard.string(forKey: "assetSortOrder") ?? "desc"
17+
func fetchAssets(page: Int = 1, limit: Int = 50, albumId: String? = nil, personId: String? = nil, tagId: String? = nil, isAllPhotos: Bool = false) async throws -> SearchResult {
18+
// Use separate sort order for All Photos tab vs everything else
19+
let sortOrder = isAllPhotos
20+
? UserDefaults.standard.allPhotosSortOrder
21+
: (UserDefaults.standard.string(forKey: "assetSortOrder") ?? "desc")
1922
var searchRequest: [String: Any] = [
2023
"page": page,
2124
"size": limit,

Immich Gallery/Services/MockImmichService.swift

Lines changed: 100 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -41,68 +41,40 @@ class MockAssetService: AssetService {
4141
super.init(networkService: networkService)
4242
}
4343

44-
func fetchAssets(page: Int = 1, limit: Int = 50, albumId: String? = nil, personId: String? = nil) async throws -> SearchResult {
45-
// Return mock assets
46-
let mockAssets = [
44+
override func fetchAssets(page: Int = 1, limit: Int = 50, albumId: String? = nil, personId: String? = nil, tagId: String? = nil, isAllPhotos: Bool = false) async throws -> SearchResult {
45+
// Generate different mock assets based on tagId for animation preview
46+
let baseId = tagId ?? "default"
47+
let mockAssets = (1...limit).map { index in
4748
ImmichAsset(
48-
id: "mock-asset-1",
49-
deviceAssetId: "mock-device-1",
49+
id: "mock-asset-\(baseId)-\(index)",
50+
deviceAssetId: "mock-device-\(index)",
5051
deviceId: "mock-device",
5152
ownerId: "mock-owner",
5253
libraryId: nil,
5354
type: .image,
54-
originalPath: "/mock/path1",
55-
originalFileName: "mock1.jpg",
55+
originalPath: "/mock/path\(index)",
56+
originalFileName: "mock\(index).jpg",
5657
originalMimeType: "image/jpeg",
5758
resized: false,
5859
thumbhash: nil,
59-
fileModifiedAt: "2023-01-01",
60-
fileCreatedAt: "2023-01-01",
61-
localDateTime: "2023-01-01",
62-
updatedAt: "2023-01-01",
63-
isFavorite: false,
60+
fileModifiedAt: "2023-01-\(String(format: "%02d", index))",
61+
fileCreatedAt: "2023-01-\(String(format: "%02d", index))",
62+
localDateTime: "2023-01-\(String(format: "%02d", index))",
63+
updatedAt: "2023-01-\(String(format: "%02d", index))",
64+
isFavorite: index % 3 == 0,
6465
isArchived: false,
6566
isOffline: false,
6667
isTrashed: false,
67-
checksum: "mock-checksum-1",
68+
checksum: "mock-checksum-\(baseId)-\(index)",
6869
duration: nil,
6970
hasMetadata: false,
7071
livePhotoVideoId: nil,
7172
people: [],
7273
visibility: "public",
7374
duplicateId: nil,
7475
exifInfo: nil
75-
),
76-
ImmichAsset(
77-
id: "mock-asset-2",
78-
deviceAssetId: "mock-device-2",
79-
deviceId: "mock-device",
80-
ownerId: "mock-owner",
81-
libraryId: nil,
82-
type: .video,
83-
originalPath: "/mock/path2",
84-
originalFileName: "mock2.mp4",
85-
originalMimeType: "video/mp4",
86-
resized: false,
87-
thumbhash: nil,
88-
fileModifiedAt: "2023-01-02",
89-
fileCreatedAt: "2023-01-02",
90-
localDateTime: "2023-01-02",
91-
updatedAt: "2023-01-02",
92-
isFavorite: true,
93-
isArchived: false,
94-
isOffline: false,
95-
isTrashed: false,
96-
checksum: "mock-checksum-2",
97-
duration: "PT1M30S",
98-
hasMetadata: false,
99-
livePhotoVideoId: nil,
100-
people: [],
101-
visibility: "public",
102-
duplicateId: nil,
103-
exifInfo: nil
10476
)
105-
]
77+
}
10678

10779
return SearchResult(
10880
assets: mockAssets,
@@ -112,8 +84,10 @@ class MockAssetService: AssetService {
11284
}
11385

11486
override func loadImage(asset: ImmichAsset, size: String = "thumbnail") async throws -> UIImage? {
115-
// Fetch a random image from picsum.photos
116-
let url = URL(string: "https://picsum.photos/300/300")!
87+
// Generate different colored images based on asset ID for visual variety
88+
let hash = abs(asset.id.hashValue)
89+
let seed = hash % 1000
90+
let url = URL(string: "https://picsum.photos/seed/\(seed)/300/300")!
11791
let (data, _) = try await URLSession.shared.data(from: url)
11892
return UIImage(data: data)
11993
}
@@ -166,6 +140,33 @@ class MockAlbumService: AlbumService {
166140
order: "desc",
167141
startDate: "2023-01-01",
168142
endDate: "2023-01-31"
143+
),
144+
ImmichAlbum(
145+
id: "mock-album-2",
146+
albumName: "Mock Album 1",
147+
description: "This is a mock album for testing",
148+
albumThumbnailAssetId: "mock-asset-1",
149+
createdAt: "2023-01-01",
150+
updatedAt: "2023-01-01",
151+
albumUsers: [],
152+
assets: [],
153+
assetCount: 5,
154+
ownerId: "mock-owner",
155+
owner: Owner(
156+
id: "mock-owner",
157+
email: "mock@example.com",
158+
name: "Mock Owner",
159+
profileImagePath: "",
160+
profileChangedAt: "2023-01-01",
161+
avatarColor: "primary"
162+
),
163+
shared: false,
164+
hasSharedLink: false,
165+
isActivityEnabled: true,
166+
lastModifiedAssetTimestamp: "2023-01-01",
167+
order: "desc",
168+
startDate: "2023-01-01",
169+
endDate: "2023-01-31"
169170
)
170171
]
171172

@@ -180,6 +181,57 @@ class MockAlbumService: AlbumService {
180181
}
181182
}
182183

184+
// MARK: - Mock tag service
185+
class MockTagService: TagService {
186+
override init(networkService: NetworkService) {
187+
super.init(networkService: networkService)
188+
}
189+
190+
override func fetchTags() async throws -> [Tag] {
191+
// Return mock tags
192+
let mockTags = [
193+
Tag(
194+
id: "1",
195+
name: "Nature",
196+
value: "nature",
197+
color: "green",
198+
createdAt: "2023-01-01",
199+
updatedAt: "2023-01-01",
200+
parentId: nil
201+
),
202+
Tag(
203+
id: "2",
204+
name: "Travel",
205+
value: "travel",
206+
color: "blue",
207+
createdAt: "2023-01-02",
208+
updatedAt: "2023-01-02",
209+
parentId: nil
210+
),
211+
Tag(
212+
id: "3",
213+
name: "Family",
214+
value: "family",
215+
color: "red",
216+
createdAt: "2023-01-03",
217+
updatedAt: "2023-01-03",
218+
parentId: nil
219+
),
220+
Tag(
221+
id: "4",
222+
name: "Work",
223+
value: "work",
224+
color: "orange",
225+
createdAt: "2023-01-04",
226+
updatedAt: "2023-01-04",
227+
parentId: nil
228+
)
229+
]
230+
231+
return mockTags
232+
}
233+
}
234+
183235
// MARK: - Mock People Service
184236
class MockPeopleService: PeopleService {
185237
override init(networkService: NetworkService) {
@@ -224,13 +276,14 @@ class MockPeopleService: PeopleService {
224276

225277
// MARK: - Convenience Factory
226278
class MockServiceFactory {
227-
static func createMockServices() -> (NetworkService, AuthenticationService, AssetService, AlbumService, PeopleService) {
279+
static func createMockServices() -> (NetworkService, AuthenticationService, AssetService, AlbumService, PeopleService, TagService) {
228280
let networkService = MockNetworkService()
229281
let authService = MockAuthenticationService(networkService: networkService)
230282
let assetService = MockAssetService(networkService: networkService)
231283
let albumService = MockAlbumService(networkService: networkService)
232284
let peopleService = MockPeopleService(networkService: networkService)
285+
let tagService = MockTagService(networkService: networkService)
233286

234-
return (networkService, authService, assetService, albumService, peopleService)
287+
return (networkService, authService, assetService, albumService, peopleService, tagService)
235288
}
236289
}

0 commit comments

Comments
 (0)