From a223896ea03ea69f72bc27f0cf0c7c964ed2bb5a Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 15 Dec 2022 02:46:01 +0100 Subject: [PATCH 01/12] Improved the styling of the action buttons in the ProfileView and the MessageView views. --- Modules/Sources/Browse/UI/Components/MessageView.swift | 2 +- Modules/Sources/Profile/UI/Views/ProfileView.swift | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/Sources/Browse/UI/Components/MessageView.swift b/Modules/Sources/Browse/UI/Components/MessageView.swift index eaa6356..47cd6f9 100644 --- a/Modules/Sources/Browse/UI/Components/MessageView.swift +++ b/Modules/Sources/Browse/UI/Components/MessageView.swift @@ -48,7 +48,7 @@ struct MessageView: View { bundle: .module ) .font(.body) - .foregroundColor(.primary) + .fontWeight(.semibold) .frame(maxWidth: .infinity) } .tint(.red) diff --git a/Modules/Sources/Profile/UI/Views/ProfileView.swift b/Modules/Sources/Profile/UI/Views/ProfileView.swift index e508eb7..5592c90 100644 --- a/Modules/Sources/Profile/UI/Views/ProfileView.swift +++ b/Modules/Sources/Profile/UI/Views/ProfileView.swift @@ -90,6 +90,7 @@ public struct ProfileView: View { "profile.button.log_out.text", bundle: .module ) + .font(.body) .fontWeight(.semibold) .foregroundColor(.primary) .frame(maxWidth: .infinity) From 1f1d7fa592323703113a7d0aa85a9dede1bd1966 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 15 Dec 2022 22:54:23 +0100 Subject: [PATCH 02/12] Fixed some UI issues in the modules. --- Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift | 10 ++++++++-- Modules/Sources/Login/UI/Styles/LogInLabelStyle.swift | 2 -- Modules/Sources/Profile/UI/Views/ProfileView.swift | 1 - 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift b/Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift index a76540e..19b5582 100644 --- a/Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift +++ b/Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift @@ -58,7 +58,10 @@ struct BrowseToolbar: ToolbarContent { ) } icon: { Image.add - .foregroundColor(.red) + .resizable() + .scaledToFit() + .frame(width: 24, height: 24) + .tint(.red) } } } @@ -75,7 +78,10 @@ struct BrowseToolbar: ToolbarContent { ) } icon: { Image.profile - .foregroundColor(.red) + .resizable() + .scaledToFit() + .frame(width: 24, height: 24) + .tint(.red) } } } diff --git a/Modules/Sources/Login/UI/Styles/LogInLabelStyle.swift b/Modules/Sources/Login/UI/Styles/LogInLabelStyle.swift index a025527..7601885 100644 --- a/Modules/Sources/Login/UI/Styles/LogInLabelStyle.swift +++ b/Modules/Sources/Login/UI/Styles/LogInLabelStyle.swift @@ -15,10 +15,8 @@ struct LogInLabelStyle: LabelStyle { configuration.title .font(.body) - .foregroundColor(.primary) configuration.icon - .tint(.primary) Spacer() } diff --git a/Modules/Sources/Profile/UI/Views/ProfileView.swift b/Modules/Sources/Profile/UI/Views/ProfileView.swift index 5592c90..9e3b7fd 100644 --- a/Modules/Sources/Profile/UI/Views/ProfileView.swift +++ b/Modules/Sources/Profile/UI/Views/ProfileView.swift @@ -92,7 +92,6 @@ public struct ProfileView: View { ) .font(.body) .fontWeight(.semibold) - .foregroundColor(.primary) .frame(maxWidth: .infinity) } .tint(.red) From 74b40ae5c372a8df7c99fd2b6e9253a955fd9172 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 15 Dec 2022 23:39:08 +0100 Subject: [PATCH 03/12] Tinted the navigation view in the ContentView view as red for the app target. --- BeReal/UI/Views/ContentView.swift | 1 + Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/BeReal/UI/Views/ContentView.swift b/BeReal/UI/Views/ContentView.swift index a3b8a2c..dc7eccd 100644 --- a/BeReal/UI/Views/ContentView.swift +++ b/BeReal/UI/Views/ContentView.swift @@ -91,6 +91,7 @@ private extension ContentView { login: login ) } + .tint(.red) } else { EmptyView() } diff --git a/Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift b/Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift index 19b5582..5f2d65c 100644 --- a/Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift +++ b/Modules/Sources/Browse/UI/Toolbars/BrowseToolbar.swift @@ -61,7 +61,6 @@ struct BrowseToolbar: ToolbarContent { .resizable() .scaledToFit() .frame(width: 24, height: 24) - .tint(.red) } } } @@ -81,7 +80,6 @@ struct BrowseToolbar: ToolbarContent { .resizable() .scaledToFit() .frame(width: 24, height: 24) - .tint(.red) } } } From 6b1089235aeae160b4daa32575712a5264843012 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 16 Dec 2022 00:09:50 +0100 Subject: [PATCH 04/12] Implemented the DateAdapter and the SizeAdapter adapter for the Browse module. --- .../Browse/Logic/Adapters/DateAdapter.swift | 49 +++++++++++++++++ .../Browse/Logic/Adapters/SizeAdapter.swift | 54 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 Modules/Sources/Browse/Logic/Adapters/DateAdapter.swift create mode 100644 Modules/Sources/Browse/Logic/Adapters/SizeAdapter.swift diff --git a/Modules/Sources/Browse/Logic/Adapters/DateAdapter.swift b/Modules/Sources/Browse/Logic/Adapters/DateAdapter.swift new file mode 100644 index 0000000..ba42356 --- /dev/null +++ b/Modules/Sources/Browse/Logic/Adapters/DateAdapter.swift @@ -0,0 +1,49 @@ +// +// DateAdapter.swift +// Browse +// +// Created by Javier Cicchelli on 15/12/2022. +// Copyright © 2022 Röck+Cöde. All rights reserved. +// + +import Foundation + +struct DateAdapter { + + // MARK: Properties + + private let dateFormatter: DateFormatter = .dateTimeFormatter + + // MARK: Functions + + func callAsFunction(value: Date?) -> String { + if let value { + return dateFormatter.string(from: value) + } else { + return .Constants.noValue + } + } + +} + +// MARK: - DateFormatter+Formats + +private extension DateFormatter { + static let dateTimeFormatter = { + let formatter = DateFormatter() + + formatter.dateStyle = .long + formatter.timeStyle = .short + formatter.locale = .current + + return formatter + }() +} + +// MARK: - String+Constants + +private extension String { + enum Constants { + static let noValue = "-" + } +} diff --git a/Modules/Sources/Browse/Logic/Adapters/SizeAdapter.swift b/Modules/Sources/Browse/Logic/Adapters/SizeAdapter.swift new file mode 100644 index 0000000..55fa92f --- /dev/null +++ b/Modules/Sources/Browse/Logic/Adapters/SizeAdapter.swift @@ -0,0 +1,54 @@ +// +// SizeAdapter.swift +// Browse +// +// Created by Javier Cicchelli on 15/12/2022. +// Copyright © 2022 Röck+Cöde. All rights reserved. +// + +import Foundation + +struct SizeAdapter { + + // MARK: Properties + + private let measurementFormatter: MeasurementFormatter = .informationSizeFormatter + + // MARK: Functions + + func callAsFunction(value: Int?) -> String { + guard let value else { return .Constants.noValue } + + var sizeInBytes = Measurement( + value: Double(value), + unit: UnitInformationStorage.bytes + ) + + return measurementFormatter.string( + from: sizeInBytes.converted(to: .megabytes) + ) + } + +} + +// MARK: - DateFormatter+Formats + +private extension MeasurementFormatter { + static let informationSizeFormatter = { + let formatter = MeasurementFormatter() + + formatter.unitStyle = .medium + formatter.numberFormatter.maximumFractionDigits = 2 + formatter.locale = .current + + return formatter + }() +} + +// MARK: - String+Constants + +private extension String { + enum Constants { + static let noValue = "-" + } +} From e982a8f689c44dbac8f4739bda3815b7c89383c1 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 16 Dec 2022 00:10:42 +0100 Subject: [PATCH 05/12] Integrated the DateAdapter and the SizeAdapter adapters into the DocumentItem component for the Browse module. --- Modules/Sources/Browse/Logic/Adapters/SizeAdapter.swift | 2 +- Modules/Sources/Browse/UI/Components/DocumentItem.swift | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/Sources/Browse/Logic/Adapters/SizeAdapter.swift b/Modules/Sources/Browse/Logic/Adapters/SizeAdapter.swift index 55fa92f..2adfa79 100644 --- a/Modules/Sources/Browse/Logic/Adapters/SizeAdapter.swift +++ b/Modules/Sources/Browse/Logic/Adapters/SizeAdapter.swift @@ -19,7 +19,7 @@ struct SizeAdapter { func callAsFunction(value: Int?) -> String { guard let value else { return .Constants.noValue } - var sizeInBytes = Measurement( + let sizeInBytes = Measurement( value: Double(value), unit: UnitInformationStorage.bytes ) diff --git a/Modules/Sources/Browse/UI/Components/DocumentItem.swift b/Modules/Sources/Browse/UI/Components/DocumentItem.swift index 7a6ae70..73b9f48 100644 --- a/Modules/Sources/Browse/UI/Components/DocumentItem.swift +++ b/Modules/Sources/Browse/UI/Components/DocumentItem.swift @@ -18,6 +18,9 @@ struct DocumentItem: View { let download: ActionClosure let delete: ActionClosure + private let dateAdapter = DateAdapter() + private let sizeAdapter = SizeAdapter() + // MARK: Body var body: some View { @@ -34,11 +37,11 @@ struct DocumentItem: View { .itemName() HStack { - Text("lastModified") + Text(dateAdapter(value: document?.lastModifiedAt)) Spacer() - Text("fileSize") + Text(sizeAdapter(value: document?.size)) } .font(.subheadline) .foregroundColor(.secondary) From bc40f46649347b151a8e4f733c7194bfd69369fe Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 16 Dec 2022 01:13:05 +0100 Subject: [PATCH 06/12] Added the "notSupported" case to the MessageView component for the Browse module. --- .../Browse/Resources/en.lproj/Localizable.strings | 3 +++ .../Sources/Browse/UI/Components/MessageView.swift | 12 ++++++++++++ Modules/Sources/Browse/UI/Views/BrowseView.swift | 2 ++ 3 files changed, 17 insertions(+) diff --git a/Modules/Sources/Browse/Resources/en.lproj/Localizable.strings b/Modules/Sources/Browse/Resources/en.lproj/Localizable.strings index d6f8df1..6cef50f 100644 --- a/Modules/Sources/Browse/Resources/en.lproj/Localizable.strings +++ b/Modules/Sources/Browse/Resources/en.lproj/Localizable.strings @@ -21,6 +21,9 @@ "message.type_error.text.first" = "An error occurred while loading this data"; "message.type_error.text.second" = "Please try loading this data again at a later time."; "message.type_error.button.text" = "Try again"; +"message.type_not_supported.text.first" = "This type of document cannot be opened yet"; +"message.type_not_supported.text.second" = "Please be patient while the support for this type of document is being built by our development team."; +"message.type_not_supported.button.text" = "Go back to folder"; // BrowseView diff --git a/Modules/Sources/Browse/UI/Components/MessageView.swift b/Modules/Sources/Browse/UI/Components/MessageView.swift index 47cd6f9..a31135b 100644 --- a/Modules/Sources/Browse/UI/Components/MessageView.swift +++ b/Modules/Sources/Browse/UI/Components/MessageView.swift @@ -66,6 +66,7 @@ struct MessageView: View { extension MessageView { enum MessageType { case noCredentials + case notSupported case empty case error } @@ -76,6 +77,8 @@ private extension MessageView.MessageType { switch self { case .noCredentials: return "message.type_no_credentials.text.first" + case .notSupported: + return "message.type_not_supported.text.first" case .empty: return "message.type_empty.text.first" case .error: @@ -87,6 +90,8 @@ private extension MessageView.MessageType { switch self { case .noCredentials: return "message.type_no_credentials.text.second" + case .notSupported: + return "message.type_not_supported.text.second" case .empty: return "message.type_empty.text.second" case .error: @@ -98,6 +103,8 @@ private extension MessageView.MessageType { switch self { case .noCredentials: return "message.type_no_credentials.button.text" + case .notSupported: + return "message.type_not_supported.button.text" case .empty: return "message.type_empty.button.text" case .error: @@ -121,6 +128,11 @@ struct MessageView_Previews: PreviewProvider { } .previewDisplayName("View of type no credentials") + MessageView(type: .notSupported) { + // action closure. + } + .previewDisplayName("View of type not supported") + MessageView(type: .empty) { // action closure. } diff --git a/Modules/Sources/Browse/UI/Views/BrowseView.swift b/Modules/Sources/Browse/UI/Views/BrowseView.swift index 030c0ed..af72a0e 100644 --- a/Modules/Sources/Browse/UI/Views/BrowseView.swift +++ b/Modules/Sources/Browse/UI/Views/BrowseView.swift @@ -79,6 +79,8 @@ private extension BrowseView { type: .noCredentials, action: login ) + case .notSupported: + EmptyView() case .loading: LoadingView() case .loaded: From 64b89b488f2ae9d099592d5f3504c16e1060f605 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 16 Dec 2022 01:15:04 +0100 Subject: [PATCH 07/12] Moved the ViewStatus enumeration from the BrowseView view to its own file for the Browse module. --- .../Browse/UI/Enumerations/ViewStatus.swift | 16 ++++++++++++++++ Modules/Sources/Browse/UI/Views/BrowseView.swift | 12 ------------ 2 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 Modules/Sources/Browse/UI/Enumerations/ViewStatus.swift diff --git a/Modules/Sources/Browse/UI/Enumerations/ViewStatus.swift b/Modules/Sources/Browse/UI/Enumerations/ViewStatus.swift new file mode 100644 index 0000000..83e871b --- /dev/null +++ b/Modules/Sources/Browse/UI/Enumerations/ViewStatus.swift @@ -0,0 +1,16 @@ +// +// ViewStatus.swift +// Browse +// +// Created by Javier Cicchelli on 16/12/2022. +// Copyright © 2022 Röck+Cöde. All rights reserved. +// + +enum ViewStatus { + case noCredentials + case notSupported + case loading + case loaded + case empty + case error +} diff --git a/Modules/Sources/Browse/UI/Views/BrowseView.swift b/Modules/Sources/Browse/UI/Views/BrowseView.swift index af72a0e..bd11042 100644 --- a/Modules/Sources/Browse/UI/Views/BrowseView.swift +++ b/Modules/Sources/Browse/UI/Views/BrowseView.swift @@ -175,18 +175,6 @@ private extension BrowseView { } } -// MARK: - Enumerations - -private extension BrowseView { - enum ViewStatus { - case noCredentials - case loading - case loaded - case empty - case error - } -} - // MARK: - Previews struct BrowseView_Previews: PreviewProvider { From 3ec892ae935864cd08d2ecc103d4f7990de69fff Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 16 Dec 2022 01:15:56 +0100 Subject: [PATCH 08/12] Added the "open" case to the Stack enumeration for the Browse module. --- Modules/Sources/Browse/UI/Enumerations/Stack.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/Sources/Browse/UI/Enumerations/Stack.swift b/Modules/Sources/Browse/UI/Enumerations/Stack.swift index d43cd4a..029bd47 100644 --- a/Modules/Sources/Browse/UI/Enumerations/Stack.swift +++ b/Modules/Sources/Browse/UI/Enumerations/Stack.swift @@ -8,6 +8,7 @@ enum Stack { case browse(Folder) + case open(Document) } // MARK: - Computed @@ -16,6 +17,8 @@ extension Stack { var tag: String { if case .browse(let folder) = self { return folder.id + } else if case .open(let document) = self { + return document.id } else { return .Constants.noId } From 63914197560612ea89d99bfa2758bf4b2c4d9380 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 16 Dec 2022 01:18:01 +0100 Subject: [PATCH 09/12] Implemented the stack navigation for the DocumentItem views in the BrowseView view for the Browse module. --- .../Sources/Browse/UI/Views/BrowseView.swift | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/Modules/Sources/Browse/UI/Views/BrowseView.swift b/Modules/Sources/Browse/UI/Views/BrowseView.swift index bd11042..74821b9 100644 --- a/Modules/Sources/Browse/UI/Views/BrowseView.swift +++ b/Modules/Sources/Browse/UI/Views/BrowseView.swift @@ -89,13 +89,7 @@ private extension BrowseView { case is Folder: makeFolderItem(for: item) case is Document: - DocumentItem(item: item) { - // TODO: show the item id in a viewer... - } download: { - // TODO: download the item id from the backend. - } delete: { - // TODO: delete the item id from the backend. - } + makeDocumentItem(for: item) default: EmptyView() } @@ -120,27 +114,50 @@ private extension BrowseView { @ViewBuilder func makeFolderItem( for item: any FileSystemItemIdentifiable ) -> some View { - let folder = Folder( - id: item.id, - name: item.name - ) - - FolderItem(item: item) { - stack = .browse(folder) - } delete: { - // TODO: delete the item id from the backend. + if let folder = item as? Folder { + FolderItem(item: item) { + stack = .browse(folder) + } delete: { + // TODO: delete the item id from the backend. + } + .navigate( + to: BrowseView( + folder: folder, + createFolder: createFolder, + uploadFile: uploadFile, + showProfile: showProfile, + login: login + ), + tagged: .browse(folder), + in: $stack + ) + } else { + EmptyView() + } + } + + @ViewBuilder func makeDocumentItem( + for item: any FileSystemItemIdentifiable + ) -> some View { + if let document = item as? Document { + DocumentItem(item: item) { + stack = .open(document) + } download: { + // TODO: download the item id from the backend. + } delete: { + // TODO: delete the item id from the backend. + } + .navigate( + to: DocumentView( + document: document, + login: login + ), + tagged: .open(document), + in: $stack + ) + } else { + EmptyView() } - .navigate( - to: BrowseView( - folder: folder, - createFolder: createFolder, - uploadFile: uploadFile, - showProfile: showProfile, - login: login - ), - tagged: .browse(folder), - in: $stack - ) } } From 0cb5510539332bbfd5514c671190bfb01d92b87c Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 16 Dec 2022 01:46:42 +0100 Subject: [PATCH 10/12] Hide the disclosure indicator added by the NavigationLink component in the StackNavigationViewModifiers view modifier for the Browse module. --- .../Browse/UI/View Modifiers/StackNavigationViewModifier.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/Sources/Browse/UI/View Modifiers/StackNavigationViewModifier.swift b/Modules/Sources/Browse/UI/View Modifiers/StackNavigationViewModifier.swift index 72c3c12..3f8fba5 100644 --- a/Modules/Sources/Browse/UI/View Modifiers/StackNavigationViewModifier.swift +++ b/Modules/Sources/Browse/UI/View Modifiers/StackNavigationViewModifier.swift @@ -30,6 +30,7 @@ struct StackNavigationViewModifier: ViewModifier { ) { EmptyView() } + .hidden() ) } From 745ba87fb5c6c0cc20c37a89dcdc1c7095e7cd08 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 16 Dec 2022 01:47:06 +0100 Subject: [PATCH 11/12] Implemented the GetDataUseCase use case for the Browse module. --- .../Logic/Use Cases/GetDataUseCase.swift | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Modules/Sources/Browse/Logic/Use Cases/GetDataUseCase.swift diff --git a/Modules/Sources/Browse/Logic/Use Cases/GetDataUseCase.swift b/Modules/Sources/Browse/Logic/Use Cases/GetDataUseCase.swift new file mode 100644 index 0000000..5b87e28 --- /dev/null +++ b/Modules/Sources/Browse/Logic/Use Cases/GetDataUseCase.swift @@ -0,0 +1,36 @@ +// +// GetDataUseCase.swift +// Browse +// +// Created by Javier Cicchelli on 16/12/2022. +// Copyright © 2022 Röck+Cöde. All rights reserved. +// + +import APIService +import DependencyInjection +import Dependencies +import Foundation + +struct GetDataUseCase { + + // MARK: Dependencies + + @Dependency(\.apiService) private var apiService + + // MARK: Functions + + func callAsFunction( + id: String, + username: String, + password: String + ) async throws -> Data { + return try await apiService.getData( + id: id, + credentials: .init( + username: username, + password: password + ) + ) + } + +} From 3c433bf72eb9a3d4499d8271ebbd99e82870aa97 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 16 Dec 2022 01:47:44 +0100 Subject: [PATCH 12/12] Implemented the DocumentView view for the Browse module. --- .../Browse/UI/Views/DocumentView.swift | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 Modules/Sources/Browse/UI/Views/DocumentView.swift diff --git a/Modules/Sources/Browse/UI/Views/DocumentView.swift b/Modules/Sources/Browse/UI/Views/DocumentView.swift new file mode 100644 index 0000000..740247a --- /dev/null +++ b/Modules/Sources/Browse/UI/Views/DocumentView.swift @@ -0,0 +1,152 @@ +// +// DocumentView.swift +// Browse +// +// Created by Javier Cicchelli on 16/12/2022. +// Copyright © 2022 Röck+Cöde. All rights reserved. +// + +import DataModels +import KeychainStorage +import SwiftUI + +struct DocumentView: View { + + // MARK: Environments + + @Environment(\.dismiss) private var dismiss + + // MARK: Storages + + @KeychainStorage(key: .KeychainStorage.account) private var account: Account? + + // MARK: States + + @State private var status: ViewStatus = .loading + @State private var loadedData: Data? + + private let getData = GetDataUseCase() + + // MARK: Properties + + let document: Document + let login: ActionClosure + + // MARK: Body + + var body: some View { + content + .navigationTitle(document.name) + .navigationBarTitleDisplayMode(.inline) + .task { + await loadDataIfPossible() + } + } + +} + +// MARK: - UI + +private extension DocumentView { + @ViewBuilder var content: some View { + switch status { + case .noCredentials: + MessageView( + type: .noCredentials, + action: login + ) + case .notSupported: + MessageView(type: .notSupported) { + dismiss() + } + case .loading: + LoadingView() + case .loaded: + Image(uiImage: imageFromData) + .resizable() + .scaledToFit() + case .empty: + EmptyView() + case .error: + MessageView(type: .error) { + Task { + await loadDataIfPossible() + } + } + } + } +} + +// MARK: - Helpers + +private extension DocumentView { + + // MARK: Computed + + var imageFromData: UIImage { + guard + let loadedData, + let image = UIImage(data: loadedData) + else { + return .init() + } + + return image + } + + // MARK: Functions + + func loadDataIfPossible() async { + guard document.contentType == .Constants.supportedContentType else { + status = .notSupported + return + } + guard let account else { + status = .noCredentials + return + } + + do { + status = .loading + + let data = try await getData( + id: document.id, + username: account.username, + password: account.password + ) + + if data.isEmpty { + status = .error + } else { + loadedData = data + status = .loaded + } + } catch { + status = .error + } + } +} + +// MARK: - String+Constants + +private extension String { + enum Constants { + static let supportedContentType = "image/jpeg" + } +} + +// MARK: - Previews + +struct DocumentView_Previews: PreviewProvider { + static var previews: some View { + DocumentView(document: .init( + id: "1234567890", + name: "Some document name goes in here...", + contentType: "some content type", + size: .random(in: 1 ... 100), + lastModifiedAt: .now + )) { + // login closure. + } + } +}