From c12cec032dea3ae85e7de900b747c117b7f396d6 Mon Sep 17 00:00:00 2001 From: Oleg Date: Mon, 26 Feb 2024 20:09:39 +0700 Subject: [PATCH] MOB-1771 - Fixed send image functionality in private and group chats (#395) * Added API to upload remote attachments for domain. Removed all approach to upload attachments. * Updated upload request details * Send images as media embed in group chats * Adjusted chat input field UI * Check if user able to send attachments. * Chat row view refactoring * Show number of members in community --- .../project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 2 +- .../Messaging/Chat/ChatView/ChatView.swift | 4 +- .../Chat/ChatView/ChatViewModel.swift | 12 ++-- .../InputView/ExpandableTextEditor.swift | 2 +- .../ChatView/InputView/MessageInputView.swift | 63 ++++++++++++------ .../ChatListRows/ChatListChatRowView.swift | 18 +++++- .../ChatsList/SelectableChatRowView.swift | 1 + .../ApiRequestBuilder.swift | 19 ++++++ .../AnalyticsServiceEnvironment.swift | 1 + .../MessagingAPIServiceHelper.swift | 11 ++++ .../PushMessagingAPIService.swift | 27 +++++--- .../XMTPMessagingAPIService.swift | 64 ++----------------- .../NetworkService+ProfilesApi.swift | 15 +++++ .../NetworkService+ProfilesEntities.swift | 24 ++++++- .../Services/Networking/NetworkService.swift | 8 +-- 16 files changed, 168 insertions(+), 105 deletions(-) diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj index 7bab4e2e4..9345014b8 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.pbxproj @@ -10999,7 +10999,7 @@ repositoryURL = "https://github.com/Oleg-Pecheneg/push-swift-sdk"; requirement = { kind = revision; - revision = ff3151a6834db9ca841ee0564979298f4b0df053; + revision = 7aa3a1aeea89acd91dcbefcb0473a2a993a66334; }; }; C66811F02B47B0F500BDABB0 /* XCRemoteSwiftPackageReference "xmtp-ios" */ = { diff --git a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index db3970c4a..3a8a9428f 100644 --- a/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/unstoppable-ios-app/domains-manager-ios.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -131,7 +131,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Oleg-Pecheneg/push-swift-sdk", "state" : { - "revision" : "ff3151a6834db9ca841ee0564979298f4b0df053" + "revision" : "7aa3a1aeea89acd91dcbefcb0473a2a993a66334" } }, { diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/ChatView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/ChatView.swift index 165327514..d9d131a87 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/ChatView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/ChatView.swift @@ -28,7 +28,6 @@ struct ChatView: View, ViewAnalyticsLogger { ProgressView() } } - .environmentObject(viewModel) .displayError($viewModel.error) .background(Color.backgroundMuted2) .onChange(of: viewModel.keyboardFocused) { keyboardFocused in @@ -50,6 +49,9 @@ struct ChatView: View, ViewAnalyticsLogger { .background(.regularMaterial) } } + .environmentObject(viewModel) + .environment(\.analyticsViewName, analyticsName) + .environment(\.analyticsAdditionalProperties, additionalAppearAnalyticParameters) .onAppear(perform: onAppear) } } diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/ChatViewModel.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/ChatViewModel.swift index 7099dcc95..b09dc44a9 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/ChatViewModel.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/ChatViewModel.swift @@ -63,7 +63,7 @@ final class ChatViewModel: ObservableObject, ViewAnalyticsLogger { chatState = .loading setupTitle() setupPlaceholder() - setupFunctionality() + setIfUserCanSendAttachments() loadAndShowData() } @@ -258,9 +258,13 @@ private extension ChatViewModel { } } - func setupFunctionality() { + func setIfUserCanSendAttachments() { + let isProfileHasDomain = appContext.walletsDataService.wallets.findWithAddress(profile.wallet)?.rrDomain != nil if isCommunityChat() { - canSendAttachments = featureFlagsService.valueFor(flag: .communityMediaEnabled) + let isCommunityMediaEnabled = featureFlagsService.valueFor(flag: .communityMediaEnabled) + canSendAttachments = isCommunityMediaEnabled && isProfileHasDomain + } else { + canSendAttachments = isProfileHasDomain } } @@ -899,7 +903,7 @@ extension ChatViewModel: UDFeatureFlagsListener { switch flag { case .communityMediaEnabled: if isCommunityChat() { - canSendAttachments = newValue + setIfUserCanSendAttachments() reloadCachedMessages() } default: diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/InputView/ExpandableTextEditor.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/InputView/ExpandableTextEditor.swift index 5498ad172..8393e8840 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/InputView/ExpandableTextEditor.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/InputView/ExpandableTextEditor.swift @@ -33,7 +33,7 @@ struct ExpandableTextEditor: View { .foregroundStyle(Color.foregroundDefault) .frame(height: max(40, textEditorHeight)) .scrollContentBackground(.hidden) - .padding(EdgeInsets(top: 0, leading: 8, + .padding(EdgeInsets(top: 4, leading: 8, bottom: 0, trailing: 8)) .background(focused ? Color.backgroundMuted : Color.backgroundSubtle) .tint(Color.foregroundAccent) diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/InputView/MessageInputView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/InputView/MessageInputView.swift index 4b2807c48..c63d659bf 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/InputView/MessageInputView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/Chat/ChatView/InputView/MessageInputView.swift @@ -7,8 +7,12 @@ import SwiftUI -struct MessageInputView: View { +struct MessageInputView: View, ViewAnalyticsLogger { + @Environment(\.analyticsViewName) var analyticsName + @Environment(\.analyticsAdditionalProperties) var additionalAppearAnalyticParameters + @EnvironmentObject var viewModel: ChatViewModel + let input: Binding let placeholder: String @FocusState.Binding var focused: Bool @@ -17,30 +21,13 @@ struct MessageInputView: View { var body: some View { HStack(alignment: .bottom, spacing: 16) { - Menu { - ForEach(AdditionalAction.allCases.filter { $0.isAvailable }, id: \.self) { action in - Button { - UDVibration.buttonTap.vibrate() - additionalActionCallback(action) - } label: { - Label(action.title, systemImage: action.icon) - } - } - } label: { - Image.plusIcon18 - .resizable() - .squareFrame(20) - .padding(EdgeInsets(10)) - .foregroundStyle(Color.foregroundSecondary) - } - .onButtonTap { -// logButtonPressedAnalyticEvents(button: action.analyticButton) + if viewModel.canSendAttachments { + additionalActionsView() } - textFieldView() - if !input.wrappedValue.isEmpty { Button { + UDVibration.buttonTap.vibrate() sendCallback() } label: { Image.arrowUp24 @@ -88,11 +75,45 @@ struct MessageInputView: View { return UnstoppableImagePicker.isCameraAvailable } } + + var analyticButton: Analytics.Button { + switch self { + case .takePhoto: + return .takePhoto + case .choosePhoto: + return .choosePhoto + } + } } } // MARK: - Private methods private extension MessageInputView { + @MainActor + @ViewBuilder + func additionalActionsView() -> some View { + Menu { + ForEach(AdditionalAction.allCases.filter { $0.isAvailable }, id: \.self) { action in + Button { + UDVibration.buttonTap.vibrate() + logButtonPressedAnalyticEvents(button: action.analyticButton) + additionalActionCallback(action) + } label: { + Label(action.title, systemImage: action.icon) + } + } + } label: { + Image.plusIcon18 + .resizable() + .squareFrame(20) + .padding(EdgeInsets(10)) + .foregroundStyle(Color.foregroundSecondary) + } + .onButtonTap { + logButtonPressedAnalyticEvents(button: .chatInputActions) + } + } + @ViewBuilder func textFieldView() -> some View { ExpandableTextEditor(text: input, diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/ChatsList/ChatListRows/ChatListChatRowView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/ChatsList/ChatListRows/ChatListChatRowView.swift index 6053a2b4e..b7be171ad 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/ChatsList/ChatListRows/ChatListChatRowView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/ChatsList/ChatListRows/ChatListChatRowView.swift @@ -119,14 +119,28 @@ private extension ChatListChatRowView { @ViewBuilder func subtitleView() -> some View { - if let lastMessage = chat.lastMessage { - Text(lastMessageTextFrom(message: lastMessage)) + if let subtitle = getSubtitleText() { + Text(subtitle) .lineLimit(2) .foregroundStyle(Color.foregroundSecondary) .font(.currentFont(size: 14)) } } + func getSubtitleText() -> String? { + if let lastMessage = chat.lastMessage { + return lastMessageTextFrom(message: lastMessage) + } else if case .community(let details) = chat.type { + switch details.type { + case .badge(let badgeDetailedInfo): + let holders = badgeDetailedInfo.usage.holders + let holdersKsString = holders.asFormattedKsString + return String.Constants.pluralNHolders.localized(holdersKsString, holders) + } + } + return nil + } + func lastMessageTextFrom(message: MessagingChatMessageDisplayInfo) -> String { switch message.type { case .text(let description): diff --git a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/ChatsList/SelectableChatRowView.swift b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/ChatsList/SelectableChatRowView.swift index d09fb6118..0c1b52a95 100644 --- a/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/ChatsList/SelectableChatRowView.swift +++ b/unstoppable-ios-app/domains-manager-ios/Modules/Messaging/ChatsList/SelectableChatRowView.swift @@ -26,6 +26,7 @@ struct SelectableChatRowView: View, ViewAnalyticsLogger { ChatListChatRowView(chat: chat, joinCommunityCallback: { joinCommunityCallback?(chat) }) + .padding(.init(horizontal: 12)) } } } diff --git a/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift b/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift index 7b3dee4d6..385445673 100644 --- a/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift +++ b/unstoppable-ios-app/domains-manager-ios/NetworkEnvironment/ApiRequestBuilder.swift @@ -836,6 +836,25 @@ extension Endpoint { ) } + static func uploadRemoteAttachment(for domain: DomainItem, + with timedSignature: PersistedTimedSignature, + body: String) throws -> Endpoint { + // https://profile.ud-staging.com/api/user/aaron.x/attachment + let expires = "\(timedSignature.expires)" + let signature = timedSignature.sign + let headers = [ + SignatureComponentHeaders.CodingKeys.domain.rawValue: domain.name, + SignatureComponentHeaders.CodingKeys.expires.rawValue: expires, + SignatureComponentHeaders.CodingKeys.signature.rawValue: signature + ] + return Endpoint( + host: NetworkConfig.baseProfileHost, + path: "/profile/user/\(domain.name)/attachment", + queryItems: [], + body: body, + headers: headers + ) + } } // MARK: - Open methods diff --git a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift index 6b7086bbb..baadb9098 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/AnalyticsService/AnalyticsServiceEnvironment.swift @@ -345,6 +345,7 @@ extension Analytics { case copyChatMessageToClipboard case saveChatImage case blockUserInGroupChat + case chatInputActions, takePhoto, choosePhoto // Public profile case follow, unfollow diff --git a/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/MessagingAPIServiceHelper.swift b/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/MessagingAPIServiceHelper.swift index 2c8bff03c..376abfaed 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/MessagingAPIServiceHelper.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/MessagingAPIServiceHelper.swift @@ -44,6 +44,17 @@ struct MessagingAPIServiceHelper { return serviceMetadata } + static func uploadDataToWeb3Storage(_ data: Data, + ofType type: String, + by wallet: HexAddress) async throws -> URL { + let domain = try await MessagingAPIServiceHelper.getAnyDomainItem(for: wallet) + let response = try await NetworkService().uploadRemoteAttachment(for: domain, + base64: data.base64EncodedString(), + type: type) + return response.url + } + + enum MessagingHelperError: String, LocalizedError { case noDomainForWallet case failedToDecodeServiceData diff --git a/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/PushMessagingAPIService.swift b/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/PushMessagingAPIService.swift index f844bbc83..a67242fb5 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/PushMessagingAPIService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/PushMessagingAPIService.swift @@ -645,7 +645,8 @@ private extension PushMessagingAPIService { receiver: String, by user: MessagingChatUserProfile) async throws -> Push.PushChat.SendOptions { let env = getCurrentPushEnvironment() - let pushMessageContent = try getPushMessageContentFrom(displayType: messageType) + let pushMessageContent = try await getPushMessageContentFrom(displayType: messageType, + by: user) let pushMessageType = try getPushMessageTypeFrom(displayType: messageType) let pgpPrivateKey = try await getPGPPrivateKeyFor(user: user) let reference = getPushMessageReferenceFrom(displayType: messageType) @@ -668,19 +669,16 @@ private extension PushMessagingAPIService { try MessagingAPIServiceHelper.decodeServiceMetadata(from: data) } - func getPushMessageContentFrom(displayType: MessagingChatMessageDisplayType) throws -> String { + func getPushMessageContentFrom(displayType: MessagingChatMessageDisplayType, + by user: MessagingChatUserProfile) async throws -> String { switch displayType { case .text(let details): return details.text case .imageBase64(let details): - let entity = PushEnvironment.PushMessageContentResponse(content: details.base64) - guard let jsonString = entity.jsonString() else { throw PushMessagingAPIServiceError.failedToPrepareMessageContent } - return jsonString + guard let data = Data(base64Encoded: details.base64) else { throw PushMessagingAPIServiceError.failedToPrepareMessageContent } + return try await getImagePushMessageContentFrom(data: data, by: user) case .imageData(let details): - guard let base64 = details.image?.base64String else { throw PushMessagingAPIServiceError.unsupportedType } - let preparedBase64 = Base64DataTransformer.addingImageIdentifier(to: base64) - let imageBase64TypeDetails = MessagingChatMessageImageBase64TypeDisplayInfo(base64: preparedBase64) - return try getPushMessageContentFrom(displayType: .imageBase64(imageBase64TypeDetails)) + return try await getImagePushMessageContentFrom(data: details.data, by: user) case .reaction(let details): return details.content case .unknown, .remoteContent: @@ -688,6 +686,15 @@ private extension PushMessagingAPIService { } } + func getImagePushMessageContentFrom(data: Data, + by user: MessagingChatUserProfile) async throws -> String { + let wallet = user.wallet + let url = try await MessagingAPIServiceHelper.uploadDataToWeb3Storage(data, + ofType: "image/png", + by: wallet) + return url.absoluteString + } + func getPushMessageReferenceFrom(displayType: MessagingChatMessageDisplayType) -> String? { switch displayType { case .reaction(let details): @@ -704,7 +711,7 @@ private extension PushMessagingAPIService { case .reaction: return .reaction case .imageBase64, .imageData: - return .image + return .mediaEmbed case .unknown, .remoteContent: throw PushMessagingAPIServiceError.unsupportedType } diff --git a/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/XMTPMessagingAPIService.swift b/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/XMTPMessagingAPIService.swift index 5fd770588..ebd8ad48a 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/XMTPMessagingAPIService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/MessagingService/SubServices/MessagingAPI/XMTPMessagingAPIService.swift @@ -22,7 +22,6 @@ final class XMTPMessagingAPIService { private let blockedUsersStorage = XMTPBlockedUsersStorage.shared private let approvedUsersStorage = XMTPApprovedTopicsStorage.shared - private let cachedDataHolder = CachedDataHolder() let capabilities = MessagingServiceCapabilities(canContactWithoutProfile: false, canBlockUsers: true, isSupportChatsListPagination: false, @@ -530,70 +529,19 @@ private extension XMTPMessagingAPIService { let encryptedAttachment = try RemoteAttachment.encodeEncrypted(content: attachment, codec: AttachmentCodec(), with: client) - let url = try await uploadDataToWeb3Storage(encryptedAttachment.payload, by: wallet) - let remoteAttachment = try RemoteAttachment(url: url, + let url = try await MessagingAPIServiceHelper.uploadDataToWeb3Storage(encryptedAttachment.payload, + ofType: mimeType, + by: wallet) + let urlString = url.absoluteString + let remoteAttachment = try RemoteAttachment(url: urlString, encryptedEncodedContent: encryptedAttachment) return try await conversation.send(content: remoteAttachment, options: .init(contentType: ContentTypeRemoteAttachment)) } - - func uploadDataToWeb3Storage(_ data: Data, - by wallet: HexAddress) async throws -> String { - struct Web3StorageResponse: Codable { - let carCid: String - let cid: String - } - - let token = await getWeb3StorageKey(for: wallet) - let url = URL(string: "https://api.web3.storage/upload")! - var request = URLRequest(url: url) - request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization") - request.addValue("XMTP", forHTTPHeaderField: "X-NAME") - request.httpMethod = "POST" - - let responseData = try await URLSession.shared.upload(for: request, from: data).0 - let response = try Web3StorageResponse.objectFromDataThrowing(responseData) - - return "https://\(response.cid).ipfs.w3s.link" - } - + func getXMTPConversationTopicFromChat(_ chat: MessagingChat) -> String { chat.displayInfo.id // XMTP Chat's topic = chat's id } - - func getWeb3StorageKey(for wallet: HexAddress) async -> String { - if let cachedKey = await cachedDataHolder.getKeyFor(wallet: wallet) { - return cachedKey - } else if let domain = try? await MessagingAPIServiceHelper.getAnyDomainItem(for: wallet), - let profile = try? await NetworkService().fetchUserDomainProfile(for: domain, fields: [.profile]), - let storage = profile.storage, - storage.type == .web3 { - let key = storage.apiKey - await cachedDataHolder.setKey(key, for: wallet) - return key - } - - if User.instance.getSettings().isTestnetUsed { - return Web3Storage.StagingAPIKey - } else { - return Web3Storage.ProductionAPIKey - } - } -} - -// MARK: - Private methods -private extension XMTPMessagingAPIService { - actor CachedDataHolder { - var walletsToStorageKeys: [HexAddress : String] = [:] - - func getKeyFor(wallet: HexAddress) -> String? { - walletsToStorageKeys[wallet] - } - - func setKey(_ key: String, for wallet: HexAddress) { - walletsToStorageKeys[wallet] = key - } - } } // MARK: - Open methods diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesApi.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesApi.swift index f5f28cd08..1b8a852a2 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesApi.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesApi.swift @@ -145,6 +145,21 @@ extension NetworkService { return try await updateUserDomainProfile(for: domain, body: body) } + @discardableResult + public func uploadRemoteAttachment(for domain: DomainItem, + base64: String, + type: String) async throws -> ProfileUploadRemoteAttachmentResponse { + let request = ProfileUploadRemoteAttachmentRequest(base64: base64, type: type) + let body = try prepareRequestBodyFrom(entity: request) + let persistedSignature = try await getOrCreateAndStorePersistedProfileSignature(for: domain) + let endpoint = try Endpoint.uploadRemoteAttachment(for: domain, + with: persistedSignature, + body: body) + let data = try await fetchDataHandlingThrottleFor(endpoint: endpoint, method: .post) + let info = try ProfileUploadRemoteAttachmentResponse.objectFromDataThrowing(data) + return info + } + public func updatePendingDomainProfiles(with requests: [UpdateProfilePendingChangesRequest]) async throws { try await withThrowingTaskGroup(of: Void.self) { group in for request in requests { diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesEntities.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesEntities.swift index b179dc6f9..c1b81e42b 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesEntities.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService+ProfilesEntities.swift @@ -401,8 +401,8 @@ struct UserDomainNotificationsPreferences: Codable { } struct UserDomainStorageDetails: Codable { - let apiKey: String - let type: StorageType + let apiKey: String? + let type: StorageType? enum StorageType: String, Codable { case web3 = "web3.storage" @@ -692,3 +692,23 @@ struct WalletTokenPortfolio: Codable, Hashable { let value: WalletTokenPortfolio.Value? } } + +// MARK: Profile Update Request Structures +struct ProfileUploadRemoteAttachmentRequest: Codable { + let attachment: Attachment + + init(base64: String, + type: String) { + self.attachment = .init(base64: base64, type: type) + } + + struct Attachment: Codable { + let base64: String + let type: String + } +} + +// MARK: Profile Update Request Structures +struct ProfileUploadRemoteAttachmentResponse: Codable { + let url: URL +} diff --git a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift index 25297cc4d..40ef31dc6 100644 --- a/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift +++ b/unstoppable-ios-app/domains-manager-ios/Services/Networking/NetworkService.swift @@ -119,16 +119,16 @@ struct NetworkService { @discardableResult func fetchDataHandlingThrottleFor(endpoint: Endpoint, - method: HttpRequestMethod) async throws -> Data { + method: HttpRequestMethod) async throws -> Data { guard let url = endpoint.url else { throw NetworkLayerError.creatingURLFailed } let data = try await fetchDataHandlingThrottle(for: url, body: endpoint.body, method: method, extraHeaders: endpoint.headers) return data } func fetchDataHandlingThrottle(for url: URL, - body: String = "", - method: HttpRequestMethod = .post, - extraHeaders: [String: String] = [:]) async throws -> Data { + body: String = "", + method: HttpRequestMethod = .post, + extraHeaders: [String: String] = [:]) async throws -> Data { let data: Data do { data = try await fetchData(for: url, body: body, method: method, extraHeaders: extraHeaders)