Skip to content

Commit 9331748

Browse files
committed
[feat] #173 편집하기 로직 구성
- 에러로직 추가 - 삭제하기 바텀시트 구성 - 편집할 링크가 없을 때 보여줄 화면 분기처리
1 parent f3a36ad commit 9331748

File tree

2 files changed

+173
-37
lines changed

2 files changed

+173
-37
lines changed

Projects/Feature/FeaturePokit/Sources/PokitLinkEditFeature.swift

Lines changed: 112 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import ComposableArchitecture
88
import CoreKit
99
import Domain
10+
import DSKit
11+
import FeatureCategorySetting
1012
import Util
1113

1214
@Reducer
@@ -18,6 +20,7 @@ public struct PokitLinkEditFeature {
1820
/// - State
1921
@ObservableState
2022
public struct State: Equatable {
23+
@Presents var addPokit: PokitCategorySettingFeature.State?
2124
/// 링크 아이템 Doamin
2225
var item: BaseContentListInquiry
2326
/// 카테고리 아이템 Domain
@@ -27,7 +30,9 @@ public struct PokitLinkEditFeature {
2730
/// 선택한 링크 목록
2831
var selectedItems = IdentifiedArrayOf<BaseContentItem>()
2932
/// 포킷 이동 눌렀을 때 sheet
30-
var isPresented: Bool = false
33+
var categorySelectSheetPresetend: Bool = false
34+
var linkDeleteSheetPresented: Bool = false
35+
var linkPopup: PokitLinkPopup.PopupType?
3136

3237
public init(linkList: BaseContentListInquiry) {
3338
self.item = linkList
@@ -49,22 +54,31 @@ public struct PokitLinkEditFeature {
4954
public enum View: BindableAction, Equatable {
5055
case binding(BindingAction<State>)
5156
case dismiss
52-
case onAppear
5357

54-
case 카테고리_추가_버튼_눌렀을때
58+
case 뷰가_나타났을때
59+
case 포킷_추가하기_버튼_눌렀을때
60+
case 링크팝업_버튼_눌렀을때
61+
case 경고시트_해제
62+
case 삭제확인_버튼_눌렀을때
5563
case 체크박스_선택했을때(BaseContentItem)
5664
case 카테고리_선택했을때(BaseCategoryItem)
5765
}
5866

59-
public enum InnerAction: Equatable {
67+
public enum InnerAction {
68+
case error(Error)
69+
case 카테고리_이동_시트_활성화(Bool)
70+
case 카테고리_삭제_시트_활성화(Bool)
71+
case 경고팝업_활성화(PokitLinkPopup.PopupType)
6072
case 카테고리_목록_조회_API_반영(BaseCategoryListInquiry)
61-
case 미분류_카테고리_이동_API_반영
73+
case 미분류_API_반영(LinkEditType)
6274
}
6375

6476
public enum AsyncAction: Equatable { case 없음 }
6577

66-
public enum ScopeAction: Equatable {
78+
@CasePathable
79+
public enum ScopeAction {
6780
case floatButtonAction(PokitLinkEditFloatView.Delegate)
81+
case addPokit(PresentationAction<PokitCategorySettingFeature.Action>)
6882
}
6983

7084
public enum DelegateAction: Equatable { case 없음 }
@@ -102,6 +116,9 @@ public struct PokitLinkEditFeature {
102116
public var body: some ReducerOf<Self> {
103117
BindingReducer(action: \.view)
104118
Reduce(self.core)
119+
.ifLet(\.$addPokit, action: \.scope.addPokit) {
120+
PokitCategorySettingFeature()
121+
}
105122
}
106123
}
107124
//MARK: - FeatureAction Effect
@@ -115,12 +132,21 @@ private extension PokitLinkEditFeature {
115132
case .dismiss:
116133
return .run { _ in await dismiss() }
117134

118-
case .onAppear:
135+
case .뷰가_나타났을때:
119136
return fetchCateogryList()
120137

121-
case .카테고리_추가_버튼_눌렀을때:
138+
case .포킷_추가하기_버튼_눌렀을때:
139+
state.categorySelectSheetPresetend = false
140+
state.linkDeleteSheetPresented = false
141+
state.addPokit = PokitCategorySettingFeature.State(type: .추가)
122142
return .none
123143

144+
case .경고시트_해제:
145+
return .send(.inner(.카테고리_삭제_시트_활성화(false)))
146+
147+
case .삭제확인_버튼_눌렀을때:
148+
return linkDelete(state: &state)
149+
124150
case let .체크박스_선택했을때(item):
125151
/// 이미 체크되어 있다면 해제
126152
if state.selectedItems.contains(item) {
@@ -131,20 +157,60 @@ private extension PokitLinkEditFeature {
131157
return .none
132158

133159
case let .카테고리_선택했을때(pokit):
134-
return moveContentList(categoryId: pokit.id, state: &state)
160+
/// 🚨 Error Case [1]: 체크한 것이 없는데 카테고리를 선택했을 때
161+
if state.selectedItems.isEmpty {
162+
return .merge(
163+
.send(.inner(.카테고리_이동_시트_활성화(false))),
164+
.send(.inner(.경고팝업_활성화(.error(title: "링크를 선택해주세요."))))
165+
)
166+
} else {
167+
return moveContentList(categoryId: pokit.id, state: &state)
168+
}
169+
170+
case .링크팝업_버튼_눌렀을때:
171+
state.linkPopup = nil
172+
return .none
135173
}
136174
}
137175

138176
/// - Inner Effect
139177
func handleInnerAction(_ action: Action.InnerAction, state: inout State) -> Effect<Action> {
140178
switch action {
179+
case let .error(error):
180+
guard let errorResponse = error as? ErrorResponse else { return .none }
181+
state.categorySelectSheetPresetend = false
182+
state.linkDeleteSheetPresented = false
183+
return .merge(
184+
.send(.inner(.카테고리_이동_시트_활성화(false))),
185+
.send(.inner(.카테고리_삭제_시트_활성화(false))),
186+
.send(.inner(.경고팝업_활성화(.error(title: errorResponse.message))),
187+
animation: .pokitSpring
188+
)
189+
)
190+
191+
case let .경고팝업_활성화(type):
192+
state.linkPopup = type
193+
return .none
194+
195+
case let .카테고리_이동_시트_활성화(isPresented):
196+
state.categorySelectSheetPresetend = isPresented
197+
return .none
198+
199+
case let .카테고리_삭제_시트_활성화(isPresented):
200+
state.linkDeleteSheetPresented = isPresented
201+
return .none
202+
141203
case let .카테고리_목록_조회_API_반영(response):
142204
state.category = response
143205
return .none
144206

145-
case .미분류_카테고리_이동_API_반영:
207+
case let .미분류_API_반영(type):
146208
/// 1. 시트 내리기
147-
state.isPresented = false
209+
if type == .링크이동 {
210+
state.categorySelectSheetPresetend = false
211+
} else {
212+
state.linkDeleteSheetPresented = false
213+
}
148214
/// 2. 선택했던 체크리스트 삭제
149215
state.selectedItems
150216
.map { $0.id }
@@ -165,7 +231,11 @@ private extension PokitLinkEditFeature {
165231
case let .floatButtonAction(delegate):
166232
switch delegate {
167233
case .링크삭제_버튼_눌렀을때:
168-
return .none
234+
if state.selectedItems.isEmpty {
235+
return .send(.inner(.경고팝업_활성화(.error(title: "링크를 선택해주세요."))))
236+
} else {
237+
return .send(.inner(.카테고리_삭제_시트_활성화(true)))
238+
}
169239

170240
case .전체선택_버튼_눌렀을때:
171241
state.selectedItems = state.list
@@ -176,9 +246,18 @@ private extension PokitLinkEditFeature {
176246
return .none
177247

178248
case .포킷이동_버튼_눌렀을때:
179-
state.isPresented = true
180-
return .none
249+
return .send(.inner(.카테고리_이동_시트_활성화(true)))
181250
}
251+
252+
case .addPokit(.presented(.delegate(.settingSuccess))):
253+
state.addPokit = nil
254+
return .merge(
255+
fetchCateogryList(),
256+
.send(.inner(.카테고리_이동_시트_활성화(true)))
257+
)
258+
259+
case .addPokit:
260+
return .none
182261
}
183262
}
184263

@@ -202,7 +281,25 @@ private extension PokitLinkEditFeature {
202281
let contentIds = contentIds.map { $0.id }
203282
let request = ContentMoveRequest(contentIds: contentIds, categoryId: categoryId)
204283
try await contentClient.미분류_링크_포킷_이동(request)
205-
await send(.inner(.미분류_카테고리_이동_API_반영))
284+
await send(.inner(.미분류_API_반영(.링크이동)))
285+
} catch: { error, send in
286+
await send(.inner(.error(error)))
287+
}
288+
}
289+
290+
func linkDelete(state: inout State) -> Effect<Action> {
291+
return .run { [contentIds = state.selectedItems.ids] send in
292+
let request = ContentDeleteRequest(contentId: Array(contentIds))
293+
try await contentClient.미분류_링크_삭제(request)
294+
await send(.inner(.미분류_API_반영(.링크삭제)))
295+
} catch: { error, send in
296+
await send(.inner(.error(error)))
206297
}
207298
}
208299
}
300+
public extension PokitLinkEditFeature {
301+
enum LinkEditType: Equatable {
302+
case 링크이동
303+
case 링크삭제
304+
}
305+
}

Projects/Feature/FeaturePokit/Sources/PokitLinkEditView.swift

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import ComposableArchitecture
1010
import DSKit
1111
import Domain
1212
import CoreKit
13+
import FeatureCategorySetting
1314
import Util
1415

1516
@ViewAction(for: PokitLinkEditFeature.self)
@@ -27,48 +28,86 @@ public extension PokitLinkEditView {
2728
var body: some View {
2829
WithPerceptionTracking {
2930
VStack(spacing: 0) {
30-
ScrollView {
31-
ForEach(store.list, id: \.id) { item in
32-
let isFirst = item.id == self.store.list.first?.id
33-
let isLast = item.id == self.store.list.last?.id
34-
WithPerceptionTracking {
35-
PokitLinkCard(
36-
link: item,
37-
state: isFirst
38-
? .top
39-
: isLast ? .bottom : .middle,
40-
type: .unCatgorized(isSelected: store.selectedItems.contains(item)),
41-
action: nil,
42-
kebabAction: nil,
43-
fetchMetaData: {},
44-
favoriteAction: nil,
45-
selectAction: { send(.체크박스_선택했을때(item)) }
46-
)
31+
if store.list.isEmpty {
32+
PokitCaution(type: .미분류_링크없음)
33+
Spacer()
34+
} else {
35+
ScrollView {
36+
ForEach(store.list, id: \.id) { item in
37+
let isFirst = item.id == self.store.list.first?.id
38+
let isLast = item.id == self.store.list.last?.id
39+
WithPerceptionTracking {
40+
PokitLinkCard(
41+
link: item,
42+
state: isFirst
43+
? .top
44+
: isLast ? .bottom : .middle,
45+
type: .unCatgorized(isSelected: store.selectedItems.contains(item)),
46+
action: nil,
47+
kebabAction: nil,
48+
fetchMetaData: {},
49+
favoriteAction: nil,
50+
selectAction: { send(.체크박스_선택했을때(item)) }
51+
)
52+
}
4753
}
4854
}
55+
.scrollIndicators(.hidden)
56+
.padding(.bottom, 38)
4957
}
50-
.padding(.bottom, 38)
5158
}
5259
.padding(.top, 16)
5360
.overlay(alignment: .bottom) {
54-
actionFloatButtonView
61+
if !store.list.isEmpty {
62+
actionFloatButtonView
63+
}
5564
}
5665
.padding(.horizontal, 20)
5766
.padding(.bottom, 24)
5867
.pokitNavigationBar(navigationBar)
5968
.ignoresSafeArea(edges: .bottom)
60-
.sheet(isPresented: $store.isPresented) {
69+
.sheet(isPresented: $store.categorySelectSheetPresetend) {
6170
PokitSelectSheet(
6271
list: store.category?.data ?? nil,
6372
itemSelected: { send(.카테고리_선택했을때($0)) },
64-
pokitAddAction: {}
73+
pokitAddAction: { send(.포킷_추가하기_버튼_눌렀을때) }
6574
)
6675
.presentationDragIndicator(.visible)
6776
.pokitPresentationCornerRadius()
6877
.presentationDetents([.height(564)])
6978
.pokitPresentationBackground()
79+
80+
}
81+
.sheet(isPresented: $store.linkDeleteSheetPresented) {
82+
PokitAlert(
83+
"링크를 정말 삭제하시겠습니까?",
84+
message: "함께 저장한 모든 정보가 삭제되며, \n복구하실 수 없습니다.",
85+
confirmText: "삭제",
86+
action: { send(.삭제확인_버튼_눌렀을때) },
87+
cancelAction: { send(.경고시트_해제) }
88+
)
89+
}
90+
.overlay(alignment: .bottom) {
91+
if store.linkPopup != nil {
92+
PokitLinkPopup(
93+
type: $store.linkPopup,
94+
action: { send(.링크팝업_버튼_눌렀을때, animation: .pokitSpring) }
95+
)
96+
.pokitMaxWidth()
97+
}
98+
}
99+
/// fullScreenCover를 통해 새로운 Destination을 만들었음
100+
/// 그렇지 않으면 MainPath에서 관리해야 하고, `LinkEdit`을 모듈로 빼야 함
101+
/// 추후 여러 군데에서 사용 된다면 그때 진행
102+
.fullScreenCover(
103+
item: $store.scope(
104+
state: \.addPokit,
105+
action: \.scope.addPokit
106+
)
107+
) { store in
108+
PokitCategorySettingView(store: store)
70109
}
71-
.task { await send(.onAppear).finish() }
110+
.task { await send(.뷰가_나타났을때).finish() }
72111
}
73112
}
74113
}

0 commit comments

Comments
 (0)