Merge pull request #14 from rock-n-code/feature/delete-item
Feature: Delete items from the folders
This commit is contained in:
commit
225e4994f8
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct Document {
|
struct Document: FileSystemItem {
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
@ -35,7 +35,3 @@ struct Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - FileSystemIdIdentifiable
|
|
||||||
|
|
||||||
extension Document: FileSystemItemIdentifiable {}
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
public struct Folder {
|
public struct Folder: FileSystemItem {
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
@ -24,11 +24,3 @@ public struct Folder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - FileSystemIdIdentifiable
|
|
||||||
|
|
||||||
extension Folder: FileSystemItemIdentifiable {}
|
|
||||||
|
|
||||||
// MARK: - Equatable
|
|
||||||
|
|
||||||
extension Folder: Equatable {}
|
|
||||||
|
@ -6,11 +6,7 @@
|
|||||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
protocol FileSystemItem {
|
protocol FileSystemItem: Identifiable, Hashable, Equatable {
|
||||||
var id: String { get }
|
var id: String { get }
|
||||||
var name: String { get }
|
var name: String { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Type aliases
|
|
||||||
|
|
||||||
typealias FileSystemItemIdentifiable = FileSystemItem & Identifiable & Hashable
|
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// DeleteItemUseCase.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
|
||||||
|
|
||||||
|
struct DeleteItemUseCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
let apiService: APIService
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
func callAsFunction(
|
||||||
|
id: String,
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
) async throws {
|
||||||
|
try await apiService.deleteItem(
|
||||||
|
id: id,
|
||||||
|
credentials: .init(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension DeleteItemUseCase {
|
||||||
|
init() {
|
||||||
|
@Dependency(\.apiService) var apiService
|
||||||
|
|
||||||
|
self.init(apiService: apiService)
|
||||||
|
}
|
||||||
|
}
|
@ -13,9 +13,9 @@ import Foundation
|
|||||||
|
|
||||||
struct GetDataUseCase {
|
struct GetDataUseCase {
|
||||||
|
|
||||||
// MARK: Dependencies
|
// MARK: Properties
|
||||||
|
|
||||||
@Dependency(\.apiService) private var apiService
|
let apiService: APIService
|
||||||
|
|
||||||
// MARK: Functions
|
// MARK: Functions
|
||||||
|
|
||||||
@ -34,3 +34,13 @@ struct GetDataUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension GetDataUseCase {
|
||||||
|
init() {
|
||||||
|
@Dependency(\.apiService) var apiService
|
||||||
|
|
||||||
|
self.init(apiService: apiService)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,9 +12,9 @@ import Dependencies
|
|||||||
|
|
||||||
struct GetItemsUseCase {
|
struct GetItemsUseCase {
|
||||||
|
|
||||||
// MARK: Dependencies
|
// MARK: Properties
|
||||||
|
|
||||||
@Dependency(\.apiService) private var apiService
|
let apiService: APIService
|
||||||
|
|
||||||
// MARK: Functions
|
// MARK: Functions
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ struct GetItemsUseCase {
|
|||||||
id: String,
|
id: String,
|
||||||
username: String,
|
username: String,
|
||||||
password: String
|
password: String
|
||||||
) async throws -> [any FileSystemItemIdentifiable] {
|
) async throws -> [any FileSystemItem] {
|
||||||
let items = try await apiService.getItems(
|
let items = try await apiService.getItems(
|
||||||
id: id,
|
id: id,
|
||||||
credentials: .init(
|
credentials: .init(
|
||||||
@ -32,7 +32,7 @@ struct GetItemsUseCase {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
.compactMap { item -> any FileSystemItemIdentifiable in
|
.compactMap { item -> any FileSystemItem in
|
||||||
if item.isDirectory {
|
if item.isDirectory {
|
||||||
return Folder(
|
return Folder(
|
||||||
id: item.id,
|
id: item.id,
|
||||||
@ -51,3 +51,13 @@ struct GetItemsUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension GetItemsUseCase {
|
||||||
|
init() {
|
||||||
|
@Dependency(\.apiService) var apiService
|
||||||
|
|
||||||
|
self.init(apiService: apiService)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -25,6 +25,16 @@
|
|||||||
"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.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";
|
"message.type_not_supported.button.text" = "Go back to folder";
|
||||||
|
|
||||||
|
// DeleteItemViewModifier
|
||||||
|
|
||||||
|
"delete_item.action_sheet.title" = "Delete an item";
|
||||||
|
"delete_item.action_sheet.message %@" = "You are about to delete an item named \"%@\" from this folder.\n\nAre you sure you wish to proceed?";
|
||||||
|
"delete_item.action_sheet.button.ok" = "Yes, please delete it.";
|
||||||
|
"delete_item.action_sheet.button.cancel" = "No, I reconsidered.";
|
||||||
|
"delete_item.system_alert.title" = "...";
|
||||||
|
"delete_item.system_alert.message" = "...";
|
||||||
|
"delete_item.system_alert.button.dismiss" = "...";
|
||||||
|
|
||||||
// BrowseView
|
// BrowseView
|
||||||
|
|
||||||
"browse.toolbar_item.menu.add_actions.text" = "Add file and/or folder";
|
"browse.toolbar_item.menu.add_actions.text" = "Add file and/or folder";
|
||||||
|
@ -13,7 +13,7 @@ struct DocumentItem: View {
|
|||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
let item: FileSystemItem
|
let item: any FileSystemItem
|
||||||
let select: ActionClosure
|
let select: ActionClosure
|
||||||
let download: ActionClosure
|
let download: ActionClosure
|
||||||
let delete: ActionClosure
|
let delete: ActionClosure
|
||||||
|
@ -13,7 +13,7 @@ struct FolderItem: View {
|
|||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
let item: FileSystemItem
|
let item: any FileSystemItem
|
||||||
let select: ActionClosure
|
let select: ActionClosure
|
||||||
let delete: ActionClosure
|
let delete: ActionClosure
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import DataModels
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
@ -20,4 +21,14 @@ extension View {
|
|||||||
destination: { destination }
|
destination: { destination }
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func delete(
|
||||||
|
item: Binding<(any FileSystemItem)?>,
|
||||||
|
deleted: @escaping ActionClosure
|
||||||
|
) -> some View {
|
||||||
|
modifier(DeleteItemViewModifier(
|
||||||
|
item: item,
|
||||||
|
deleted: deleted
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,131 @@
|
|||||||
|
//
|
||||||
|
// DeleteItemViewModifier.swift
|
||||||
|
// Browse
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 16/12/2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
import DataModels
|
||||||
|
import KeychainStorage
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DeleteItemViewModifier: ViewModifier {
|
||||||
|
|
||||||
|
// MARK: Storages
|
||||||
|
|
||||||
|
@KeychainStorage(key: .KeychainStorage.account) private var account: Account?
|
||||||
|
|
||||||
|
// MARK: States
|
||||||
|
|
||||||
|
@State private var showErrorAlert: Bool = false
|
||||||
|
|
||||||
|
// MARK: Bindings
|
||||||
|
|
||||||
|
@Binding var item: (any FileSystemItem)?
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
let deleted: ActionClosure
|
||||||
|
|
||||||
|
private let deleteItem: DeleteItemUseCase = .init()
|
||||||
|
|
||||||
|
// MARK: Body
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.actionSheet(isPresented: showDeletionConfirmation) {
|
||||||
|
ActionSheet(
|
||||||
|
title: Text(
|
||||||
|
"delete_item.action_sheet.title",
|
||||||
|
bundle: .module
|
||||||
|
),
|
||||||
|
message: Text(
|
||||||
|
"delete_item.action_sheet.message \(itemName)",
|
||||||
|
bundle: .module
|
||||||
|
),
|
||||||
|
buttons: [
|
||||||
|
.destructive(Text(
|
||||||
|
"delete_item.action_sheet.button.ok",
|
||||||
|
bundle: .module
|
||||||
|
)) {
|
||||||
|
Task {
|
||||||
|
await removeItem()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.cancel(Text(
|
||||||
|
"delete_item.action_sheet.button.cancel",
|
||||||
|
bundle: .module
|
||||||
|
)) {
|
||||||
|
item = nil
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.alert(isPresented: $showErrorAlert) {
|
||||||
|
Alert(
|
||||||
|
title: Text(
|
||||||
|
"delete_item.system_alert.title",
|
||||||
|
bundle: .module
|
||||||
|
),
|
||||||
|
message: Text(
|
||||||
|
"delete_item.system_alert.message",
|
||||||
|
bundle: .module
|
||||||
|
),
|
||||||
|
dismissButton: .cancel(Text(
|
||||||
|
"delete_item.system_alert.button.cancel",
|
||||||
|
bundle: .module
|
||||||
|
)) {
|
||||||
|
item = nil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private extension DeleteItemViewModifier {
|
||||||
|
|
||||||
|
// MARK: Computed
|
||||||
|
|
||||||
|
var itemName: String {
|
||||||
|
item?.name ?? .Constants.noName
|
||||||
|
}
|
||||||
|
|
||||||
|
var showDeletionConfirmation: Binding<Bool> {
|
||||||
|
.init { item != nil } set: { _ in }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
func removeItem() async {
|
||||||
|
guard
|
||||||
|
let id = item?.id,
|
||||||
|
let account
|
||||||
|
else {
|
||||||
|
showErrorAlert = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
try await deleteItem(
|
||||||
|
id: id,
|
||||||
|
username: account.username,
|
||||||
|
password: account.password
|
||||||
|
)
|
||||||
|
|
||||||
|
deleted()
|
||||||
|
} catch {
|
||||||
|
showErrorAlert = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - String+Constants
|
||||||
|
|
||||||
|
private extension String {
|
||||||
|
enum Constants {
|
||||||
|
static let noName = "no name"
|
||||||
|
}
|
||||||
|
}
|
@ -19,8 +19,9 @@ public struct BrowseView: View {
|
|||||||
// MARK: States
|
// MARK: States
|
||||||
|
|
||||||
@State private var status: ViewStatus = .loading
|
@State private var status: ViewStatus = .loading
|
||||||
@State private var items: [any FileSystemItemIdentifiable] = []
|
@State private var items: [any FileSystemItem] = []
|
||||||
@State private var stack: Stack?
|
@State private var stack: Stack?
|
||||||
|
@State private var itemToDelete: (any FileSystemItem)?
|
||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
@ -60,6 +61,11 @@ public struct BrowseView: View {
|
|||||||
showProfile: showProfile
|
showProfile: showProfile
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
.delete(item: $itemToDelete) {
|
||||||
|
Task {
|
||||||
|
await updateItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
.task(id: folder) {
|
.task(id: folder) {
|
||||||
await loadItems()
|
await loadItems()
|
||||||
}
|
}
|
||||||
@ -112,13 +118,13 @@ private extension BrowseView {
|
|||||||
// MARK: Functions
|
// MARK: Functions
|
||||||
|
|
||||||
@ViewBuilder func makeFolderItem(
|
@ViewBuilder func makeFolderItem(
|
||||||
for item: any FileSystemItemIdentifiable
|
for item: any FileSystemItem
|
||||||
) -> some View {
|
) -> some View {
|
||||||
if let folder = item as? Folder {
|
if let folder = item as? Folder {
|
||||||
FolderItem(item: item) {
|
FolderItem(item: item) {
|
||||||
stack = .browse(folder)
|
stack = .browse(folder)
|
||||||
} delete: {
|
} delete: {
|
||||||
// TODO: delete the item id from the backend.
|
itemToDelete = item
|
||||||
}
|
}
|
||||||
.navigate(
|
.navigate(
|
||||||
to: BrowseView(
|
to: BrowseView(
|
||||||
@ -137,7 +143,7 @@ private extension BrowseView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder func makeDocumentItem(
|
@ViewBuilder func makeDocumentItem(
|
||||||
for item: any FileSystemItemIdentifiable
|
for item: any FileSystemItem
|
||||||
) -> some View {
|
) -> some View {
|
||||||
if let document = item as? Document {
|
if let document = item as? Document {
|
||||||
DocumentItem(item: item) {
|
DocumentItem(item: item) {
|
||||||
@ -145,7 +151,7 @@ private extension BrowseView {
|
|||||||
} download: {
|
} download: {
|
||||||
// TODO: download the item id from the backend.
|
// TODO: download the item id from the backend.
|
||||||
} delete: {
|
} delete: {
|
||||||
// TODO: delete the item id from the backend.
|
itemToDelete = item
|
||||||
}
|
}
|
||||||
.navigate(
|
.navigate(
|
||||||
to: DocumentView(
|
to: DocumentView(
|
||||||
@ -190,6 +196,11 @@ private extension BrowseView {
|
|||||||
status = .error
|
status = .error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateItems() async {
|
||||||
|
items = items.filter { $0.id != itemToDelete?.id }
|
||||||
|
itemToDelete = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
Loading…
x
Reference in New Issue
Block a user