diff --git a/Sources/Screens/Browser/BrowserStore.swift b/Sources/Screens/Browser/BrowserStore.swift index 2042a31..521a23b 100644 --- a/Sources/Screens/Browser/BrowserStore.swift +++ b/Sources/Screens/Browser/BrowserStore.swift @@ -189,12 +189,12 @@ class BrowserStore: ObservableObject { } } } - if let current = self.folder { + if self.folder != nil { Task { - let file = try await self.fetchFolder() - if let file { + let folder = try await self.fetchFolder() + if let folder { DispatchQueue.main.async { - self.folder = file + self.folder = folder } } } diff --git a/Sources/Screens/File/FileCopy.swift b/Sources/Screens/File/FileCopy.swift index 7dd8262..a08a687 100644 --- a/Sources/Screens/File/FileCopy.swift +++ b/Sources/Screens/File/FileCopy.swift @@ -16,7 +16,7 @@ struct FileCopy: View { @Environment(\.dismiss) private var dismiss @Environment(\.colorScheme) private var colorScheme @State private var isProcessing = true - @State private var showError = false + @State private var errorIsPresented = false @State private var errorSeverity: ErrorSeverity? @State private var errorMessage: String? private let destinationID: String @@ -28,10 +28,10 @@ struct FileCopy: View { var body: some View { VStack { - if isProcessing, !showError { + if isProcessing, !errorIsPresented { VOSheetProgressView() Text("Copying \(fileStore.selection.count) item(s).") - } else if showError, errorSeverity == .full { + } else if errorIsPresented, errorSeverity == .full { VOErrorIcon() if let errorMessage { Text(errorMessage) @@ -43,7 +43,7 @@ struct FileCopy: View { } .voSecondaryButton(colorScheme: colorScheme) .padding(.horizontal) - } else if showError, errorSeverity == .partial { + } else if errorIsPresented, errorSeverity == .partial { VOWarningIcon() if let errorMessage { Text(errorMessage) @@ -80,17 +80,17 @@ struct FileCopy: View { } else if result.failed.count == fileStore.selection.count { errorSeverity = .full } - showError = true + errorIsPresented = true } } return false } success: { - showError = false + errorIsPresented = false dismiss() } failure: { _ in errorMessage = "Failed to copy \(fileStore.selection.count) item(s)." errorSeverity = .full - showError = true + errorIsPresented = true } anyways: { isProcessing = false } diff --git a/Sources/Screens/File/FileDelete.swift b/Sources/Screens/File/FileDelete.swift index f1621c3..56940a6 100644 --- a/Sources/Screens/File/FileDelete.swift +++ b/Sources/Screens/File/FileDelete.swift @@ -16,7 +16,7 @@ struct FileDelete: View { @Environment(\.dismiss) private var dismiss @Environment(\.colorScheme) private var colorScheme @State private var isProcessing = true - @State private var showError = false + @State private var errorIsPresented = false @State private var errorSeverity: ErrorSeverity? @State private var errorMessage: String? @@ -26,10 +26,10 @@ struct FileDelete: View { var body: some View { VStack { - if isProcessing, !showError { + if isProcessing, !errorIsPresented { VOSheetProgressView() Text("Deleting \(fileStore.selection.count) item(s).") - } else if showError, errorSeverity == .full { + } else if errorIsPresented, errorSeverity == .full { VOErrorIcon() if let errorMessage { Text(errorMessage) @@ -41,7 +41,7 @@ struct FileDelete: View { } .voSecondaryButton(colorScheme: colorScheme) .padding(.horizontal) - } else if showError, errorSeverity == .partial { + } else if errorIsPresented, errorSeverity == .partial { VOWarningIcon() if let errorMessage { Text(errorMessage) @@ -76,17 +76,17 @@ struct FileDelete: View { } else if result.failed.count == fileStore.selection.count { errorSeverity = .full } - showError = true + errorIsPresented = true } } return false } success: { - showError = false + errorIsPresented = false dismiss() } failure: { _ in errorMessage = "Failed to delete \(fileStore.selection.count) item(s)." errorSeverity = .full - showError = true + errorIsPresented = true } anyways: { isProcessing = false } diff --git a/Sources/Screens/File/FileDownload.swift b/Sources/Screens/File/FileDownload.swift index 362e7ef..4b16e5a 100644 --- a/Sources/Screens/File/FileDownload.swift +++ b/Sources/Screens/File/FileDownload.swift @@ -17,7 +17,7 @@ struct FileDownload: View { @Environment(\.colorScheme) private var colorScheme @State private var urls: [URL] = [] @State private var isProcessing = true - @State private var showError = false + @State private var errorIsPresented = false @State private var errorSeverity: ErrorSeverity? @State private var errorMessage: String? private let onCompletion: (([URL]) -> Void)? @@ -29,10 +29,10 @@ struct FileDownload: View { var body: some View { VStack { - if isProcessing, !showError { + if isProcessing, !errorIsPresented { VOSheetProgressView() Text("Downloading \(fileStore.selectionFiles.count) item(s).") - } else if showError, errorSeverity == .full { + } else if errorIsPresented, errorSeverity == .full { VOErrorIcon() if let errorMessage { Text(errorMessage) @@ -44,7 +44,7 @@ struct FileDownload: View { } .voSecondaryButton(colorScheme: colorScheme) .padding(.horizontal) - } else if showError, errorSeverity == .partial { + } else if errorIsPresented, errorSeverity == .partial { VOWarningIcon() if let errorMessage { Text(errorMessage) @@ -103,7 +103,7 @@ struct FileDownload: View { } dispatchGroup.notify(queue: .main) { if urls.count == fileStore.selection.count { - showError = false + errorIsPresented = false isProcessing = false onCompletion?(urls) dismiss() @@ -115,7 +115,7 @@ struct FileDownload: View { } else { errorSeverity = .full } - showError = true + errorIsPresented = true isProcessing = false } } diff --git a/Sources/Screens/File/FileGrid.swift b/Sources/Screens/File/FileGrid.swift index 7e8cd8b..7dc6d83 100644 --- a/Sources/Screens/File/FileGrid.swift +++ b/Sources/Screens/File/FileGrid.swift @@ -11,7 +11,7 @@ import SwiftUI import VoltaserveCore -struct FileGrid: View { +struct FileGrid: View, ListItemScrollable { @ObservedObject private var fileStore: FileStore @ObservedObject private var workspaceStore: WorkspaceStore @State private var tappedItem: VOFile.Entity? @@ -72,7 +72,9 @@ struct FileGrid: View { } } - private func onListItemAppear(_ id: String) { + // MARK: - ListItemScrollable + + func onListItemAppear(_ id: String) { if fileStore.isEntityThreshold(id) { fileStore.fetchNextPage() } diff --git a/Sources/Screens/File/FileInfo.swift b/Sources/Screens/File/FileInfo.swift index 9ce40e9..d0ba12f 100644 --- a/Sources/Screens/File/FileInfo.swift +++ b/Sources/Screens/File/FileInfo.swift @@ -11,11 +11,10 @@ import SwiftUI import VoltaserveCore -struct FileInfo: View { +struct FileInfo: View, ViewDataProvider, LoadStateProvider, TimerLifecycle, TokenDistributing { @EnvironmentObject private var tokenStore: TokenStore @StateObject private var fileStore = FileStore() @Environment(\.dismiss) private var dismiss - @State private var showError = false private let file: VOFile.Entity init(_ file: VOFile.Entity) { @@ -24,103 +23,112 @@ struct FileInfo: View { var body: some View { NavigationView { - Form { - Section(header: VOSectionHeader("Properties")) { - NavigationLink { - Form { - Text(file.name) - } - .navigationTitle(file.name) - } label: { - HStack { - Text("Name") - Spacer() - Text(file.name) - .lineLimit(1) - .truncationMode(.middle) - .foregroundStyle(.secondary) - } - } - HStack { - Text("Type") - Spacer() - Text(fileType) - .foregroundStyle(.secondary) - } - if let fileExtension = file.snapshot?.original.fileExtension { - HStack { - Text("Extension") - Spacer() - VOColorBadge(fileExtension, color: .gray300, style: .fill) - } - } - HStack { - Text("Permission") - Spacer() - VOPermissionBadge(file.permission) - } - } - if let image = file.snapshot?.original.image { - Section(header: VOSectionHeader("Image")) { - HStack { - Text("Dimensions") - Spacer() - Text("\(image.width)x\(image.height)") - .foregroundStyle(.secondary) - } - } - } - if let document = file.snapshot?.original.document { - Section(header: VOSectionHeader("Document")) { - if let pages = document.pages { + VStack { + if isLoading { + ProgressView() + } else if let error { + VOErrorMessage(error) + } else { + Form { + Section(header: VOSectionHeader("Properties")) { + NavigationLink { + Form { + Text(file.name) + } + .navigationTitle(file.name) + } label: { + HStack { + Text("Name") + Spacer() + Text(file.name) + .lineLimit(1) + .truncationMode(.middle) + .foregroundStyle(.secondary) + } + } HStack { - Text("Pages") + Text("Type") Spacer() - Text("\(pages.count)") + Text(fileType) .foregroundStyle(.secondary) } + if let fileExtension = file.snapshot?.original.fileExtension { + HStack { + Text("Extension") + Spacer() + VOColorBadge(fileExtension, color: .gray300, style: .fill) + } + } + HStack { + Text("Permission") + Spacer() + VOPermissionBadge(file.permission) + } } - } - } - if file.type == .folder { - HStack { - Text("Item Count") - Spacer() - if let itemCount = fileStore.itemCount { - Text("\(itemCount)") - .foregroundStyle(.secondary) - } else { - Text("Calculating…") - .foregroundStyle(.secondary) + if let image = file.snapshot?.original.image { + Section(header: VOSectionHeader("Image")) { + HStack { + Text("Dimensions") + Spacer() + Text("\(image.width)x\(image.height)") + .foregroundStyle(.secondary) + } + } } - } - } - Section(header: VOSectionHeader("Storage")) { - VStack(alignment: .leading) { - if let storageUsage = fileStore.storageUsage { - Text("\(storageUsage.bytes.prettyBytes()) of \(storageUsage.maxBytes.prettyBytes()) used") - ProgressView(value: Double(storageUsage.percentage) / 100.0) - } else { - Text("Calculating…") - ProgressView() + if let document = file.snapshot?.original.document { + Section(header: VOSectionHeader("Document")) { + if let pages = document.pages { + HStack { + Text("Pages") + Spacer() + Text("\(pages.count)") + .foregroundStyle(.secondary) + } + } + } } - } - } - Section(header: VOSectionHeader("Time")) { - if let createTime = file.createTime.date?.pretty { - HStack { - Text("Create time") - Spacer() - Text(createTime) - .foregroundStyle(.secondary) + if file.type == .folder { + HStack { + Text("Item Count") + Spacer() + if let itemCount = fileStore.itemCount { + Text("\(itemCount)") + .foregroundStyle(.secondary) + } else { + Text("Calculating…") + .foregroundStyle(.secondary) + } + } } - } - if let updateTime = file.updateTime?.date?.pretty { - HStack { - Text("Update time") - Spacer() - Text(updateTime) - .foregroundStyle(.secondary) + Section(header: VOSectionHeader("Storage")) { + VStack(alignment: .leading) { + if let storageUsage = fileStore.storageUsage { + // swiftlint:disable:next line_length + Text("\(storageUsage.bytes.prettyBytes()) of \(storageUsage.maxBytes.prettyBytes()) used") + ProgressView(value: Double(storageUsage.percentage) / 100.0) + } else { + Text("Calculating…") + ProgressView() + } + } + } + Section(header: VOSectionHeader("Time")) { + if let createTime = file.createTime.date?.pretty { + HStack { + Text("Create time") + Spacer() + Text(createTime) + .foregroundStyle(.secondary) + } + } + if let updateTime = file.updateTime?.date?.pretty { + HStack { + Text("Update time") + Spacer() + Text(updateTime) + .foregroundStyle(.secondary) + } + } } } } @@ -135,13 +143,8 @@ struct FileInfo: View { } } } - .voErrorAlert( - isPresented: $showError, - title: fileStore.errorTitle, - message: fileStore.errorMessage - ) .onAppear { - fileStore.current = file + fileStore.file = file if let token = tokenStore.token { assignTokenToStores(token) startTimers() @@ -152,7 +155,6 @@ struct FileInfo: View { fileStore.clear() stopTimers() } - .sync($fileStore.showError, with: $showError) } private var fileType: String { @@ -162,26 +164,42 @@ struct FileInfo: View { } } - private func onAppearOrChange() { + // MARK: - LoadStateProvider + + var isLoading: Bool { + fileStore.storageUsageIsLoading || fileStore.itemCountIsLoading + } + + var error: String? { + fileStore.storageUsageError ?? fileStore.itemCountError + } + + // MARK: - ViewDataProvider + + func onAppearOrChange() { fetchData() } - private func fetchData() { + func fetchData() { fileStore.fetchStorageUsage() if file.type == .folder { fileStore.fetchItemCount() } } - private func startTimers() { + // MARK: - TimerLifecycle + + func startTimers() { fileStore.startTimer() } - private func stopTimers() { + func stopTimers() { fileStore.stopTimer() } - private func assignTokenToStores(_ token: VOToken.Value) { + // MARK: - TokenDistributing + + func assignTokenToStores(_ token: VOToken.Value) { fileStore.token = token } } diff --git a/Sources/Screens/File/FileList.swift b/Sources/Screens/File/FileList.swift index 5287630..42cbb5a 100644 --- a/Sources/Screens/File/FileList.swift +++ b/Sources/Screens/File/FileList.swift @@ -11,10 +11,9 @@ import SwiftUI import VoltaserveCore -struct FileList: View { +struct FileList: View, ListItemScrollable { @ObservedObject private var fileStore: FileStore @ObservedObject private var workspaceStore: WorkspaceStore - @State private var selection = Set() @State private var tappedItem: VOFile.Entity? init(fileStore: FileStore, workspaceStore: WorkspaceStore) { @@ -24,7 +23,7 @@ struct FileList: View { var body: some View { if let entities = fileStore.entities { - List(selection: $selection) { + List(selection: $fileStore.selection) { ForEach(entities, id: \.id) { file in if file.type == .file { Button { @@ -56,11 +55,12 @@ struct FileList: View { .navigationDestination(item: $tappedItem) { Viewer($0) } - .sync($fileStore.selection, with: $selection) } } - private func onListItemAppear(_ id: String) { + // MARK: - ListItemScrollable + + func onListItemAppear(_ id: String) { if fileStore.isEntityThreshold(id) { fileStore.fetchNextPage() } diff --git a/Sources/Screens/File/FileMove.swift b/Sources/Screens/File/FileMove.swift index 0893357..2a27de6 100644 --- a/Sources/Screens/File/FileMove.swift +++ b/Sources/Screens/File/FileMove.swift @@ -16,7 +16,7 @@ struct FileMove: View { @Environment(\.dismiss) private var dismiss @Environment(\.colorScheme) private var colorScheme @State private var isProcessing = true - @State private var showError = false + @State private var errorIsPresented = false @State private var errorSeverity: ErrorSeverity? @State private var errorMessage: String? private let destinationID: String @@ -28,10 +28,10 @@ struct FileMove: View { var body: some View { VStack { - if isProcessing, !showError { + if isProcessing, !errorIsPresented { VOSheetProgressView() Text("Moving \(fileStore.selection.count) item(s).") - } else if showError, errorSeverity == .full { + } else if errorIsPresented, errorSeverity == .full { VOErrorIcon() if let errorMessage { Text(errorMessage) @@ -43,7 +43,7 @@ struct FileMove: View { } .voSecondaryButton(colorScheme: colorScheme) .padding(.horizontal) - } else if showError, errorSeverity == .partial { + } else if errorIsPresented, errorSeverity == .partial { VOWarningIcon() if let errorMessage { Text(errorMessage) @@ -78,17 +78,17 @@ struct FileMove: View { } else if result.failed.count == fileStore.selection.count { errorSeverity = .full } - showError = true + errorIsPresented = true } } return false } success: { - showError = false + errorIsPresented = false dismiss() } failure: { _ in errorMessage = "Failed to move \(fileStore.selection.count) item(s)." errorSeverity = .full - showError = true + errorIsPresented = true } anyways: { isProcessing = false } diff --git a/Sources/Screens/File/FileOverview.swift b/Sources/Screens/File/FileOverview.swift index bac3539..01d0233 100644 --- a/Sources/Screens/File/FileOverview.swift +++ b/Sources/Screens/File/FileOverview.swift @@ -13,12 +13,11 @@ import SwiftUI import UIKit import VoltaserveCore -struct FileOverview: View { +struct FileOverview: View, ViewDataProvider, LoadStateProvider, TimerLifecycle, TokenDistributing { @EnvironmentObject private var tokenStore: TokenStore @StateObject private var fileStore = FileStore() @ObservedObject private var workspaceStore: WorkspaceStore @State private var searchText = "" - @State private var showError = false private let file: VOFile.Entity init(_ file: VOFile.Entity, workspaceStore: WorkspaceStore) { @@ -28,34 +27,33 @@ struct FileOverview: View { var body: some View { VStack { - if let entities = fileStore.entities { - Group { - if entities.count == 0 { - Text("There are no items.") - } else { - if fileStore.viewMode == .list { - FileList(fileStore: fileStore, workspaceStore: workspaceStore) - } else if fileStore.viewMode == .grid { - FileGrid(fileStore: fileStore, workspaceStore: workspaceStore) + if isLoading { + ProgressView() + } else if let error { + VOErrorMessage(error) + } else { + if let entities = fileStore.entities { + Group { + if entities.count == 0 { + Text("There are no items.") + } else { + if fileStore.viewMode == .list { + FileList(fileStore: fileStore, workspaceStore: workspaceStore) + } else if fileStore.viewMode == .grid { + FileGrid(fileStore: fileStore, workspaceStore: workspaceStore) + } } } + .searchable(text: $searchText) + .onChange(of: fileStore.searchText) { fileStore.searchPublisher.send($1) } + .refreshable { fileStore.fetchNextPage(replace: true) } } - .searchable(text: $searchText) - .onChange(of: fileStore.searchText) { fileStore.searchPublisher.send($1) } - .refreshable { fileStore.fetchNextPage(replace: true) } - } else { - ProgressView() } } - .voErrorAlert( - isPresented: $showError, - title: fileStore.errorTitle, - message: fileStore.errorMessage - ) .fileSheets(fileStore: fileStore, workspaceStore: workspaceStore) .fileToolbar(fileStore: fileStore) .onAppear { - fileStore.current = file + fileStore.file = file fileStore.loadViewModeFromUserDefaults() if let token = tokenStore.token { assignTokenToStores(token) @@ -76,29 +74,43 @@ struct FileOverview: View { fileStore.clear() fileStore.fetchNextPage(replace: true) } - .sync($fileStore.searchText, with: $searchText) - .sync($fileStore.showError, with: $showError) } - private func onAppearOrChange() { + // MARK: - LoadStateProvider + + var isLoading: Bool { + fileStore.entities == nil || fileStore.fileIsLoading || fileStore.taskCountIsLoading + } + + var error: String? { + fileStore.entitiesError ?? fileStore.fileError ?? fileStore.taskCountError + } + + // MARK: - ViewDataProvider + + func onAppearOrChange() { fetchData() } - private func fetchData() { - fileStore.fetch() + func fetchData() { + fileStore.fetchFile() fileStore.fetchNextPage(replace: true) fileStore.fetchTaskCount() } - private func startTimers() { + // MARK: - TimerLifecycle + + func startTimers() { fileStore.startTimer() } - private func stopTimers() { + func stopTimers() { fileStore.stopTimer() } - private func assignTokenToStores(_ token: VOToken.Value) { + // MARK: - TokenDistributing + + func assignTokenToStores(_ token: VOToken.Value) { fileStore.token = token } } diff --git a/Sources/Screens/File/FileRename.swift b/Sources/Screens/File/FileRename.swift index 3996ec8..c2a5347 100644 --- a/Sources/Screens/File/FileRename.swift +++ b/Sources/Screens/File/FileRename.swift @@ -11,15 +11,18 @@ import SwiftUI import VoltaserveCore -struct FileRename: View { +struct FileRename: View, + ViewDataProvider, + LoadStateProvider, + TimerLifecycle, + TokenDistributing, + FormValidatable, + ErrorPresentable { @EnvironmentObject private var tokenStore: TokenStore @StateObject private var fileStore = FileStore() @Environment(\.dismiss) private var dismiss @State private var isSaving = false @State private var value = "" - @State private var errorTitle: String? - @State private var errorMessage: String? - @State private var showError = false private let file: VOFile.Entity private let onCompletion: ((VOFile.Entity) -> Void)? @@ -31,14 +34,17 @@ struct FileRename: View { var body: some View { NavigationView { VStack { - if !value.isEmpty { - Form { - TextField("Name", text: $value) - .disabled(isSaving) - } - - } else { + if isLoading { ProgressView() + } else if let error { + VOErrorMessage(error) + } else { + if !value.isEmpty { + Form { + TextField("Name", text: $value) + .disabled(isSaving) + } + } } } .navigationBarTitleDisplayMode(.inline) @@ -60,9 +66,9 @@ struct FileRename: View { } } } - .voErrorAlert(isPresented: $showError, title: errorTitle, message: errorMessage) + .voErrorSheet(isPresented: $errorIsPresented, message: errorMessage) .onAppear { - fileStore.current = file + fileStore.file = file if let token = tokenStore.token { assignTokenToStores(token) startTimers() @@ -78,7 +84,7 @@ struct FileRename: View { onAppearOrChange() } } - .onChange(of: fileStore.current) { _, file in + .onChange(of: fileStore.file) { _, file in if let file { value = file.name } @@ -86,32 +92,12 @@ struct FileRename: View { } } - private func onAppearOrChange() { - fetchData() - } - - private func fetchData() { - fileStore.fetch() - } - - private func startTimers() { - fileStore.startTimer() - } - - private func stopTimers() { - fileStore.stopTimer() - } - - private func assignTokenToStores(_ token: VOToken.Value) { - fileStore.token = token - } - private var normalizedValue: String { value.trimmingCharacters(in: .whitespaces) } private func performRename() { - guard let file = fileStore.current else { return } + guard let file = fileStore.file else { return } isSaving = true var updatedFile: VOFile.Entity? @@ -124,16 +110,58 @@ struct FileRename: View { onCompletion(updatedFile) } } failure: { message in - errorTitle = "Error: Renaming File" errorMessage = message - showError = true + errorIsPresented = true } anyways: { isSaving = false } } - private func isValid() -> Bool { - if let file = fileStore.current { + // MARK: - LoadStateProvider + + var isLoading: Bool { + fileStore.fileIsLoading + } + + var error: String? { + fileStore.fileError + } + + // MARK: - ErrorPresentable + + @State var errorIsPresented: Bool = false + @State var errorMessage: String? + + // MARK: - ViewDataProvider + + func onAppearOrChange() { + fetchData() + } + + func fetchData() { + fileStore.fetchFile() + } + + // MARK: - TimerLifecycle + + func startTimers() { + fileStore.startTimer() + } + + func stopTimers() { + fileStore.stopTimer() + } + + // MARK: - TokenDistributing + + func assignTokenToStores(_ token: VOToken.Value) { + fileStore.token = token + } + + // MARK: - FormValidatable + + func isValid() -> Bool { + if let file = fileStore.file { return !normalizedValue.isEmpty && normalizedValue != file.name } return false diff --git a/Sources/Screens/File/FileStore.swift b/Sources/Screens/File/FileStore.swift index 3fd9ea5..c0a64a5 100644 --- a/Sources/Screens/File/FileStore.swift +++ b/Sources/Screens/File/FileStore.swift @@ -15,10 +15,20 @@ import VoltaserveCore // swiftlint:disable:next type_body_length class FileStore: ObservableObject { @Published var entities: [VOFile.Entity]? - @Published var current: VOFile.Entity? + @Published var entitiesIsLoading: Bool = false + @Published var entitiesError: String? @Published var taskCount: Int? + @Published var taskCountIsLoading: Bool = false + @Published var taskCountError: String? @Published var storageUsage: VOStorage.Usage? + @Published var storageUsageIsLoading: Bool = false + @Published var storageUsageError: String? @Published var itemCount: Int? + @Published var itemCountIsLoading: Bool = false + @Published var itemCountError: String? + @Published var file: VOFile.Entity? + @Published var fileIsLoading: Bool = false + @Published var fileError: String? @Published var query: VOFile.Query? @Published var selection = Set() { willSet { @@ -26,29 +36,25 @@ class FileStore: ObservableObject { } } - @Published var showRename = false - @Published var showDelete = false - @Published var showDownload = false - @Published var showBrowserForMove = false - @Published var showBrowserForCopy = false - @Published var showUploadDocumentPicker = false - @Published var showDownloadDocumentPicker = false - @Published var showCreateFolder = false - @Published var showUpload = false - @Published var showMove = false - @Published var showCopy = false - @Published var showSharing = false - @Published var showSnapshots = false - @Published var showTasks = false - @Published var showMosaic = false - @Published var showInsights = false - @Published var showInfo = false + @Published var renameIsPresented = false + @Published var deleteIsPresented = false + @Published var downloadIsPresented = false + @Published var browserForMoveIsPresented = false + @Published var browserForCopyIsPresented = false + @Published var uploadDocumentPickerIsPresented = false + @Published var downloadDocumentPickerIsPresented = false + @Published var createFolderIsPresented = false + @Published var uploadIsPresented = false + @Published var moveIsPresented = false + @Published var copyIsPresented = false + @Published var sharingIsPresented = false + @Published var snapshotsIsPresented = false + @Published var tasksIsPresented = false + @Published var mosaicIsPresented = false + @Published var insightsIsPresented = false + @Published var infoIsPresented = false @Published var viewMode: ViewMode = .grid @Published var searchText = "" - @Published var showError = false - @Published var errorTitle: String? - @Published var errorMessage: String? - @Published var isLoading = false private var list: VOFile.List? private var cancellables = Set() private var timer: Timer? @@ -100,23 +106,24 @@ class FileStore: ObservableObject { // MARK: - Fetch - private func fetch(_ id: String) async throws -> VOFile.Entity? { - try await fileClient?.fetch(id) + private func fetchFile() async throws -> VOFile.Entity? { + guard let file else { return nil } + return try await fileClient?.fetch(file.id) } - func fetch() { - guard let current else { return } - var file: VOFile.Entity? - + func fetchFile() { + var folder: VOFile.Entity? withErrorHandling { - file = try await self.fetch(current.id) + folder = try await self.fetchFile() return true + } before: { + self.fileIsLoading = true } success: { - self.current = file + self.file = folder } failure: { message in - self.errorTitle = "Error: Fetching File" - self.errorMessage = message - self.showError = true + self.fileError = message + } anyways: { + self.fileIsLoading = false } } @@ -129,15 +136,15 @@ class FileStore: ObservableObject { } func fetchNextPage(replace: Bool = false) { - guard let current else { return } - guard !isLoading else { return } + guard let file else { return } + guard !entitiesIsLoading else { return } var nextPage = -1 var list: VOFile.List? withErrorHandling { if let list = self.list { - let probe = try await self.fetchProbe(current.id, size: Constants.pageSize) + let probe = try await self.fetchProbe(file.id, size: Constants.pageSize) if let probe { self.list = .init( data: list.data, @@ -151,10 +158,10 @@ class FileStore: ObservableObject { } if !self.hasNextPage() { return false } nextPage = self.nextPage() - list = try await self.fetchList(current.id, page: nextPage) + list = try await self.fetchList(file.id, page: nextPage) return true } before: { - self.isLoading = true + self.entitiesIsLoading = true } success: { self.list = list if let list { @@ -165,11 +172,9 @@ class FileStore: ObservableObject { } } } failure: { message in - self.errorTitle = "Error: Fetching Files" - self.errorMessage = message - self.showError = true + self.entitiesError = message } anyways: { - self.isLoading = false + self.entitiesIsLoading = false } } @@ -182,18 +187,20 @@ class FileStore: ObservableObject { withErrorHandling { taskCount = try await self.fetchTaskCount() return true + } before: { + self.taskCountIsLoading = true } success: { self.taskCount = taskCount } failure: { message in - self.errorTitle = "Error: Fetching Task Count" - self.errorMessage = message - self.showError = true + self.taskCountError = message + } anyways: { + self.taskCountIsLoading = false } } private func fetchStorageUsage() async throws -> VOStorage.Usage? { - guard let current else { return nil } - return try await storageClient?.fetchFileUsage(current.id) + guard let file else { return nil } + return try await storageClient?.fetchFileUsage(file.id) } func fetchStorageUsage() { @@ -201,18 +208,20 @@ class FileStore: ObservableObject { withErrorHandling { storageUsage = try await self.fetchStorageUsage() return true + } before: { + self.storageUsageIsLoading = true } success: { self.storageUsage = storageUsage } failure: { message in - self.errorTitle = "Error: Fetching File Storage Usage" - self.errorMessage = message - self.showError = true + self.storageUsageError = message + } anyways: { + self.storageUsageIsLoading = false } } private func fetchItemCount() async throws -> Int? { - guard let current else { return nil } - return try await fileClient?.fetchCount(current.id) + guard let file else { return nil } + return try await fileClient?.fetchCount(file.id) } func fetchItemCount() { @@ -220,12 +229,14 @@ class FileStore: ObservableObject { withErrorHandling { itemCount = try await self.fetchItemCount() return true + } before: { + self.itemCountIsLoading = true } success: { self.itemCount = itemCount } failure: { message in - self.errorTitle = "Error: Fetching Item Count" - self.errorMessage = message - self.showError = true + self.itemCountError = message + } anyways: { + self.itemCountIsLoading = false } } @@ -252,11 +263,11 @@ class FileStore: ObservableObject { } func upload(_ url: URL, workspaceID: String) async throws -> VOFile.Entity? { - guard let current else { return nil } + guard let file else { return nil } if let data = try? Data(contentsOf: url) { return try await fileClient?.createFile(.init( workspaceID: workspaceID, - parentID: current.id, + parentID: file.id, name: url.lastPathComponent, data: data )) @@ -337,7 +348,7 @@ class FileStore: ObservableObject { func startTimer() { guard timer == nil else { return } timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { _ in - if let current = self.current { + if let current = self.file { Task { var size = Constants.pageSize if let list = self.list { @@ -351,12 +362,12 @@ class FileStore: ObservableObject { } } } - if let current = self.current { + if self.file != nil { Task { - let file = try await self.fetch(current.id) + let file = try await self.fetchFile() if let file { DispatchQueue.main.async { - self.current = file + self.file = file } } } diff --git a/Sources/Screens/File/FileToolbar.swift b/Sources/Screens/File/FileToolbar.swift index b761544..5525172 100644 --- a/Sources/Screens/File/FileToolbar.swift +++ b/Sources/Screens/File/FileToolbar.swift @@ -41,7 +41,7 @@ struct FileToolbar: ViewModifier { uploadMenu } ToolbarItem(placement: .topBarLeading) { - if fileStore.isLoading, fileStore.entities != nil { + if fileStore.entitiesIsLoading { ProgressView() } } @@ -59,7 +59,7 @@ struct FileToolbar: ViewModifier { private var tasksButton: some View { ZStack { Button { - fileStore.showTasks = true + fileStore.tasksIsPresented = true } label: { Image(systemName: "square.3.layers.3d") } @@ -75,12 +75,12 @@ struct FileToolbar: ViewModifier { private var uploadMenu: some View { Menu { Button { - fileStore.showUploadDocumentPicker = true + fileStore.uploadDocumentPickerIsPresented = true } label: { Label("Upload Files", systemImage: "icloud.and.arrow.up") } Button { - fileStore.showCreateFolder = true + fileStore.createFolderIsPresented = true } label: { Label("New Folder", systemImage: "folder.badge.plus") } @@ -93,25 +93,25 @@ struct FileToolbar: ViewModifier { FileMenu( fileStore: fileStore, onSharing: { - fileStore.showSharing = true + fileStore.sharingIsPresented = true }, onUpload: { - fileStore.showUploadDocumentPicker = true + fileStore.uploadDocumentPickerIsPresented = true }, onDownload: { - fileStore.showDownload = true + fileStore.downloadIsPresented = true }, onDelete: { - fileStore.showDelete = true + fileStore.deleteIsPresented = true }, onRename: { - fileStore.showRename = true + fileStore.renameIsPresented = true }, onMove: { - fileStore.showBrowserForMove = true + fileStore.browserForMoveIsPresented = true }, onCopy: { - fileStore.showBrowserForCopy = true + fileStore.browserForCopyIsPresented = true } ) } diff --git a/Sources/Screens/File/FileUpload.swift b/Sources/Screens/File/FileUpload.swift index 7ed8c5d..752a126 100644 --- a/Sources/Screens/File/FileUpload.swift +++ b/Sources/Screens/File/FileUpload.swift @@ -17,7 +17,7 @@ struct FileUpload: View { @Environment(\.dismiss) private var dismiss @Environment(\.colorScheme) private var colorScheme @State private var isProcessing = true - @State private var showError = false + @State private var errorIsPresented = false @State private var errorSeverity: ErrorSeverity? @State private var errorMessage: String? private let urls: [URL] @@ -30,10 +30,10 @@ struct FileUpload: View { var body: some View { VStack { - if isProcessing, !showError { + if isProcessing, !errorIsPresented { VOSheetProgressView() Text("Uploading \(urls.count) item(s).") - } else if showError, errorSeverity == .full { + } else if errorIsPresented, errorSeverity == .full { VOErrorIcon() if let errorMessage { Text(errorMessage) @@ -45,7 +45,7 @@ struct FileUpload: View { } .voSecondaryButton(colorScheme: colorScheme) .padding(.horizontal) - } else if showError, errorSeverity == .partial { + } else if errorIsPresented, errorSeverity == .partial { VOWarningIcon() if let errorMessage { Text(errorMessage) @@ -91,7 +91,7 @@ struct FileUpload: View { } dispatchGroup.notify(queue: .main) { if failedCount == 0 { - showError = false + errorIsPresented = false dismiss() } else { errorMessage = "Failed to upload \(failedCount) item(s)." @@ -100,7 +100,7 @@ struct FileUpload: View { } else if failedCount < urls.count { errorSeverity = .partial } - showError = true + errorIsPresented = true } isProcessing = false } diff --git a/Sources/Screens/File/FolderCreate.swift b/Sources/Screens/File/FolderCreate.swift index 251c77e..f74c4ae 100644 --- a/Sources/Screens/File/FolderCreate.swift +++ b/Sources/Screens/File/FolderCreate.swift @@ -11,14 +11,11 @@ import SwiftUI import VoltaserveCore -struct FolderCreate: View { +struct FolderCreate: View, ErrorPresentable, FormValidatable { @ObservedObject private var fileStore: FileStore @Environment(\.dismiss) private var dismiss @State private var name = "" @State private var isProcessing = false - @State private var showError = false - @State private var errorTitle: String? - @State private var errorMessage: String? private let parentID: String private let workspaceId: String @@ -53,7 +50,7 @@ struct FolderCreate: View { } } } - .voErrorAlert(isPresented: $showError, title: errorTitle, message: errorMessage) + .voErrorSheet(isPresented: $errorIsPresented, message: errorMessage) } } @@ -76,15 +73,21 @@ struct FolderCreate: View { } success: { dismiss() } failure: { message in - errorTitle = "Error: Creating Folder" errorMessage = message - showError = true + errorIsPresented = true } anyways: { isProcessing = false } } - private func isValid() -> Bool { + // MARK: - ErrorPresentable + + @State var errorIsPresented: Bool = false + @State var errorMessage: String? + + // MARK: - FormValidatable + + func isValid() -> Bool { !normalizedName.isEmpty } } diff --git a/Sources/Screens/File/Menu/FileActions.swift b/Sources/Screens/File/Menu/FileActions.swift index bb0b8b8..07088c3 100644 --- a/Sources/Screens/File/Menu/FileActions.swift +++ b/Sources/Screens/File/Menu/FileActions.swift @@ -26,34 +26,34 @@ struct FileActions: ViewModifier { file, fileStore: fileStore, onInsights: { - fileStore.showInsights = true + fileStore.insightsIsPresented = true }, onMosaic: { - fileStore.showMosaic = true + fileStore.mosaicIsPresented = true }, onSharing: { - fileStore.showSharing = true + fileStore.sharingIsPresented = true }, onSnapshots: { - fileStore.showSnapshots = true + fileStore.snapshotsIsPresented = true }, onUpload: { - fileStore.showUploadDocumentPicker = true + fileStore.uploadDocumentPickerIsPresented = true }, onDownload: { - fileStore.showDownload = true + fileStore.downloadIsPresented = true }, onDelete: { - fileStore.showDelete = true + fileStore.deleteIsPresented = true }, onRename: { - fileStore.showRename = true + fileStore.renameIsPresented = true }, onMove: { - fileStore.showBrowserForMove = true + fileStore.browserForMoveIsPresented = true }, onCopy: { - fileStore.showBrowserForCopy = true + fileStore.browserForCopyIsPresented = true }, onOpen: { if let snapshot = file.snapshot, @@ -63,7 +63,7 @@ struct FileActions: ViewModifier { } }, onInfo: { - fileStore.showInfo = true + fileStore.infoIsPresented = true } ) } diff --git a/Sources/Screens/File/Sheet/FileSheetCopy.swift b/Sources/Screens/File/Sheet/FileSheetCopy.swift index 413eaae..9077061 100644 --- a/Sources/Screens/File/Sheet/FileSheetCopy.swift +++ b/Sources/Screens/File/Sheet/FileSheetCopy.swift @@ -13,8 +13,6 @@ import SwiftUI struct FileSheetCopy: ViewModifier { @ObservedObject private var fileStore: FileStore @ObservedObject private var workspaceStore: WorkspaceStore - @State private var showBrowser = false - @State private var showCopy = false @State private var destinationID: String? init(fileStore: FileStore, workspaceStore: WorkspaceStore) { @@ -24,25 +22,23 @@ struct FileSheetCopy: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showBrowser) { + .sheet(isPresented: $fileStore.browserForCopyIsPresented) { NavigationStack { if let workspace = workspaceStore.current { BrowserOverview(workspaceStore: workspaceStore, confirmLabelText: "Copy Here") { id in destinationID = id - fileStore.showCopy = true + fileStore.copyIsPresented = true } .navigationBarTitleDisplayMode(.inline) .navigationTitle(workspace.name) } } } - .sheet(isPresented: $showCopy) { + .sheet(isPresented: $fileStore.copyIsPresented) { if let destinationID, !fileStore.selection.isEmpty { FileCopy(fileStore: fileStore, to: destinationID) } } - .sync($fileStore.showBrowserForCopy, with: $showBrowser) - .sync($fileStore.showCopy, with: $showCopy) } } diff --git a/Sources/Screens/File/Sheet/FileSheetCreateFolder.swift b/Sources/Screens/File/Sheet/FileSheetCreateFolder.swift index f258dc1..9a55a82 100644 --- a/Sources/Screens/File/Sheet/FileSheetCreateFolder.swift +++ b/Sources/Screens/File/Sheet/FileSheetCreateFolder.swift @@ -13,7 +13,6 @@ import SwiftUI struct FileSheetCreateFolder: ViewModifier { @ObservedObject private var fileStore: FileStore @ObservedObject private var workspaceStore: WorkspaceStore - @State private var showCreate = false init(fileStore: FileStore, workspaceStore: WorkspaceStore) { self.fileStore = fileStore @@ -22,12 +21,11 @@ struct FileSheetCreateFolder: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showCreate) { - if let parent = fileStore.current, let workspace = workspaceStore.current { + .sheet(isPresented: $fileStore.createFolderIsPresented) { + if let parent = fileStore.file, let workspace = workspaceStore.current { FolderCreate(parentID: parent.id, workspaceId: workspace.id, fileStore: fileStore) } } - .sync($fileStore.showCreateFolder, with: $showCreate) } } diff --git a/Sources/Screens/File/Sheet/FileSheetDelete.swift b/Sources/Screens/File/Sheet/FileSheetDelete.swift index f630775..87abd20 100644 --- a/Sources/Screens/File/Sheet/FileSheetDelete.swift +++ b/Sources/Screens/File/Sheet/FileSheetDelete.swift @@ -12,7 +12,6 @@ import SwiftUI struct FileSheetDelete: ViewModifier { @ObservedObject private var fileStore: FileStore - @State private var showDelete = false init(fileStore: FileStore) { self.fileStore = fileStore @@ -20,12 +19,11 @@ struct FileSheetDelete: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showDelete) { + .sheet(isPresented: $fileStore.deleteIsPresented) { if !fileStore.selection.isEmpty { FileDelete(fileStore: fileStore) } } - .sync($fileStore.showDelete, with: $showDelete) } } diff --git a/Sources/Screens/File/Sheet/FileSheetDownload.swift b/Sources/Screens/File/Sheet/FileSheetDownload.swift index 452ce4b..641fef5 100644 --- a/Sources/Screens/File/Sheet/FileSheetDownload.swift +++ b/Sources/Screens/File/Sheet/FileSheetDownload.swift @@ -12,8 +12,6 @@ import SwiftUI struct FileSheetDownload: ViewModifier { @ObservedObject private var fileStore: FileStore - @State private var showPicker = false - @State private var showDownload = false @State private var pickerURLs: [URL]? init(fileStore: FileStore) { @@ -22,15 +20,15 @@ struct FileSheetDownload: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showDownload) { + .sheet(isPresented: $fileStore.downloadIsPresented) { if !fileStore.selection.isEmpty { FileDownload(fileStore: fileStore) { localURLs in pickerURLs = localURLs - fileStore.showDownloadDocumentPicker = true + fileStore.downloadDocumentPickerIsPresented = true } } } - .sheet(isPresented: $showPicker, onDismiss: handleDismiss) { + .sheet(isPresented: $fileStore.downloadDocumentPickerIsPresented, onDismiss: handleDismiss) { if let pickerURLs { FileDownloadPicker( sourceURLs: pickerURLs, @@ -38,8 +36,6 @@ struct FileSheetDownload: ViewModifier { ) } } - .sync($fileStore.showDownloadDocumentPicker, with: $showPicker) - .sync($fileStore.showDownload, with: $showDownload) } private func handleDismiss() { @@ -53,7 +49,7 @@ struct FileSheetDownload: ViewModifier { } } } - fileStore.showDownloadDocumentPicker = false + fileStore.downloadDocumentPickerIsPresented = false } } diff --git a/Sources/Screens/File/Sheet/FileSheetInfo.swift b/Sources/Screens/File/Sheet/FileSheetInfo.swift index f0d7840..2e2eafe 100644 --- a/Sources/Screens/File/Sheet/FileSheetInfo.swift +++ b/Sources/Screens/File/Sheet/FileSheetInfo.swift @@ -13,7 +13,6 @@ import VoltaserveCore struct FileSheetInfo: ViewModifier { @ObservedObject private var fileStore: FileStore - @State private var showInfo = false init(fileStore: FileStore) { self.fileStore = fileStore @@ -21,12 +20,11 @@ struct FileSheetInfo: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showInfo) { + .sheet(isPresented: $fileStore.infoIsPresented) { if let file { FileInfo(file) } } - .sync($fileStore.showInfo, with: $showInfo) } private var file: VOFile.Entity? { diff --git a/Sources/Screens/File/Sheet/FileSheetInsights.swift b/Sources/Screens/File/Sheet/FileSheetInsights.swift index b8eb988..263ca01 100644 --- a/Sources/Screens/File/Sheet/FileSheetInsights.swift +++ b/Sources/Screens/File/Sheet/FileSheetInsights.swift @@ -13,7 +13,6 @@ import VoltaserveCore struct FileSheetInsights: ViewModifier { @ObservedObject private var fileStore: FileStore - @State private var showInsights = false init(fileStore: FileStore) { self.fileStore = fileStore @@ -21,7 +20,7 @@ struct FileSheetInsights: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showInsights) { + .sheet(isPresented: $fileStore.insightsIsPresented) { if let file { if let snapshot = file.snapshot, snapshot.hasEntities() { InsightsOverview(file) @@ -30,7 +29,6 @@ struct FileSheetInsights: ViewModifier { } } } - .sync($fileStore.showInsights, with: $showInsights) } private var file: VOFile.Entity? { diff --git a/Sources/Screens/File/Sheet/FileSheetMosaic.swift b/Sources/Screens/File/Sheet/FileSheetMosaic.swift index a55d6e8..982b522 100644 --- a/Sources/Screens/File/Sheet/FileSheetMosaic.swift +++ b/Sources/Screens/File/Sheet/FileSheetMosaic.swift @@ -13,7 +13,6 @@ import VoltaserveCore struct FileSheetMosaic: ViewModifier { @ObservedObject private var fileStore: FileStore - @State private var showMosaic = false init(fileStore: FileStore) { self.fileStore = fileStore @@ -21,7 +20,7 @@ struct FileSheetMosaic: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showMosaic) { + .sheet(isPresented: $fileStore.mosaicIsPresented) { if let file, let snapshot = file.snapshot { if snapshot.hasMosaic() { MosaicSettings(file) @@ -30,7 +29,6 @@ struct FileSheetMosaic: ViewModifier { } } } - .sync($fileStore.showMosaic, with: $showMosaic) } private var file: VOFile.Entity? { diff --git a/Sources/Screens/File/Sheet/FileSheetMove.swift b/Sources/Screens/File/Sheet/FileSheetMove.swift index 24f6840..59245ce 100644 --- a/Sources/Screens/File/Sheet/FileSheetMove.swift +++ b/Sources/Screens/File/Sheet/FileSheetMove.swift @@ -13,8 +13,6 @@ import SwiftUI struct FileSheetMove: ViewModifier { @ObservedObject private var fileStore: FileStore @ObservedObject private var workspaceStore: WorkspaceStore - @State private var showBrowser = false - @State private var showMove = false @State private var destinationID: String? init(fileStore: FileStore, workspaceStore: WorkspaceStore) { @@ -24,25 +22,23 @@ struct FileSheetMove: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showBrowser) { + .sheet(isPresented: $fileStore.browserForMoveIsPresented) { if let workspace = workspaceStore.current { NavigationStack { BrowserOverview(workspaceStore: workspaceStore, confirmLabelText: "Move Here") { id in destinationID = id - fileStore.showMove = true + fileStore.moveIsPresented = true } .navigationBarTitleDisplayMode(.inline) .navigationTitle(workspace.name) } } } - .sheet(isPresented: $showMove) { + .sheet(isPresented: $fileStore.moveIsPresented) { if let destinationID, !fileStore.selection.isEmpty { FileMove(fileStore: fileStore, to: destinationID) } } - .sync($fileStore.showBrowserForMove, with: $showBrowser) - .sync($fileStore.showMove, with: $showMove) } } diff --git a/Sources/Screens/File/Sheet/FileSheetRename.swift b/Sources/Screens/File/Sheet/FileSheetRename.swift index 6622ab3..55ccb7c 100644 --- a/Sources/Screens/File/Sheet/FileSheetRename.swift +++ b/Sources/Screens/File/Sheet/FileSheetRename.swift @@ -12,7 +12,6 @@ import SwiftUI struct FileSheetRename: ViewModifier { @ObservedObject private var fileStore: FileStore - @State private var showRename = false init(fileStore: FileStore) { self.fileStore = fileStore @@ -20,7 +19,7 @@ struct FileSheetRename: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showRename) { + .sheet(isPresented: $fileStore.renameIsPresented) { if !fileStore.selection.isEmpty, let file = fileStore.selectionFiles.first { FileRename(file) { updatedFile in if let index = fileStore.entities?.firstIndex(where: { $0.id == file.id }) { @@ -29,7 +28,6 @@ struct FileSheetRename: ViewModifier { } } } - .sync($fileStore.showRename, with: $showRename) } } diff --git a/Sources/Screens/File/Sheet/FileSheetSharing.swift b/Sources/Screens/File/Sheet/FileSheetSharing.swift index b7c427f..57ae42d 100644 --- a/Sources/Screens/File/Sheet/FileSheetSharing.swift +++ b/Sources/Screens/File/Sheet/FileSheetSharing.swift @@ -14,7 +14,6 @@ import VoltaserveCore struct FileSheetSharing: ViewModifier { @ObservedObject private var fileStore: FileStore @ObservedObject private var workspaceStore: WorkspaceStore - @State private var showSharing = false init(fileStore: FileStore, workspaceStore: WorkspaceStore) { self.fileStore = fileStore @@ -23,14 +22,13 @@ struct FileSheetSharing: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showSharing) { + .sheet(isPresented: $fileStore.sharingIsPresented) { if let fileID { SharingOverview(fileID, workspaceStore: workspaceStore) } else if fileStore.selection.count > 1 { SharingBatch(Array(fileStore.selection), workspaceStore: workspaceStore) } } - .sync($fileStore.showSharing, with: $showSharing) } private var fileID: String? { diff --git a/Sources/Screens/File/Sheet/FileSheetSnapshots.swift b/Sources/Screens/File/Sheet/FileSheetSnapshots.swift index eff220a..52b85a8 100644 --- a/Sources/Screens/File/Sheet/FileSheetSnapshots.swift +++ b/Sources/Screens/File/Sheet/FileSheetSnapshots.swift @@ -13,7 +13,6 @@ import VoltaserveCore struct FileSheetSnapshots: ViewModifier { @ObservedObject private var fileStore: FileStore - @State private var showSnapshots = false init(fileStore: FileStore) { self.fileStore = fileStore @@ -21,12 +20,11 @@ struct FileSheetSnapshots: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showSnapshots) { + .sheet(isPresented: $fileStore.snapshotsIsPresented) { if let file { SnapshotList(fileID: file.id) } } - .sync($fileStore.showSnapshots, with: $showSnapshots) } private var file: VOFile.Entity? { diff --git a/Sources/Screens/File/Sheet/FileSheetTasks.swift b/Sources/Screens/File/Sheet/FileSheetTasks.swift index 765201a..96bb1b0 100644 --- a/Sources/Screens/File/Sheet/FileSheetTasks.swift +++ b/Sources/Screens/File/Sheet/FileSheetTasks.swift @@ -12,7 +12,6 @@ import SwiftUI struct FileSheetTasks: ViewModifier { @ObservedObject private var fileStore: FileStore - @State private var showTasks = false init(fileStore: FileStore) { self.fileStore = fileStore @@ -20,10 +19,9 @@ struct FileSheetTasks: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showTasks) { + .sheet(isPresented: $fileStore.tasksIsPresented) { TaskList(fileStore: fileStore) } - .sync($fileStore.showTasks, with: $showTasks) } } diff --git a/Sources/Screens/File/Sheet/FileSheetUpload.swift b/Sources/Screens/File/Sheet/FileSheetUpload.swift index e353977..b3c7ee4 100644 --- a/Sources/Screens/File/Sheet/FileSheetUpload.swift +++ b/Sources/Screens/File/Sheet/FileSheetUpload.swift @@ -13,8 +13,6 @@ import SwiftUI struct FileSheetUpload: ViewModifier { @ObservedObject private var fileStore: FileStore @ObservedObject private var workspaceStore: WorkspaceStore - @State private var showPicker = false - @State private var showUpload = false @State private var pickerURLs: [URL]? init(fileStore: FileStore, workspaceStore: WorkspaceStore) { @@ -24,20 +22,18 @@ struct FileSheetUpload: ViewModifier { func body(content: Content) -> some View { content - .sheet(isPresented: $showPicker) { + .sheet(isPresented: $fileStore.uploadDocumentPickerIsPresented) { FileUploadPicker { urls in pickerURLs = urls - fileStore.showUploadDocumentPicker = false - fileStore.showUpload = true + fileStore.uploadDocumentPickerIsPresented = false + fileStore.uploadIsPresented = true } } - .sheet(isPresented: $showUpload) { + .sheet(isPresented: $fileStore.uploadIsPresented) { if let pickerURLs { FileUpload(pickerURLs, fileStore: fileStore, workspaceStore: workspaceStore) } } - .sync($fileStore.showUploadDocumentPicker, with: $showPicker) - .sync($fileStore.showUpload, with: $showUpload) } } diff --git a/Sources/Screens/Sharing/SharingBatch.swift b/Sources/Screens/Sharing/SharingBatch.swift index b3deb7a..5239876 100644 --- a/Sources/Screens/Sharing/SharingBatch.swift +++ b/Sources/Screens/Sharing/SharingBatch.swift @@ -11,7 +11,8 @@ import SwiftUI import VoltaserveCore -struct SharingBatch: View { +struct SharingBatch: View, TokenDistributing { + @EnvironmentObject private var tokenStore: TokenStore @StateObject private var sharingStore = SharingStore() @ObservedObject private var workspaceStore: WorkspaceStore @Environment(\.dismiss) private var dismiss @@ -55,10 +56,21 @@ struct SharingBatch: View { } .navigationBarTitleDisplayMode(.inline) .navigationTitle("Sharing") + .onAppear { + if let token = tokenStore.token { + assignTokenToStores(token) + } + } } enum Tag { case users case groups } + + // MARK: - TokenDistributing + + func assignTokenToStores(_ token: VOToken.Value) { + sharingStore.token = token + } } diff --git a/Sources/Screens/Workspace/WorkspaceList.swift b/Sources/Screens/Workspace/WorkspaceList.swift index 3d06a3f..2f657c7 100644 --- a/Sources/Screens/Workspace/WorkspaceList.swift +++ b/Sources/Screens/Workspace/WorkspaceList.swift @@ -18,11 +18,10 @@ struct WorkspaceList: View { @StateObject private var accountStore = AccountStore() @StateObject private var invitationStore = InvitationStore() @Environment(\.dismiss) private var dismiss - @State private var showAccount = false - @State private var showCreate = false - @State private var showOverview = false - @State private var searchText = "" - @State private var newWorkspace: VOWorkspace.Entity? + @State private var accountIsPresented = false + @State private var createIsPresented = false + @State private var overviewIsPresented = false + @State private var newlyCreatedEntity: VOWorkspace.Entity? var body: some View { NavigationStack { @@ -48,7 +47,7 @@ struct WorkspaceList: View { } } } - .searchable(text: $searchText) + .searchable(text: $workspaceStore.searchText) .onChange(of: workspaceStore.searchText) { workspaceStore.searchPublisher.send($1) } @@ -69,29 +68,29 @@ struct WorkspaceList: View { } ToolbarItem(placement: .topBarLeading) { Button { - showCreate = true + createIsPresented = true } label: { Image(systemName: "plus") } } ToolbarItem(placement: .topBarLeading) { - if workspaceStore.entitiesIsLoading, workspaceStore.entities != nil { + if workspaceStore.entitiesIsLoading { ProgressView() } } } - .sheet(isPresented: $showCreate) { - WorkspaceCreate(workspaceStore: workspaceStore) { newWorkspace in - self.newWorkspace = newWorkspace - showOverview = true + .sheet(isPresented: $createIsPresented) { + WorkspaceCreate(workspaceStore: workspaceStore) { newlyCreatedEntity in + self.newlyCreatedEntity = newlyCreatedEntity + overviewIsPresented = true } } - .sheet(isPresented: $showAccount) { + .sheet(isPresented: $accountIsPresented) { AccountOverview() } - .navigationDestination(isPresented: $showOverview) { - if let newWorkspace { - WorkspaceOverview(newWorkspace, workspaceStore: workspaceStore) + .navigationDestination(isPresented: $overviewIsPresented) { + if let newlyCreatedEntity { + WorkspaceOverview(newlyCreatedEntity, workspaceStore: workspaceStore) } } } @@ -118,7 +117,6 @@ struct WorkspaceList: View { workspaceStore.clear() workspaceStore.fetchNextPage() } - .sync($workspaceStore.searchText, with: $searchText) } private var isLoading: Bool { @@ -136,7 +134,7 @@ struct WorkspaceList: View { private var accountButton: some View { ZStack { Button { - showAccount.toggle() + accountIsPresented.toggle() } label: { if let identityUser = accountStore.identityUser { VOAvatar( diff --git a/Sources/Screens/Workspace/WorkspaceStore.swift b/Sources/Screens/Workspace/WorkspaceStore.swift index fc3742b..f00c1bf 100644 --- a/Sources/Screens/Workspace/WorkspaceStore.swift +++ b/Sources/Screens/Workspace/WorkspaceStore.swift @@ -16,13 +16,13 @@ class WorkspaceStore: ObservableObject { @Published var entities: [VOWorkspace.Entity]? @Published var entitiesIsLoading: Bool = false @Published var entitiesError: String? - @Published var current: VOWorkspace.Entity? @Published var root: VOFile.Entity? @Published var rootIsLoading: Bool = false @Published var rootError: String? @Published var storageUsage: VOStorage.Usage? @Published var storageUsageIsLoading: Bool = false @Published var storageUsageError: String? + @Published var current: VOWorkspace.Entity? @Published var query: String? @Published var searchText = "" private var list: VOWorkspace.List?