From 4d84ee5d838623739221be9501ff19331b107b0a Mon Sep 17 00:00:00 2001 From: YESEONG LEE Date: Wed, 5 Aug 2026 15:56:16 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[#60]=20feat:=20ListView=20=EB=84=88?= =?UTF-8?q?=EB=B9=84=20=EC=88=98=EC=A0=95=20/=20=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20=EB=AC=B8=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Components/DVTitleBar.swift | 4 +- .../Resources/Localizable.xcstrings | 21 +++--- .../Sources/Dependencies/SecretClient.swift | 72 ++++++++++++------- .../CreateSecret/CreateSecretFeature.swift | 6 +- .../Sources/Features/Main/MainView.swift | 1 + .../Features/SecretList/SecretListView.swift | 6 +- .../Features/Sidebar/SidebarView.swift | 2 +- 7 files changed, 71 insertions(+), 41 deletions(-) diff --git a/Projects/DVDesign/Sources/Components/DVTitleBar.swift b/Projects/DVDesign/Sources/Components/DVTitleBar.swift index 08d51e0..f85b184 100644 --- a/Projects/DVDesign/Sources/Components/DVTitleBar.swift +++ b/Projects/DVDesign/Sources/Components/DVTitleBar.swift @@ -33,7 +33,7 @@ public struct DVTitleBar: View { titleRow searchField } - .frame(width: 280) + .frame(maxWidth: .infinity) } } @@ -72,8 +72,10 @@ extension DVTitleBar { .dvFont(.bodyMD) .foregroundStyle(Color.dv(.gray900)) .textFieldStyle(.plain) + .frame(maxWidth: .infinity) } .padding(.horizontal, 10) + .frame(maxWidth: .infinity) .frame(height: 36) .background(.regularMaterial) .clipShape(RoundedRectangle(cornerRadius: 18)) diff --git a/Projects/DVPresentation/Resources/Localizable.xcstrings b/Projects/DVPresentation/Resources/Localizable.xcstrings index 7505b8f..913bfa5 100644 --- a/Projects/DVPresentation/Resources/Localizable.xcstrings +++ b/Projects/DVPresentation/Resources/Localizable.xcstrings @@ -12,12 +12,24 @@ }, "Detail" : { + }, + "Failed to load" : { + "comment" : "A message displayed when loading projects fails.", + "isCommentAutoGenerated" : true + }, + "Failed to load the list" : { + "comment" : "A title for an alert that indicates that the list of secrets failed to load.", + "isCommentAutoGenerated" : true }, "If Touch ID is unavailable,\nsystem password will be used" : { }, "LOGO" : { + }, + "No secrets" : { + "comment" : "A message displayed when there are no secrets.", + "isCommentAutoGenerated" : true }, "Project" : { @@ -33,15 +45,6 @@ }, "Vault" : { - }, - "목록을 불러오지 못했어요" : { - - }, - "불러오지 못했어요" : { - - }, - "시크릿이 없어요" : { - } }, "version" : "1.1" diff --git a/Projects/DVPresentation/Sources/Dependencies/SecretClient.swift b/Projects/DVPresentation/Sources/Dependencies/SecretClient.swift index e077e9e..aaa7a8a 100644 --- a/Projects/DVPresentation/Sources/Dependencies/SecretClient.swift +++ b/Projects/DVPresentation/Sources/Dependencies/SecretClient.swift @@ -26,33 +26,55 @@ public struct SecretClient: Sendable { extension SecretClient: TestDependencyKey { public static let testValue = SecretClient() + public static let previewValue = dummyClient() +} - public static let previewValue = SecretClient( - fetchByQuery: { query in - switch query.collection { - case .expired(let referenceDate): - return [Secret].preview.filter { - $0.deletedAt == nil && ($0.expiresAt.map { $0 < referenceDate } ?? false) +private extension SecretClient { + static func dummyClient() -> SecretClient { + SecretClient( + fetchByQuery: { query in + let filtered: [Secret] + switch query.collection { + case .expired(let referenceDate): + filtered = [Secret].preview.filter { + $0.deletedAt == nil && ($0.expiresAt.map { $0 < referenceDate } ?? false) + } + case .deleted: + filtered = [Secret].preview.filter { $0.deletedAt != nil } + case .liked: + filtered = [Secret].preview.filter { $0.deletedAt == nil && $0.liked } + case .project(let id): + filtered = [Secret].preview(in: id) + case .all: + filtered = [Secret].preview.filter { $0.deletedAt == nil } } - case .deleted: - return [Secret].preview.filter { $0.deletedAt != nil } - case .liked: - return [Secret].preview.filter { $0.deletedAt == nil && $0.liked } - case .project(let id): - return [Secret].preview(in: id) - case .all: - return [Secret].preview.filter { $0.deletedAt == nil } - } - }, - softDelete: { _ in .preview }, - restore: { _ in .preview }, - permanentlyDelete: { _ in }, - fetchProjects: { .preview }, - createProject: { name in - Project(id: UUID(), name: name, createdAt: .now, updatedAt: .now) - }, - linkProject: { _, _ in } - ) + return sorted(filtered, by: query.sort) + }, + softDelete: { _ in .preview }, + restore: { _ in .preview }, + permanentlyDelete: { _ in }, + fetchProjects: { .preview }, + createProject: { name in + Project(id: UUID(), name: name, createdAt: .now, updatedAt: .now) + }, + linkProject: { _, _ in } + ) + } + + static func sorted(_ secrets: [Secret], by sort: SecretQuery.Sort) -> [Secret] { + switch sort { + case .recentlyAdded: + return secrets.sorted { $0.createdAt > $1.createdAt } + case .oldestFirst: + return secrets.sorted { $0.createdAt < $1.createdAt } + case .expiringSoon: + return secrets.sorted { ($0.expiresAt ?? .distantFuture) < ($1.expiresAt ?? .distantFuture) } + case .nameAscending: + return secrets.sorted { $0.name < $1.name } + case .nameDescending: + return secrets.sorted { $0.name > $1.name } + } + } } public extension DependencyValues { diff --git a/Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift b/Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift index 1b79507..4231c40 100644 --- a/Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift +++ b/Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift @@ -179,8 +179,10 @@ struct CreateSecretFeature { case .alert: return .none - case .createProject(.presented(.delegate(.projectCreated(let project)))): - state.availableProjects.append(project) + case .createProject(.presented(.delegate(.projectCreated(let projectItem)))): + state.availableProjects.append( + Project(id: projectItem.id, name: projectItem.name, createdAt: .now, updatedAt: .now) + ) return .none case .createProject: diff --git a/Projects/DVPresentation/Sources/Features/Main/MainView.swift b/Projects/DVPresentation/Sources/Features/Main/MainView.swift index 96463f5..ae200a0 100644 --- a/Projects/DVPresentation/Sources/Features/Main/MainView.swift +++ b/Projects/DVPresentation/Sources/Features/Main/MainView.swift @@ -64,6 +64,7 @@ extension MainView { private var contentColumn: some View { SecretListView(store: store.scope(state: \.secretList, action: \.secretList)) .navigationTitle("") + .navigationSplitViewColumnWidth(min: 300, ideal: 320, max: 350) } private var detailColumn: some View { diff --git a/Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift b/Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift index fd7fb11..25747d6 100644 --- a/Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift +++ b/Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift @@ -20,7 +20,7 @@ struct SecretListView: View { var body: some View { content - .task { store.send(.task) } + .task(id: store.collection) { store.send(.task) } .sheet( item: $store.scope(state: \.destination?.addToProject, action: \.destination.addToProject) ) { addToProjectStore in @@ -189,7 +189,7 @@ extension SecretListView { Image(systemName: "exclamationmark.triangle") .dvFont(.bodyLG) .foregroundStyle(Color.dv(.gray400)) - Text("목록을 불러오지 못했어요") + Text("Failed to load the list") .dvFont(.bodyMD) .foregroundStyle(Color.dv(.gray500)) DVButton(titleText: "Retry", style: .secondary) { @@ -203,7 +203,7 @@ extension SecretListView { private var emptyView: some View { VStack(spacing: 12) { Spacer() - Text("시크릿이 없어요") + Text("No secrets") .dvFont(.bodyMD) .foregroundStyle(Color.dv(.gray400)) Spacer() diff --git a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift index dc5760e..c11adbf 100644 --- a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift +++ b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift @@ -111,7 +111,7 @@ extension SidebarView { case .loaded: projectList case .failed: - Text("불러오지 못했어요") + Text("Failed to load") .dvFont(.bodyMD) .foregroundStyle(Color.dv(.danger)) .frame(maxWidth: .infinity) From 778c0425c48dbc349183af20b3ea1d072914e3e7 Mon Sep 17 00:00:00 2001 From: YESEONG LEE Date: Wed, 5 Aug 2026 16:02:17 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[#60]=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Features/CreateSecret/CreateSecretFeature.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift b/Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift index 4231c40..1744dc6 100644 --- a/Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift +++ b/Projects/DVPresentation/Sources/Features/CreateSecret/CreateSecretFeature.swift @@ -100,6 +100,7 @@ struct CreateSecretFeature { @Dependency(\.secretManagementClient) var secretManagementClient @Dependency(\.projectClient) var projectClient + @Dependency(\.date.now) var now // MARK: - Init @@ -181,7 +182,7 @@ struct CreateSecretFeature { case .createProject(.presented(.delegate(.projectCreated(let projectItem)))): state.availableProjects.append( - Project(id: projectItem.id, name: projectItem.name, createdAt: .now, updatedAt: .now) + Project(id: projectItem.id, name: projectItem.name, createdAt: now, updatedAt: now) ) return .none