Skip to content

Commit 2253d5a

Browse files
committed
enhance preview swiping and page loading
1 parent f70d253 commit 2253d5a

File tree

5 files changed

+176
-72
lines changed

5 files changed

+176
-72
lines changed

Django Files/Django_FilesApp.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ struct Django_FilesApp: App {
177177
showingPreview: $previewStateManager.showingDeepLinkPreview,
178178
showFileInfo: $showFileInfo,
179179
fileListDelegate: nil,
180-
allFiles: [file],
180+
allFiles: .constant([file]),
181181
currentIndex: 0,
182182
onNavigate: { _ in }
183183
)

Django Files/Utils/ImageCache.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Foundation
1111
class ImageCache {
1212
static let shared = ImageCache()
1313
private let cache = NSCache<NSString, UIImage>()
14+
private let contentCache = NSCache<NSString, NSData>()
1415

1516
private init() {
1617
// Cache is unlimited
@@ -23,6 +24,14 @@ class ImageCache {
2324
func get(for key: String) -> UIImage? {
2425
return cache.object(forKey: key as NSString)
2526
}
27+
28+
func setContent(_ data: Data, for key: String) {
29+
contentCache.setObject(data as NSData, forKey: key as NSString)
30+
}
31+
32+
func getContent(for key: String) -> Data? {
33+
return contentCache.object(forKey: key as NSString) as Data?
34+
}
2635
}
2736

2837
struct CachedAsyncImage<Content: View, Placeholder: View>: View {
@@ -98,3 +107,20 @@ struct CachedAsyncImage<Content: View, Placeholder: View>: View {
98107
}
99108
}
100109
}
110+
111+
// Add a new generic content loader
112+
struct CachedContentLoader {
113+
static func loadContent(from url: URL) async throws -> Data {
114+
let urlString = url.absoluteString
115+
116+
// Check cache first
117+
if let cachedData = ImageCache.shared.getContent(for: urlString) {
118+
return cachedData
119+
}
120+
121+
// Download and cache if not found
122+
let (data, _) = try await URLSession.shared.data(from: url)
123+
ImageCache.shared.setContent(data, for: urlString)
124+
return data
125+
}
126+
}

Django Files/Views/Lists/FileList.swift

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -369,18 +369,26 @@ struct FileListView: View {
369369
}
370370
}
371371
.fullScreenCover(isPresented: $showingPreview) {
372-
if let index = files.firstIndex(where: { $0.id == selectedFile?.id }) {
372+
if let index = fileListManager.files.firstIndex(where: { $0.id == selectedFile?.id }) {
373373
FilePreviewView(
374374
file: $fileListManager.files[index],
375375
server: server,
376376
showingPreview: $showingPreview,
377377
showFileInfo: $showFileInfo,
378378
fileListDelegate: fileListManager,
379-
allFiles: files,
379+
allFiles: Binding(
380+
get: { fileListManager.files },
381+
set: { fileListManager.files = $0 }
382+
),
380383
currentIndex: index,
381384
onNavigate: { newIndex in
382-
if newIndex >= 0 && newIndex < files.count {
383-
selectedFile = files[newIndex]
385+
if newIndex >= 0 && newIndex < fileListManager.files.count {
386+
selectedFile = fileListManager.files[newIndex]
387+
}
388+
},
389+
onLoadMore: {
390+
if hasNextPage && !isLoading {
391+
await loadNextPage()
384392
}
385393
}
386394
)
@@ -711,7 +719,11 @@ struct FileListView: View {
711719

712720
if let filesResponse = await api.getFiles(page: page, album: albumID, selectedServer: serverInstance, filterUserID: filterUserID) {
713721
if append {
714-
files.append(contentsOf: filesResponse.files)
722+
// Only append new files that aren't already in the list
723+
let newFiles = filesResponse.files.filter { newFile in
724+
!files.contains { $0.id == newFile.id }
725+
}
726+
files.append(contentsOf: newFiles)
715727
} else {
716728
files = filesResponse.files
717729
}

Django Files/Views/Preview/Image.swift

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,21 @@ struct ImageScrollView: UIViewRepresentable {
3030
doubleTapGesture.numberOfTapsRequired = 2
3131
scrollView.addGestureRecognizer(doubleTapGesture)
3232

33-
// Calculate initial zoom scale
3433
let widthScale = UIScreen.main.bounds.width / image.size.width
3534
let heightScale = UIScreen.main.bounds.height / image.size.height
3635
let minScale = min(widthScale, heightScale)
3736

3837
scrollView.minimumZoomScale = minScale
3938
scrollView.maximumZoomScale = 5.0
4039

41-
// Set content size to image size
4240
scrollView.contentSize = image.size
4341

44-
// Set initial zoom scale
4542
scrollView.zoomScale = minScale
4643

44+
scrollView.decelerationRate = .fast
45+
scrollView.showsHorizontalScrollIndicator = false
46+
scrollView.showsVerticalScrollIndicator = false
47+
4748
return scrollView
4849
}
4950

@@ -65,39 +66,39 @@ struct ImageScrollView: UIViewRepresentable {
6566
return imageView
6667
}
6768

68-
func updateZoomScaleForSize(_ size: CGSize) {
69-
guard let imageView = imageView,
70-
let image = imageView.image,
71-
let scrollView = scrollView,
72-
size.width > 0,
73-
size.height > 0,
74-
image.size.width > 0,
75-
image.size.height > 0 else { return }
76-
77-
let widthScale = size.width / image.size.width
78-
let heightScale = size.height / image.size.height
79-
let minScale = min(widthScale, heightScale)
69+
func updateZoomScaleForSize(_ size: CGSize) {
70+
guard let imageView = imageView,
71+
let image = imageView.image,
72+
let scrollView = scrollView,
73+
size.width > 0,
74+
size.height > 0,
75+
image.size.width > 0,
76+
image.size.height > 0 else { return }
77+
78+
let widthScale = size.width / image.size.width
79+
let heightScale = size.height / image.size.height
80+
let minScale = min(widthScale, heightScale)
8081

81-
scrollView.minimumZoomScale = minScale
82-
scrollView.maximumZoomScale = max(minScale * 5, 5.0)
82+
scrollView.minimumZoomScale = minScale
83+
scrollView.maximumZoomScale = max(minScale * 5, 5.0)
84+
}
85+
86+
@objc func handleDoubleTap(_ gesture: UITapGestureRecognizer) {
87+
guard let scrollView = gesture.view as? UIScrollView else { return }
88+
89+
if scrollView.zoomScale > scrollView.minimumZoomScale {
90+
scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
91+
} else {
92+
let point = gesture.location(in: imageView)
93+
let size = scrollView.bounds.size
94+
let w = size.width / (scrollView.maximumZoomScale / 5)
95+
let h = size.height / (scrollView.maximumZoomScale / 5)
96+
let x = point.x - (w / 2.0)
97+
let y = point.y - (h / 2.0)
98+
let rect = CGRect(x: x, y: y, width: w, height: h)
99+
scrollView.zoom(to: rect, animated: true)
100+
}
83101
}
84-
85-
@objc func handleDoubleTap(_ gesture: UITapGestureRecognizer) {
86-
guard let scrollView = gesture.view as? UIScrollView else { return }
87-
88-
if scrollView.zoomScale > scrollView.minimumZoomScale {
89-
scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
90-
} else {
91-
let point = gesture.location(in: imageView)
92-
let size = scrollView.bounds.size
93-
let w = size.width / (scrollView.maximumZoomScale / 5)
94-
let h = size.height / (scrollView.maximumZoomScale / 5)
95-
let x = point.x - (w / 2.0)
96-
let y = point.y - (h / 2.0)
97-
let rect = CGRect(x: x, y: y, width: w, height: h)
98-
scrollView.zoom(to: rect, animated: true)
99-
}
100-
}
101102

102103
func scrollViewDidZoom(_ scrollView: UIScrollView) {
103104
guard let imageView = imageView else { return }

0 commit comments

Comments
 (0)