From e1dde9c256dbc3dbe71887e2e9fbe735617a143b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Fri, 29 Nov 2024 14:59:35 +0900 Subject: [PATCH 01/31] =?UTF-8?q?[feat]=20#157=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EC=97=90=EC=84=9C=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EB=AF=B8=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EC=8A=A4=EC=BC=88?= =?UTF-8?q?=EB=A0=88=ED=86=A4=20ui=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ContentSettingFeature.swift | 8 +++++-- .../ContentSetting/ContentSettingView.swift | 22 ++++++++++++------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift index 089b9fa3..4091a4f3 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift @@ -197,6 +197,9 @@ private extension ContentSettingFeature { return .merge(mergeEffect) case .저장_버튼_눌렀을때: let isEdit = state.domain.categoryId != nil + if state.domain.title == "제목을 입력해주세요" { + state.domain.title = state.title + } return isEdit ? .send(.async(.컨텐츠_수정_API)) @@ -239,8 +242,9 @@ private extension ContentSettingFeature { ) } case let .메타데이텨_조회_반영(title: title, imageURL: imageURL): - state.linkTitle = title - state.linkImageURL = imageURL + let contentTitle = state.title.isEmpty ? "제목을 입력해주세요" : state.title + state.linkTitle = title ?? contentTitle + state.linkImageURL = imageURL ?? "https://pokit-storage.s3.ap-northeast-2.amazonaws.com/logo/pokit.png" if let title, state.domain.title.isEmpty { state.domain.title = title } diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift index 7e91b2b5..8bdab04a 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift @@ -69,7 +69,9 @@ public extension ContentSettingView { .pokitMaxWidth() } - let isDisable = store.urlText.isEmpty || store.title.isEmpty || store.memoTextAreaState == .error(message: "최대 100자까지 입력가능합니다.") + let isDisable = store.urlText.isEmpty || + store.title.isEmpty || + store.memoTextAreaState == .error(message: "최대 100자까지 입력가능합니다.") PokitBottomButton( "저장하기", @@ -104,13 +106,15 @@ private extension ContentSettingView { } var linkTextField: some View { VStack(spacing: 16) { - if store.showLinkPreview { + if !store.urlText.isEmpty { + let isParsed = store.linkTitle != nil || store.linkImageURL != nil + PokitLinkPreview( - title: store.linkTitle ?? ( - store.title.isEmpty ? "제목을 입력해주세요" : store.title - ), - url: store.urlText, - imageURL: store.linkImageURL ?? "https://pokit-storage.s3.ap-northeast-2.amazonaws.com/logo/pokit.png" + title: store.linkTitle == "제목을 입력해주세요" + ? store.title.isEmpty ? "제목을 입력해주세요" : store.title + : store.linkTitle, + url: isParsed ? store.urlText : nil, + imageURL: store.linkImageURL ) .pokitBlurReplaceTransition(.pokitDissolve) } @@ -123,6 +127,7 @@ private extension ContentSettingView { equals: .link ) } + .animation(.pokitSpring, value: store.urlText) } var titleTextField: some View { @@ -131,7 +136,8 @@ private extension ContentSettingView { label: "제목", state: $store.titleTextInpuState, focusState: $focusedType, - equals: .title) { } + equals: .title + ) { } } var pokitSelectButton: some View { From 719458d52b3eba27f19edae2902ee59452cd0547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:21:55 +0900 Subject: [PATCH 02/31] =?UTF-8?q?[desing]=20#157=20PokitIconInput=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Components/PokitIconLInput.swift | 37 ++++++++++++------- .../Sources/Components/PokitIconRInput.swift | 37 ++++++++++++------- 2 files changed, 46 insertions(+), 28 deletions(-) diff --git a/Projects/DSKit/Sources/Components/PokitIconLInput.swift b/Projects/DSKit/Sources/Components/PokitIconLInput.swift index 2662c978..ab807879 100644 --- a/Projects/DSKit/Sources/Components/PokitIconLInput.swift +++ b/Projects/DSKit/Sources/Components/PokitIconLInput.swift @@ -12,12 +12,13 @@ public struct PokitIconLInput: View { private let icon: PokitImage - @State private var state: PokitInputStyle.State + @Binding private var state: PokitInputStyle.State private var focusState: FocusState.Binding private let shape: PokitInputStyle.Shape private let equals: Value + private let label: String? private let placeholder: String private let onSubmit: (() -> Void)? private let iconTappedAction: (() -> Void)? @@ -25,7 +26,8 @@ public struct PokitIconLInput: View { public init( text: Binding, icon: PokitImage, - state: PokitInputStyle.State = .default, + state: Binding = .constant(.default), + label: String? = nil, placeholder: String = "내용을 입력해주세요.", shape: PokitInputStyle.Shape, focusState: FocusState.Binding, @@ -35,7 +37,8 @@ public struct PokitIconLInput: View { ) { self._text = text self.icon = icon - self._state = State(initialValue: state) + self._state = state + self.label = label self.shape = shape self.focusState = focusState self.equals = equals @@ -45,19 +48,25 @@ public struct PokitIconLInput: View { } public var body: some View { - HStack(spacing: 8) { - iconButton(icon: icon) + VStack(alignment: .leading, spacing: 8) { + if let label { + PokitLabel(text: label, size: .large) + } - textField + HStack(spacing: 8) { + iconButton(icon: icon) + + textField + } + .onChange(of: text) { onChangedText($0) } + .padding(.vertical, shape == .round ? 8 : 13) + .padding(.trailing, shape == .round ? 20 : 12) + .padding(.leading, 13) + .background( + state: self.state, + shape: shape + ) } - .onChange(of: text) { onChangedText($0) } - .padding(.vertical, shape == .round ? 8 : 13) - .padding(.trailing, shape == .round ? 20 : 12) - .padding(.leading, 13) - .background( - state: self.state, - shape: shape - ) } private var textField: some View { diff --git a/Projects/DSKit/Sources/Components/PokitIconRInput.swift b/Projects/DSKit/Sources/Components/PokitIconRInput.swift index 674bd280..5cdcacbd 100644 --- a/Projects/DSKit/Sources/Components/PokitIconRInput.swift +++ b/Projects/DSKit/Sources/Components/PokitIconRInput.swift @@ -12,12 +12,13 @@ public struct PokitIconRInput: View { private let icon: PokitImage - @State private var state: PokitInputStyle.State + @Binding private var state: PokitInputStyle.State private var focusState: FocusState.Binding private let shape: PokitInputStyle.Shape private let equals: Value + private let label: String? private let placeholder: String private let onSubmit: (() -> Void)? private let iconTappedAction: (() -> Void)? @@ -25,7 +26,8 @@ public struct PokitIconRInput: View { public init( text: Binding, icon: PokitImage, - state: PokitInputStyle.State = .default, + state: Binding = .constant(.default), + label: String? = nil, placeholder: String = "내용을 입력해주세요.", shape: PokitInputStyle.Shape, focusState: FocusState.Binding, @@ -35,7 +37,8 @@ public struct PokitIconRInput: View { ) { self._text = text self.icon = icon - self._state = State(initialValue: state) + self._state = state + self.label = label self.shape = shape self.focusState = focusState self.equals = equals @@ -45,19 +48,25 @@ public struct PokitIconRInput: View { } public var body: some View { - HStack(spacing: 8) { - textField + VStack(alignment: .leading, spacing: 8) { + if let label { + PokitLabel(text: label, size: .large) + } - iconButton(icon: icon) + HStack(spacing: 8) { + textField + + iconButton(icon: icon) + } + .onChange(of: text) { onChangedText($0) } + .padding(.vertical, shape == .round ? 8 : 13) + .padding(.leading, shape == .round ? 20 : 12) + .padding(.trailing, 13) + .background( + state: self.state, + shape: shape + ) } - .onChange(of: text) { onChangedText($0) } - .padding(.vertical, shape == .round ? 8 : 13) - .padding(.leading, shape == .round ? 20 : 12) - .padding(.trailing, 13) - .background( - state: self.state, - shape: shape - ) } private var textField: some View { From d46d05249ccc5bd447d48f292ae25b4f4aa6bbe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:22:14 +0900 Subject: [PATCH 03/31] =?UTF-8?q?[feat]=20#157=20=EB=A7=81=ED=81=AC?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EC=97=90=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A7=80=EC=9A=B0=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ContentSettingFeature.swift | 9 ++++++++- .../ContentSetting/ContentSettingView.swift | 20 ++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift index 4091a4f3..8466c030 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift @@ -96,7 +96,8 @@ public struct ContentSettingFeature { case 저장_버튼_눌렀을때 case 포킷추가_버튼_눌렀을때 case 링크복사_버튼_눌렀을때 - + case 링크지우기_버튼_눌렀을때 + case 제목지우기_버튼_눌렀을때 case 뒤로가기_버튼_눌렀을때 } @@ -218,6 +219,12 @@ private extension ContentSettingFeature { : .run { _ in await dismiss() } case .링크복사_버튼_눌렀을때: return .send(.inner(.링크복사_반영(state.link))) + case .링크지우기_버튼_눌렀을때: + state.domain.data = "" + return .none + case .제목지우기_버튼_눌렀을때: + state.title = "" + return .none } } diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift index 8bdab04a..6835027d 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift @@ -119,25 +119,31 @@ private extension ContentSettingView { .pokitBlurReplaceTransition(.pokitDissolve) } - PokitTextInput( + PokitIconRInput( text: $store.urlText, - label: "링크", + icon: .icon(.x), state: $store.linkTextInputState, + label: "링크", + shape: .rectangle, focusState: $focusedType, - equals: .link + equals: .link, + iconTappedAction: { send(.링크지우기_버튼_눌렀을때) } ) } .animation(.pokitSpring, value: store.urlText) } var titleTextField: some View { - PokitTextInput( + PokitIconRInput( text: $store.title, - label: "제목", + icon: .icon(.x), state: $store.titleTextInpuState, + label: "제목", + shape: .rectangle, focusState: $focusedType, - equals: .title - ) { } + equals: .title, + iconTappedAction: { send(.제목지우기_버튼_눌렀을때) } + ) } var pokitSelectButton: some View { From 0b20da17e34dd85221873468c1b8995aa512cc65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Fri, 29 Nov 2024 22:53:10 +0900 Subject: [PATCH 04/31] =?UTF-8?q?[design]=20#157=20PokitTextInput=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Components/PokitTextInput.swift | 86 +++++++++++++++++-- .../Sources/Foundation/PokitInputStyle.swift | 12 +++ .../Sources/PokitCategorySettingView.swift | 7 +- .../ContentSetting/ContentSettingView.swift | 24 +++--- .../RegisterNicknameView.swift | 1 + .../Sources/Search/PokitSearchView.swift | 13 +-- .../NickNameSetting/NickNameSettingView.swift | 1 + 7 files changed, 121 insertions(+), 23 deletions(-) diff --git a/Projects/DSKit/Sources/Components/PokitTextInput.swift b/Projects/DSKit/Sources/Components/PokitTextInput.swift index fe9564e7..1418a71d 100644 --- a/Projects/DSKit/Sources/Components/PokitTextInput.swift +++ b/Projects/DSKit/Sources/Components/PokitTextInput.swift @@ -15,6 +15,8 @@ public struct PokitTextInput: View { private var focusState: FocusState.Binding + private let type: PokitInputStyle.InputType + private let shape: PokitInputStyle.Shape private let equals: Value private let label: String? private let placeholder: String @@ -25,6 +27,8 @@ public struct PokitTextInput: View { public init( text: Binding, label: String? = nil, + type: PokitInputStyle.InputType = .text, + shape: PokitInputStyle.Shape, state: Binding, placeholder: String = "내용을 입력해주세요.", info: String? = nil, @@ -35,6 +39,8 @@ public struct PokitTextInput: View { ) { self._text = text self.label = label + self.type = type + self.shape = shape self._state = state self.focusState = focusState self.equals = equals @@ -51,7 +57,24 @@ public struct PokitTextInput: View { .padding(.bottom, 8) } - textField + HStack(spacing: 8) { + if case let .iconL(icon, action) = type { + iconButton(icon: icon, action: action) + } + + textField + + if case let .iconR(icon, action) = type { + iconButton(icon: icon, action: action) + } + } + .padding(.vertical, vPadding) + .padding(.leading, lPadding) + .padding(.trailing, tPadding) + .background( + state: self.state, + shape: self.shape + ) infoLabel } @@ -69,12 +92,6 @@ public struct PokitTextInput: View { .focused(focusState, equals: equals) .pokitFont(.b3(.m)) .foregroundStyle(.pokit(.text(.secondary))) - .padding(.vertical, 16) - .padding(.horizontal, 12) - .background( - state: self.state, - shape: .rectangle - ) .disabled(state == .disable || state == .readOnly) .onSubmit { onSubmit?() @@ -131,6 +148,61 @@ public struct PokitTextInput: View { .padding(.top, 4) } + @ViewBuilder + private func iconButton( + icon: PokitImage, + action: (() -> Void)? + ) -> some View { + Button { + if let action { + action() + } else { + onSubmit?() + } + } label: { + Image(icon) + .resizable() + .frame(width: 24, height: 24) + .foregroundStyle(state.iconColor) + .animation(.pokitDissolve, value: self.state) + } + } + + private var vPadding: CGFloat { + switch type { + case .text: return 16 + case .iconR, .iconL: + switch shape { + case .rectangle: return 13 + case .round: return 8 + } + } + } + + private var tPadding: CGFloat { + switch type { + case .text: return 12 + case .iconR: + switch shape { + case .rectangle: return 12 + case .round: return 20 + } + case .iconL: return 13 + } + } + + private var lPadding: CGFloat { + switch type { + case .text: return 12 + case .iconL: + switch shape { + case .rectangle: return 12 + case .round: return 20 + } + case .iconR: return 13 + } + } + private func onChangedText(_ newValue: String) { guard let maxLetter else { return } diff --git a/Projects/DSKit/Sources/Foundation/PokitInputStyle.swift b/Projects/DSKit/Sources/Foundation/PokitInputStyle.swift index 9c5fb34d..5a39c2e4 100644 --- a/Projects/DSKit/Sources/Foundation/PokitInputStyle.swift +++ b/Projects/DSKit/Sources/Foundation/PokitInputStyle.swift @@ -78,4 +78,16 @@ public enum PokitInputStyle: Equatable { } } } + + public enum InputType { + case text + case iconR( + icon: PokitImage, + action: (() -> Void)? = nil + ) + case iconL( + icon: PokitImage, + action: (() -> Void)? = nil + ) + } } diff --git a/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift b/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift index e0244982..b3d47829 100644 --- a/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift +++ b/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift @@ -121,9 +121,14 @@ private extension PokitCategorySettingView { Text("포킷명") .pokitFont(.b2(.m)) .foregroundStyle(.pokit(.text(.secondary))) + PokitTextInput( text: $store.categoryName, - state: $store.pokitNameTextInpuState, + type: .iconR( + icon: .icon(.x), + action: { } + ), + shape: .rectangle, state: $store.pokitNameTextInpuState, placeholder: "포킷명을 입력해주세요.", maxLetter: 10, focusState: $isFocused, diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift index 6835027d..369ab80c 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift @@ -119,30 +119,34 @@ private extension ContentSettingView { .pokitBlurReplaceTransition(.pokitDissolve) } - PokitIconRInput( + PokitTextInput( text: $store.urlText, - icon: .icon(.x), - state: $store.linkTextInputState, label: "링크", + type: .iconR( + icon: .icon(.x), + action: { send(.링크지우기_버튼_눌렀을때) } + ), shape: .rectangle, + state: $store.linkTextInputState, focusState: $focusedType, - equals: .link, - iconTappedAction: { send(.링크지우기_버튼_눌렀을때) } + equals: .link ) } .animation(.pokitSpring, value: store.urlText) } var titleTextField: some View { - PokitIconRInput( + PokitTextInput( text: $store.title, - icon: .icon(.x), - state: $store.titleTextInpuState, label: "제목", + type: .iconR( + icon: .icon(.x), + action: { send(.제목지우기_버튼_눌렀을때) } + ), shape: .rectangle, + state: $store.titleTextInpuState, focusState: $focusedType, - equals: .title, - iconTappedAction: { send(.제목지우기_버튼_눌렀을때) } + equals: .title ) } diff --git a/Projects/Feature/FeatureLogin/Sources/RegisterNickname/RegisterNicknameView.swift b/Projects/Feature/FeatureLogin/Sources/RegisterNickname/RegisterNicknameView.swift index 83cf8c24..cc8f3cc3 100644 --- a/Projects/Feature/FeatureLogin/Sources/RegisterNickname/RegisterNicknameView.swift +++ b/Projects/Feature/FeatureLogin/Sources/RegisterNickname/RegisterNicknameView.swift @@ -72,6 +72,7 @@ extension RegisterNicknameView { private var textField: some View { PokitTextInput( text: $store.nicknameText, + shape: .rectangle, state: $store.textfieldState, info: "한글, 영어, 숫자로만 입력이 가능합니다.", maxLetter: 10, diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift index 016dd7fb..01ac43ba 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift @@ -86,15 +86,18 @@ private extension PokitSearchView { action: { send(.dismiss) } ) - PokitIconRInput( + PokitTextInput( text: $store.searchText, - icon: store.isSearching ? .icon(.x) : .icon(.search), - placeholder: "제목, 메모를 검색해보세요.", + type: .iconR( + icon: store.isSearching ? .icon(.x) : .icon(.search), + action: store.isSearching ? { send(.검색_버튼_눌렀을때) } : nil + ), shape: .round, + state: .constant(.default), + placeholder: "제목, 메모를 검색해보세요.", focusState: $focused, equals: true, - onSubmit: { send(.검색_키보드_엔터_눌렀을때) }, - iconTappedAction: store.isSearching ? { send(.검색_버튼_눌렀을때) } : nil + onSubmit: { send(.검색_키보드_엔터_눌렀을때) } ) } .padding(.vertical, 8) diff --git a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift index f22a4af0..f069deef 100644 --- a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift @@ -31,6 +31,7 @@ public extension NickNameSettingView { } else { PokitTextInput( text: $store.text, + shape: .rectangle, state: $store.textfieldState, info: "한글, 영어, 숫자로만 입력이 가능합니다.", maxLetter: 10, From b7a7b7292c1ad4d95c112a6dd2b3553f494d4df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Fri, 29 Nov 2024 22:53:27 +0900 Subject: [PATCH 05/31] =?UTF-8?q?[remove]=20#157=20PokitIconInput=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Components/PokitIconLInput.swift | 117 ------------------ .../Sources/Components/PokitIconRInput.swift | 117 ------------------ 2 files changed, 234 deletions(-) delete mode 100644 Projects/DSKit/Sources/Components/PokitIconLInput.swift delete mode 100644 Projects/DSKit/Sources/Components/PokitIconRInput.swift diff --git a/Projects/DSKit/Sources/Components/PokitIconLInput.swift b/Projects/DSKit/Sources/Components/PokitIconLInput.swift deleted file mode 100644 index ab807879..00000000 --- a/Projects/DSKit/Sources/Components/PokitIconLInput.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// PokitIconLInput.swift -// DSKit -// -// Created by 김도형 on 6/28/24. -// - -import SwiftUI - -public struct PokitIconLInput: View { - @Binding private var text: String - - private let icon: PokitImage - - @Binding private var state: PokitInputStyle.State - - private var focusState: FocusState.Binding - - private let shape: PokitInputStyle.Shape - private let equals: Value - private let label: String? - private let placeholder: String - private let onSubmit: (() -> Void)? - private let iconTappedAction: (() -> Void)? - - public init( - text: Binding, - icon: PokitImage, - state: Binding = .constant(.default), - label: String? = nil, - placeholder: String = "내용을 입력해주세요.", - shape: PokitInputStyle.Shape, - focusState: FocusState.Binding, - equals: Value, - onSubmit: (() -> Void)? = nil, - iconTappedAction: (() -> Void)? = nil - ) { - self._text = text - self.icon = icon - self._state = state - self.label = label - self.shape = shape - self.focusState = focusState - self.equals = equals - self.placeholder = placeholder - self.onSubmit = onSubmit - self.iconTappedAction = iconTappedAction - } - - public var body: some View { - VStack(alignment: .leading, spacing: 8) { - if let label { - PokitLabel(text: label, size: .large) - } - - HStack(spacing: 8) { - iconButton(icon: icon) - - textField - } - .onChange(of: text) { onChangedText($0) } - .padding(.vertical, shape == .round ? 8 : 13) - .padding(.trailing, shape == .round ? 20 : 12) - .padding(.leading, 13) - .background( - state: self.state, - shape: shape - ) - } - } - - private var textField: some View { - TextField(text: $text) { - placeholderLabel - } - .autocorrectionDisabled() - .textInputAutocapitalization(.never) - .focused(focusState, equals: equals) - .pokitFont(.b3(.m)) - .foregroundStyle(.pokit(.text(.secondary))) - .disabled(state == .disable || state == .readOnly) - .onSubmit { - onSubmit?() - } - .onChange(of: focusState.wrappedValue) { onChangedFocuseState($0) } - } - - private var placeholderLabel: some View { - Text(placeholder) - .foregroundStyle(self.state.infoColor) - } - - @ViewBuilder - private func iconButton(icon: PokitImage) -> some View { - Button { - if let iconTappedAction { - iconTappedAction() - } else { - onSubmit?() - } - } label: { - Image(icon) - .resizable() - .frame(width: 24, height: 24) - .foregroundStyle(state.iconColor) - .animation(.pokitDissolve, value: self.state) - } - } - - private func onChangedText(_ newValue: String) { - state = newValue != "" ? .input : .default - } - - private func onChangedFocuseState(_ newValue: Value) { - state = newValue == equals ? .active : .default - } -} diff --git a/Projects/DSKit/Sources/Components/PokitIconRInput.swift b/Projects/DSKit/Sources/Components/PokitIconRInput.swift deleted file mode 100644 index 5cdcacbd..00000000 --- a/Projects/DSKit/Sources/Components/PokitIconRInput.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// PokitIconRInput.swift -// DSKit -// -// Created by 김도형 on 6/28/24. -// - -import SwiftUI - -public struct PokitIconRInput: View { - @Binding private var text: String - - private let icon: PokitImage - - @Binding private var state: PokitInputStyle.State - - private var focusState: FocusState.Binding - - private let shape: PokitInputStyle.Shape - private let equals: Value - private let label: String? - private let placeholder: String - private let onSubmit: (() -> Void)? - private let iconTappedAction: (() -> Void)? - - public init( - text: Binding, - icon: PokitImage, - state: Binding = .constant(.default), - label: String? = nil, - placeholder: String = "내용을 입력해주세요.", - shape: PokitInputStyle.Shape, - focusState: FocusState.Binding, - equals: Value, - onSubmit: (() -> Void)? = nil, - iconTappedAction: (() -> Void)? = nil - ) { - self._text = text - self.icon = icon - self._state = state - self.label = label - self.shape = shape - self.focusState = focusState - self.equals = equals - self.placeholder = placeholder - self.onSubmit = onSubmit - self.iconTappedAction = iconTappedAction - } - - public var body: some View { - VStack(alignment: .leading, spacing: 8) { - if let label { - PokitLabel(text: label, size: .large) - } - - HStack(spacing: 8) { - textField - - iconButton(icon: icon) - } - .onChange(of: text) { onChangedText($0) } - .padding(.vertical, shape == .round ? 8 : 13) - .padding(.leading, shape == .round ? 20 : 12) - .padding(.trailing, 13) - .background( - state: self.state, - shape: shape - ) - } - } - - private var textField: some View { - TextField(text: $text) { - placeholderLabel - } - .autocorrectionDisabled() - .textInputAutocapitalization(.never) - .focused(focusState, equals: equals) - .pokitFont(.b3(.m)) - .foregroundStyle(.pokit(.text(.secondary))) - .disabled(state == .disable || state == .readOnly) - .onSubmit { - onSubmit?() - } - .onChange(of: focusState.wrappedValue) { onChangedFocuseState($0) } - } - - private var placeholderLabel: some View { - Text(placeholder) - .foregroundStyle(self.state.infoColor) - } - - @ViewBuilder - private func iconButton(icon: PokitImage) -> some View { - Button { - if let iconTappedAction { - iconTappedAction() - } else { - onSubmit?() - } - } label: { - Image(icon) - .resizable() - .frame(width: 24, height: 24) - .foregroundStyle(state.iconColor) - .animation(.pokitDissolve, value: self.state) - } - } - - private func onChangedText(_ newValue: String) { - state = newValue != "" ? .input : .default - } - - private func onChangedFocuseState(_ newValue: Value) { - state = newValue == equals ? .active : .default - } -} From 416b2b0ffd91afd0f0de8e1b7d772426d9cf3ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Fri, 29 Nov 2024 23:00:05 +0900 Subject: [PATCH 06/31] =?UTF-8?q?[feat]=20#157=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=88=98=EC=A0=95=EC=97=90=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=A7=80=EC=9A=B0=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Setting/NickNameSetting/NickNameSettingFeature.swift | 4 ++++ .../Sources/Setting/NickNameSetting/NickNameSettingView.swift | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingFeature.swift b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingFeature.swift index d0810b0b..7d339461 100644 --- a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingFeature.swift @@ -48,6 +48,7 @@ public struct NickNameSettingFeature { case dismiss case 저장_버튼_눌렀을때 case 뷰가_나타났을때 + case 닉네임지우기_버튼_눌렀을때 } public enum InnerAction: Equatable { @@ -131,6 +132,9 @@ private extension NickNameSettingFeature { case .뷰가_나타났을때: return .send(.async(.닉네임_조회_API)) + case .닉네임지우기_버튼_눌렀을때: + state.domain.nickname = "" + return .none } } diff --git a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift index f069deef..62790830 100644 --- a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift @@ -31,6 +31,10 @@ public extension NickNameSettingView { } else { PokitTextInput( text: $store.text, + type: .iconR( + icon: .icon(.x), + action: { send(.닉네임지우기_버튼_눌렀을때) } + ), shape: .rectangle, state: $store.textfieldState, info: "한글, 영어, 숫자로만 입력이 가능합니다.", From 1ff6183b34a7a3f48215355f7a34c89f14d29f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Fri, 29 Nov 2024 23:59:08 +0900 Subject: [PATCH 07/31] =?UTF-8?q?[design]=20#157=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EC=97=90=EC=84=9C=20=ED=8F=AC=ED=82=B7=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B2=84=ED=8A=BC=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Components/PokitSelect.swift | 62 ++++++++++++++++--- .../ContentSetting/ContentSettingView.swift | 18 +----- 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/Projects/DSKit/Sources/Components/PokitSelect.swift b/Projects/DSKit/Sources/Components/PokitSelect.swift index 6c322356..853b1547 100644 --- a/Projects/DSKit/Sources/Components/PokitSelect.swift +++ b/Projects/DSKit/Sources/Components/PokitSelect.swift @@ -20,13 +20,15 @@ public struct PokitSelect: View { private let label: String private let list: [Item]? private let action: (Item) -> Void + private let addAction: (() -> Void)? public init( selectedItem: Binding = .constant(nil), state: PokitSelect.SelectState = .default, label: String, list: [Item]?, - action: @escaping (Item) -> Void + action: @escaping (Item) -> Void, + addAction: (() -> Void)? ) { self._selectedItem = selectedItem if selectedItem.wrappedValue != nil { @@ -37,6 +39,7 @@ public struct PokitSelect: View { self.label = label self.list = list self.action = action + self.addAction = addAction } public var body: some View { @@ -96,14 +99,23 @@ public struct PokitSelect: View { private var listSheet: some View { Group { if let list { - PokitList( - selectedItem: selectedItem, - list: list - ) { item in - action(item) - listCellTapped(item) + VStack(spacing: 0) { + if let addAction { + addButton { + listDismiss() + addAction() + } + } + + PokitList( + selectedItem: selectedItem, + list: list + ) { item in + action(item) + listCellTapped(item) + } } - .padding(.top, 36) + .padding(.top, 24) .padding(.bottom, 20) } else { PokitLoading() @@ -111,6 +123,36 @@ public struct PokitSelect: View { } } + @ViewBuilder + private func addButton( + action: @escaping () -> Void + ) -> some View { + Button(action: action) { + HStack(spacing: 20) { + PokitIconButton( + .icon(.plusR), + state: .default(.secondary), + size: .medium, + shape: .round, + action: action + ) + + Text("포킷 추가하기") + .pokitFont(.b1(.b)) + .foregroundStyle(.pokit(.text(.primary))) + + Spacer() + } + .padding(.vertical, 22) + .padding(.horizontal, 30) + .background(alignment: .bottom) { + Rectangle() + .fill(.pokit(.border(.tertiary))) + .frame(height: 1) + } + } + } + private func partSelectButtonTapped() { showSheet = true } @@ -125,6 +167,10 @@ public struct PokitSelect: View { private func onChangedSeletedItem(_ newValue: Item?) { state = newValue != nil ? .input : .default } + + private func listDismiss() { + showSheet = false + } } public extension PokitSelect { diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift index 369ab80c..55dad4f4 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift @@ -36,11 +36,7 @@ public extension ContentSettingView { titleTextField - HStack(alignment: .bottom, spacing: 8) { - pokitSelectButton - - addPokitButton - } + pokitSelectButton memoTextArea @@ -155,19 +151,11 @@ private extension ContentSettingView { selectedItem: $store.selectedPokit, label: "포킷", list: store.pokitList, - action: { send(.포킷선택_항목_눌렀을때(pokit: $0), animation: .pokitDissolve) } + action: { send(.포킷선택_항목_눌렀을때(pokit: $0), animation: .pokitDissolve) }, + addAction: { send(.포킷추가_버튼_눌렀을때, animation: .pokitSpring) } ) } - var addPokitButton: some View { - PokitIconButton( - .icon(.plusR), - state: .filled(.primary), - size: .large, - shape: .rectangle - ) { send(.포킷추가_버튼_눌렀을때, animation: .pokitSpring) } - } - var memoTextArea: some View { PokitTextArea( text: $store.memo, From c4878b62cc18b01d2b08624982e16c0c6ce5baed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:57:28 +0900 Subject: [PATCH 08/31] =?UTF-8?q?[refactor]=20#157=20PokitLinkPopup=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Sources/MainTab/MainTabFeature.swift | 10 +- .../Sources/MainTab/MainTabFeatureView.swift | 12 +-- .../Sources/Components/PokitLinkPopup.swift | 100 ++++++++++-------- .../ContentSettingFeature.swift | 14 +-- .../ContentSetting/ContentSettingView.swift | 14 +-- 5 files changed, 78 insertions(+), 72 deletions(-) diff --git a/Projects/App/Sources/MainTab/MainTabFeature.swift b/Projects/App/Sources/MainTab/MainTabFeature.swift index 3da32829..7118bb19 100644 --- a/Projects/App/Sources/MainTab/MainTabFeature.swift +++ b/Projects/App/Sources/MainTab/MainTabFeature.swift @@ -11,6 +11,7 @@ import FeaturePokit import FeatureRemind import FeatureContentDetail import Domain +import DSKit import Util import CoreKit @@ -28,7 +29,7 @@ public struct MainTabFeature { public struct State: Equatable { var selectedTab: MainTab = .pokit var isBottomSheetPresented: Bool = false - var isLinkSheetPresented: Bool = false + var linkPopup: PokitLinkPopup.PopupType? var isErrorSheetPresented: Bool = false var link: String? @@ -157,7 +158,7 @@ private extension MainTabFeature { } case .linkCopyButtonTapped: - state.isLinkSheetPresented = false + state.linkPopup = nil return .run { send in await send(.delegate(.링크추가하기)) } case .onAppear: @@ -198,7 +199,10 @@ private extension MainTabFeature { switch action { case let .linkCopySuccess(url): guard let url else { return .none } - state.isLinkSheetPresented = true + state.linkPopup = .link( + title: "복사한 링크 저장하기", + url: url.absoluteString + ) state.link = url.absoluteString return .none diff --git a/Projects/App/Sources/MainTab/MainTabFeatureView.swift b/Projects/App/Sources/MainTab/MainTabFeatureView.swift index abe6d5d6..b89bdd6a 100644 --- a/Projects/App/Sources/MainTab/MainTabFeatureView.swift +++ b/Projects/App/Sources/MainTab/MainTabFeatureView.swift @@ -73,11 +73,9 @@ public extension MainTabView { } } - if self.store.isLinkSheetPresented { + if self.store.linkPopup != nil { PokitLinkPopup( - "복사한 링크 저장하기", - isPresented: $store.isLinkSheetPresented, - type: .link(url: self.store.link ?? ""), + type: $store.linkPopup, action: { send(.linkCopyButtonTapped) } ) } @@ -94,11 +92,9 @@ private extension MainTabView { tabView .overlay(alignment: .bottom) { VStack(spacing: 0) { - if store.isLinkSheetPresented { + if store.linkPopup != nil { PokitLinkPopup( - "복사한 링크 저장하기", - isPresented: $store.isLinkSheetPresented, - type: .link(url: store.link ?? ""), + type: $store.linkPopup, action: { send(.linkCopyButtonTapped) } ) .padding(.bottom, 20) diff --git a/Projects/DSKit/Sources/Components/PokitLinkPopup.swift b/Projects/DSKit/Sources/Components/PokitLinkPopup.swift index cf11a534..90d2f479 100644 --- a/Projects/DSKit/Sources/Components/PokitLinkPopup.swift +++ b/Projects/DSKit/Sources/Components/PokitLinkPopup.swift @@ -9,11 +9,11 @@ import SwiftUI import Combine public struct PokitLinkPopup: View { - @Binding private var isPresented: Bool + @Binding + private var type: PokitLinkPopup.PopupType? - @State private var second: Int = 0 - private let titleKey: String - private let type: PokitLinkPopup.PopupType + @State + private var second: Int = 0 private let action: (() -> Void)? private let timer = Timer.publish( every: 1, @@ -22,14 +22,10 @@ public struct PokitLinkPopup: View { ).autoconnect() public init( - _ titleKey: String, - isPresented: Binding, - type: PokitLinkPopup.PopupType, + type: Binding, action: (() -> Void)? = nil ) { - self.titleKey = titleKey - self._isPresented = isPresented - self.type = type + self._type = type self.action = action } @@ -44,7 +40,7 @@ public struct PokitLinkPopup: View { .frame(width: 335, height: 60) .transition(.move(edge: .bottom).combined(with: .opacity)) .onReceive(timer) { _ in - guard second < 2 && isPresented else { + guard second < 2 && type != nil else { closedPopup() return } @@ -58,17 +54,13 @@ public struct PokitLinkPopup: View { action?() } label: { VStack(alignment: .leading, spacing: 0) { - Text(titleKey) + Text(title) .lineLimit(2) .pokitFont(.b2(.b)) .multilineTextAlignment(.leading) - .foregroundStyle( - type == .warning - ? .pokit(.text(.primary)) - : .pokit(.text(.inverseWh)) - ) + .foregroundStyle(textColor) - if case .link(let url) = type { + if case let .link(_, url) = type { Text(url) .lineLimit(1) .pokitFont(.detail2) @@ -100,18 +92,14 @@ public struct PokitLinkPopup: View { Image(.icon(.x)) .resizable() .frame(width: 24, height: 24) - .foregroundStyle( - type == .warning - ? .pokit(.icon(.primary)) - : .pokit(.icon(.inverseWh)) - ) + .foregroundStyle(iconColor) } } private func closedPopup() { withAnimation(.pokitSpring) { second = 0 - isPresented = false + type = nil } } @@ -125,50 +113,74 @@ public struct PokitLinkPopup: View { return .pokit(.bg(.error)) case .warning: return .pokit(.bg(.warning)) + case .none: return .clear + } + } + + private var iconColor: Color { + switch type { + case .warning: + return .pokit(.icon(.primary)) + default: + return .pokit(.icon(.inverseWh)) + } + } + + private var textColor: Color { + switch type { + case .warning: + return .pokit(.text(.primary)) + default: + return .pokit(.text(.inverseWh)) + } + } + + private var title: String { + switch type { + case let .link(title, _), + let .text(title), + let .success(title), + let .error(title), + let .warning(title): + return title + default: return "" } } } public extension PokitLinkPopup { enum PopupType: Equatable { - case link(url: String) - case text - case success - case error - case warning + case link(title: String, url: String) + case text(title: String) + case success(title: String) + case error(title: String) + case warning(title: String) } } #Preview { VStack { PokitLinkPopup( - "복사한 링크 저장하기", - isPresented: .constant(true), - type: .link(url: "https://www.youtube.com/watch?v=xSTwqKUyM8k") + type: .constant(.link( + title: "복사한 링크 저장하기", + url: "https://www.youtube.com/watch?v=xSTwqKUyM8k" + )) ) PokitLinkPopup( - "최대 30개의 포킷을 생성할 수 있습니다.\n포킷을 삭제한 뒤에 추가해주세요.", - isPresented: .constant(true), - type: .text + type: .constant(.text(title: "최대 30개의 포킷을 생성할 수 있습니다.\n포킷을 삭제한 뒤에 추가해주세요.")) ) PokitLinkPopup( - "링크저장 완료", - isPresented: .constant(true), - type: .success + type: .constant(.success(title: "링크저장 완료")) ) PokitLinkPopup( - "링크저장 실패", - isPresented: .constant(true), - type: .error + type: .constant(.error(title: "링크저장 실패")) ) PokitLinkPopup( - "저장공간 부족", - isPresented: .constant(true), - type: .warning + type: .constant(.warning(title: "저장공간 부족")) ) } } diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift index 8466c030..c5fd9ccc 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift @@ -68,8 +68,7 @@ public struct ContentSettingFeature { var selectedPokit: BaseCategoryItem? var linkTitle: String? = nil var linkImageURL: String? = nil - var showMaxCategoryPopup: Bool = false - var showDetectedURLPopup: Bool = false + var linkPopup: PokitLinkPopup.PopupType? var contentLoading: Bool = false var saveIsLoading: Bool = false var link: String? @@ -208,7 +207,7 @@ private extension ContentSettingFeature { case .포킷추가_버튼_눌렀을때: guard state.domain.categoryTotalCount < 30 else { /// 🚨 Error Case [1]: 포킷 갯수가 30개 이상일 경우 - state.showMaxCategoryPopup = true + state.linkPopup = .text(title: "최대 30개의 포킷을 생성할 수 있습니다.\n포킷을 삭제한 뒤에 추가해주세요.") return .none } @@ -234,7 +233,10 @@ private extension ContentSettingFeature { case let .linkPopup(url): guard let url else { return .none } state.link = url.absoluteString - state.showDetectedURLPopup = true + state.linkPopup = .link( + title: "복사한 링크 저장하기", + url: url.absoluteString + ) return .none case .linkPreview: state.showLinkPreview = true @@ -261,7 +263,7 @@ private extension ContentSettingFeature { guard let url = URL(string: state.domain.data), !state.domain.data.isEmpty else { /// 🚨 Error Case [1]: 올바른 링크가 아닐 때 - state.showDetectedURLPopup = false + state.linkPopup = nil state.linkTitle = nil state.domain.title = "" state.linkImageURL = nil @@ -270,7 +272,7 @@ private extension ContentSettingFeature { } return .send(.inner(.메타데이터_조회_수행(url: url)), animation: .pokitDissolve) case .링크복사_반영(let urlText): - state.showDetectedURLPopup = false + state.linkPopup = nil state.link = nil guard let urlText else { return .none } state.domain.data = urlText diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift index 55dad4f4..5d4b1b72 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift @@ -46,20 +46,12 @@ public extension ContentSettingView { .padding(.top, 16) } .overlay(alignment: .bottom) { - if store.state.showMaxCategoryPopup { + if store.linkPopup != nil { PokitLinkPopup( - "최대 30개의 포킷을 생성할 수 있습니다. \n포킷을 삭제한 뒤에 추가해주세요.", - isPresented: $store.showMaxCategoryPopup, - type: .text - ) - .animation(.pokitSpring, value: store.showMaxCategoryPopup) - } else if store.state.showDetectedURLPopup { - PokitLinkPopup( - "복사한 링크 저장하기", - isPresented: $store.showDetectedURLPopup, - type: .link(url: store.link ?? ""), + type: $store.linkPopup, action: { send(.링크복사_버튼_눌렀을때, animation: .pokitSpring) } ) + .animation(.pokitSpring, value: store.linkPopup) } } .pokitMaxWidth() From 9f85b9911474ad73387f27373aa7a8569b7ea657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sat, 30 Nov 2024 22:57:07 +0900 Subject: [PATCH 09/31] =?UTF-8?q?[feat]=20#157=20=EB=A7=81=ED=81=AC?= =?UTF-8?q?=ED=8C=9D=EC=97=85=20=ED=95=B8=EB=93=A4=EB=A7=81=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Sources/MainTab/MainTabFeature.swift | 32 +++++++++-- .../Sources/MainTab/MainTabFeatureView.swift | 4 +- .../App/Sources/MainTab/MainTabPath.swift | 10 ++-- .../ContentSettingFeature.swift | 54 ++++++++++++++----- .../ContentSetting/ContentSettingView.swift | 3 +- 5 files changed, 79 insertions(+), 24 deletions(-) diff --git a/Projects/App/Sources/MainTab/MainTabFeature.swift b/Projects/App/Sources/MainTab/MainTabFeature.swift index 7118bb19..2e100816 100644 --- a/Projects/App/Sources/MainTab/MainTabFeature.swift +++ b/Projects/App/Sources/MainTab/MainTabFeature.swift @@ -41,6 +41,7 @@ public struct MainTabFeature { @Presents var contentDetail: ContentDetailFeature.State? @Shared(.inMemory("SelectCategory")) var categoryId: Int? @Shared(.inMemory("PushTapped")) var isPushTapped: Bool = false + var savedContentId: Int? public init() { self.pokit = .init() @@ -65,7 +66,7 @@ public struct MainTabFeature { public enum View: Equatable { case addButtonTapped case addSheetTypeSelected(TabAddSheetType) - case linkCopyButtonTapped + case 링크팝업_버튼_눌렀을때 case onAppear case onOpenURL(url: URL) case 경고_확인버튼_클릭 @@ -76,6 +77,7 @@ public struct MainTabFeature { case 공유포킷_이동(sharedCategory: CategorySharing.SharedCategory) case 경고_띄움(BaseError) case errorSheetPresented(Bool) + case 링크팝업_활성화(PokitLinkPopup.PopupType) } public enum AsyncAction: Equatable { case 공유받은_카테고리_조회(categoryId: Int) @@ -94,6 +96,10 @@ public struct MainTabFeature { /// - Reducer Core private func core(into state: inout State, action: Action) -> Effect { switch action { + case .binding(\.linkPopup): + guard state.linkPopup == nil else { return .none } + state.savedContentId = nil + return .none case .binding: return .none case let .pushAlertTapped(isTapped): @@ -157,9 +163,8 @@ private extension MainTabFeature { case .포킷추가: return .send(.delegate(.포킷추가하기)) } - case .linkCopyButtonTapped: - state.linkPopup = nil - return .run { send in await send(.delegate(.링크추가하기)) } + case .링크팝업_버튼_눌렀을때: + return linkPopupButtonTapped(state: &state) case .onAppear: if state.isPushTapped { @@ -213,6 +218,10 @@ private extension MainTabFeature { case let .errorSheetPresented(isPresented): state.isErrorSheetPresented = isPresented return .none + + case let .링크팝업_활성화(type): + state.linkPopup = type + return .none default: return .none } @@ -242,4 +251,19 @@ private extension MainTabFeature { func handleDelegateAction(_ action: Action.DelegateAction, state: inout State) -> Effect { return .none } + + func linkPopupButtonTapped(state: inout State) -> Effect { + switch state.linkPopup { + case .link: + state.linkPopup = nil + return .send(.delegate(.링크추가하기)) + case .success: + state.linkPopup = nil + state.contentDetail = .init(contentId: state.savedContentId) + state.savedContentId = nil + return .none + case .error, .text, .warning, .none: + return .none + } + } } diff --git a/Projects/App/Sources/MainTab/MainTabFeatureView.swift b/Projects/App/Sources/MainTab/MainTabFeatureView.swift index b89bdd6a..f97ad2a9 100644 --- a/Projects/App/Sources/MainTab/MainTabFeatureView.swift +++ b/Projects/App/Sources/MainTab/MainTabFeatureView.swift @@ -76,7 +76,7 @@ public extension MainTabView { if self.store.linkPopup != nil { PokitLinkPopup( type: $store.linkPopup, - action: { send(.linkCopyButtonTapped) } + action: { send(.링크팝업_버튼_눌렀을때, animation: .pokitSpring) } ) } } @@ -95,7 +95,7 @@ private extension MainTabView { if store.linkPopup != nil { PokitLinkPopup( type: $store.linkPopup, - action: { send(.linkCopyButtonTapped) } + action: { send(.링크팝업_버튼_눌렀을때, animation: .pokitSpring) } ) .padding(.bottom, 20) } diff --git a/Projects/App/Sources/MainTab/MainTabPath.swift b/Projects/App/Sources/MainTab/MainTabPath.swift index 695295d1..aa3008c1 100644 --- a/Projects/App/Sources/MainTab/MainTabPath.swift +++ b/Projects/App/Sources/MainTab/MainTabPath.swift @@ -173,13 +173,17 @@ public extension MainTabFeature { return .none /// - 링크추가 및 수정에서 저장하기 눌렀을 때 - case let .path(.element(stackElementId, action: .링크추가및수정(.delegate(.저장하기_완료)))): + case let .path(.element(stackElementId, action: .링크추가및수정(.delegate(.저장하기_완료(contentId))))): + state.savedContentId = contentId state.path.removeLast() switch state.path.last { case .검색: - return .send(.path(.element(id: stackElementId, action: .검색(.delegate(.컨텐츠_검색))))) + return .merge( + .send(.path(.element(id: stackElementId, action: .검색(.delegate(.컨텐츠_검색))))), + .send(.inner(.링크팝업_활성화(.success(title: "링크 저장 완료"))), animation: .pokitSpring) + ) default: - return .none + return .send(.inner(.링크팝업_활성화(.success(title: "링크 저장 완료"))), animation: .pokitSpring) } /// - 각 화면에서 링크 복사 감지했을 때 (링크 추가 및 수정 화면 제외) case let .path(.element(_, action: .알림함(.delegate(.linkCopyDetected(url))))), diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift index c5fd9ccc..6b2d23ae 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift @@ -94,7 +94,7 @@ public struct ContentSettingFeature { case 뷰가_나타났을때 case 저장_버튼_눌렀을때 case 포킷추가_버튼_눌렀을때 - case 링크복사_버튼_눌렀을때 + case 링크팝업_버튼_눌렀을때 case 링크지우기_버튼_눌렀을때 case 제목지우기_버튼_눌렀을때 case 뒤로가기_버튼_눌렀을때 @@ -111,6 +111,7 @@ public struct ContentSettingFeature { case 카테고리_상세_조회_API_반영(category: BaseCategory) case 카테고리_목록_조회_API_반영(categoryList: BaseCategoryListInquiry) case 선택한_포킷_인메모리_삭제 + case 링크팝업_활성화(PokitLinkPopup.PopupType) } public enum AsyncAction: Equatable { @@ -125,7 +126,7 @@ public struct ContentSettingFeature { public enum ScopeAction: Equatable { case 없음 } public enum DelegateAction: Equatable { - case 저장하기_완료 + case 저장하기_완료(contentId: Int) case 포킷추가하기 case dismiss } @@ -200,6 +201,7 @@ private extension ContentSettingFeature { if state.domain.title == "제목을 입력해주세요" { state.domain.title = state.title } + state.saveIsLoading = true return isEdit ? .send(.async(.컨텐츠_수정_API)) @@ -216,7 +218,8 @@ private extension ContentSettingFeature { return state.isShareExtension ? .send(.delegate(.dismiss)) : .run { _ in await dismiss() } - case .링크복사_버튼_눌렀을때: + case .링크팝업_버튼_눌렀을때: + guard case .link = state.linkPopup else { return .none } return .send(.inner(.링크복사_반영(state.link))) case .링크지우기_버튼_눌렀을때: state.domain.data = "" @@ -325,6 +328,10 @@ private extension ContentSettingFeature { case .선택한_포킷_인메모리_삭제: state.selectedPokit = nil return .none + case let .링크팝업_활성화(type): + state.linkPopup = type + state.saveIsLoading = false + return .none } } @@ -372,11 +379,20 @@ private extension ContentSettingFeature { alertYn: state.domain.alertYn.rawValue, thumbNail: state.domain.thumbNail ) - return .concatenate( - contentEdit(request: request, contentId: contentId), - .send(.inner(.선택한_포킷_인메모리_삭제)), - .send(.delegate(.저장하기_완료)) - ) + return .run { send in + let _ = try await contentClient.컨텐츠_수정( + "\(contentId)", + request + ) + await send(.inner(.선택한_포킷_인메모리_삭제)) + await send(.delegate(.저장하기_완료(contentId: contentId))) + } catch: { error, send in + guard let errorResponse = error as? ErrorResponse else { return } + await send( + .inner(.링크팝업_활성화(.error(title: errorResponse.message))), + animation: .pokitSpring + ) + } case .컨텐츠_추가_API: guard let categoryId = state.selectedPokit?.id else { return .none @@ -389,11 +405,17 @@ private extension ContentSettingFeature { alertYn: state.domain.alertYn.rawValue, thumbNail: state.domain.thumbNail ) - return .concatenate( - .run { _ in let _ = try await contentClient.컨텐츠_추가(request) }, - .send(.inner(.선택한_포킷_인메모리_삭제)), - .send(.delegate(.저장하기_완료)) - ) + return .run { send in + let content = try await contentClient.컨텐츠_추가(request) + await send(.inner(.선택한_포킷_인메모리_삭제)) + await send(.delegate(.저장하기_완료(contentId: content.contentId))) + } catch: { error, send in + guard let errorResponse = error as? ErrorResponse else { return } + await send( + .inner(.링크팝업_활성화(.error(title: errorResponse.message))), + animation: .pokitSpring + ) + } case .클립보드_감지: return .run { send in for await _ in self.pasteboard.changes() { @@ -420,6 +442,12 @@ private extension ContentSettingFeature { "\(contentId)", request ) + } catch: { error, send in + guard let errorResponse = error as? ErrorResponse else { return } + await send( + .inner(.링크팝업_활성화(.error(title: errorResponse.message))), + animation: .pokitSpring + ) } } diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift index 5d4b1b72..88e65f0d 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift @@ -49,9 +49,8 @@ public extension ContentSettingView { if store.linkPopup != nil { PokitLinkPopup( type: $store.linkPopup, - action: { send(.링크복사_버튼_눌렀을때, animation: .pokitSpring) } + action: { send(.링크팝업_버튼_눌렀을때, animation: .pokitSpring) } ) - .animation(.pokitSpring, value: store.linkPopup) } } .pokitMaxWidth() From 030a6274940bc817f596cbee728439dad2832e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sat, 30 Nov 2024 23:28:10 +0900 Subject: [PATCH 10/31] =?UTF-8?q?[feat]=20#157=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=83=81=EC=84=B8=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC=20=EC=83=81=EC=84=B8=20=ED=94=8C=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0=20=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/CategoryDetailFeature.swift | 86 ++++--------------- .../Sources/CategoryDetailView.swift | 4 +- 2 files changed, 19 insertions(+), 71 deletions(-) diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift index 5de0c984..8dfda423 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift @@ -53,8 +53,6 @@ public struct CategoryDetailFeature { return identifiedArray } var contents: IdentifiedArrayOf = [] - var kebobSelectedType: PokitDeleteBottomSheet.SheetType? - var selectedContentItem: BaseContentItem? var shareSheetItem: BaseContentItem? = nil /// sheet Presented var isCategorySheetPresented: Bool = false @@ -86,7 +84,7 @@ public struct CategoryDetailFeature { case binding(BindingAction) case dismiss case pagenation - case 카테고리_케밥_버튼_눌렀을때(PokitDeleteBottomSheet.SheetType, selectedItem: BaseContentItem?) + case 카테고리_케밥_버튼_눌렀을때 case 카테고리_선택_버튼_눌렀을때 case 카테고리_선택했을때(BaseCategoryItem) case 필터_버튼_눌렀을때 @@ -110,7 +108,6 @@ public struct CategoryDetailFeature { public enum AsyncAction: Equatable { case 카테고리_내_컨텐츠_목록_조회_API case 카테고리_목록_조회_API - case 컨텐츠_삭제_API(id: Int) case 페이징_재조회 case 클립보드_감지 } @@ -181,9 +178,7 @@ private extension CategoryDetailFeature { case .binding: return .none - case let .카테고리_케밥_버튼_눌렀을때(selectedType, selectedItem): - state.kebobSelectedType = selectedType - state.selectedContentItem = selectedItem + case .카테고리_케밥_버튼_눌렀을때: return .run { send in await send(.inner(.카테고리_시트_활성화(true))) } case .카테고리_선택_버튼_눌렀을때: @@ -261,9 +256,7 @@ private extension CategoryDetailFeature { state.domain.contentList.data?.removeAll { $0.id == id } state.contents.removeAll { $0.content.id == id } state.domain.category.contentCount -= 1 - state.selectedContentItem = nil state.isPokitDeleteSheetPresented = false - state.kebobSelectedType = nil return .none case .pagenation_API_반영(let contentList): let list = state.domain.contentList.data ?? [] @@ -313,12 +306,6 @@ private extension CategoryDetailFeature { : await send(.inner(.pagenation_API_반영(contentList))) } - case let .컨텐츠_삭제_API(contentId): - return .run { send in - let _ = try await contentClient.컨텐츠_삭제("\(contentId)") - await send(.inner(.컨텐츠_삭제_API_반영(id: contentId)), animation: .pokitSpring) - } - case .페이징_재조회: return .run { [ pageable = state.domain.pageable, @@ -376,41 +363,20 @@ private extension CategoryDetailFeature { case .categoryBottomSheet(let delegateAction): switch delegateAction { case .shareCellButtonTapped: - switch state.kebobSelectedType { - case .링크삭제: - state.shareSheetItem = state.selectedContentItem - case .포킷삭제: - kakaoShareClient.카테고리_카카오톡_공유( - CategoryKaKaoShareModel( - categoryName: state.domain.category.categoryName, - categoryId: state.domain.category.id, - imageURL: state.domain.category.categoryImage.imageURL - ) + kakaoShareClient.카테고리_카카오톡_공유( + CategoryKaKaoShareModel( + categoryName: state.domain.category.categoryName, + categoryId: state.domain.category.id, + imageURL: state.domain.category.categoryImage.imageURL ) - default: return .none - } - + ) state.isCategorySheetPresented = false return .none - case .editCellButtonTapped: - return .run { [ - content = state.selectedContentItem, - type = state.kebobSelectedType, - category = state.category - ] send in - guard let type else { return } - switch type { - case .링크삭제: - guard let content else { return } - await send(.inner(.카테고리_시트_활성화(false))) - await send(.delegate(.링크수정(contentId: content.id))) - case .포킷삭제: - await send(.inner(.카테고리_시트_활성화(false))) - await send(.delegate(.포킷수정(category))) - } + return .run { [category = state.category] send in + await send(.inner(.카테고리_시트_활성화(false))) + await send(.delegate(.포킷수정(category))) } - case .deleteCellButtonTapped: return .run { send in await send(.inner(.카테고리_시트_활성화(false))) @@ -426,27 +392,11 @@ private extension CategoryDetailFeature { return .run { send in await send(.inner(.카테고리_삭제_시트_활성화(false))) } case .deleteButtonTapped: - guard let selectedType = state.kebobSelectedType else { - /// 🚨 Error Case [1]: 해당 타입의 항목을 삭제하려는데 선택한 `타입`이 없을 때 - state.isPokitDeleteSheetPresented = false - return .none - } - switch selectedType { - case .링크삭제: - guard let selectedItem = state.selectedContentItem else { - /// 🚨 Error Case [1]: 링크 타입의 항목을 삭제하려는데 선택한 `링크항목`이 없을 때 - state.isPokitDeleteSheetPresented = false - return .none - } - return .send(.async(.컨텐츠_삭제_API(id: selectedItem.id))) - case .포킷삭제: - state.isPokitDeleteSheetPresented = false - state.kebobSelectedType = nil - return .run { [categoryId = state.domain.category.id] send in - await send(.inner(.카테고리_삭제_시트_활성화(false))) - await send(.delegate(.포킷삭제)) - try await categoryClient.카테고리_삭제(categoryId) - } + state.isPokitDeleteSheetPresented = false + return .run { [categoryId = state.domain.category.id] send in + await send(.inner(.카테고리_삭제_시트_활성화(false))) + await send(.delegate(.포킷삭제)) + try await categoryClient.카테고리_삭제(categoryId) } } /// - 필터 버튼을 눌렀을 때 @@ -472,9 +422,7 @@ private extension CategoryDetailFeature { case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_눌렀을때(content)))): return .send(.delegate(.contentItemTapped(content))) case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content)))): - state.kebobSelectedType = .링크삭제 - state.selectedContentItem = content - return .send(.inner(.카테고리_시트_활성화(true))) + return .send(.delegate(.contentItemTapped(content))) case .contents: return .none } diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift index 50eb73e7..96b18f2b 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift @@ -66,7 +66,7 @@ public extension CategoryDetailView { } .sheet(isPresented: $store.isPokitDeleteSheetPresented) { PokitDeleteBottomSheet( - type: store.kebobSelectedType ?? .포킷삭제, + type: .포킷삭제, delegateSend: { store.send(.scope(.categoryDeleteBottomSheet($0))) } ) } @@ -95,7 +95,7 @@ private extension CategoryDetailView { PokitHeaderItems(placement: .trailing) { PokitToolbarButton( .icon(.kebab), - action: { send(.카테고리_케밥_버튼_눌렀을때(.포킷삭제, selectedItem: nil)) } + action: { send(.카테고리_케밥_버튼_눌렀을때) } ) } } From c7752ca5279190b75d919f720fb5b8ec0e25db51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sat, 30 Nov 2024 23:37:17 +0900 Subject: [PATCH 11/31] =?UTF-8?q?[feat]=20#157=20=EC=BB=A8=ED=85=90?= =?UTF-8?q?=EC=B8=A0=EB=AA=A9=EB=A1=9D=EC=97=90=EC=84=9C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=EC=83=81=EC=84=B8=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ContentList/ContentListFeature.swift | 80 +------------------ .../Sources/ContentList/ContentListView.swift | 27 ------- 2 files changed, 2 insertions(+), 105 deletions(-) diff --git a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift index dfbf0c2d..396f75ad 100644 --- a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift +++ b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift @@ -38,10 +38,6 @@ public struct ContentListFeature { get { domain.contentCount } } var isListDescending = true - /// sheet item - var bottomSheetItem: BaseContentItem? = nil - var alertItem: BaseContentItem? = nil - var shareSheetItem: BaseContentItem? = nil /// pagenation var hasNext: Bool { domain.contentList.hasNext @@ -64,34 +60,22 @@ public struct ContentListFeature { case binding(BindingAction) case pagenation - /// - Button Tapped - case bottomSheet( - delegate: PokitBottomSheet.Delegate, - content: BaseContentItem - ) + case 컨텐츠_항목_눌렀을때(content: BaseContentItem) - case 컨텐츠_항목_케밥_버튼_눌렀을때(content: BaseContentItem) - case 컨텐츠_삭제_눌렀을때(content: BaseContentItem) case 정렬_버튼_눌렀을때 case dismiss /// - On Appeared case 뷰가_나타났을때 - - case 링크_공유시트_해제 - case 경고시트_해제 } public enum InnerAction: Equatable { - case 바텀시트_해제 case 컨텐츠_목록_조회_API_반영(BaseContentListInquiry) - case 컨텐츠_삭제_API_반영(id: Int) case 컨텐츠_목록_조회_페이징_API_반영(BaseContentListInquiry) case 페이징_초기화 case 컨텐츠_개수_업데이트(Int) } public enum AsyncAction: Equatable { - case 컨텐츠_삭제_API(id: Int) case 컨텐츠_목록_조회_페이징_API case 컨텐츠_목록_조회_API case 컨텐츠_개수_조회_API @@ -99,10 +83,6 @@ public struct ContentListFeature { } public enum ScopeAction { - case bottomSheet( - delegate: PokitBottomSheet.Delegate, - content: BaseContentItem - ) case contents(IdentifiedActionOf) } @@ -158,19 +138,8 @@ private extension ContentListFeature { /// - View Effect func handleViewAction(_ action: Action.View, state: inout State) -> Effect { switch action { - case .컨텐츠_항목_케밥_버튼_눌렀을때(let content): - state.bottomSheetItem = content - return .none case .컨텐츠_항목_눌렀을때(let content): return .send(.delegate(.링크상세(content: content))) - case .bottomSheet(let delegate, let content): - return .concatenate( - .send(.inner(.바텀시트_해제)), - .send(.scope(.bottomSheet(delegate: delegate, content: content))) - ) - case .컨텐츠_삭제_눌렀을때: - guard let id = state.alertItem?.id else { return .none } - return .send(.async(.컨텐츠_삭제_API(id: id))) case .binding: return .none case .정렬_버튼_눌렀을때: @@ -189,21 +158,12 @@ private extension ContentListFeature { ) case .pagenation: return .send(.async(.컨텐츠_목록_조회_페이징_API)) - case .링크_공유시트_해제: - state.shareSheetItem = nil - return .none - case .경고시트_해제: - state.alertItem = nil - return .none } } /// - Inner Effect func handleInnerAction(_ action: Action.InnerAction, state: inout State) -> Effect { switch action { - case .바텀시트_해제: - state.bottomSheetItem = nil - return .none case .컨텐츠_목록_조회_페이징_API_반영(let contentList): let list = state.domain.contentList.data ?? [] guard let newList = contentList.data else { return .none } @@ -213,11 +173,6 @@ private extension ContentListFeature { newList.forEach { state.contents.append(.init(content: $0)) } return .none - case .컨텐츠_삭제_API_반영(id: let id): - state.alertItem = nil - state.domain.contentList.data?.removeAll { $0.id == id } - state.contents.removeAll { $0.content.id == id } - return .none case .컨텐츠_목록_조회_API_반영(let contentList): state.domain.contentList = contentList @@ -242,15 +197,6 @@ private extension ContentListFeature { /// - Async Effect func handleAsyncAction(_ action: Action.AsyncAction, state: inout State) -> Effect { switch action { - case .컨텐츠_삭제_API(id: let id): - let count = state.domain.contentCount - let newCount = count - 1 - - return .merge( - .send(.inner(.컨텐츠_개수_업데이트(newCount))), - contentDelete(contentId: id) - ) - case .컨텐츠_목록_조회_페이징_API: state.domain.pageable.page += 1 return .run { [ @@ -303,25 +249,10 @@ private extension ContentListFeature { func handleScopeAction(_ action: Action.ScopeAction, state: inout State) -> Effect { /// - 링크에 대한 `공유` / `수정` / `삭제` delegate switch action { - case .bottomSheet(let delegate, let content): - switch delegate { - case .deleteCellButtonTapped: - state.alertItem = content - return .none - case .editCellButtonTapped: - return .send(.delegate(.링크수정(contentId: content.id))) - case .favoriteCellButtonTapped: - return .none - case .shareCellButtonTapped: - state.shareSheetItem = content - return .none - } - case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_눌렀을때(content)))): return .send(.delegate(.링크상세(content: content))) case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content)))): - state.bottomSheetItem = content - return .none + return .send(.delegate(.링크상세(content: content))) case .contents: return .none } @@ -377,13 +308,6 @@ private extension ContentListFeature { await send(.inner(.컨텐츠_목록_조회_API_반영(contentItems)), animation: .pokitDissolve) } } - - func contentDelete(contentId: Int) -> Effect { - return .run { send in - let _ = try await contentClient.컨텐츠_삭제("\(contentId)") - await send(.inner(.컨텐츠_삭제_API_반영(id: contentId)), animation: .pokitSpring) - } - } } public extension ContentListFeature { diff --git a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift index b626fad1..2bec1245 100644 --- a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift +++ b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift @@ -34,33 +34,6 @@ public extension ContentListView { .padding(.top, 12) .pokitNavigationBar { toolbar } .ignoresSafeArea(edges: .bottom) - .sheet(item: $store.bottomSheetItem) { content in - PokitBottomSheet( - items: [.share, .edit, .delete], - height: 224, - delegateSend: { - send(.bottomSheet(delegate: $0, content: content)) - } - ) - } - .sheet(item: $store.shareSheetItem) { content in - if let shareURL = URL(string: content.data) { - PokitShareSheet( - items: [shareURL], - completion: { send(.링크_공유시트_해제) } - ) - .presentationDetents([.medium, .large]) - } - } - .sheet(item: $store.alertItem) { content in - PokitAlert( - "링크를 정말 삭제하시겠습니까?", - message: "함께 저장한 모든 정보가 삭제되며, \n복구하실 수 없습니다.", - confirmText: "삭제", - action: { send(.컨텐츠_삭제_눌렀을때(content: content)) }, - cancelAction: { send(.경고시트_해제) } - ) - } .task { await send(.뷰가_나타났을때, animation: .pokitDissolve).finish() } } } From bb8974cb0e9200e5d0730a45a524bd7da29f78dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sat, 30 Nov 2024 23:43:22 +0900 Subject: [PATCH 12/31] =?UTF-8?q?[feat]=20#157=20=EC=BB=A8=ED=85=90?= =?UTF-8?q?=EC=B8=A0=20=EA=B2=80=EC=83=89=EC=97=90=EC=84=9C=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=20=EC=83=81=EC=84=B8=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Search/PokitSearchFeature.swift | 71 +------------------ .../Sources/Search/PokitSearchView.swift | 22 ------ 2 files changed, 1 insertion(+), 92 deletions(-) diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift index 9664bf83..32f3c7c0 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift @@ -72,11 +72,6 @@ public struct PokitSearchFeature { get { domain.contentList.hasNext } } var isLoading: Bool = false - - /// sheet item - var bottomSheetItem: BaseContentItem? = nil - var alertItem: BaseContentItem? = nil - var shareSheetItem: BaseContentItem? = nil } /// - Action @@ -93,10 +88,6 @@ public struct PokitSearchFeature { public enum View: Equatable, BindableAction { case binding(BindingAction) case dismiss - case bottomSheet( - delegate: PokitBottomSheet.Delegate, - content: BaseContentItem - ) case 자동저장_버튼_눌렀을때 case 검색_버튼_눌렀을때 case 최근검색_태그_눌렀을때(text: String) @@ -110,14 +101,10 @@ public struct PokitSearchFeature { case 안읽음_태그_눌렀을때 case 전체_삭제_버튼_눌렀을때 case 컨텐츠_항목_눌렀을때(content: BaseContentItem) - case 컨텐츠_항목_케밥_버튼_눌렀을때(content: BaseContentItem) case 정렬_버튼_눌렀을때 case 검색_키보드_엔터_눌렀을때 - case 링크_삭제_눌렀을때 - case 링크_공유_완료되었을때 case 뷰가_나타났을때 case 로딩중일때 - } public enum InnerAction: Equatable { @@ -127,10 +114,8 @@ public struct PokitSearchFeature { case 모아보기_업데이트(favoriteFilter: Bool, unreadFilter: Bool) case 필터_업데이트 case 카테고리_ID_목록_업데이트 - case 바텀시트_해제 case 컨텐츠_검색_API_반영(BaseContentListInquiry) case 컨텐츠_검색_페이징_API_반영(BaseContentListInquiry) - case 컨텐츠_삭제_API_반영(id: Int) case 최근검색어_불러오기 case 자동저장_불러오기 case 최근검색어_추가 @@ -141,17 +126,12 @@ public struct PokitSearchFeature { case 컨텐츠_검색_API case 최근검색어_갱신_수행 case 자동저장_수행 - case 컨텐츠_삭제_API(id: Int) case 컨텐츠_검색_페이징_API case 클립보드_감지 } public enum ScopeAction { case filterBottomSheet(FilterBottomFeature.Action.DelegateAction) - case bottomSheet( - delegate: PokitBottomSheet.Delegate, - content: BaseContentItem - ) case contents(IdentifiedActionOf) } @@ -285,21 +265,6 @@ private extension PokitSearchFeature { case let .컨텐츠_항목_눌렀을때(content): return .send(.delegate(.linkCardTapped(content: content))) - case let .컨텐츠_항목_케밥_버튼_눌렀을때(content): - state.bottomSheetItem = content - return .none - - case .bottomSheet(delegate: let delegate, content: let content): - return .run { send in - await send(.inner(.바텀시트_해제)) - await send(.scope(.bottomSheet(delegate: delegate, content: content))) - } - - case .링크_삭제_눌렀을때: - guard let id = state.alertItem?.id else { return .none } - state.alertItem = nil - return .send(.async(.컨텐츠_삭제_API(id: id))) - case .정렬_버튼_눌렀을때: state.isResultAscending.toggle() state.domain.pageable.sort = [ @@ -340,9 +305,6 @@ private extension PokitSearchFeature { state.domain.condition.isRead = false return .send(.inner(.페이징_초기화)) - case .링크_공유_완료되었을때: - state.shareSheetItem = nil - return .none case .로딩중일때: return .send(.async(.컨텐츠_검색_페이징_API)) } @@ -392,10 +354,6 @@ private extension PokitSearchFeature { state.domain.condition.isRead = unreadFilter return .none - case .바텀시트_해제: - state.bottomSheetItem = nil - return .none - case .필터_업데이트: state.isFiltered = !state.categoryFilter.isEmpty || state.favoriteFilter @@ -438,12 +396,6 @@ private extension PokitSearchFeature { } return .send(.async(.최근검색어_갱신_수행)) - case let .컨텐츠_삭제_API_반영(id): - state.alertItem = nil - state.domain.contentList.data?.removeAll { $0.id == id } - state.contents.removeAll { $0.content.id == id } - return .none - case let .컨텐츠_검색_페이징_API_반영(contentList): let list = state.domain.contentList.data ?? [] guard let newList = contentList.data else { return .none } @@ -484,12 +436,6 @@ private extension PokitSearchFeature { await userDefaults.setBool(isAutoSaveSearch, .autoSaveSearch) } - case let .컨텐츠_삭제_API(id): - return .run { send in - let _ = try await contentClient.컨텐츠_삭제("\(id)") - await send(.inner(.컨텐츠_삭제_API_반영(id: id)), animation: .pokitSpring) - } - case .컨텐츠_검색_페이징_API: state.domain.pageable.page += 1 let formatter = DateFormat.yearMonthDate.formatter @@ -560,25 +506,10 @@ private extension PokitSearchFeature { await send(.inner(.페이징_초기화)) } - case .bottomSheet(let delegate, let content): - switch delegate { - case .deleteCellButtonTapped: - state.alertItem = content - return .none - case .editCellButtonTapped: - return .send(.delegate(.링크수정(contentId: content.id))) - case .favoriteCellButtonTapped: - return .none - case .shareCellButtonTapped: - state.shareSheetItem = content - return .none - } - case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_눌렀을때(content)))): return .send(.delegate(.linkCardTapped(content: content))) case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content)))): - state.bottomSheetItem = content - return .none + return .send(.delegate(.linkCardTapped(content: content))) case .contents: return .none } diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift index 01ac43ba..c641d9ea 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift @@ -51,28 +51,6 @@ public extension PokitSearchView { ) { store in FilterBottomSheet(store: store) } - .sheet(item: $store.bottomSheetItem) { content in - PokitBottomSheet( - items: [.share, .edit, .delete], - height: 224 - ) { send(.bottomSheet(delegate: $0, content: content)) } - } - .sheet(item: $store.shareSheetItem) { content in - if let shareURL = URL(string: content.data) { - PokitShareSheet( - items: [shareURL], - completion: { send(.링크_공유_완료되었을때) } - ) - .presentationDetents([.medium, .large]) - } - } - .sheet(item: $store.alertItem) { content in - PokitAlert( - "링크를 정말 삭제하시겠습니까?", - message: "함께 저장한 모든 정보가 삭제되며, \n복구하실 수 없습니다.", - confirmText: "삭제" - ) { send(.링크_삭제_눌렀을때) } - } .task { await send(.뷰가_나타났을때).finish() } } } From 1cb5ff9cf522130e15ca8e20f4380b2c97da4c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sat, 30 Nov 2024 23:51:59 +0900 Subject: [PATCH 13/31] =?UTF-8?q?[feat]=20#157=20=EB=A6=AC=EB=A7=88?= =?UTF-8?q?=EC=9D=B8=EB=93=9C=EC=97=90=EC=84=9C=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Remind/RemindFeature.swift | 65 +------------------ .../Sources/Remind/RemindView.swift | 22 ------- 2 files changed, 3 insertions(+), 84 deletions(-) diff --git a/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift b/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift index 24766a92..dcc420b8 100644 --- a/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift +++ b/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift @@ -53,10 +53,6 @@ public struct RemindFeature { favoriteList.forEach { identifiedArray.append($0) } return identifiedArray } - /// sheet item - var bottomSheetItem: BaseContentItem? = nil - var alertItem: BaseContentItem? = nil - var shareSheetItem: BaseContentItem? = nil } /// - Action public enum Action: FeatureAction, ViewAction { @@ -68,10 +64,6 @@ public struct RemindFeature { public enum View: Equatable, BindableAction { case binding(BindingAction) - case bottomSheet( - delegate: PokitBottomSheet.Delegate, - content: BaseContentItem - ) /// - Button Tapped case 알림_버튼_눌렀을때 @@ -80,9 +72,6 @@ public struct RemindFeature { case 컨텐츠_항목_케밥_버튼_눌렀을때(content: BaseContentItem) case 안읽음_목록_버튼_눌렀을때 case 즐겨찾기_목록_버튼_눌렀을때 - case 링크_삭제_눌렀을때(content: BaseContentItem) - - case 링크_공유_완료 case 뷰가_나타났을때 case 즐겨찾기_항목_이미지_조회(contentId: Int) @@ -90,31 +79,23 @@ public struct RemindFeature { case 리마인드_항목_이미지오류_나타났을때(contentId: Int) } public enum InnerAction: Equatable { - case 바텀시트_해제 case 오늘의_리마인드_조회_API_반영(contents: [BaseContentItem]) case 읽지않음_컨텐츠_조회_API_반영(contentList: BaseContentListInquiry) case 즐겨찾기_링크모음_조회_API_반영(contentList: BaseContentListInquiry) case 즐겨찾기_이미지_조회_수행_반영(imageURL: String, index: Int) case 읽지않음_이미지_조회_수행_반영(imageURL: String, index: Int) case 리마인드_이미지_조회_수행_반영(imageURL: String, index: Int) - case 컨텐츠_삭제_API_반영(id: Int) } public enum AsyncAction: Equatable { case 오늘의_리마인드_조회_API case 읽지않음_컨텐츠_조회_API case 즐겨찾기_링크모음_조회_API - case 컨텐츠_삭제_API(id: Int) case 즐겨찾기_이미지_조회_수행(contentId: Int) case 읽지않음_이미지_조회_수행(contentId: Int) case 리마인드_이미지_조회_수행(contentId: Int) } - public enum ScopeAction: Equatable { - case bottomSheet( - delegate: PokitBottomSheet.Delegate, - content: BaseContentItem - ) - } + public enum ScopeAction: Equatable { case 없음 } public enum DelegateAction: Equatable { case 링크상세(content: BaseContentItem) case alertButtonTapped @@ -160,11 +141,6 @@ private extension RemindFeature { switch action { case .binding: return .none - case .bottomSheet(let delegate, let content): - return .run { send in - await send(.inner(.바텀시트_해제)) - await send(.scope(.bottomSheet(delegate: delegate, content: content))) - } case .알림_버튼_눌렀을때: return .send(.delegate(.alertButtonTapped)) case .검색_버튼_눌렀을때: @@ -174,18 +150,11 @@ private extension RemindFeature { case .안읽음_목록_버튼_눌렀을때: return .send(.delegate(.링크목록_안읽음)) case .컨텐츠_항목_케밥_버튼_눌렀을때(let content): - state.bottomSheetItem = content - return .none + return .send(.delegate(.링크상세(content: content))) case .컨텐츠_항목_눌렀을때(let content): return .send(.delegate(.링크상세(content: content))) - case .링크_삭제_눌렀을때: - guard let id = state.alertItem?.id else { return .none } - return .send(.async(.컨텐츠_삭제_API(id: id))) case .뷰가_나타났을때: return allContentFetch(animation: .pokitDissolve) - case .링크_공유_완료: - state.shareSheetItem = nil - return .none case let .즐겨찾기_항목_이미지_조회(contentId): return .send(.async(.즐겨찾기_이미지_조회_수행(contentId: contentId))) case let .읽지않음_항목_이미지_조회(contentId): @@ -197,9 +166,6 @@ private extension RemindFeature { /// - Inner Effect func handleInnerAction(_ action: Action.InnerAction, state: inout State) -> Effect { switch action { - case .바텀시트_해제: - state.bottomSheetItem = nil - return .none case .오늘의_리마인드_조회_API_반영(contents: let contents): state.domain.recommendedList = contents return .none @@ -209,12 +175,6 @@ private extension RemindFeature { case .즐겨찾기_링크모음_조회_API_반영(contentList: let contentList): state.domain.favoriteList = contentList return .none - case .컨텐츠_삭제_API_반영(id: let contentId): - state.alertItem = nil - state.domain.recommendedList?.removeAll { $0.id == contentId } - state.domain.unreadList.data?.removeAll { $0.id == contentId } - state.domain.favoriteList.data?.removeAll { $0.id == contentId } - return .none case let .즐겨찾기_이미지_조회_수행_반영(imageURL, index): var content = state.domain.favoriteList.data?.remove(at: index) content?.thumbNail = imageURL @@ -265,11 +225,6 @@ private extension RemindFeature { ).toDomain() await send(.inner(.즐겨찾기_링크모음_조회_API_반영(contentList: contentList)), animation: .pokitDissolve) } - case .컨텐츠_삭제_API(id: let id): - return .run { send in - let _ = try await contentClient.컨텐츠_삭제("\(id)") - await send(.inner(.컨텐츠_삭제_API_반영(id: id)), animation: .pokitSpring) - } case let .즐겨찾기_이미지_조회_수행(contentId): return .run { [favoriteContents = state.favoriteContents] send in guard let index = favoriteContents?.index(id: contentId), @@ -321,21 +276,7 @@ private extension RemindFeature { /// - Scope Effect func handleScopeAction(_ action: Action.ScopeAction, state: inout State) -> Effect { /// - 링크에 대한 `공유` / `수정` / `삭제` delegate - switch action { - case .bottomSheet(let delegate, let content): - switch delegate { - case .deleteCellButtonTapped: - state.alertItem = content - return .none - case .editCellButtonTapped: - return .send(.delegate(.링크수정(id: content.id))) - case .favoriteCellButtonTapped: - return .none - case .shareCellButtonTapped: - state.shareSheetItem = content - return .none - } - } + return .none } /// - Delegate Effect func handleDelegateAction(_ action: Action.DelegateAction, state: inout State) -> Effect { diff --git a/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift b/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift index 68309559..ab8b0142 100644 --- a/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift +++ b/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift @@ -31,28 +31,6 @@ public extension RemindView { .background(.pokit(.bg(.base))) .ignoresSafeArea(edges: .bottom) .navigationBarBackButtonHidden(true) - .sheet(item: $store.bottomSheetItem) { content in - PokitBottomSheet( - items: [.share, .edit, .delete], - height: 224 - ) { send(.bottomSheet(delegate: $0, content: content)) } - } - .sheet(item: $store.shareSheetItem) { content in - if let shareURL = URL(string: content.data) { - PokitShareSheet( - items: [shareURL], - completion: { send(.링크_공유_완료) } - ) - .presentationDetents([.medium, .large]) - } - } - .sheet(item: $store.alertItem) { content in - PokitAlert( - "링크를 정말 삭제하시겠습니까?", - message: "함께 저장한 모든 정보가 삭제되며, \n복구하실 수 없습니다.", - confirmText: "삭제" - ) { send(.링크_삭제_눌렀을때(content: content)) } - } .task { await send(.뷰가_나타났을때, animation: .pokitDissolve).finish() } } } From 9e8b2afbc1dd5d12bc22e456e0a14fc09c90dd74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sat, 30 Nov 2024 23:52:29 +0900 Subject: [PATCH 14/31] =?UTF-8?q?[fix]=20#157=20=EC=B6=94=EC=B2=9C=20?= =?UTF-8?q?=ED=95=AD=EB=AA=A9=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=9E=98?= =?UTF-8?q?=EB=A6=AC=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeatureRemind/Sources/Remind/RemindView.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift b/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift index ab8b0142..f6400565 100644 --- a/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift +++ b/Projects/Feature/FeatureRemind/Sources/Remind/RemindView.swift @@ -93,7 +93,6 @@ extension RemindView { HStack(spacing: 12) { ForEach(recommendedContents, id: \.id) { content in recommendedContentCell(content: content) - } } .padding(.horizontal, 20) @@ -112,12 +111,6 @@ extension RemindView { @ViewBuilder private func recommendedContentCellLabel(content: BaseContentItem) -> some View { ZStack(alignment: .bottom) { - if let url = URL(string: content.thumbNail) { - recommendedContentCellImage(url: url, contentId: content.id) - } else { - imagePlaceholder - } - LinearGradient( stops: [ Gradient.Stop( @@ -162,6 +155,13 @@ extension RemindView { .padding(12) } .frame(width: 216, height: 194) + .background { + if let url = URL(string: content.thumbNail) { + recommendedContentCellImage(url: url, contentId: content.id) + } else { + imagePlaceholder + } + } .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) .clipped() } From 47f8bbacbbaa124dd0e8c48b8de9f9bdfaa6017f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 1 Dec 2024 00:02:27 +0900 Subject: [PATCH 15/31] =?UTF-8?q?[feat]=20#157=20=EC=BB=A8=ED=85=90?= =?UTF-8?q?=EC=B8=A0=20=ED=95=AD=EB=AA=A9=20=EB=88=8C=EB=A0=80=EC=9D=84?= =?UTF-8?q?=EB=95=8C=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/CategoryDetailFeature.swift | 2 -- .../CategorySharing/CategorySharingFeature.swift | 10 ---------- .../Sources/ContentCard/ContentCardFeature.swift | 8 ++++++-- .../Sources/ContentList/ContentListFeature.swift | 2 -- .../FeaturePokit/Sources/PokitRootFeature.swift | 2 -- .../FeatureRemind/Sources/Remind/RemindFeature.swift | 7 ++++++- .../Sources/Search/PokitSearchFeature.swift | 2 -- 7 files changed, 12 insertions(+), 21 deletions(-) diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift index 8dfda423..f2209d30 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift @@ -419,8 +419,6 @@ private extension CategoryDetailFeature { ) } - case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_눌렀을때(content)))): - return .send(.delegate(.contentItemTapped(content))) case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content)))): return .send(.delegate(.contentItemTapped(content))) case .contents: diff --git a/Projects/Feature/FeatureCategorySharing/Sources/CategorySharing/CategorySharingFeature.swift b/Projects/Feature/FeatureCategorySharing/Sources/CategorySharing/CategorySharingFeature.swift index 7d57eb96..fb4d1b5b 100644 --- a/Projects/Feature/FeatureCategorySharing/Sources/CategorySharing/CategorySharingFeature.swift +++ b/Projects/Feature/FeatureCategorySharing/Sources/CategorySharing/CategorySharingFeature.swift @@ -224,16 +224,6 @@ private extension CategorySharingFeature { /// - Scope Effect func handleScopeAction(_ action: Action.ScopeAction, state: inout State) -> Effect { switch action { - case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_눌렀을때(content)))): - let sharedContent = state.domain.sharedCategory.contentList.data.first { item in - item.id == content.id - } - guard let sharedContent else { return .none } - - return .send(.delegate(.컨텐츠_아이템_클릭( - categoryId: state.category.categoryId, - content: sharedContent - ))) case .contents: return .none } diff --git a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift index e2480df9..9f4ebf66 100644 --- a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift +++ b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift @@ -16,6 +16,8 @@ public struct ContentCardFeature { /// - Dependency @Dependency(SwiftSoupClient.self) private var swiftSoupClient + @Dependency(\.openURL) + private var openURL /// - State @ObservableState public struct State: Equatable, Identifiable { @@ -53,7 +55,6 @@ public struct ContentCardFeature { public enum ScopeAction: Equatable { case doNothing } public enum DelegateAction: Equatable { - case 컨텐츠_항목_눌렀을때(content: BaseContentItem) case 컨텐츠_항목_케밥_버튼_눌렀을때(content: BaseContentItem) } } @@ -97,7 +98,10 @@ private extension ContentCardFeature { func handleViewAction(_ action: Action.View, state: inout State) -> Effect { switch action { case .컨텐츠_항목_눌렀을때: - return .send(.delegate(.컨텐츠_항목_눌렀을때(content: state.content))) + guard let url = URL(string: state.content.data) else { + return .none + } + return .run { _ in await openURL(url) } case .컨텐츠_항목_케밥_버튼_눌렀을때: return .send(.delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content: state.content))) case .메타데이터_조회: diff --git a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift index 396f75ad..22ec8666 100644 --- a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift +++ b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift @@ -249,8 +249,6 @@ private extension ContentListFeature { func handleScopeAction(_ action: Action.ScopeAction, state: inout State) -> Effect { /// - 링크에 대한 `공유` / `수정` / `삭제` delegate switch action { - case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_눌렀을때(content)))): - return .send(.delegate(.링크상세(content: content))) case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content)))): return .send(.delegate(.링크상세(content: content))) case .contents: diff --git a/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift b/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift index 30b93937..c632fbaf 100644 --- a/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift +++ b/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift @@ -548,8 +548,6 @@ private extension PokitRootFeature { default: return .none } - case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_눌렀을때(content)))): - return .send(.delegate(.contentDetailTapped(content))) case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content)))): state.selectedUnclassifiedItem = content return .send(.inner(.카테고리_시트_활성화(true))) diff --git a/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift b/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift index dcc420b8..51e5129d 100644 --- a/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift +++ b/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift @@ -17,6 +17,8 @@ public struct RemindFeature { /// - Dependency @Dependency(\.dismiss) private var dismiss + @Dependency(\.openURL) + private var openURL @Dependency(RemindClient.self) private var remindClient @Dependency(ContentClient.self) @@ -152,7 +154,10 @@ private extension RemindFeature { case .컨텐츠_항목_케밥_버튼_눌렀을때(let content): return .send(.delegate(.링크상세(content: content))) case .컨텐츠_항목_눌렀을때(let content): - return .send(.delegate(.링크상세(content: content))) + guard let url = URL(string: content.data) else { + return .none + } + return .run { _ in await openURL(url) } case .뷰가_나타났을때: return allContentFetch(animation: .pokitDissolve) case let .즐겨찾기_항목_이미지_조회(contentId): diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift index 32f3c7c0..11fcefbf 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift @@ -506,8 +506,6 @@ private extension PokitSearchFeature { await send(.inner(.페이징_초기화)) } - case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_눌렀을때(content)))): - return .send(.delegate(.linkCardTapped(content: content))) case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content)))): return .send(.delegate(.linkCardTapped(content: content))) case .contents: From 4591cad3ba4514f2acb4fe4823e25ba4307e3ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 1 Dec 2024 00:07:29 +0900 Subject: [PATCH 16/31] =?UTF-8?q?[chore]=20#157=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/CategoryDetailFeature.swift | 17 ----------------- .../Sources/CategoryDetailView.swift | 9 --------- .../ContentList/ContentListFeature.swift | 3 --- .../FeatureLogin/Sources/Intro/IntroView.swift | 1 - .../Sources/Search/PokitSearchFeature.swift | 4 ---- 5 files changed, 34 deletions(-) diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift index f2209d30..f5d2b9fd 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailFeature.swift @@ -53,7 +53,6 @@ public struct CategoryDetailFeature { return identifiedArray } var contents: IdentifiedArrayOf = [] - var shareSheetItem: BaseContentItem? = nil /// sheet Presented var isCategorySheetPresented: Bool = false var isCategorySelectSheetPresented: Bool = false @@ -88,9 +87,7 @@ public struct CategoryDetailFeature { case 카테고리_선택_버튼_눌렀을때 case 카테고리_선택했을때(BaseCategoryItem) case 필터_버튼_눌렀을때 - case 컨텐츠_항목_눌렀을때(BaseContentItem) case 뷰가_나타났을때 - case 링크_공유_완료되었을때 } public enum InnerAction: Equatable { @@ -100,7 +97,6 @@ public struct CategoryDetailFeature { case 카테고리_목록_조회_API_반영(BaseCategoryListInquiry) case 카테고리_내_컨텐츠_목록_조회_API_반영(BaseContentListInquiry) - case 컨텐츠_삭제_API_반영(id: Int) case pagenation_API_반영(BaseContentListInquiry) case pagenation_초기화 } @@ -196,9 +192,6 @@ private extension CategoryDetailFeature { state.isFilterSheetPresented.toggle() return .none - case .컨텐츠_항목_눌렀을때(let selectedItem): - return .run { send in await send(.delegate(.contentItemTapped(selectedItem))) } - case .dismiss: return .run { _ in await dismiss() } @@ -212,10 +205,6 @@ private extension CategoryDetailFeature { case .pagenation: state.domain.pageable.page += 1 return .send(.async(.카테고리_내_컨텐츠_목록_조회_API)) - - case .링크_공유_완료되었을때: - state.shareSheetItem = nil - return .none } } @@ -252,12 +241,6 @@ private extension CategoryDetailFeature { state.isLoading = false return .none - case let .컨텐츠_삭제_API_반영(id): - state.domain.contentList.data?.removeAll { $0.id == id } - state.contents.removeAll { $0.content.id == id } - state.domain.category.contentCount -= 1 - state.isPokitDeleteSheetPresented = false - return .none case .pagenation_API_반영(let contentList): let list = state.domain.contentList.data ?? [] guard let newList = contentList.data else { return .none } diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift index 96b18f2b..3c188c83 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift @@ -42,15 +42,6 @@ public extension CategoryDetailView { delegateSend: { store.send(.scope(.categoryBottomSheet($0))) } ) } - .sheet(item: $store.shareSheetItem) { content in - if let shareURL = URL(string: content.data) { - PokitShareSheet( - items: [shareURL], - completion: { send(.링크_공유_완료되었을때) } - ) - .presentationDetents([.medium, .large]) - } - } .sheet(isPresented: $store.isCategorySelectSheetPresented) { if let categories = store.categories { PokitCategorySheet( diff --git a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift index 22ec8666..48ca2c76 100644 --- a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift +++ b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListFeature.swift @@ -61,7 +61,6 @@ public struct ContentListFeature { case pagenation - case 컨텐츠_항목_눌렀을때(content: BaseContentItem) case 정렬_버튼_눌렀을때 case dismiss /// - On Appeared @@ -138,8 +137,6 @@ private extension ContentListFeature { /// - View Effect func handleViewAction(_ action: Action.View, state: inout State) -> Effect { switch action { - case .컨텐츠_항목_눌렀을때(let content): - return .send(.delegate(.링크상세(content: content))) case .binding: return .none case .정렬_버튼_눌렀을때: diff --git a/Projects/Feature/FeatureLogin/Sources/Intro/IntroView.swift b/Projects/Feature/FeatureLogin/Sources/Intro/IntroView.swift index 74e865e9..4d0ef886 100644 --- a/Projects/Feature/FeatureLogin/Sources/Intro/IntroView.swift +++ b/Projects/Feature/FeatureLogin/Sources/Intro/IntroView.swift @@ -7,7 +7,6 @@ import SwiftUI import ComposableArchitecture -import FeatureLogin public struct IntroView: View { /// - Properties diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift index 11fcefbf..706cb9f7 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchFeature.swift @@ -100,7 +100,6 @@ public struct PokitSearchFeature { case 즐겨찾기_태그_눌렀을때 case 안읽음_태그_눌렀을때 case 전체_삭제_버튼_눌렀을때 - case 컨텐츠_항목_눌렀을때(content: BaseContentItem) case 정렬_버튼_눌렀을때 case 검색_키보드_엔터_눌렀을때 case 뷰가_나타났을때 @@ -262,9 +261,6 @@ private extension PokitSearchFeature { state.recentSearchTexts.remove(at: predicate) return .send(.async(.최근검색어_갱신_수행)) - case let .컨텐츠_항목_눌렀을때(content): - return .send(.delegate(.linkCardTapped(content: content))) - case .정렬_버튼_눌렀을때: state.isResultAscending.toggle() state.domain.pageable.sort = [ From 72cfdbb6d68aeeb445f13260af714e35d795000f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 1 Dec 2024 16:28:57 +0900 Subject: [PATCH 17/31] =?UTF-8?q?[feat]=20#157=20PokitListButton=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Components/PokitListButton.swift | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 Projects/DSKit/Sources/Components/PokitListButton.swift diff --git a/Projects/DSKit/Sources/Components/PokitListButton.swift b/Projects/DSKit/Sources/Components/PokitListButton.swift new file mode 100644 index 00000000..f6becf72 --- /dev/null +++ b/Projects/DSKit/Sources/Components/PokitListButton.swift @@ -0,0 +1,132 @@ +// +// PokitListButton.swift +// DSKit +// +// Created by 김도형 on 12/1/24. +// + +import SwiftUI + +public struct PokitListButton: View { + @Binding + private var isOn: Bool + + private let title: String + private let type: PokitListButton.ListButtonType + private let action: () -> Void + + public init( + title: String, + type: PokitListButton.ListButtonType, + isOn: Binding = .constant(false), + action: @escaping () -> Void + ) { + self.title = title + self.type = type + self._isOn = isOn + self.action = action + } + + public var body: some View { + Button(action: action) { + label + } + } + + private var label: some View { + VStack(alignment: .leading, spacing: 4) { + HStack { + switch type { + case let .default(icon), + let .bottomSheet(icon), + let .subText(icon, _): + Text(title) + .pokitFont(.b1(.m)) + .foregroundStyle(.pokit(.text(.secondary))) + + Spacer() + + Image(icon) + .resizable() + .frame(width: 24, height: 24) + .foregroundStyle(.pokit(.icon(.primary))) + case .toggle: + Toggle(isOn: $isOn) { + Text(title) + .pokitFont(.b1(.m)) + .foregroundStyle(.pokit(.text(.secondary))) + } + .tint(.pokit(.icon(.brand))) + } + + } + + if case let .subText(_, subeText) = type { + Text(subeText) + .pokitFont(.detail1) + .foregroundStyle(.pokit(.text(.tertiary))) + .lineLimit(1) + } + + if case let .toggle(subeText) = type { + Text(subeText) + .pokitFont(.detail1) + .foregroundStyle(.pokit(.text(.tertiary))) + .lineLimit(1) + } + } + .padding(.horizontal, 24) + .padding(.vertical, 16) + .background(alignment: .bottom) { + if case .bottomSheet = type { + Rectangle() + .fill(.pokit(.border(.tertiary))) + .frame(height: 1) + } + } + } +} + +extension PokitListButton { + public enum ListButtonType { + case `default`(icon: PokitImage) + case bottomSheet(icon: PokitImage) + case subText(icon: PokitImage, subeText: String) + case toggle(subeText: String) + + } +} + +@available(iOS 18.0, *) +#Preview { + @Previewable + @State var isOn: Bool = false + + PokitListButton( + title: "공지사항", + type: .default(icon: .icon(.arrowRight)), + action: { } + ) + + PokitListButton( + title: "공지사항", + type: .bottomSheet(icon: .icon(.edit)), + action: { } + ) + + PokitListButton( + title: "공지사항", + type: .subText( + icon: .icon(.arrowRight), + subeText: "포킷에 저장된 링크가 다른 사용자에게 추천됩니다." + ), + action: { } + ) + + PokitListButton( + title: "공지사항", + type: .toggle(subeText: "포킷에 저장된 링크가 다른 사용자에게 추천됩니다."), + isOn: $isOn, + action: { } + ) +} From 4ae7e46e37adc58db0c43675b1e0a246de9f62a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 1 Dec 2024 16:57:39 +0900 Subject: [PATCH 18/31] =?UTF-8?q?[feat]=20#157=20=EC=BB=A8=ED=85=90?= =?UTF-8?q?=EC=B8=A0=20=EC=83=81=EC=84=B8=20=EB=B3=80=EA=B2=BD=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Components/PokitListButton.swift | 27 ++- .../Components/PokitPartTextArea.swift | 18 +- .../Sources/Components/PokitTextArea.swift | 62 +++--- .../Sources/Foundation/PokitInputStyle.swift | 13 +- .../Sources/Base/BaseContentDetail.swift | 2 +- .../Domain/Sources/Base/BaseContentItem.swift | 2 +- .../ContentDetail/ContentDetailFeature.swift | 101 +++++---- .../ContentDetail/ContentDetailView.swift | 196 ++++++++---------- .../Sources/Extension/View+Extension.swift | 40 ++++ 9 files changed, 256 insertions(+), 205 deletions(-) create mode 100644 Projects/Util/Sources/Extension/View+Extension.swift diff --git a/Projects/DSKit/Sources/Components/PokitListButton.swift b/Projects/DSKit/Sources/Components/PokitListButton.swift index f6becf72..bf1fb69e 100644 --- a/Projects/DSKit/Sources/Components/PokitListButton.swift +++ b/Projects/DSKit/Sources/Components/PokitListButton.swift @@ -37,9 +37,9 @@ public struct PokitListButton: View { VStack(alignment: .leading, spacing: 4) { HStack { switch type { - case let .default(icon), - let .bottomSheet(icon), - let .subText(icon, _): + case let .default(icon, iconColor), + let .bottomSheet(icon, iconColor), + let .subText(icon, iconColor, _): Text(title) .pokitFont(.b1(.m)) .foregroundStyle(.pokit(.text(.secondary))) @@ -49,7 +49,7 @@ public struct PokitListButton: View { Image(icon) .resizable() .frame(width: 24, height: 24) - .foregroundStyle(.pokit(.icon(.primary))) + .foregroundStyle(iconColor) case .toggle: Toggle(isOn: $isOn) { Text(title) @@ -61,7 +61,7 @@ public struct PokitListButton: View { } - if case let .subText(_, subeText) = type { + if case let .subText(_, _, subeText) = type { Text(subeText) .pokitFont(.detail1) .foregroundStyle(.pokit(.text(.tertiary))) @@ -89,9 +89,9 @@ public struct PokitListButton: View { extension PokitListButton { public enum ListButtonType { - case `default`(icon: PokitImage) - case bottomSheet(icon: PokitImage) - case subText(icon: PokitImage, subeText: String) + case `default`(icon: PokitImage, iconColor: Color) + case bottomSheet(icon: PokitImage, iconColor: Color) + case subText(icon: PokitImage, iconColor: Color, subeText: String) case toggle(subeText: String) } @@ -104,13 +104,19 @@ extension PokitListButton { PokitListButton( title: "공지사항", - type: .default(icon: .icon(.arrowRight)), + type: .default( + icon: .icon(.arrowRight), + iconColor: .pokit(.icon(.primary)) + ), action: { } ) PokitListButton( title: "공지사항", - type: .bottomSheet(icon: .icon(.edit)), + type: .bottomSheet( + icon: .icon(.edit), + iconColor: .pokit(.icon(.primary)) + ), action: { } ) @@ -118,6 +124,7 @@ extension PokitListButton { title: "공지사항", type: .subText( icon: .icon(.arrowRight), + iconColor: .pokit(.icon(.primary)), subeText: "포킷에 저장된 링크가 다른 사용자에게 추천됩니다." ), action: { } diff --git a/Projects/DSKit/Sources/Components/PokitPartTextArea.swift b/Projects/DSKit/Sources/Components/PokitPartTextArea.swift index 37f91e25..8f2ec0ac 100644 --- a/Projects/DSKit/Sources/Components/PokitPartTextArea.swift +++ b/Projects/DSKit/Sources/Components/PokitPartTextArea.swift @@ -10,24 +10,26 @@ import SwiftUI public struct PokitPartTextArea: View { @Binding private var text: String - @State private var state: PokitInputStyle.State + @Binding private var state: PokitInputStyle.State private var focusState: FocusState.Binding - + private let baseState: PokitInputStyle.State private let equals: Value private let placeholder: String private let onSubmit: (() -> Void)? public init( text: Binding, - state: PokitInputStyle.State = .default, + state: Binding, + baseState: PokitInputStyle.State = .default, placeholder: String = "내용을 입력해주세요.", focusState: FocusState.Binding, equals: Value, onSubmit: (() -> Void)? = nil ) { self._text = text - self._state = State(initialValue: state) + self._state = state + self.baseState = baseState self.focusState = focusState self.equals = equals self.placeholder = placeholder @@ -47,7 +49,11 @@ public struct PokitPartTextArea: View { .foregroundStyle(.pokit(.text(.primary))) .scrollContentBackground(.hidden) .focused(focusState, equals: equals) - .disabled(state == .disable || state == .readOnly) + .disabled( + state == .disable || + state == .readOnly || + state == .memo(isReadOnly: true) + ) .onSubmit { onSubmit?() } @@ -77,7 +83,7 @@ public struct PokitPartTextArea: View { case .error(message: let message): state = .error(message: message) default: - state = .default + state = baseState } } } diff --git a/Projects/DSKit/Sources/Components/PokitTextArea.swift b/Projects/DSKit/Sources/Components/PokitTextArea.swift index ef8eb530..0a02a2f1 100644 --- a/Projects/DSKit/Sources/Components/PokitTextArea.swift +++ b/Projects/DSKit/Sources/Components/PokitTextArea.swift @@ -15,9 +15,10 @@ public struct PokitTextArea: View { private var focusState: FocusState.Binding + private let baseState: PokitInputStyle.State private let errorMessage: String? private let equals: Value - private let label: String + private let label: String? private let placeholder: String private let info: String? private let maxLetter: Int @@ -25,8 +26,9 @@ public struct PokitTextArea: View { public init( text: Binding, - label: String, + label: String? = nil, state: Binding, + baseState: PokitInputStyle.State = .default, errorMessage: String? = nil, placeholder: String = "내용을 입력해주세요.", info: String? = nil, @@ -38,6 +40,7 @@ public struct PokitTextArea: View { self._text = text self.label = label self._state = state + self.baseState = baseState self.errorMessage = errorMessage self.focusState = focusState self.equals = equals @@ -49,18 +52,20 @@ public struct PokitTextArea: View { public var body: some View { VStack(alignment: .leading, spacing: 0) { - PokitLabel(text: label, size: .large) - .padding(.bottom, 8) + if let label { + PokitLabel(text: label, size: .large) + .padding(.bottom, 8) + } PokitPartTextArea( text: $text, - state: state, + state: $state, + baseState: baseState, placeholder: placeholder, focusState: focusState, equals: equals, onSubmit: onSubmit ) - .onChange(of: focusState.wrappedValue) { onChangedFocuseState($0) } .onChange(of: state) { onChangedState($0) } infoLabel @@ -94,23 +99,31 @@ public struct PokitTextArea: View { Spacer() - Group { - switch state { - case .error: - Text("\(text.count > maxLetter ? maxLetter : text.count)/\(maxLetter)") - .foregroundStyle(.pokit(.text(.error))) - default: - Text("\(text.count > maxLetter ? maxLetter : text.count)/\(maxLetter)") - .foregroundStyle(.pokit(.text(.tertiary))) - } + if state != .memo(isReadOnly: true) && + state != .memo(isReadOnly: false) { + textCount + .pokitBlurReplaceTransition(.pokitDissolve) } - .pokitFont(.detail1) - .contentTransition(.numericText()) - .animation(.pokitDissolve, value: text) } .padding(.top, 4) } + private var textCount: some View { + Group { + switch state { + case .error: + Text("\(text.count > maxLetter ? maxLetter : text.count)/\(maxLetter)") + .foregroundStyle(.pokit(.text(.error))) + default: + Text("\(text.count > maxLetter ? maxLetter : text.count)/\(maxLetter)") + .foregroundStyle(.pokit(.text(.tertiary))) + } + } + .pokitFont(.detail1) + .contentTransition(.numericText()) + .animation(.pokitDissolve, value: text) + } + private func onChangedText(_ newValue: String) { if isMaxLetters { self.text = String(newValue.prefix(maxLetter + 1)) @@ -122,19 +135,6 @@ public struct PokitTextArea: View { state = newValue ? .error(message: "최대 \(maxLetter)자까지 입력가능합니다.") : .active } - private func onChangedFocuseState(_ newValue: Value) { - if newValue == equals { - state = .active - } else { - switch state { - case .error(message: let message): - state = .error(message: message) - default: - state = .default - } - } - } - private func onChangedState(_ newValue: PokitInputStyle.State) { switch newValue { case .error: diff --git a/Projects/DSKit/Sources/Foundation/PokitInputStyle.swift b/Projects/DSKit/Sources/Foundation/PokitInputStyle.swift index 5a39c2e4..41b32aec 100644 --- a/Projects/DSKit/Sources/Foundation/PokitInputStyle.swift +++ b/Projects/DSKit/Sources/Foundation/PokitInputStyle.swift @@ -15,6 +15,7 @@ public enum PokitInputStyle: Equatable { case disable case readOnly case error(message: String) + case memo(isReadOnly: Bool) var infoColor: Color { switch self { @@ -28,23 +29,27 @@ public enum PokitInputStyle: Equatable { var backgroundColor: Color { switch self { case .default, .input, .active, .error: - return .pokit(.bg(.primary)) + return .pokit(.bg(.base)) case .disable: return .pokit(.bg(.disable)) case .readOnly: return .pokit(.bg(.secondary)) + case let .memo(isReadOnly): + return isReadOnly + ? .pokit(.bg(.primary)) + : Color(red: 1, green: 0.96, blue: 0.89) } } var backgroundStrokeColor: Color { switch self { - case .default, .input: + case .input, .memo: return .clear case .active: return .pokit(.border(.brand)) case .disable: return .pokit(.border(.disable)) - case .readOnly: + case .readOnly, .default: return .pokit(.border(.secondary)) case .error: return .pokit(.border(.error)) @@ -53,7 +58,7 @@ public enum PokitInputStyle: Equatable { var iconColor: Color { switch self { - case .default, .readOnly: + case .default, .readOnly, .memo: return .pokit(.icon(.secondary)) case .input, .active: return .pokit(.icon(.primary)) diff --git a/Projects/Domain/Sources/Base/BaseContentDetail.swift b/Projects/Domain/Sources/Base/BaseContentDetail.swift index 9a59096d..8e8fef34 100644 --- a/Projects/Domain/Sources/Base/BaseContentDetail.swift +++ b/Projects/Domain/Sources/Base/BaseContentDetail.swift @@ -12,7 +12,7 @@ public struct BaseContentDetail: Equatable { public let category: BaseCategoryInfo public let title: String public let data: String - public let memo: String + public var memo: String public let createdAt: String public var favorites: Bool? public var alertYn: RemindState diff --git a/Projects/Domain/Sources/Base/BaseContentItem.swift b/Projects/Domain/Sources/Base/BaseContentItem.swift index 93861d2d..99e7fe17 100644 --- a/Projects/Domain/Sources/Base/BaseContentItem.swift +++ b/Projects/Domain/Sources/Base/BaseContentItem.swift @@ -14,7 +14,7 @@ public struct BaseContentItem: Identifiable, Equatable, PokitLinkCardItem, Sorta public let categoryName: String public let categoryId: Int public let title: String - public let memo: String? + public var memo: String? public var thumbNail: String public let data: String public let domain: String diff --git a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift index e6f39e3e..6502e96d 100644 --- a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift +++ b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift @@ -40,12 +40,13 @@ public struct ContentDetailFeature { var contentId: Int? { get { domain.contentId } } - + var memo: String = "" var linkTitle: String? = nil var linkImageURL: String? = nil var showAlert: Bool = false - var showLinkPreview = false var showShareSheet: Bool = false + var memoTextAreaState: PokitInputStyle.State = .memo(isReadOnly: true) + var linkPopup: PokitLinkPopup.PopupType? } /// - Action @@ -71,15 +72,13 @@ public struct ContentDetailFeature { case 경고시트_해제 case 링크_공유_완료되었을때 + case 메모포커스_변경되었을때(Bool) } public enum InnerAction: Equatable { - case linkPreview - case 메타데이터_조회_수행(url: URL) - case 메타데이터_조회_반영(title: String?, imageURL: String?) - case URL_유효성_확인 case 컨텐츠_상세_조회_API_반영(content: BaseContentDetail) case 즐겨찾기_API_반영(Bool) + case 링크팝업_활성화(PokitLinkPopup.PopupType) } public enum AsyncAction: Equatable { @@ -87,6 +86,7 @@ public struct ContentDetailFeature { case 즐겨찾기_API(id: Int) case 즐겨찾기_취소_API(id: Int) case 컨텐츠_삭제_API(id: Int) + case 컨텐츠_수정_API } public enum ScopeAction: Equatable { case 없음 } @@ -129,6 +129,7 @@ public struct ContentDetailFeature { /// - Reducer body public var body: some ReducerOf { + BindingReducer(action: \.view) Reduce(self.core) } } @@ -138,11 +139,14 @@ private extension ContentDetailFeature { func handleViewAction(_ action: Action.View, state: inout State) -> Effect { switch action { case .뷰가_나타났을때: - if let content = state.content { - state.domain.content = content - return .send(.inner(.URL_유효성_확인)) - } else if let id = state.domain.contentId { + /// - 나중에 공유 받은 컨텐츠인지 확인해야함 + state.memoTextAreaState = .memo(isReadOnly: false) + + if let id = state.domain.contentId { return .send(.async(.컨텐츠_상세_조회_API(id: id))) + } else if let content = state.domain.content { + state.memo = content.memo + return .none } else { return .none } @@ -176,47 +180,29 @@ private extension ContentDetailFeature { case .경고시트_해제: state.showAlert = false return .none + case let .메모포커스_변경되었을때(isFocused): + guard + !isFocused, + state.memo != state.domain.content?.memo + else { return .none } + let memo = state.memo + state.domain.content?.memo = memo + return .send(.async(.컨텐츠_수정_API)) } } /// - Inner Effect func handleInnerAction(_ action: Action.InnerAction, state: inout State) -> Effect { switch action { - case .메타데이터_조회_수행(url: let url): - return .run { send in - /// - 링크에 대한 메타데이터의 제목 및 썸네일 항목 파싱 - async let title = swiftSoup.parseOGTitle(url) - async let imageURL = swiftSoup.parseOGImageURL(url) - try await send( - .inner(.메타데이터_조회_반영(title: title, imageURL: imageURL)), - animation: .pokitDissolve - ) - } - case let .메타데이터_조회_반영(title: title, imageURL: imageURL): - state.linkTitle = title - state.linkImageURL = imageURL - return .send(.inner(.linkPreview), animation: .pokitDissolve) - case .URL_유효성_확인: - guard let urlString = state.domain.content?.data, - let url = URL(string: urlString) else { - /// 🚨 Error Case [1]: 올바른 링크가 아닐 때 - state.showLinkPreview = false - state.linkTitle = nil - state.linkImageURL = nil - return .none - } - return .send(.inner(.메타데이터_조회_수행(url: url)), animation: .pokitDissolve) case .컨텐츠_상세_조회_API_반영(content: let content): state.domain.content = content - return .merge( - .send(.delegate(.컨텐츠_조회_완료)), - .send(.inner(.URL_유효성_확인)) - ) + state.memo = state.domain.content?.memo ?? "" + return .send(.delegate(.컨텐츠_조회_완료)) case .즐겨찾기_API_반영(let favorite): state.domain.content?.favorites = favorite return .send(.delegate(.즐겨찾기_갱신_완료)) - case .linkPreview: - state.showLinkPreview = true + case let .링크팝업_활성화(type): + state.linkPopup = type return .none } } @@ -232,12 +218,12 @@ private extension ContentDetailFeature { case .즐겨찾기_API(id: let id): return .run { send in let _ = try await contentClient.즐겨찾기("\(id)") - await send(.inner(.즐겨찾기_API_반영(true))) + await send(.inner(.즐겨찾기_API_반영(true)), animation: .pokitDissolve) } case .즐겨찾기_취소_API(id: let id): return .run { send in try await contentClient.즐겨찾기_취소("\(id)") - await send(.inner(.즐겨찾기_API_반영(false))) + await send(.inner(.즐겨찾기_API_반영(false)), animation: .pokitDissolve) } case .컨텐츠_삭제_API(id: let id): return .run { send in @@ -245,6 +231,37 @@ private extension ContentDetailFeature { await send(.delegate(.컨텐츠_삭제_완료)) await dismiss() } + case .컨텐츠_수정_API: + guard + let content = state.domain.content, + let url = URL(string: content.data) + else { return .none } + return .run { send in + let imageURL = try? await swiftSoup.parseOGImageURL(url) + + let request = ContentBaseRequest( + data: content.data, + title: content.title, + categoryId: content.category.categoryId, + memo: content.memo, + alertYn: content.alertYn.rawValue, + thumbNail: imageURL + ) + let _ = try await contentClient.컨텐츠_수정( + contentId: "\(content.id)", + model: request + ) + await send( + .inner(.링크팝업_활성화(.success(title: "메모 수정 완료"))), + animation: .pokitSpring + ) + } catch: { error, send in + guard let errorResponse = error as? ErrorResponse else { return } + await send( + .inner(.링크팝업_활성화(.error(title: errorResponse.message))), + animation: .pokitSpring + ) + } } } diff --git a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailView.swift b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailView.swift index 6cbca8ea..95319266 100644 --- a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailView.swift +++ b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailView.swift @@ -15,7 +15,8 @@ public struct ContentDetailView: View { /// - Properties @Perception.Bindable public var store: StoreOf - + @FocusState + private var isFocused: Bool /// - Initializer public init(store: StoreOf) { self.store = store @@ -26,17 +27,19 @@ public extension ContentDetailView { var body: some View { WithPerceptionTracking { VStack(spacing: 0) { - if let content = store.content { + if let content = store.content, + let favorites = content.favorites { title(content: content) ScrollView { - VStack { - contentLinkPreview(content: content) - .padding(.vertical, 24) + VStack(spacing: 0) { + contentMemo + + Divider() + .foregroundStyle(.pokit(.border(.tertiary))) + + bottomList(favorites: favorites) } } - .overlay(alignment: .bottom) { - bottomToolbar(content: content) - } } else { PokitLoading() } @@ -47,7 +50,14 @@ public extension ContentDetailView { .pokitPresentationBackground() .pokitPresentationCornerRadius() .presentationDragIndicator(.visible) - .presentationDetents([.medium, .large]) + .presentationDetents([.height(588), .large]) + .overlay(alignment: .bottom) { + if store.linkPopup != nil { + PokitLinkPopup(type: $store.linkPopup) + } + } + .dismissKeyboard(focused: $isFocused) + .onChange(of: isFocused) { send(.메모포커스_변경되었을때($0)) } .sheet(isPresented: $store.showAlert) { PokitAlert( "링크를 정말 삭제하시겠습니까?", @@ -124,113 +134,79 @@ private extension ContentDetailView { } } - @ViewBuilder - func contentLinkPreview(content: BaseContentDetail) -> some View { - VStack(spacing: 16) { - if store.showLinkPreview { - PokitLinkPreview( - title: store.linkTitle ?? content.title, - url: content.data, - imageURL: store.linkImageURL ?? "https://pokit-storage.s3.ap-northeast-2.amazonaws.com/logo/pokit.png" - ) - .pokitBlurReplaceTransition(.pokitDissolve) - } - - contentMemo(content: content) - } - .padding(.horizontal, 20) - } - - @ViewBuilder - func contentMemo(content: BaseContentDetail) -> some View { - let isEmpty = content.memo.isEmpty - - HStack { - VStack { - Group { - if isEmpty { - Text("메모를 작성해보세요.") - .foregroundStyle(.pokit(.text(.tertiary))) - } else { - Text(content.memo) - .foregroundStyle(.pokit(.text(.primary))) - } - } - .pokitFont(.b3(.r)) - .multilineTextAlignment(.leading) - + var contentMemo: some View { + VStack(spacing: 12) { + HStack(alignment: .bottom) { + Text("메모") + .pokitFont(.b1(.m)) + .foregroundStyle(.pokit(.text(.primary))) + .padding(.top, 16) + Spacer() + + Image(.icon(.memo)) + .resizable() + .frame(width: 24, height: 24) + .foregroundStyle(.pokit(.border(.primary))) } - .padding(16) - - Spacer() - } - .frame(minHeight: 132) - .background { - RoundedRectangle(cornerRadius: 8, style: .continuous) - .fill(Color(red: 1, green: 0.96, blue: 0.89)) - } - } - - @ViewBuilder - func favorite(favorites: Bool) -> some View { - Button(action: { send(.즐겨찾기_버튼_눌렀을때, animation: .pokitDissolve) }) { - Image(favorites ? .icon(.starFill) : .icon(.starFill)) - .resizable() - .scaledToFit() - .foregroundStyle(.pokit(.icon(favorites ? .brand : .tertiary))) - .frame(width: 24, height: 24) - } - } - - @ViewBuilder - func bottomToolbar(content: BaseContentDetail) -> some View { - HStack(spacing: 14) { - if let favorites = content.favorites { - favorite(favorites: favorites) - } - - Spacer() - - Group { - toolbarButton( - .icon(.share), - action: { send(.공유_버튼_눌렀을때) } - ) - - toolbarButton( - .icon(.edit), - action: { send(.수정_버튼_눌렀을때) } - ) - - toolbarButton( - .icon(.trash), - action: { send(.삭제_버튼_눌렀을때) } - ) - } - .disabled(store.contentId == nil) - .opacity(store.contentId == nil ? 0 : 1) - } - .padding(.top, 12) - .padding(.bottom, 40) - .padding(.horizontal, 16) - .background(.pokit(.bg(.base))) - .overlay(alignment: .top) { - Divider() - .foregroundStyle(.pokit(.border(.tertiary))) + + PokitTextArea( + text: $store.memo, + state: $store.memoTextAreaState, + baseState: .memo(isReadOnly: false), + placeholder: "메모를 입력해주세요.", + maxLetter: 100, + focusState: $isFocused, + equals: true + ) + .frame(minHeight: 132) } + .padding(.bottom, 24) + .padding(.horizontal, 20) } @ViewBuilder - func toolbarButton( - _ icon: PokitImage, - action: @escaping () -> Void - ) -> some View { - Button(action: action) { - Image(icon) - .resizable() - .frame(width: 24, height: 24) - .foregroundStyle(.pokit(.icon(.secondary))) + func bottomList(favorites: Bool) -> some View { + VStack(spacing: 0) { + PokitListButton( + title: "즐겨찾기", + type: .bottomSheet( + icon: favorites + ? .icon(.starFill) + : .icon(.star), + iconColor: favorites + ? .pokit(.icon(.brand)) + : .pokit(.icon(.primary)) + ), + action: { send(.즐겨찾기_버튼_눌렀을때) } + ) + + PokitListButton( + title: "공유하기", + type: .bottomSheet( + icon: .icon(.share), + iconColor: .pokit(.icon(.primary)) + ), + action: { send(.공유_버튼_눌렀을때) } + ) + + PokitListButton( + title: "수정하기", + type: .bottomSheet( + icon: .icon(.edit), + iconColor: .pokit(.icon(.primary)) + ), + action: { send(.수정_버튼_눌렀을때) } + ) + + PokitListButton( + title: "삭제하기", + type: .bottomSheet( + icon: .icon(.trash), + iconColor: .pokit(.icon(.primary)) + ), + action: { send(.삭제_버튼_눌렀을때) } + ) } } } diff --git a/Projects/Util/Sources/Extension/View+Extension.swift b/Projects/Util/Sources/Extension/View+Extension.swift new file mode 100644 index 00000000..d0e7c9b0 --- /dev/null +++ b/Projects/Util/Sources/Extension/View+Extension.swift @@ -0,0 +1,40 @@ +// +// View+Extension.swift +// Util +// +// Created by 김도형 on 12/1/24. +// + +import SwiftUI + +extension View { + public func dismissKeyboard( + focused: FocusState.Binding + ) -> some View { + self + .overlay { + if focused.wrappedValue { + Color.clear + .contentShape(Rectangle()) + .onTapGesture { + focused.wrappedValue = false + } + } + } + } + + public func dismissKeyboard( + focused: FocusState.Binding + ) -> some View { + self + .overlay { + if focused.wrappedValue != nil { + Color.clear + .contentShape(Rectangle()) + .onTapGesture { + focused.wrappedValue = nil + } + } + } + } +} From e88aeaa664e535d27bc48d49f01cac5f1ce05be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:05:38 +0900 Subject: [PATCH 19/31] =?UTF-8?q?[feat]=20#157=20ContentCard=EC=97=90=20?= =?UTF-8?q?=EC=A6=90=EA=B2=A8=EC=B0=BE=EA=B8=B0=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Domain/Sources/Base/BaseContentItem.swift | 2 +- .../ContentCard/ContentCardFeature.swift | 27 +++++++++++++++++++ .../Sources/ContentCard/ContentCardView.swift | 3 ++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Projects/Domain/Sources/Base/BaseContentItem.swift b/Projects/Domain/Sources/Base/BaseContentItem.swift index 99e7fe17..d630964d 100644 --- a/Projects/Domain/Sources/Base/BaseContentItem.swift +++ b/Projects/Domain/Sources/Base/BaseContentItem.swift @@ -20,7 +20,7 @@ public struct BaseContentItem: Identifiable, Equatable, PokitLinkCardItem, Sorta public let domain: String public let createdAt: String public let isRead: Bool? - public let isFavorite: Bool? + public var isFavorite: Bool? public init( id: Int, diff --git a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift index 9f4ebf66..00f8286e 100644 --- a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift +++ b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift @@ -9,6 +9,7 @@ import Foundation import ComposableArchitecture import Domain import CoreKit +import DSKit import Util @Reducer @@ -18,6 +19,8 @@ public struct ContentCardFeature { private var swiftSoupClient @Dependency(\.openURL) private var openURL + @Dependency(ContentClient.self) + private var contentClient /// - State @ObservableState public struct State: Equatable, Identifiable { @@ -41,15 +44,19 @@ public struct ContentCardFeature { public enum View: Equatable { case 컨텐츠_항목_눌렀을때 case 컨텐츠_항목_케밥_버튼_눌렀을때 + case 즐겨찾기_버튼_눌렀을때 case 메타데이터_조회 } public enum InnerAction: Equatable { case 메타데이터_조회_수행_반영(String) + case 즐겨찾기_API_반영(Bool) } public enum AsyncAction: Equatable { case 메타데이터_조회_수행 + case 즐겨찾기_API + case 즐겨찾기_취소_API } public enum ScopeAction: Equatable { case doNothing } @@ -106,6 +113,13 @@ private extension ContentCardFeature { return .send(.delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content: state.content))) case .메타데이터_조회: return .send(.async(.메타데이터_조회_수행)) + case .즐겨찾기_버튼_눌렀을때: + guard let isFavorite = state.content.isFavorite else { + return .none + } + return isFavorite + ? .send(.async(.즐겨찾기_취소_API)) + : .send(.async(.즐겨찾기_API)) } } @@ -115,6 +129,9 @@ private extension ContentCardFeature { case let .메타데이터_조회_수행_반영(imageURL): state.content.thumbNail = imageURL return .none + case .즐겨찾기_API_반영(let favorite): + state.content.isFavorite = favorite + return .none } } @@ -130,6 +147,16 @@ private extension ContentCardFeature { guard let imageURL else { return } await send(.inner(.메타데이터_조회_수행_반영(imageURL))) } + case .즐겨찾기_API: + return .run { [id = state.content.id] send in + let _ = try await contentClient.즐겨찾기("\(id)") + await send(.inner(.즐겨찾기_API_반영(true)), animation: .pokitDissolve) + } + case .즐겨찾기_취소_API: + return .run { [id = state.content.id] send in + try await contentClient.즐겨찾기_취소("\(id)") + await send(.inner(.즐겨찾기_API_반영(false)), animation: .pokitDissolve) + } } } diff --git a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardView.swift b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardView.swift index f871f86c..a3b8cca2 100644 --- a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardView.swift +++ b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardView.swift @@ -43,7 +43,8 @@ public extension ContentCardView { type: type, action: { send(.컨텐츠_항목_눌렀을때) }, kebabAction: { send(.컨텐츠_항목_케밥_버튼_눌렀을때) }, - fetchMetaData: { send(.메타데이터_조회) } + fetchMetaData: { send(.메타데이터_조회) }, + favoriteAction: { send(.즐겨찾기_버튼_눌렀을때) } ) } } From 41bccb19337d957b00a0bb8ceb72890b869730e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:36:20 +0900 Subject: [PATCH 20/31] =?UTF-8?q?[feat]=20#157=20=EC=9E=AC=ED=8C=8C?= =?UTF-8?q?=EC=8B=B1=ED=95=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/DTO/Content/ThumbnailRequest.swift | 16 ++++++++++++++++ .../Network/Content/ContentClient+LiveKey.swift | 5 +++++ .../Network/Content/ContentClient+TestKey.swift | 3 ++- .../Data/Network/Content/ContentClient.swift | 4 ++++ .../Data/Network/Content/ContentEndpoint.swift | 8 +++++++- .../Sources/ContentCard/ContentCardFeature.swift | 12 +++++++++++- .../Sources/Remind/RemindFeature.swift | 16 +++++++++++++--- 7 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 Projects/CoreKit/Sources/Data/DTO/Content/ThumbnailRequest.swift diff --git a/Projects/CoreKit/Sources/Data/DTO/Content/ThumbnailRequest.swift b/Projects/CoreKit/Sources/Data/DTO/Content/ThumbnailRequest.swift new file mode 100644 index 00000000..e44ff24e --- /dev/null +++ b/Projects/CoreKit/Sources/Data/DTO/Content/ThumbnailRequest.swift @@ -0,0 +1,16 @@ +// +// ThumbnailRequest.swift +// CoreKit +// +// Created by 김도형 on 12/1/24. +// + +import Foundation + +public struct ThumbnailRequest: Encodable { + private let thumbnail: String + + public init(thumbnail: String) { + self.thumbnail = thumbnail + } +} diff --git a/Projects/CoreKit/Sources/Data/Network/Content/ContentClient+LiveKey.swift b/Projects/CoreKit/Sources/Data/Network/Content/ContentClient+LiveKey.swift index d5027625..ee6d1954 100644 --- a/Projects/CoreKit/Sources/Data/Network/Content/ContentClient+LiveKey.swift +++ b/Projects/CoreKit/Sources/Data/Network/Content/ContentClient+LiveKey.swift @@ -50,6 +50,11 @@ extension ContentClient: DependencyKey { condition: condition ) ) + }, + 썸네일_수정: { id, model in + try await provider.requestNoBody( + .썸네일_수정(contentId: id, model: model) + ) } ) }() diff --git a/Projects/CoreKit/Sources/Data/Network/Content/ContentClient+TestKey.swift b/Projects/CoreKit/Sources/Data/Network/Content/ContentClient+TestKey.swift index 7b22cf86..ab5031b2 100644 --- a/Projects/CoreKit/Sources/Data/Network/Content/ContentClient+TestKey.swift +++ b/Projects/CoreKit/Sources/Data/Network/Content/ContentClient+TestKey.swift @@ -17,7 +17,8 @@ extension ContentClient: TestDependencyKey { 즐겨찾기_취소: { _ in }, 카테고리_내_컨텐츠_목록_조회: { _, _, _ in .mock }, 미분류_카테고리_컨텐츠_조회: { _ in .mock }, - 컨텐츠_검색: { _, _ in .mock } + 컨텐츠_검색: { _, _ in .mock }, + 썸네일_수정: { _, _ in } ) }() } diff --git a/Projects/CoreKit/Sources/Data/Network/Content/ContentClient.swift b/Projects/CoreKit/Sources/Data/Network/Content/ContentClient.swift index c7210f37..62de49d9 100644 --- a/Projects/CoreKit/Sources/Data/Network/Content/ContentClient.swift +++ b/Projects/CoreKit/Sources/Data/Network/Content/ContentClient.swift @@ -40,5 +40,9 @@ public struct ContentClient { _ pageable: BasePageableRequest, _ condition: BaseConditionRequest ) async throws -> ContentListInquiryResponse + public var 썸네일_수정: @Sendable ( + _ contentId: String, + _ model: ThumbnailRequest + ) async throws -> Void } diff --git a/Projects/CoreKit/Sources/Data/Network/Content/ContentEndpoint.swift b/Projects/CoreKit/Sources/Data/Network/Content/ContentEndpoint.swift index 593fd99a..0c1c5769 100644 --- a/Projects/CoreKit/Sources/Data/Network/Content/ContentEndpoint.swift +++ b/Projects/CoreKit/Sources/Data/Network/Content/ContentEndpoint.swift @@ -27,6 +27,7 @@ public enum ContentEndpoint { pageable: BasePageableRequest, condition: BaseConditionRequest ) + case 썸네일_수정(contentId: String, model: ThumbnailRequest) } extension ContentEndpoint: TargetType { @@ -54,6 +55,8 @@ extension ContentEndpoint: TargetType { return "/uncategorized" case .컨텐츠_검색: return "" + case let .썸네일_수정(contentId, _): + return "/thumbnail/\(contentId)" } } @@ -68,7 +71,8 @@ extension ContentEndpoint: TargetType { .컨텐츠_추가: return .post - case .컨텐츠_수정: + case .컨텐츠_수정, + .썸네일_수정: return .patch case .카태고리_내_컨텐츠_목록_조회, @@ -129,6 +133,8 @@ extension ContentEndpoint: TargetType { ], encoding: URLEncoding.default ) + case let .썸네일_수정(_, model): + return .requestJSONEncodable(model) } } diff --git a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift index 00f8286e..92ad2fcb 100644 --- a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift +++ b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift @@ -57,6 +57,7 @@ public struct ContentCardFeature { case 메타데이터_조회_수행 case 즐겨찾기_API case 즐겨찾기_취소_API + case 썸네일_수정_API } public enum ScopeAction: Equatable { case doNothing } @@ -128,7 +129,7 @@ private extension ContentCardFeature { switch action { case let .메타데이터_조회_수행_반영(imageURL): state.content.thumbNail = imageURL - return .none + return .send(.async(.썸네일_수정_API)) case .즐겨찾기_API_반영(let favorite): state.content.isFavorite = favorite return .none @@ -157,6 +158,15 @@ private extension ContentCardFeature { try await contentClient.즐겨찾기_취소("\(id)") await send(.inner(.즐겨찾기_API_반영(false)), animation: .pokitDissolve) } + case .썸네일_수정_API: + return .run { [content = state.content] _ in + let request = ThumbnailRequest(thumbnail: content.thumbNail) + + try await contentClient.썸네일_수정( + contentId: "\(content.id)", + model: request + ) + } } } diff --git a/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift b/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift index 51e5129d..64bd7235 100644 --- a/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift +++ b/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift @@ -93,6 +93,7 @@ public struct RemindFeature { case 오늘의_리마인드_조회_API case 읽지않음_컨텐츠_조회_API case 즐겨찾기_링크모음_조회_API + case 썸네일_수정_API(imageURL: String, contentId: Int) case 즐겨찾기_이미지_조회_수행(contentId: Int) case 읽지않음_이미지_조회_수행(contentId: Int) case 리마인드_이미지_조회_수행(contentId: Int) @@ -185,19 +186,19 @@ private extension RemindFeature { content?.thumbNail = imageURL guard let content else { return .none } state.domain.favoriteList.data?.insert(content, at: index) - return .none + return .send(.async(.썸네일_수정_API(imageURL: imageURL, contentId: content.id))) case let .읽지않음_이미지_조회_수행_반영(imageURL, index): var content = state.domain.unreadList.data?.remove(at: index) content?.thumbNail = imageURL guard let content else { return .none } state.domain.unreadList.data?.insert(content, at: index) - return .none + return .send(.async(.썸네일_수정_API(imageURL: imageURL, contentId: content.id))) case let .리마인드_이미지_조회_수행_반영(imageURL, index): var content = state.domain.recommendedList?.remove(at: index) content?.thumbNail = imageURL guard let content else { return .none } state.domain.recommendedList?.insert(content, at: index) - return .none + return .send(.async(.썸네일_수정_API(imageURL: imageURL, contentId: content.id))) } } /// - Async Effect @@ -276,6 +277,15 @@ private extension RemindFeature { index: index ))) } + case let .썸네일_수정_API(imageURL, contentId): + return .run { send in + let request = ThumbnailRequest(thumbnail: imageURL) + + try await contentClient.썸네일_수정( + contentId: "\(contentId)", + model: request + ) + } } } /// - Scope Effect From b761afbbe403cc2c5113e120bcc93e306ea9c007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:43:58 +0900 Subject: [PATCH 21/31] =?UTF-8?q?[feat]=20#157=20=ED=8F=AC=ED=82=B7?= =?UTF-8?q?=EC=97=90=20=EC=BB=A8=ED=85=90=EC=B8=A0=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EB=B3=80=EA=B2=BD=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/PokitRootFeature.swift | 112 +++++------------- .../FeaturePokit/Sources/PokitRootView.swift | 9 -- 2 files changed, 27 insertions(+), 94 deletions(-) diff --git a/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift b/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift index c632fbaf..059db362 100644 --- a/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift +++ b/Projects/Feature/FeaturePokit/Sources/PokitRootFeature.swift @@ -40,8 +40,6 @@ public struct PokitRootFeature { var contents: IdentifiedArrayOf = [] var selectedKebobItem: BaseCategoryItem? - var selectedUnclassifiedItem: BaseContentItem? - var shareSheetItem: BaseContentItem? = nil var isKebobSheetPresented: Bool = false var isPokitDeleteSheetPresented: Bool = false @@ -71,12 +69,10 @@ public struct PokitRootFeature { case 필터_버튼_눌렀을때(PokitRootFilterType.Folder) case 분류_버튼_눌렀을때 case 케밥_버튼_눌렀을때(BaseCategoryItem) - case 미분류_케밥_버튼_눌렀을때(BaseContentItem) case 포킷추가_버튼_눌렀을때 case 링크추가_버튼_눌렀을때 case 카테고리_눌렀을때(BaseCategoryItem) case 컨텐츠_항목_눌렀을때(BaseContentItem) - case 링크_공유_완료되었을때 case 뷰가_나타났을때 case 페이지_로딩중일때 } @@ -105,7 +101,6 @@ public struct PokitRootFeature { case 미분류_카테고리_조회_API case 미분류_카테고리_페이징_조회_API case 미분류_카테고리_페이징_재조회_API - case 미분류_카테고리_컨텐츠_삭제_API(contentId: Int) } public enum ScopeAction { @@ -210,10 +205,6 @@ private extension PokitRootFeature { case .케밥_버튼_눌렀을때(let selectedItem): state.selectedKebobItem = selectedItem return .run { send in await send(.inner(.카테고리_시트_활성화(true))) } - - case .미분류_케밥_버튼_눌렀을때(let selectedItem): - state.selectedUnclassifiedItem = selectedItem - return .run { send in await send(.inner(.카테고리_시트_활성화(true))) } case .포킷추가_버튼_눌렀을때: return .run { send in await send(.delegate(.포킷추가_버튼_눌렀을때)) } @@ -255,10 +246,6 @@ private extension PokitRootFeature { default: return .none } - - case .링크_공유_완료되었을때: - state.shareSheetItem = nil - return .none } } @@ -449,13 +436,6 @@ private extension PokitRootFeature { guard let categoryItems else { return } await send(.inner(.카테고리_조회_API_반영(categoryList: categoryItems)), animation: .pokitSpring) } - - case let .미분류_카테고리_컨텐츠_삭제_API(contentId): - return .run { send in - let _ = try await contentClient.컨텐츠_삭제("\(contentId)") - await send(.inner(.미분류_카테고리_컨텐츠_삭제_API_반영(contentId: contentId)), animation: .pokitSpring) - } - } } @@ -465,51 +445,26 @@ private extension PokitRootFeature { /// - Kebob BottomSheet Delegate case .bottomSheet(.shareCellButtonTapped): /// Todo: 공유하기 - switch state.folderType { - case .folder(.미분류): - guard let selectedItem = state.selectedUnclassifiedItem else { - /// 🚨 Error Case [1]: 항목을 공유하려는데 항목이 없을 때 - return .none - } - state.isKebobSheetPresented = false - state.shareSheetItem = selectedItem + guard let selectedItem = state.selectedKebobItem else { + /// 🚨 Error Case [1]: 항목을 공유하려는데 항목이 없을 때 return .none - case .folder(.포킷): - guard let selectedItem = state.selectedKebobItem else { - /// 🚨 Error Case [1]: 항목을 공유하려는데 항목이 없을 때 - return .none - } - kakaoShareClient.카테고리_카카오톡_공유( - CategoryKaKaoShareModel( - categoryName: selectedItem.categoryName, - categoryId: selectedItem.id, - imageURL: selectedItem.categoryImage.imageURL - ) - ) - state.isKebobSheetPresented = false - return .none - - default: return .none } + kakaoShareClient.카테고리_카카오톡_공유( + CategoryKaKaoShareModel( + categoryName: selectedItem.categoryName, + categoryId: selectedItem.id, + imageURL: selectedItem.categoryImage.imageURL + ) + ) + state.isKebobSheetPresented = false + return .none case .bottomSheet(.editCellButtonTapped): - switch state.folderType { - case .folder(.미분류): - state.isKebobSheetPresented = false - return .run { [item = state.selectedUnclassifiedItem] send in - guard let item else { return } - await send(.delegate(.링크수정하기(id: item.id))) - } - - case .folder(.포킷): - /// [1] 케밥을 종료 - state.isKebobSheetPresented = false - /// [2] 수정하기로 이동 - return .run { [item = state.selectedKebobItem] send in - guard let item else { return } - await send(.delegate(.수정하기(item))) - } - default: return .none + state.isKebobSheetPresented = false + /// [2] 수정하기로 이동 + return .run { [item = state.selectedKebobItem] send in + guard let item else { return } + await send(.delegate(.수정하기(item))) } case .bottomSheet(.deleteCellButtonTapped): @@ -524,33 +479,20 @@ private extension PokitRootFeature { return .none case .deleteBottomSheet(.deleteButtonTapped): - switch state.folderType { - case .folder(.미분류): - guard let selectedItem = state.selectedUnclassifiedItem else { - /// 🚨 Error Case [1]: 항목을 삭제하려는데 항목이 없을 때 - return .none - } - return .send(.async(.미분류_카테고리_컨텐츠_삭제_API(contentId: selectedItem.id)), animation: .pokitSpring) - - case .folder(.포킷): - guard let selectedItem = state.selectedKebobItem else { - /// 🚨 Error Case [1]: 항목을 삭제하려는데 항목이 없을 때 - return .none - } - guard let index = state.domain.categoryList.data?.firstIndex(of: selectedItem) else { - return .none - } - state.domain.categoryList.data?.remove(at: index) - state.isPokitDeleteSheetPresented = false - - return .run { send in await send(.async(.카테고리_삭제_API(categoryId: selectedItem.id))) } - - default: return .none + guard let selectedItem = state.selectedKebobItem else { + /// 🚨 Error Case [1]: 항목을 삭제하려는데 항목이 없을 때 + return .none + } + guard let index = state.domain.categoryList.data?.firstIndex(of: selectedItem) else { + return .none } + state.domain.categoryList.data?.remove(at: index) + state.isPokitDeleteSheetPresented = false + + return .run { send in await send(.async(.카테고리_삭제_API(categoryId: selectedItem.id))) } case let .contents(.element(id: _, action: .delegate(.컨텐츠_항목_케밥_버튼_눌렀을때(content)))): - state.selectedUnclassifiedItem = content - return .send(.inner(.카테고리_시트_활성화(true))) + return .send(.delegate(.contentDetailTapped(content))) case .contents: return .none diff --git a/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift b/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift index 741c383e..e1299d0b 100644 --- a/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift +++ b/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift @@ -46,15 +46,6 @@ public extension PokitRootView { delegateSend: { store.send(.scope(.bottomSheet($0))) } ) } - .sheet(item: $store.shareSheetItem) { content in - if let shareURL = URL(string: content.data) { - PokitShareSheet( - items: [shareURL], - completion: { send(.링크_공유_완료되었을때) } - ) - .presentationDetents([.medium, .large]) - } - } .sheet(isPresented: $store.isPokitDeleteSheetPresented) { PokitDeleteBottomSheet( type: store.folderType == .folder(.포킷) From afd78a528eb20e6a8a80117fcc670072c4dec741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:36:45 +0900 Subject: [PATCH 22/31] =?UTF-8?q?[fix]=20#157=20ContentCardFeature=20?= =?UTF-8?q?=EC=8A=A4=EB=A0=88=EB=93=9C=20=EC=95=88=EC=A0=95=EC=84=B1=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FeatureCategoryDetail/Sources/CategoryDetailView.swift | 2 +- .../Sources/ContentList/ContentListView.swift | 2 +- Projects/Feature/FeaturePokit/Sources/PokitRootView.swift | 2 +- .../Feature/FeatureSetting/Sources/Search/PokitSearchView.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift index 3c188c83..6786bf67 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift @@ -140,7 +140,7 @@ private extension CategoryDetailView { ScrollView(showsIndicators: false) { LazyVStack(spacing: 0) { ForEach( - store.scope(state: \.contents, action: \.contents) + Array(store.scope(state: \.contents, action: \.contents)) ) { store in let isFirst = store.state.id == self.store.contents.first?.id let isLast = store.state.id == self.store.contents.last?.id diff --git a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift index 2bec1245..72d176b1 100644 --- a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift +++ b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift @@ -70,7 +70,7 @@ private extension ContentListView { ScrollView { LazyVStack(spacing: 0) { ForEach( - store.scope(state: \.contents, action: \.contents) + Array(store.scope(state: \.contents, action: \.contents)) ) { store in let isFirst = store.state.id == self.store.contents.first?.id let isLast = store.state.id == self.store.contents.last?.id diff --git a/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift b/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift index e1299d0b..41cdffbb 100644 --- a/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift +++ b/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift @@ -181,7 +181,7 @@ private extension PokitRootView { ScrollView { LazyVStack(spacing: 0) { ForEach( - store.scope(state: \.contents, action: \.contents) + Array(store.scope(state: \.contents, action: \.contents)) ) { store in let isFirst = store.state.id == self.store.contents.first?.id let isLast = store.state.id == self.store.contents.last?.id diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift index c641d9ea..1592ee72 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift @@ -277,7 +277,7 @@ private extension PokitSearchView { ScrollView { LazyVStack(spacing: 0) { ForEach( - store.scope(state: \.contents, action: \.contents) + Array(store.scope(state: \.contents, action: \.contents)) ) { store in let isFirst = store.state.id == self.store.contents.first?.id let isLast = store.state.id == self.store.contents.last?.id From c5d1a4084ea0925067a2dc6a620c77625275c02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:19:50 +0900 Subject: [PATCH 23/31] =?UTF-8?q?[fix]=20#157=20ContentCardView=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DSKit/Sources/Components/PokitLinkCard.swift | 3 ++- .../FeatureCategoryDetail/Sources/CategoryDetailView.swift | 1 + .../Sources/CategorySharing/CategorySharingView.swift | 1 + .../Sources/ContentList/ContentListView.swift | 1 + Projects/Feature/FeaturePokit/Sources/PokitRootView.swift | 1 + .../FeatureSetting/Sources/Search/PokitSearchView.swift | 1 + 6 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Projects/DSKit/Sources/Components/PokitLinkCard.swift b/Projects/DSKit/Sources/Components/PokitLinkCard.swift index ccfa53c3..a77c7e54 100644 --- a/Projects/DSKit/Sources/Components/PokitLinkCard.swift +++ b/Projects/DSKit/Sources/Components/PokitLinkCard.swift @@ -92,7 +92,8 @@ public struct PokitLinkCard: View { } } .overlay(alignment: .bottomLeading) { - if let isFavorite = link.isFavorite { + if case .linkList = type, + let isFavorite = link.isFavorite { PokitBookmark( state: isFavorite ? .active : .default, action: { favoriteAction?() } diff --git a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift index 6786bf67..5c20db9f 100644 --- a/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift +++ b/Projects/Feature/FeatureCategoryDetail/Sources/CategoryDetailView.swift @@ -147,6 +147,7 @@ private extension CategoryDetailView { ContentCardView( store: store, + type: .linkList, isFirst: isFirst, isLast: isLast ) diff --git a/Projects/Feature/FeatureCategorySharing/Sources/CategorySharing/CategorySharingView.swift b/Projects/Feature/FeatureCategorySharing/Sources/CategorySharing/CategorySharingView.swift index 25b8502c..d1bb4240 100644 --- a/Projects/Feature/FeatureCategorySharing/Sources/CategorySharing/CategorySharingView.swift +++ b/Projects/Feature/FeatureCategorySharing/Sources/CategorySharing/CategorySharingView.swift @@ -107,6 +107,7 @@ private extension CategorySharingView { ContentCardView( store: store, + type: .linkList, isFirst: isFirst, isLast: isLast ) diff --git a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift index 72d176b1..f468d1bd 100644 --- a/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift +++ b/Projects/Feature/FeatureContentList/Sources/ContentList/ContentListView.swift @@ -77,6 +77,7 @@ private extension ContentListView { ContentCardView( store: store, + type: .linkList, isFirst: isFirst, isLast: isLast ) diff --git a/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift b/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift index 41cdffbb..6766234f 100644 --- a/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift +++ b/Projects/Feature/FeaturePokit/Sources/PokitRootView.swift @@ -188,6 +188,7 @@ private extension PokitRootView { ContentCardView( store: store, + type: .linkList, isFirst: isFirst, isLast: isLast ) diff --git a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift index 1592ee72..2984dd3f 100644 --- a/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Search/PokitSearchView.swift @@ -284,6 +284,7 @@ private extension PokitSearchView { ContentCardView( store: store, + type: .linkList, isFirst: isFirst, isLast: isLast ) From 75f54da2af457965e68d76df9a9aabdd8da376a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:20:10 +0900 Subject: [PATCH 24/31] =?UTF-8?q?[feat]=20#157=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=88=98=EC=A0=95/=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=8F=AC=ED=82=B7=EB=AA=85=20=EC=A7=80?= =?UTF-8?q?=EC=9A=B0=EA=B8=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/PokitCategorySettingFeature.swift | 4 ++++ .../Sources/PokitCategorySettingView.swift | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingFeature.swift b/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingFeature.swift index b6413230..4113db9f 100644 --- a/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingFeature.swift +++ b/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingFeature.swift @@ -87,6 +87,7 @@ public struct PokitCategorySettingFeature { case 프로필_설정_버튼_눌렀을때 case 저장_버튼_눌렀을때 case 뷰가_나타났을때 + case 포킷명지우기_버튼_눌렀을때 } public enum InnerAction: Equatable { @@ -215,6 +216,9 @@ private extension PokitCategorySettingFeature { .send(.async(.프로필_목록_조회_API)), .send(.async(.클립보드_감지)) ) + case .포킷명지우기_버튼_눌렀을때: + state.domain.categoryName = "" + return .none } } diff --git a/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift b/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift index b3d47829..b7c4d50f 100644 --- a/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift +++ b/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift @@ -126,9 +126,10 @@ private extension PokitCategorySettingView { text: $store.categoryName, type: .iconR( icon: .icon(.x), - action: { } + action: { send(.포킷명지우기_버튼_눌렀을때) } ), - shape: .rectangle, state: $store.pokitNameTextInpuState, + shape: .rectangle, + state: $store.pokitNameTextInpuState, placeholder: "포킷명을 입력해주세요.", maxLetter: 10, focusState: $isFocused, From e235db4f0fcf94e103ff1a4155e853e6e644701f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:26:39 +0900 Subject: [PATCH 25/31] =?UTF-8?q?[fix]=20#157=20=EB=B9=88=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=8B=9C=20=EC=A7=80=EC=9A=B0=EA=B8=B0=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DSKit/Sources/Components/PokitTextInput.swift | 2 ++ .../Sources/PokitCategorySettingView.swift | 2 +- .../Sources/ContentSetting/ContentSettingView.swift | 4 ++-- .../Sources/Setting/NickNameSetting/NickNameSettingView.swift | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Projects/DSKit/Sources/Components/PokitTextInput.swift b/Projects/DSKit/Sources/Components/PokitTextInput.swift index 1418a71d..23875fcd 100644 --- a/Projects/DSKit/Sources/Components/PokitTextInput.swift +++ b/Projects/DSKit/Sources/Components/PokitTextInput.swift @@ -60,12 +60,14 @@ public struct PokitTextInput: View { HStack(spacing: 8) { if case let .iconL(icon, action) = type { iconButton(icon: icon, action: action) + .pokitBlurReplaceTransition(.pokitDissolve) } textField if case let .iconR(icon, action) = type { iconButton(icon: icon, action: action) + .pokitBlurReplaceTransition(.pokitDissolve) } } .padding(.vertical, vPadding) diff --git a/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift b/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift index b7c4d50f..739291c7 100644 --- a/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift +++ b/Projects/Feature/FeatureCategorySetting/Sources/PokitCategorySettingView.swift @@ -124,7 +124,7 @@ private extension PokitCategorySettingView { PokitTextInput( text: $store.categoryName, - type: .iconR( + type: store.categoryName.isEmpty ? .text : .iconR( icon: .icon(.x), action: { send(.포킷명지우기_버튼_눌렀을때) } ), diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift index 88e65f0d..047bc2ea 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift @@ -109,7 +109,7 @@ private extension ContentSettingView { PokitTextInput( text: $store.urlText, label: "링크", - type: .iconR( + type: store.urlText.isEmpty ? .text : .iconR( icon: .icon(.x), action: { send(.링크지우기_버튼_눌렀을때) } ), @@ -126,7 +126,7 @@ private extension ContentSettingView { PokitTextInput( text: $store.title, label: "제목", - type: .iconR( + type: store.title.isEmpty ? .text : .iconR( icon: .icon(.x), action: { send(.제목지우기_버튼_눌렀을때) } ), diff --git a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift index 62790830..36c81b0c 100644 --- a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift @@ -31,7 +31,7 @@ public extension NickNameSettingView { } else { PokitTextInput( text: $store.text, - type: .iconR( + type: store.text.isEmpty ? .text : .iconR( icon: .icon(.x), action: { send(.닉네임지우기_버튼_눌렀을때) } ), From e47c781049bdb21901854c4003b0a98a6368a307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:29:58 +0900 Subject: [PATCH 26/31] =?UTF-8?q?[design]=20#157=20=EC=BB=A8=ED=85=90?= =?UTF-8?q?=EC=B8=A0=20=EC=B6=94=EA=B0=80/=EC=88=98=EC=A0=95=20=EB=A1=9C?= =?UTF-8?q?=EB=94=A9=20=EC=A0=84=ED=99=98=20=EC=95=A0=EB=8B=88=EB=A9=94?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ContentSetting/ContentSettingFeature.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift index 6b2d23ae..37113f66 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift @@ -342,7 +342,7 @@ private extension ContentSettingFeature { state.contentLoading = true return .run { send in let content = try await contentClient.컨텐츠_상세_조회("\(id)").toDomain() - await send(.inner(.컨텐츠_상세_조회_API_반영(content: content))) + await send(.inner(.컨텐츠_상세_조회_API_반영(content: content)), animation: .pokitDissolve) } case let .카테고리_상세_조회_API(id, sharedId): return .run { send in From 3febcd5c4d7c92316c96ccf72f10fa8a023045cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:04:13 +0900 Subject: [PATCH 27/31] =?UTF-8?q?[chore]=20#157=20=ED=96=85=ED=8B=B1=20?= =?UTF-8?q?=ED=94=BC=EB=93=9C=EB=B0=B1=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Sources/MainTab/MainTabFeatureView.swift | 3 ++- .../Sources/Components/PokitLinkPopup.swift | 16 ++++++++++++++++ .../Sources/ContentCard/ContentCardFeature.swift | 4 +++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Projects/App/Sources/MainTab/MainTabFeatureView.swift b/Projects/App/Sources/MainTab/MainTabFeatureView.swift index f97ad2a9..3e695dd4 100644 --- a/Projects/App/Sources/MainTab/MainTabFeatureView.swift +++ b/Projects/App/Sources/MainTab/MainTabFeatureView.swift @@ -261,7 +261,8 @@ private extension MainTabView { } .padding(.horizontal, 28) .onTapGesture { - UIImpactFeedbackGenerator(style: .rigid).impactOccurred() + UIImpactFeedbackGenerator(style: .light) + .impactOccurred() store.send(.binding(.set(\.selectedTab, tab))) } } diff --git a/Projects/DSKit/Sources/Components/PokitLinkPopup.swift b/Projects/DSKit/Sources/Components/PokitLinkPopup.swift index 90d2f479..ff96df5e 100644 --- a/Projects/DSKit/Sources/Components/PokitLinkPopup.swift +++ b/Projects/DSKit/Sources/Components/PokitLinkPopup.swift @@ -46,6 +46,7 @@ public struct PokitLinkPopup: View { } second += 1 } + .onAppear(perform: feedback) } private var popup: some View { @@ -103,6 +104,21 @@ public struct PokitLinkPopup: View { } } + private func feedback() { + switch type { + case .link, .text, .warning: + UINotificationFeedbackGenerator() + .notificationOccurred(.warning) + case .success: + UINotificationFeedbackGenerator() + .notificationOccurred(.success) + case .error: + UINotificationFeedbackGenerator() + .notificationOccurred(.error) + case .none: break + } + } + private var backgroundColor: Color { switch type { case .link, .text: diff --git a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift index 92ad2fcb..5255b2b1 100644 --- a/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift +++ b/Projects/Feature/FeatureContentCard/Sources/ContentCard/ContentCardFeature.swift @@ -4,7 +4,7 @@ // // Created by 김도형 on 11/17/24. -import Foundation +import SwiftUI import ComposableArchitecture import Domain @@ -118,6 +118,8 @@ private extension ContentCardFeature { guard let isFavorite = state.content.isFavorite else { return .none } + UIImpactFeedbackGenerator(style: .light) + .impactOccurred() return isFavorite ? .send(.async(.즐겨찾기_취소_API)) : .send(.async(.즐겨찾기_API)) From 87bdb4951b6fe509cda9d0253c2492054fa06228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:13:45 +0900 Subject: [PATCH 28/31] =?UTF-8?q?[design]=20#157=20=EC=BB=A8=ED=85=90?= =?UTF-8?q?=EC=B8=A0=20=EC=83=81=EC=84=B8=20=EB=A9=94=EB=AA=A8=20=EB=86=92?= =?UTF-8?q?=EC=9D=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ContentDetail/ContentDetailView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailView.swift b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailView.swift index 95319266..ea08e3ff 100644 --- a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailView.swift +++ b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailView.swift @@ -159,7 +159,8 @@ private extension ContentDetailView { focusState: $isFocused, equals: true ) - .frame(minHeight: 132) + .frame(minHeight: isFocused ? 164 : 132) + .animation(.pokitDissolve, value: isFocused) } .padding(.bottom, 24) .padding(.horizontal, 20) From 317067b7474b18f151dd60de7ff4a76308053fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:27:07 +0900 Subject: [PATCH 29/31] =?UTF-8?q?[fix]=20#157=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B0=B0=EA=B2=BD,=20border?= =?UTF-8?q?=20=EC=83=89=20=EC=9E=98=EB=AA=BB=EB=82=98=EC=98=A4=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DSKit/Sources/Modifiers/PokitInputModifier.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Projects/DSKit/Sources/Modifiers/PokitInputModifier.swift b/Projects/DSKit/Sources/Modifiers/PokitInputModifier.swift index 86d786bb..50d92009 100644 --- a/Projects/DSKit/Sources/Modifiers/PokitInputModifier.swift +++ b/Projects/DSKit/Sources/Modifiers/PokitInputModifier.swift @@ -20,16 +20,13 @@ struct PokitInputModifier: ViewModifier { } func body(content: Content) -> some View { - let backgroundColor = state == .active ? .pokit(.bg(.base)) : self.state.backgroundColor - let backgroundStrokeColor = state == .active ? .pokit(.border(.brand)) : self.state.backgroundStrokeColor - content .background { RoundedRectangle(cornerRadius: shape.radius, style: .continuous) - .fill(backgroundColor) + .fill(self.state.backgroundColor) .overlay { RoundedRectangle(cornerRadius: shape.radius, style: .continuous) - .stroke(backgroundStrokeColor, lineWidth: 1) + .stroke(self.state.backgroundStrokeColor, lineWidth: 1) } } .animation(.pokitDissolve, value: state) From a2a407eb2d2b98302c6e8c4e2e1163f9da4694ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:03:00 +0900 Subject: [PATCH 30/31] =?UTF-8?q?[design]=20#157=20=EC=BB=A8=ED=85=90?= =?UTF-8?q?=EC=B8=A0=20=EC=83=81=EC=84=B8=20=EB=A1=9C=EB=94=A9=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/ContentDetail/ContentDetailFeature.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift index 6502e96d..780853ec 100644 --- a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift +++ b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift @@ -213,7 +213,10 @@ private extension ContentDetailFeature { case .컨텐츠_상세_조회_API(id: let id): return .run { send in let contentResponse = try await contentClient.컨텐츠_상세_조회("\(id)").toDomain() - await send(.inner(.컨텐츠_상세_조회_API_반영(content: contentResponse))) + await send( + .inner(.컨텐츠_상세_조회_API_반영(content: contentResponse)), + animation: .pokitDissolve + ) } case .즐겨찾기_API(id: let id): return .run { send in From 5f42240aaa91447a23229add6f267d30c3ad019c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8F=84=ED=98=95?= <108233361+ShapeKim98@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:13:04 +0900 Subject: [PATCH 31/31] =?UTF-8?q?[refactor]=20#157=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=EB=B0=B1=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../App/Sources/MainTab/MainTabFeature.swift | 2 +- .../App/Sources/MainTab/MainTabPath.swift | 5 +- .../DTO/Category/CategoryEditResponse.swift | 2 +- .../Sources/Components/PokitLinkPopup.swift | 4 +- .../ContentDetail/ContentDetailFeature.swift | 9 ++-- .../ContentSettingFeature.swift | 50 +++++++------------ .../ContentSetting/ContentSettingView.swift | 5 +- .../RegisterNicknameView.swift | 3 +- .../Sources/Remind/RemindFeature.swift | 5 +- .../NickNameSetting/NickNameSettingView.swift | 3 +- Projects/Util/Sources/Constants.swift | 7 +++ 11 files changed, 44 insertions(+), 51 deletions(-) diff --git a/Projects/App/Sources/MainTab/MainTabFeature.swift b/Projects/App/Sources/MainTab/MainTabFeature.swift index 2e100816..3eaeed96 100644 --- a/Projects/App/Sources/MainTab/MainTabFeature.swift +++ b/Projects/App/Sources/MainTab/MainTabFeature.swift @@ -205,7 +205,7 @@ private extension MainTabFeature { case let .linkCopySuccess(url): guard let url else { return .none } state.linkPopup = .link( - title: "복사한 링크 저장하기", + title: Constants.복사한_링크_저장하기_문구, url: url.absoluteString ) state.link = url.absoluteString diff --git a/Projects/App/Sources/MainTab/MainTabPath.swift b/Projects/App/Sources/MainTab/MainTabPath.swift index aa3008c1..bfca29dc 100644 --- a/Projects/App/Sources/MainTab/MainTabPath.swift +++ b/Projects/App/Sources/MainTab/MainTabPath.swift @@ -16,6 +16,7 @@ import FeatureContentSetting import FeatureContentList import FeatureCategorySharing import Domain +import Util @Reducer public struct MainTabPath { @@ -180,10 +181,10 @@ public extension MainTabFeature { case .검색: return .merge( .send(.path(.element(id: stackElementId, action: .검색(.delegate(.컨텐츠_검색))))), - .send(.inner(.링크팝업_활성화(.success(title: "링크 저장 완료"))), animation: .pokitSpring) + .send(.inner(.링크팝업_활성화(.success(title: Constants.링크_저장_완료_문구))), animation: .pokitSpring) ) default: - return .send(.inner(.링크팝업_활성화(.success(title: "링크 저장 완료"))), animation: .pokitSpring) + return .send(.inner(.링크팝업_활성화(.success(title: Constants.링크_저장_완료_문구))), animation: .pokitSpring) } /// - 각 화면에서 링크 복사 감지했을 때 (링크 추가 및 수정 화면 제외) case let .path(.element(_, action: .알림함(.delegate(.linkCopyDetected(url))))), diff --git a/Projects/CoreKit/Sources/Data/DTO/Category/CategoryEditResponse.swift b/Projects/CoreKit/Sources/Data/DTO/Category/CategoryEditResponse.swift index e7de613a..a1681c90 100644 --- a/Projects/CoreKit/Sources/Data/DTO/Category/CategoryEditResponse.swift +++ b/Projects/CoreKit/Sources/Data/DTO/Category/CategoryEditResponse.swift @@ -35,7 +35,7 @@ extension CategoryImageResponse { public static var mock: [Self] = [ Self( imageId: 2312, - imageUrl: "https://pokit-storage.s3.ap-northeast-2.amazonaws.com/logo/pokit.png" + imageUrl: Constants.기본_썸네일_주소.absoluteString ), Self( imageId: 23122, diff --git a/Projects/DSKit/Sources/Components/PokitLinkPopup.swift b/Projects/DSKit/Sources/Components/PokitLinkPopup.swift index ff96df5e..b4d9de71 100644 --- a/Projects/DSKit/Sources/Components/PokitLinkPopup.swift +++ b/Projects/DSKit/Sources/Components/PokitLinkPopup.swift @@ -40,7 +40,7 @@ public struct PokitLinkPopup: View { .frame(width: 335, height: 60) .transition(.move(edge: .bottom).combined(with: .opacity)) .onReceive(timer) { _ in - guard second < 2 && type != nil else { + guard second < 2 else { closedPopup() return } @@ -99,8 +99,8 @@ public struct PokitLinkPopup: View { private func closedPopup() { withAnimation(.pokitSpring) { - second = 0 type = nil + second = 0 } } diff --git a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift index 780853ec..a9047819 100644 --- a/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift +++ b/Projects/Feature/FeatureContentDetail/Sources/ContentDetail/ContentDetailFeature.swift @@ -141,15 +141,14 @@ private extension ContentDetailFeature { case .뷰가_나타났을때: /// - 나중에 공유 받은 컨텐츠인지 확인해야함 state.memoTextAreaState = .memo(isReadOnly: false) - if let id = state.domain.contentId { return .send(.async(.컨텐츠_상세_조회_API(id: id))) - } else if let content = state.domain.content { + } + if let content = state.domain.content { state.memo = content.memo return .none - } else { - return .none } + return .none case .공유_버튼_눌렀을때: state.showShareSheet = true return .none @@ -255,7 +254,7 @@ private extension ContentDetailFeature { model: request ) await send( - .inner(.링크팝업_활성화(.success(title: "메모 수정 완료"))), + .inner(.링크팝업_활성화(.success(title: Constants.메모_수정_완료_문구))), animation: .pokitSpring ) } catch: { error, send in diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift index 37113f66..a4783ab6 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingFeature.swift @@ -100,7 +100,7 @@ public struct ContentSettingFeature { case 뒤로가기_버튼_눌렀을때 } - public enum InnerAction: Equatable { + public enum InnerAction { case linkPopup(URL?) case linkPreview case 메타데이터_조회_수행(url: URL) @@ -112,6 +112,7 @@ public struct ContentSettingFeature { case 카테고리_목록_조회_API_반영(categoryList: BaseCategoryListInquiry) case 선택한_포킷_인메모리_삭제 case 링크팝업_활성화(PokitLinkPopup.PopupType) + case error(Error) } public enum AsyncAction: Equatable { @@ -198,7 +199,7 @@ private extension ContentSettingFeature { return .merge(mergeEffect) case .저장_버튼_눌렀을때: let isEdit = state.domain.categoryId != nil - if state.domain.title == "제목을 입력해주세요" { + if state.domain.title == Constants.제목을_입력해주세요_문구 { state.domain.title = state.title } state.saveIsLoading = true @@ -209,7 +210,7 @@ private extension ContentSettingFeature { case .포킷추가_버튼_눌렀을때: guard state.domain.categoryTotalCount < 30 else { /// 🚨 Error Case [1]: 포킷 갯수가 30개 이상일 경우 - state.linkPopup = .text(title: "최대 30개의 포킷을 생성할 수 있습니다.\n포킷을 삭제한 뒤에 추가해주세요.") + state.linkPopup = .text(title: Constants.포킷_최대_갯수_문구) return .none } @@ -237,7 +238,7 @@ private extension ContentSettingFeature { guard let url else { return .none } state.link = url.absoluteString state.linkPopup = .link( - title: "복사한 링크 저장하기", + title: Constants.복사한_링크_저장하기_문구, url: url.absoluteString ) return .none @@ -254,9 +255,11 @@ private extension ContentSettingFeature { ) } case let .메타데이텨_조회_반영(title: title, imageURL: imageURL): - let contentTitle = state.title.isEmpty ? "제목을 입력해주세요" : state.title + let contentTitle = state.title.isEmpty + ? Constants.제목을_입력해주세요_문구 + : state.title state.linkTitle = title ?? contentTitle - state.linkImageURL = imageURL ?? "https://pokit-storage.s3.ap-northeast-2.amazonaws.com/logo/pokit.png" + state.linkImageURL = imageURL ?? Constants.기본_썸네일_주소.absoluteString if let title, state.domain.title.isEmpty { state.domain.title = title } @@ -332,6 +335,12 @@ private extension ContentSettingFeature { state.linkPopup = type state.saveIsLoading = false return .none + case let .error(error): + guard let errorResponse = error as? ErrorResponse else { return .none } + return .send( + .inner(.링크팝업_활성화(.error(title: errorResponse.message))), + animation: .pokitSpring + ) } } @@ -387,11 +396,7 @@ private extension ContentSettingFeature { await send(.inner(.선택한_포킷_인메모리_삭제)) await send(.delegate(.저장하기_완료(contentId: contentId))) } catch: { error, send in - guard let errorResponse = error as? ErrorResponse else { return } - await send( - .inner(.링크팝업_활성화(.error(title: errorResponse.message))), - animation: .pokitSpring - ) + await send(.inner(.error(error))) } case .컨텐츠_추가_API: guard let categoryId = state.selectedPokit?.id else { @@ -410,11 +415,7 @@ private extension ContentSettingFeature { await send(.inner(.선택한_포킷_인메모리_삭제)) await send(.delegate(.저장하기_완료(contentId: content.contentId))) } catch: { error, send in - guard let errorResponse = error as? ErrorResponse else { return } - await send( - .inner(.링크팝업_활성화(.error(title: errorResponse.message))), - animation: .pokitSpring - ) + await send(.inner(.error(error))) } case .클립보드_감지: return .run { send in @@ -425,7 +426,7 @@ private extension ContentSettingFeature { } } } - + /// - Scope Effect func handleScopeAction(_ action: Action.ScopeAction, state: inout State) -> Effect { return .none @@ -436,21 +437,6 @@ private extension ContentSettingFeature { return .none } - func contentEdit(request: ContentBaseRequest, contentId: Int) -> Effect { - return .run { _ in - let _ = try await contentClient.컨텐츠_수정( - "\(contentId)", - request - ) - } catch: { error, send in - guard let errorResponse = error as? ErrorResponse else { return } - await send( - .inner(.링크팝업_활성화(.error(title: errorResponse.message))), - animation: .pokitSpring - ) - } - } - func categoryListFetch(request: BasePageableRequest) -> Effect { return .run { send in let categoryList = try await categoryClient.카테고리_목록_조회(request, false).toDomain() diff --git a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift index 047bc2ea..158c8bb4 100644 --- a/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift +++ b/Projects/Feature/FeatureContentSetting/Sources/ContentSetting/ContentSettingView.swift @@ -8,6 +8,7 @@ import SwiftUI import ComposableArchitecture import DSKit +import Util @ViewAction(for: ContentSettingFeature.self) public struct ContentSettingView: View { @@ -97,8 +98,8 @@ private extension ContentSettingView { let isParsed = store.linkTitle != nil || store.linkImageURL != nil PokitLinkPreview( - title: store.linkTitle == "제목을 입력해주세요" - ? store.title.isEmpty ? "제목을 입력해주세요" : store.title + title: store.linkTitle == Constants.제목을_입력해주세요_문구 + ? store.title.isEmpty ? Constants.제목을_입력해주세요_문구 : store.title : store.linkTitle, url: isParsed ? store.urlText : nil, imageURL: store.linkImageURL diff --git a/Projects/Feature/FeatureLogin/Sources/RegisterNickname/RegisterNicknameView.swift b/Projects/Feature/FeatureLogin/Sources/RegisterNickname/RegisterNicknameView.swift index cc8f3cc3..6ce0890f 100644 --- a/Projects/Feature/FeatureLogin/Sources/RegisterNickname/RegisterNicknameView.swift +++ b/Projects/Feature/FeatureLogin/Sources/RegisterNickname/RegisterNicknameView.swift @@ -7,6 +7,7 @@ import ComposableArchitecture import SwiftUI import DSKit +import Util @ViewAction(for: RegisterNicknameFeature.self) public struct RegisterNicknameView: View { @@ -74,7 +75,7 @@ extension RegisterNicknameView { text: $store.nicknameText, shape: .rectangle, state: $store.textfieldState, - info: "한글, 영어, 숫자로만 입력이 가능합니다.", + info: Constants.한글_영어_숫자_입력_문구, maxLetter: 10, focusState: $isFocused, equals: true diff --git a/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift b/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift index 64bd7235..50b8f1ba 100644 --- a/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift +++ b/Projects/Feature/FeatureRemind/Sources/Remind/RemindFeature.swift @@ -155,9 +155,7 @@ private extension RemindFeature { case .컨텐츠_항목_케밥_버튼_눌렀을때(let content): return .send(.delegate(.링크상세(content: content))) case .컨텐츠_항목_눌렀을때(let content): - guard let url = URL(string: content.data) else { - return .none - } + guard let url = URL(string: content.data) else { return .none } return .run { _ in await openURL(url) } case .뷰가_나타났을때: return allContentFetch(animation: .pokitDissolve) @@ -290,7 +288,6 @@ private extension RemindFeature { } /// - Scope Effect func handleScopeAction(_ action: Action.ScopeAction, state: inout State) -> Effect { - /// - 링크에 대한 `공유` / `수정` / `삭제` delegate return .none } /// - Delegate Effect diff --git a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift index 36c81b0c..e4b7c249 100644 --- a/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift +++ b/Projects/Feature/FeatureSetting/Sources/Setting/NickNameSetting/NickNameSettingView.swift @@ -8,6 +8,7 @@ import SwiftUI import ComposableArchitecture import DSKit +import Util @ViewAction(for: NickNameSettingFeature.self) public struct NickNameSettingView: View { @@ -37,7 +38,7 @@ public extension NickNameSettingView { ), shape: .rectangle, state: $store.textfieldState, - info: "한글, 영어, 숫자로만 입력이 가능합니다.", + info: Constants.한글_영어_숫자_입력_문구, maxLetter: 10, focusState: $isFocused, equals: true diff --git a/Projects/Util/Sources/Constants.swift b/Projects/Util/Sources/Constants.swift index 5b0a0e1d..b9da10f5 100644 --- a/Projects/Util/Sources/Constants.swift +++ b/Projects/Util/Sources/Constants.swift @@ -22,7 +22,14 @@ public enum Constants { public static let 개인정보_처리방침_주소: URL = URL(string: "https://www.notion.so/de3468b3be1744538c22a333ae1d0ec8")! public static let 마케팅_정보_수신_주소: URL = URL(string: "https://www.notion.so/bb6d0d6569204d5e9a7b67e5825f9d10")! public static let 고객문의_주소: URL = URL(string: "https://www.instagram.com/pokit.official/")! + public static let 기본_썸네일_주소: URL = URL(string: "https://pokit-storage.s3.ap-northeast-2.amazonaws.com/logo/pokit.png")! + public static let 포킷_최대_갯수_문구: String = "최대 30개의 포킷을 생성할 수 있습니다.\n포킷을 삭제한 뒤에 추가해주세요." + public static let 복사한_링크_저장하기_문구: String = "복사한 링크 저장하기" + public static let 제목을_입력해주세요_문구: String = "제목을 입력해주세요" + public static let 링크_저장_완료_문구: String = "링크 저장 완료" + public static let 메모_수정_완료_문구: String = "메모 수정 완료" + public static let 한글_영어_숫자_입력_문구: String = "한글, 영어, 숫자로만 입력이 가능합니다." public static var mockImageUrl: String { "https://picsum.photos/\(Int.random(in: 150...250))" } }