diff options
| author | Fuwn <[email protected]> | 2025-02-22 00:07:44 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-02-22 00:07:44 -0800 |
| commit | e42fa12dafe264c665d2574c93b54ddafe7f2e1f (patch) | |
| tree | a362b3b78ea97dc28ce5cc3682801bf89688f546 /Sora/Other | |
| parent | feat: Development commit (diff) | |
| download | sora-testing-e42fa12dafe264c665d2574c93b54ddafe7f2e1f.tar.xz sora-testing-e42fa12dafe264c665d2574c93b54ddafe7f2e1f.zip | |
feat: Development commit
Diffstat (limited to 'Sora/Other')
| -rw-r--r-- | Sora/Other/AsyncImageWithPreview.swift | 294 | ||||
| -rw-r--r-- | Sora/Other/PostLoadingStage.swift | 6 |
2 files changed, 152 insertions, 148 deletions
diff --git a/Sora/Other/AsyncImageWithPreview.swift b/Sora/Other/AsyncImageWithPreview.swift index 43b31c4..48df44e 100644 --- a/Sora/Other/AsyncImageWithPreview.swift +++ b/Sora/Other/AsyncImageWithPreview.swift @@ -1,163 +1,167 @@ import SwiftUI struct AsyncImageWithPreview<Placeholder: View>: View { - var url: URL? - @Binding var loadingState: PostLoadingState - var finalLoadingState: PostLoadingState - var postURL: URL? - let placeholder: () -> Placeholder - @State private var currentScale: CGFloat = 1.0 - @State private var finalScale: CGFloat = 1.0 - @State private var currentOffset: CGSize = .zero - @State private var finalOffset: CGSize = .zero + var url: URL? + @Binding var loadingState: PostLoadingState + var finalLoadingState: PostLoadingState + var postURL: URL? + let placeholder: () -> Placeholder + @State private var currentScale: CGFloat = 1.0 + @State private var finalScale: CGFloat = 1.0 + @State private var currentOffset: CGSize = .zero + @State private var finalOffset: CGSize = .zero - init( - url: URL?, - loadingStage: Binding<PostLoadingState>, - finalLoadingState: PostLoadingState = .loadingFile, - postURL: URL? = nil, - @ViewBuilder placeholder: @escaping () -> Placeholder = { - GeometryReader { geometry in - ProgressView() - .frame(width: geometry.size.width, height: geometry.size.height) - .position(x: geometry.size.width / 2, y: geometry.size.height / 2) - .padding() - } - } - ) { - self.url = url - _loadingState = loadingStage - self.finalLoadingState = finalLoadingState - self.postURL = postURL - self.placeholder = placeholder + init( + url: URL?, + loadingStage: Binding<PostLoadingState>, + finalLoadingState: PostLoadingState = .loadingFile, + postURL: URL? = nil, + @ViewBuilder placeholder: @escaping () -> Placeholder = { + GeometryReader { geometry in + ProgressView() + .frame(width: geometry.size.width, height: geometry.size.height) + .position(x: geometry.size.width / 2, y: geometry.size.height / 2) + .padding() + } } + ) { + self.url = url + _loadingState = loadingStage + self.finalLoadingState = finalLoadingState + self.postURL = postURL + self.placeholder = placeholder + } - var body: some View { - GeometryReader { geometry in - AsyncImage(url: url) { image in - image - .resizable() - .scaledToFit() - .onAppear { - loadingState = finalLoadingState - } - .scaleEffect(finalScale * currentScale) - .offset(x: finalOffset.width + currentOffset.width, - y: finalOffset.height + currentOffset.height) - .frame(width: geometry.size.width, height: geometry.size.height) - .position(x: geometry.size.width / 2, y: geometry.size.height / 2) - .gesture( - DragGesture() - .onChanged { value in - let translation = value.translation - let newOffset = CGSize( - width: finalOffset.width + translation.width, - height: finalOffset.height + translation.height - ) - let scale = finalScale * currentScale - let imageWidth = geometry.size.width * scale - let imageHeight = geometry.size.height * scale - let maxX = max((imageWidth - geometry.size.width) / 2, 0) - let maxY = max((imageHeight - geometry.size.height) / 2, 0) - let clampedX = min(max(newOffset.width, -maxX), maxX) - let clampedY = min(max(newOffset.height, -maxY), maxY) + var body: some View { + GeometryReader { geometry in + AsyncImage(url: url) { image in + image + .resizable() + .scaledToFit() + .onAppear { + loadingState = finalLoadingState + } + .scaleEffect(finalScale * currentScale) + .offset( + x: finalOffset.width + currentOffset.width, + y: finalOffset.height + currentOffset.height + ) + .frame(width: geometry.size.width, height: geometry.size.height) + .position(x: geometry.size.width / 2, y: geometry.size.height / 2) + .gesture( + DragGesture() + .onChanged { value in + let translation = value.translation + let newOffset = CGSize( + width: finalOffset.width + translation.width, + height: finalOffset.height + translation.height + ) + let scale = finalScale * currentScale + let imageWidth = geometry.size.width * scale + let imageHeight = geometry.size.height * scale + let maxX = max((imageWidth - geometry.size.width) / 2, 0) + let maxY = max((imageHeight - geometry.size.height) / 2, 0) + let clampedX = min(max(newOffset.width, -maxX), maxX) + let clampedY = min(max(newOffset.height, -maxY), maxY) - currentOffset = CGSize( - width: clampedX - finalOffset.width, - height: clampedY - finalOffset.height - ) - } - .onEnded { value in - let translation = value.translation - var newOffset = CGSize( - width: finalOffset.width + translation.width, - height: finalOffset.height + translation.height - ) - let scale = finalScale * currentScale - let imageWidth = geometry.size.width * scale - let imageHeight = geometry.size.height * scale - let maxX = max((imageWidth - geometry.size.width) / 2, 0) - let maxY = max((imageHeight - geometry.size.height) / 2, 0) + currentOffset = CGSize( + width: clampedX - finalOffset.width, + height: clampedY - finalOffset.height + ) + } + .onEnded { value in + let translation = value.translation + var newOffset = CGSize( + width: finalOffset.width + translation.width, + height: finalOffset.height + translation.height + ) + let scale = finalScale * currentScale + let imageWidth = geometry.size.width * scale + let imageHeight = geometry.size.height * scale + let maxX = max((imageWidth - geometry.size.width) / 2, 0) + let maxY = max((imageHeight - geometry.size.height) / 2, 0) - newOffset.width = min(max(newOffset.width, -maxX), maxX) - newOffset.height = min(max(newOffset.height, -maxY), maxY) - finalOffset = newOffset - currentOffset = .zero - } - ) - .simultaneousGesture( - MagnificationGesture() - .onChanged { value in - currentScale = value - } - .onEnded { _ in - finalScale *= currentScale - currentScale = 1.0 + newOffset.width = min(max(newOffset.width, -maxX), maxX) + newOffset.height = min(max(newOffset.height, -maxY), maxY) + finalOffset = newOffset + currentOffset = .zero + } + ) + .simultaneousGesture( + MagnificationGesture() + .onChanged { value in + currentScale = value + } + .onEnded { _ in + finalScale *= currentScale + currentScale = 1.0 - let scale = finalScale - let imageWidth = geometry.size.width * scale - let imageHeight = geometry.size.height * scale - let maxX = max((imageWidth - geometry.size.width) / 2, 0) - let maxY = max((imageHeight - geometry.size.height) / 2, 0) + let scale = finalScale + let imageWidth = geometry.size.width * scale + let imageHeight = geometry.size.height * scale + let maxX = max((imageWidth - geometry.size.width) / 2, 0) + let maxY = max((imageHeight - geometry.size.height) / 2, 0) - finalOffset.width = min(max(finalOffset.width, -maxX), maxX) - finalOffset.height = min(max(finalOffset.height, -maxY), maxY) - } - ) - .highPriorityGesture( - TapGesture(count: 2) - .onEnded { - withAnimation { - finalScale = 1.0 - currentScale = 1.0 - finalOffset = .zero - currentOffset = .zero - } - } - ) - .contextMenu { - #if os(iOS) - Button { - guard let url else { return } + finalOffset.width = min(max(finalOffset.width, -maxX), maxX) + finalOffset.height = min(max(finalOffset.height, -maxY), maxY) + } + ) + .highPriorityGesture( + TapGesture(count: 2) + .onEnded { + withAnimation { + finalScale = 1.0 + currentScale = 1.0 + finalOffset = .zero + currentOffset = .zero + } + } + ) + .contextMenu { + #if os(iOS) + Button { + guard let url else { return } - URLSession.shared.dataTask(with: url) { data, _, _ in - guard let data, let uiImage = UIImage(data: data) else { return } + URLSession.shared.dataTask(with: url) { data, _, _ in + guard let data, let uiImage = UIImage(data: data) else { return } - UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil) - }.resume() - } label: { - Label("Save Image", systemImage: "square.and.arrow.down") - } - #endif + UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil) + }.resume() + } label: { + Label("Save Image", systemImage: "square.and.arrow.down") + } + #endif - #if os(iOS) - Button { - let activityViewController = UIActivityViewController(activityItems: [url ?? URL(string: "")!], applicationActivities: nil) + #if os(iOS) + Button { + let activityViewController = UIActivityViewController( + activityItems: [url ?? URL(string: "")!], applicationActivities: nil) - UIApplication.shared.windows.first?.rootViewController?.present(activityViewController, animated: true) - } label: { - Label("Share Image", systemImage: "square.and.arrow.up") - } - #endif + UIApplication.shared.windows.first?.rootViewController?.present( + activityViewController, animated: true) + } label: { + Label("Share Image", systemImage: "square.and.arrow.up") + } + #endif - if let url = postURL { - Button { - #if os(iOS) - UIApplication.shared.open(url) - #else - NSWorkspace.shared.open(url) - #endif - } label: { - Label("Open in Safari", systemImage: "safari") - } - } - } - } placeholder: { - placeholder() - .onAppear { - loadingState = .loadingPreview - } + if let url = postURL { + Button { + #if os(iOS) + UIApplication.shared.open(url) + #else + NSWorkspace.shared.open(url) + #endif + } label: { + Label("Open in Safari", systemImage: "safari") + } } - } + } + } placeholder: { + placeholder() + .onAppear { + loadingState = .loadingPreview + } + } } + } } diff --git a/Sora/Other/PostLoadingStage.swift b/Sora/Other/PostLoadingStage.swift index 4a8c7f3..800b4dc 100644 --- a/Sora/Other/PostLoadingStage.swift +++ b/Sora/Other/PostLoadingStage.swift @@ -1,5 +1,5 @@ enum PostLoadingState { - case loadingPreview - case loadingFile - case loaded + case loadingPreview + case loadingFile + case loaded } |