Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Projects/DVDesign/Sources/Components/DVTitleBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public struct DVTitleBar: View {
titleRow
searchField
}
.frame(width: 280)
.frame(maxWidth: .infinity)
}
}

Expand Down Expand Up @@ -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))
Expand Down
21 changes: 12 additions & 9 deletions Projects/DVPresentation/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -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" : {

Expand All @@ -33,15 +45,6 @@
},
"Vault" : {

},
"목록을 불러오지 못했어요" : {

},
"불러오지 못했어요" : {

},
"시크릿이 없어요" : {

}
},
"version" : "1.1"
Expand Down
72 changes: 47 additions & 25 deletions Projects/DVPresentation/Sources/Dependencies/SecretClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment on lines +35 to +51

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

미리보기 쿼리에 searchText 필터를 적용하세요.

fetchByQueryquery.collectionquery.sort만 적용합니다. 따라서 미리보기에서 검색어를 변경해도 결과가 검색어와 무관하게 유지됩니다. 프로덕션의 InMemorySecretQueryFilter.apply와 동일한 검색 조건을 정렬 전에 적용하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Projects/DVPresentation/Sources/Dependencies/SecretClient.swift` around lines
35 - 51, Update fetchByQuery to apply query.searchText filtering to the
collection-specific filtered secrets before calling sorted, matching the search
predicate used by InMemorySecretQueryFilter.apply. Preserve the existing
collection selection and sorting behavior.

},
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ struct CreateSecretFeature {

@Dependency(\.secretManagementClient) var secretManagementClient
@Dependency(\.projectClient) var projectClient
@Dependency(\.date.now) var now

// MARK: - Init

Expand Down Expand Up @@ -179,8 +180,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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down