-
Notifications
You must be signed in to change notification settings - Fork 0
Design/#49 - 사이드바 디자인 수정 및 온보딩/잠금 화면 구현 #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7be999f
34670a7
1cce2db
6112b6e
c117387
4d6e74c
cee120a
5648894
d8bc01b
b14aa79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ let project = Project.project( | |
|
|
||
| // 3rd-party dependency | ||
| .tca(), | ||
| .lottie(), | ||
| ] | ||
| ), | ||
| ] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| // Copyright © 2026 Devault. All rights reserved | ||
|
|
||
| import Foundation | ||
|
|
||
| import ComposableArchitecture | ||
|
|
||
| // MARK: - LockFeature | ||
|
|
||
| @Reducer | ||
| public struct LockFeature { | ||
|
|
||
| // MARK: - State | ||
|
|
||
| @ObservableState | ||
| public struct State: Equatable { | ||
| public var isPostOnboarding: Bool | ||
|
|
||
| public init(isPostOnboarding: Bool = false) { | ||
| self.isPostOnboarding = isPostOnboarding | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Action | ||
|
|
||
| public enum Action: Equatable { | ||
|
|
||
| // MARK: - View | ||
|
|
||
| case didTapUnlock | ||
|
|
||
| // MARK: - Delegate | ||
|
|
||
| case delegate(Delegate) | ||
|
|
||
| public enum Delegate: Equatable { | ||
| case unlockCompleted | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Init | ||
|
|
||
| public init() {} | ||
|
|
||
| // MARK: - Body | ||
|
|
||
| public var body: some ReducerOf<Self> { | ||
| Reduce { state, action in | ||
| switch action { | ||
| case .didTapUnlock: | ||
| return .send(.delegate(.unlockCompleted)) | ||
|
|
||
| case .delegate: | ||
| return .none | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| // Copyright © 2026 Devault. All rights reserved | ||
|
|
||
| import SwiftUI | ||
|
|
||
| import ComposableArchitecture | ||
| import DVDesign | ||
|
|
||
| // MARK: - LockView | ||
|
|
||
| public struct LockView: View { | ||
|
|
||
| // MARK: - Properties | ||
|
|
||
| @Bindable public var store: StoreOf<LockFeature> | ||
|
|
||
| // MARK: - Init | ||
|
|
||
| public init(store: StoreOf<LockFeature>) { | ||
| self.store = store | ||
| } | ||
|
|
||
| // MARK: - Body | ||
|
|
||
| public var body: some View { | ||
| content | ||
| .dvScreenBackground() | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Subviews | ||
|
|
||
| extension LockView { | ||
|
|
||
| private var content: some View { | ||
| ZStack { | ||
| unlockView | ||
| if store.isPostOnboarding { | ||
| VStack { | ||
| Spacer() | ||
| DVStepIndicator(totalSteps: 4, currentStep: 3) | ||
| .padding(.bottom, 40) | ||
| } | ||
| } | ||
| } | ||
| .frame(maxWidth: .infinity, maxHeight: .infinity) | ||
| } | ||
|
|
||
| private var unlockView: some View { | ||
| VStack(spacing: 40) { | ||
| appIconWithLogoView | ||
| DVButton(titleText: "Unlock with Touch ID", style: .primary) { | ||
| store.send(.didTapUnlock) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private var appIconWithLogoView: some View { | ||
| VStack(spacing: 20) { | ||
| RoundedRectangle(cornerRadius: 20) | ||
| .fill(Color.dv(.gray800)) | ||
| .frame(width: 80, height: 80) | ||
| ( | ||
| Text("De").foregroundStyle(Color.dv(.vaultDark)) | ||
| + Text("Vault").foregroundStyle(Color.dv(.vaultGreen)) | ||
| ) | ||
| .font(.dv(.displayBrand)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Preview | ||
|
|
||
| #Preview("Post Onboarding") { | ||
| LockView( | ||
| store: Store(initialState: LockFeature.State(isPostOnboarding: true)) { | ||
| LockFeature() | ||
| } | ||
| ) | ||
| .frame(width: 540, height: 400) | ||
| } | ||
|
|
||
| #Preview("Re-entry / Locked") { | ||
| LockView( | ||
| store: Store(initialState: LockFeature.State(isPostOnboarding: false)) { | ||
| LockFeature() | ||
| } | ||
| ) | ||
| .frame(width: 540, height: 400) | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,94 @@ | ||||||||||||||||||||||||||||||||||
| // Copyright © 2026 Devault. All rights reserved | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import Foundation | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import ComposableArchitecture | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // MARK: - OnboardingFeature | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @Reducer | ||||||||||||||||||||||||||||||||||
| public struct OnboardingFeature { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // MARK: - Step | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public enum Step: Equatable { | ||||||||||||||||||||||||||||||||||
| case welcome | ||||||||||||||||||||||||||||||||||
| case security | ||||||||||||||||||||||||||||||||||
| case icloudSync | ||||||||||||||||||||||||||||||||||
| case syncing | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // MARK: - State | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @ObservableState | ||||||||||||||||||||||||||||||||||
| public struct State: Equatable { | ||||||||||||||||||||||||||||||||||
| public var step: Step = .welcome | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public init(step: Step = .welcome) { | ||||||||||||||||||||||||||||||||||
| self.step = step | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| var currentStepIndex: Int { | ||||||||||||||||||||||||||||||||||
| switch step { | ||||||||||||||||||||||||||||||||||
| case .welcome: return 0 | ||||||||||||||||||||||||||||||||||
| case .security: return 1 | ||||||||||||||||||||||||||||||||||
| case .icloudSync: return 2 | ||||||||||||||||||||||||||||||||||
| case .syncing: return 2 | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+31
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Syncing 단계를 네 번째 indicator로 표시하세요.
수정 예시- case .syncing: return 2
+ case .syncing: return 3📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // MARK: - Action | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public enum Action: Equatable { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // MARK: - View | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| case didTapStart | ||||||||||||||||||||||||||||||||||
| case didTapEnableTouchID | ||||||||||||||||||||||||||||||||||
| case didTapNotNow | ||||||||||||||||||||||||||||||||||
| case didTapEnableSync | ||||||||||||||||||||||||||||||||||
| case syncingCompleted | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // MARK: - Delegate | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| case delegate(Delegate) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public enum Delegate: Equatable { | ||||||||||||||||||||||||||||||||||
| case completed | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // MARK: - Init | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public init() {} | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // MARK: - Body | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public var body: some ReducerOf<Self> { | ||||||||||||||||||||||||||||||||||
| Reduce { state, action in | ||||||||||||||||||||||||||||||||||
| switch action { | ||||||||||||||||||||||||||||||||||
| case .didTapStart: | ||||||||||||||||||||||||||||||||||
| state.step = .security | ||||||||||||||||||||||||||||||||||
| return .none | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| case .didTapEnableTouchID: | ||||||||||||||||||||||||||||||||||
| state.step = .icloudSync | ||||||||||||||||||||||||||||||||||
| return .none | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+75
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift Touch ID 성공 확인 후에만 다음 단계로 진행하세요. 현재 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| case .didTapNotNow: | ||||||||||||||||||||||||||||||||||
| return .send(.delegate(.completed)) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| case .didTapEnableSync: | ||||||||||||||||||||||||||||||||||
| state.step = .syncing | ||||||||||||||||||||||||||||||||||
| return .none | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| case .syncingCompleted: | ||||||||||||||||||||||||||||||||||
| return .send(.delegate(.completed)) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+82
to
+87
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift 실제 동기화 완료 이벤트를 연결하세요.
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| case .delegate: | ||||||||||||||||||||||||||||||||||
| return .none | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
Repository: DevaultProject/Devault-macOS
Length of output: 3801
🏁 Script executed:
Repository: DevaultProject/Devault-macOS
Length of output: 5929
🏁 Script executed:
Repository: DevaultProject/Devault-macOS
Length of output: 3157
unlockCompleted전송을 인증 성공 결과로 바꾸세요.didTapUnlock가 버튼을 누르는 즉시 delegate를 보내고 있어, 현재UserAuthenticationService.authenticate가 호출되지 않습니다.LockFeature에서LocalUserAuthenticationServiceImpl의존성을 받아 인증을 트리거하고, 성공 시에만unlockCompleted를 보냅니다. 실패/취소/미지원은 잠금 상태를 유지하거나 에러(delegate/state)로 전달하세요.🤖 Prompt for AI Agents