Merge pull request #14 from rock-n-code/feature/delete-item

Feature: Delete items from the folders
This commit is contained in:
Javier Cicchelli 2022-12-16 17:34:49 +01:00 committed by GitHub
commit 225e4994f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 245 additions and 33 deletions

View File

@ -8,7 +8,7 @@
import Foundation
struct Document {
struct Document: FileSystemItem {
// MARK: Properties
@ -35,7 +35,3 @@ struct Document {
}
}
// MARK: - FileSystemIdIdentifiable
extension Document: FileSystemItemIdentifiable {}

View File

@ -6,7 +6,7 @@
// Copyright © 2022 Röck+Cöde. All rights reserved.
//
public struct Folder {
public struct Folder: FileSystemItem {
// MARK: Properties
@ -24,11 +24,3 @@ public struct Folder {
}
}
// MARK: - FileSystemIdIdentifiable
extension Folder: FileSystemItemIdentifiable {}
// MARK: - Equatable
extension Folder: Equatable {}

View File

@ -6,11 +6,7 @@
// Copyright © 2022 Röck+Cöde. All rights reserved.
//
protocol FileSystemItem {
protocol FileSystemItem: Identifiable, Hashable, Equatable {
var id: String { get }
var name: String { get }
}
// MARK: - Type aliases
typealias FileSystemItemIdentifiable = FileSystemItem & Identifiable & Hashable

View File

@ -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)
}
}

View File

@ -13,9 +13,9 @@ import Foundation
struct GetDataUseCase {
// MARK: Dependencies
// MARK: Properties
@Dependency(\.apiService) private var apiService
let apiService: APIService
// MARK: Functions
@ -34,3 +34,13 @@ struct GetDataUseCase {
}
}
// MARK: - Initialisers
extension GetDataUseCase {
init() {
@Dependency(\.apiService) var apiService
self.init(apiService: apiService)
}
}

View File

@ -12,9 +12,9 @@ import Dependencies
struct GetItemsUseCase {
// MARK: Dependencies
// MARK: Properties
@Dependency(\.apiService) private var apiService
let apiService: APIService
// MARK: Functions
@ -22,7 +22,7 @@ struct GetItemsUseCase {
id: String,
username: String,
password: String
) async throws -> [any FileSystemItemIdentifiable] {
) async throws -> [any FileSystemItem] {
let items = try await apiService.getItems(
id: id,
credentials: .init(
@ -32,7 +32,7 @@ struct GetItemsUseCase {
)
return items
.compactMap { item -> any FileSystemItemIdentifiable in
.compactMap { item -> any FileSystemItem in
if item.isDirectory {
return Folder(
id: item.id,
@ -51,3 +51,13 @@ struct GetItemsUseCase {
}
}
// MARK: - Initialisers
extension GetItemsUseCase {
init() {
@Dependency(\.apiService) var apiService
self.init(apiService: apiService)
}
}

View File

@ -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.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
"browse.toolbar_item.menu.add_actions.text" = "Add file and/or folder";

View File

@ -13,7 +13,7 @@ struct DocumentItem: View {
// MARK: Properties
let item: FileSystemItem
let item: any FileSystemItem
let select: ActionClosure
let download: ActionClosure
let delete: ActionClosure

View File

@ -13,7 +13,7 @@ struct FolderItem: View {
// MARK: Properties
let item: FileSystemItem
let item: any FileSystemItem
let select: ActionClosure
let delete: ActionClosure

View File

@ -6,6 +6,7 @@
// Copyright © 2022 Röck+Cöde. All rights reserved.
//
import DataModels
import SwiftUI
extension View {
@ -20,4 +21,14 @@ extension View {
destination: { destination }
))
}
func delete(
item: Binding<(any FileSystemItem)?>,
deleted: @escaping ActionClosure
) -> some View {
modifier(DeleteItemViewModifier(
item: item,
deleted: deleted
))
}
}

View File

@ -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"
}
}

View File

@ -19,8 +19,9 @@ public struct BrowseView: View {
// MARK: States
@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 itemToDelete: (any FileSystemItem)?
// MARK: Properties
@ -60,6 +61,11 @@ public struct BrowseView: View {
showProfile: showProfile
)
}
.delete(item: $itemToDelete) {
Task {
await updateItems()
}
}
.task(id: folder) {
await loadItems()
}
@ -112,13 +118,13 @@ private extension BrowseView {
// MARK: Functions
@ViewBuilder func makeFolderItem(
for item: any FileSystemItemIdentifiable
for item: any FileSystemItem
) -> some View {
if let folder = item as? Folder {
FolderItem(item: item) {
stack = .browse(folder)
} delete: {
// TODO: delete the item id from the backend.
itemToDelete = item
}
.navigate(
to: BrowseView(
@ -137,7 +143,7 @@ private extension BrowseView {
}
@ViewBuilder func makeDocumentItem(
for item: any FileSystemItemIdentifiable
for item: any FileSystemItem
) -> some View {
if let document = item as? Document {
DocumentItem(item: item) {
@ -145,7 +151,7 @@ private extension BrowseView {
} download: {
// TODO: download the item id from the backend.
} delete: {
// TODO: delete the item id from the backend.
itemToDelete = item
}
.navigate(
to: DocumentView(
@ -190,6 +196,11 @@ private extension BrowseView {
status = .error
}
}
func updateItems() async {
items = items.filter { $0.id != itemToDelete?.id }
itemToDelete = nil
}
}
// MARK: - Previews