From 9a9e8a1c075ceab7e8b6c3b47e2a7a4a4e218dd6 Mon Sep 17 00:00:00 2001 From: Ben Pollman Date: Sat, 12 Oct 2024 01:14:03 +1100 Subject: [PATCH] Add FileCDN class enable generating signedURL's in File Attachments (#620) --- .../MessageList/FileAttachmentPreview.swift | 31 ++++++++++++++---- Sources/StreamChatSwiftUI/Utils.swift | 3 ++ .../Utils/Common/FileCDN.swift | 32 +++++++++++++++++++ StreamChatSwiftUI.xcodeproj/project.pbxproj | 4 +++ 4 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 Sources/StreamChatSwiftUI/Utils/Common/FileCDN.swift diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/FileAttachmentPreview.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/FileAttachmentPreview.swift index 1a096c82..8cef1050 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/FileAttachmentPreview.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/FileAttachmentPreview.swift @@ -10,9 +10,15 @@ public struct FileAttachmentPreview: View { @Injected(\.fonts) private var fonts @Injected(\.images) private var images + @Injected(\.utils) private var utils + + private var fileCDN: FileCDN { + utils.fileCDN + } var url: URL + @State var adjustedUrl: URL? @State private var isLoading = false @State private var title: String? @State private var error: Error? @@ -25,18 +31,31 @@ public struct FileAttachmentPreview: View { .font(fonts.body) .padding() } else { - WebView( - url: url, - isLoading: $isLoading, - title: $title, - error: $error - ) + + if let adjustedUrl = adjustedUrl { + WebView( + url: adjustedUrl, + isLoading: $isLoading, + title: $title, + error: $error + ) + } if isLoading { ProgressView() } } } + .onAppear { + fileCDN.adjustedURL(for: url) { result in + switch result { + case let .success(url): + self.adjustedUrl = url + case let .failure(error): + self.error = error + } + } + } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .principal) { diff --git a/Sources/StreamChatSwiftUI/Utils.swift b/Sources/StreamChatSwiftUI/Utils.swift index 83bc0ff2..fcce5185 100644 --- a/Sources/StreamChatSwiftUI/Utils.swift +++ b/Sources/StreamChatSwiftUI/Utils.swift @@ -14,6 +14,7 @@ public class Utils { public var imageCDN: ImageCDN public var imageProcessor: ImageProcessor public var imageMerger: ImageMerging + public var fileCDN: FileCDN public var channelNamer: ChatChannelNamer public var chatUserNamer: ChatUserNamer public var channelAvatarsMerger: ChannelAvatarsMerging @@ -69,6 +70,7 @@ public class Utils { imageCDN: ImageCDN = StreamImageCDN(), imageProcessor: ImageProcessor = NukeImageProcessor(), imageMerger: ImageMerging = DefaultImageMerger(), + fileCDN: FileCDN = DefaultFileCDN(), channelAvatarsMerger: ChannelAvatarsMerging = ChannelAvatarsMerger(), messageTypeResolver: MessageTypeResolving = MessageTypeResolver(), messageActionResolver: MessageActionsResolving = MessageActionsResolver(), @@ -92,6 +94,7 @@ public class Utils { self.imageCDN = imageCDN self.imageProcessor = imageProcessor self.imageMerger = imageMerger + self.fileCDN = fileCDN self.channelNamer = channelNamer self.chatUserNamer = chatUserNamer self.channelAvatarsMerger = channelAvatarsMerger diff --git a/Sources/StreamChatSwiftUI/Utils/Common/FileCDN.swift b/Sources/StreamChatSwiftUI/Utils/Common/FileCDN.swift new file mode 100644 index 00000000..477fc8e2 --- /dev/null +++ b/Sources/StreamChatSwiftUI/Utils/Common/FileCDN.swift @@ -0,0 +1,32 @@ +// +// Copyright © 2024 Stream.io Inc. All rights reserved. +// + +import Foundation +import StreamChat + +/// A protocol the video preview uploader implementation must conform to. +public protocol FileCDN: AnyObject { + /// Prepare and return an adjusted or signed `URL` for the given file `URL` + /// This function can be used to intercept an unsigned URL and return a valid signed URL + /// - Parameters: + /// - url: A file URL. + /// - completion: A completion that is called when an adjusted URL is ready to be provided. + func adjustedURL( + for url: URL, + completion: @escaping ((Result) -> Void) + ) +} + +/// The `DefaultFileCDN` implemenation used by default. +public final class DefaultFileCDN: FileCDN { + + // Initializer required for subclasses + public init() { + // Public init. + } + + public func adjustedURL(for url: URL, completion: @escaping ((Result) -> Void)) { + completion(.success(url)) + } +} diff --git a/StreamChatSwiftUI.xcodeproj/project.pbxproj b/StreamChatSwiftUI.xcodeproj/project.pbxproj index e6a6ac9e..2e36588e 100644 --- a/StreamChatSwiftUI.xcodeproj/project.pbxproj +++ b/StreamChatSwiftUI.xcodeproj/project.pbxproj @@ -490,6 +490,7 @@ 91B79FD9284E7E9C005B6E4F /* ChatUserNamer_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B79FD8284E7E9C005B6E4F /* ChatUserNamer_Tests.swift */; }; 91CC203A283C3E7F0049A146 /* URLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91CC2039283C3E7F0049A146 /* URLExtensions.swift */; }; 91CC203C283C4C250049A146 /* URLUtils_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91CC203B283C4C250049A146 /* URLUtils_Tests.swift */; }; + 9D9A54512CB89EAA00A76D9E /* FileCDN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D9A54502CB89EAA00A76D9E /* FileCDN.swift */; }; A35D803B283E89F50084FE25 /* StreamChatSwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8465FBB52746873A00AF091E /* StreamChatSwiftUI.framework */; }; A35D803C283E89F50084FE25 /* StreamChatSwiftUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8465FBB52746873A00AF091E /* StreamChatSwiftUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A3600B2A283E9E1900E1C930 /* UserRobot.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3600B24283E9E1900E1C930 /* UserRobot.swift */; }; @@ -1060,6 +1061,7 @@ 91B79FD8284E7E9C005B6E4F /* ChatUserNamer_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUserNamer_Tests.swift; sourceTree = ""; }; 91CC2039283C3E7F0049A146 /* URLExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLExtensions.swift; sourceTree = ""; }; 91CC203B283C4C250049A146 /* URLUtils_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLUtils_Tests.swift; sourceTree = ""; }; + 9D9A54502CB89EAA00A76D9E /* FileCDN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileCDN.swift; sourceTree = ""; }; A3600B24283E9E1900E1C930 /* UserRobot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserRobot.swift; sourceTree = ""; }; A3600B29283E9E1900E1C930 /* UserRobot+Asserts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserRobot+Asserts.swift"; sourceTree = ""; }; A3600B31283E9E4700E1C930 /* MessageList_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageList_Tests.swift; sourceTree = ""; }; @@ -1838,6 +1840,7 @@ 91B79FD6284E21E0005B6E4F /* ChatUserNamer.swift */, 8465FD3A2746A95600AF091E /* DateFormatter+Extensions.swift */, 8465FD402746A95600AF091E /* DateUtils.swift */, + 9D9A54502CB89EAA00A76D9E /* FileCDN.swift */, 8465FD3D2746A95600AF091E /* ImageCDN.swift */, 8465FD482746A95600AF091E /* ImageMerger.swift */, 8465FD422746A95600AF091E /* InputTextView.swift */, @@ -2708,6 +2711,7 @@ 8482094E2ACFFCD900EF3261 /* Throttler.swift in Sources */, 84EADEBD2B28C2EC0046B50C /* RecordingState.swift in Sources */, 4F7DD9A02BFC7C6100599AA6 /* ChatClient+Extensions.swift in Sources */, + 9D9A54512CB89EAA00A76D9E /* FileCDN.swift in Sources */, 82D64C1B2AD7E5B700C5C79E /* ImageCaching.swift in Sources */, 82D64C062AD7E5B700C5C79E /* Graphics.swift in Sources */, 8465FDCB2746A95700AF091E /* ChatChannelListView.swift in Sources */,