Skip to content

Commit

Permalink
[feat] #154 RemindSplit Store, View ์ž‘์„ฑ
Browse files Browse the repository at this point in the history
  • Loading branch information
ShapeKim98 committed Oct 27, 2024
1 parent 58455d0 commit 9f1e5fb
Show file tree
Hide file tree
Showing 3 changed files with 337 additions and 46 deletions.
24 changes: 13 additions & 11 deletions Projects/App/Sources/MainTabSplit/MainTab/MainTabSplitView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,20 @@ private extension MainTabSplitView {
iconColor: Color,
action: @escaping () -> Void
) -> some View {
VStack(spacing: 4) {
Image(icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 24, height: 24)
.foregroundStyle(iconColor)

Text(title)
.pokitFont(.detail2)
.foregroundStyle(titleColor)
Button(action: action) {
VStack(spacing: 4) {
Image(icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 24, height: 24)
.foregroundStyle(iconColor)

Text(title)
.pokitFont(.detail2)
.foregroundStyle(titleColor)
}
.padding(.horizontal, 28)
}
.padding(.horizontal, 28)
}
}

Expand Down
262 changes: 230 additions & 32 deletions Projects/App/Sources/MainTabSplit/Remind/RemindSplitFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
//
// Created by ๊น€๋„ํ˜• on 10/24/24.

import SwiftUI

import ComposableArchitecture
import FeaturePokit
import FeatureCategoryDetail
import FeatureRemind
import FeatureContentList
import FeatureCategorySetting
import FeatureCategorySharing
import FeatureSetting
Expand All @@ -23,19 +25,23 @@ public struct RemindSplitFeature {

/// - State
@ObservableState
public struct State: Equatable {
var pokit: PokitRootFeature.State = .init()
var categoryDetail: CategoryDetailFeature.State?
var contentSetting: ContentSettingFeature.State?
public struct State {
var columnVisibility: NavigationSplitViewVisibility = .doubleColumn

var ๋ฆฌ๋งˆ์ธ๋“œ: RemindFeature.State = .init()
var ๋งํฌ๋ชฉ๋ก: ContentListFeature.State = .init(contentType: .unread)
var ๋งํฌ์ถ”๊ฐ€: ContentSettingFeature.State = .init()

var path = StackState<Path.State>()

@Presents
var categorySetting: PokitCategorySettingFeature.State?
var ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •: PokitCategorySettingFeature.State?
@Presents
var search: PokitSearchFeature.State?
var ๋งํฌ์ƒ์„ธ: ContentDetailFeature.State?
@Presents
var setting: PokitSettingFeature.State?
var ์•Œ๋ฆผํ•จ: PokitAlertBoxFeature.State?
@Presents
var contentDetail: ContentDetailFeature.State?
var ๋งํฌ์ˆ˜์ •: ContentSettingFeature.State?

@Shared(.inMemory("PushTapped"))
var isPushTapped: Bool = false
Expand All @@ -50,25 +56,44 @@ public struct RemindSplitFeature {
case async(AsyncAction)
case scope(ScopeAction)
case delegate(DelegateAction)
case pokit(PokitRootFeature.Action)
case categoryDetail(CategoryDetailFeature.Action)
case contentSetting(ContentSettingFeature.Action)
case path(StackActionOf<Path>)
case ๋ฆฌ๋งˆ์ธ๋“œ(RemindFeature.Action)
case ๋งํฌ๋ชฉ๋ก(ContentListFeature.Action)
case ๋งํฌ์ถ”๊ฐ€(ContentSettingFeature.Action)
case ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •(PresentationAction<PokitCategorySettingFeature.Action>)
case ๋งํฌ์ƒ์„ธ(PresentationAction<ContentDetailFeature.Action>)
case ์•Œ๋ฆผํ•จ(PresentationAction<PokitAlertBoxFeature.Action>)
case ๋งํฌ์ˆ˜์ •(PresentationAction<ContentSettingFeature.Action>)

@CasePathable
public enum View: Equatable {
public enum View: Equatable, BindableAction {
case binding(BindingAction<State>)

case ๋ทฐ๊ฐ€_๋‚˜ํƒ€๋‚ฌ์„๋•Œ

case ๊ฒ€์ƒ‰_๋ฒ„ํŠผ_๋ˆŒ๋ €์„๋•Œ
case ์•Œ๋žŒ_๋ฒ„ํŠผ_๋ˆŒ๋ €์„๋•Œ
}

public enum InnerAction: Equatable {
case ์นดํ…Œ๊ณ ๋ฆฌ_์ƒ์„ธ_ํ™œ์„ฑํ™”(BaseCategoryItem)
case ๋งํฌ๋ชฉ๋ก_ํ™œ์„ฑํ™”(ContentListFeature.ContentType)
case ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •_ํ™œ์„ฑํ™”(BaseCategoryItem?)
case ๋งํฌ์ˆ˜์ •_ํ™œ์„ฑํ™”(Int?)
case ๋งํฌ์ƒ์„ธ_ํ™œ์„ฑํ™”(Int)
}

public enum AsyncAction: Equatable { case doNothing }

public enum ScopeAction {
case pokit(PokitRootFeature.Action)
case categoryDetail(CategoryDetailFeature.Action)
case contentSetting(ContentSettingFeature.Action)
case ๋ฆฌ๋งˆ์ธ๋“œ(RemindFeature.Action)
case ๋งํฌ๋ชฉ๋ก(ContentListFeature.Action)
case ๋งํฌ์ถ”๊ฐ€(ContentSettingFeature.Action)
case ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •(PresentationAction<PokitCategorySettingFeature.Action>)
case ๊ฒ€์ƒ‰(StackElementID, PokitSearchFeature.Action)
case ์„ค์ •(PresentationAction<PokitSettingFeature.Action>)
case ๋งํฌ์ƒ์„ธ(PresentationAction<ContentDetailFeature.Action>)
case ์•Œ๋ฆผํ•จ(PresentationAction<PokitAlertBoxFeature.Action>)
case ๋งํฌ์ˆ˜์ •(PresentationAction<ContentSettingFeature.Action>)
}

public enum DelegateAction: Equatable { case doNothing }
Expand Down Expand Up @@ -99,32 +124,96 @@ public struct RemindSplitFeature {
/// - Delegate
case .delegate(let delegateAction):
return handleDelegateAction(delegateAction, state: &state)
case .pokit(let pokitAction):
return .send(.scope(.pokit(pokitAction)))
case .categoryDetail(let categoryDetailAction):
return .send(.scope(.categoryDetail(categoryDetailAction)))
case .contentSetting(let contentSettingAction):
return .send(.scope(.contentSetting(contentSettingAction)))
case .path(let pathAction):
return handlePathAction(pathAction, state: &state)
case .๋ฆฌ๋งˆ์ธ๋“œ(let remindAction):
return .send(.scope(.๋ฆฌ๋งˆ์ธ๋“œ(remindAction)))
case .๋งํฌ๋ชฉ๋ก(let contentListAction):
return .send(.scope(.๋งํฌ๋ชฉ๋ก(contentListAction)))
case .๋งํฌ์ถ”๊ฐ€(let contentSettingAction):
return .send(.scope(.๋งํฌ์ถ”๊ฐ€(contentSettingAction)))
case .ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •(let categorySettingAction):
return .send(.scope(.ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •(categorySettingAction)))
case .๋งํฌ์ƒ์„ธ(let contentDetailAction):
return .send(.scope(.๋งํฌ์ƒ์„ธ(contentDetailAction)))
case .์•Œ๋ฆผํ•จ(let alertAction):
return .send(.scope(.์•Œ๋ฆผํ•จ(alertAction)))
case .๋งํฌ์ˆ˜์ •(let contentSettingAction):
return .send(.scope(.๋งํฌ์ˆ˜์ •(contentSettingAction)))
}
}

/// - Reducer body
public var body: some ReducerOf<Self> {
BindingReducer(action: \.view)

Scope(state: \.๋ฆฌ๋งˆ์ธ๋“œ, action: \.๋ฆฌ๋งˆ์ธ๋“œ) {
RemindFeature()
}
Scope(state: \.๋งํฌ์ถ”๊ฐ€, action: \.๋งํฌ์ถ”๊ฐ€) {
ContentSettingFeature()
}
Scope(state: \.๋งํฌ๋ชฉ๋ก, action: \.๋งํฌ๋ชฉ๋ก) {
ContentListFeature()
}

Reduce(self.core)
.forEach(\.path, action: \.path)
.ifLet(\.$ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •, action: \.ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •) {
PokitCategorySettingFeature()
}
.ifLet(\.$๋งํฌ์ƒ์„ธ, action: \.๋งํฌ์ƒ์„ธ) {
ContentDetailFeature()
}
.ifLet(\.$์•Œ๋ฆผํ•จ, action: \.์•Œ๋ฆผํ•จ) {
PokitAlertBoxFeature()
}
.ifLet(\.$๋งํฌ์ˆ˜์ •, action: \.๋งํฌ์ˆ˜์ •) {
ContentSettingFeature()
}
}
}
//MARK: - FeatureAction Effect
private extension RemindSplitFeature {
/// - View Effect
func handleViewAction(_ action: Action.View, state: inout State) -> Effect<Action> {
return .none
switch action {
case .binding:
return .none
case .๋ทฐ๊ฐ€_๋‚˜ํƒ€๋‚ฌ์„๋•Œ:
return .none
case .๊ฒ€์ƒ‰_๋ฒ„ํŠผ_๋ˆŒ๋ €์„๋•Œ:
state.path.append(.๊ฒ€์ƒ‰(.init()))
return .none
case .์•Œ๋žŒ_๋ฒ„ํŠผ_๋ˆŒ๋ €์„๋•Œ:
state.์•Œ๋ฆผํ•จ = .init()
return .none
}
}

/// - Inner Effect
func handleInnerAction(_ action: Action.InnerAction, state: inout State) -> Effect<Action> {
switch action {
case let .์นดํ…Œ๊ณ ๋ฆฌ_์ƒ์„ธ_ํ™œ์„ฑํ™”(category):
state.categoryDetail = .init(category: category)
case let .๋งํฌ๋ชฉ๋ก_ํ™œ์„ฑํ™”(contentType):
state.๋งํฌ๋ชฉ๋ก = .init(contentType: contentType)
return .none
case let .ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •_ํ™œ์„ฑํ™”(category):
if let category {
state.ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ • = .init(
type: .์ˆ˜์ •,
categoryId: category.id,
categoryImage: category.categoryImage,
categoryName: category.categoryName
)
} else {
state.ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ • = .init(type: .์ถ”๊ฐ€)
}
return .none
case let .๋งํฌ์ˆ˜์ •_ํ™œ์„ฑํ™”(contentId):
state.๋งํฌ์ˆ˜์ • = .init(contentId: contentId)
return .none
case let .๋งํฌ์ƒ์„ธ_ํ™œ์„ฑํ™”(contentId):
state.๋งํฌ์ƒ์„ธ = .init(contentId: contentId)
return .none
}
}
Expand All @@ -137,13 +226,106 @@ private extension RemindSplitFeature {
/// - Scope Effect
func handleScopeAction(_ action: Action.ScopeAction, state: inout State) -> Effect<Action> {
switch action {
case let .pokit(.delegate(.categoryTapped(category))):
return .send(.inner(.์นดํ…Œ๊ณ ๋ฆฌ_์ƒ์„ธ_ํ™œ์„ฑํ™”(category)))
case .pokit:
// - MARK: ๋ฆฌ๋งˆ์ธ๋“œ
case .๋ฆฌ๋งˆ์ธ๋“œ(.delegate(.๋งํฌ๋ชฉ๋ก_์•ˆ์ฝ์Œ)):
return .send(.inner(.๋งํฌ๋ชฉ๋ก_ํ™œ์„ฑํ™”(.unread)))
case .๋ฆฌ๋งˆ์ธ๋“œ(.delegate(.๋งํฌ๋ชฉ๋ก_์ฆ๊ฒจ์ฐพ๊ธฐ)):
return .send(.inner(.๋งํฌ๋ชฉ๋ก_ํ™œ์„ฑํ™”(.favorite)))
case let .๋ฆฌ๋งˆ์ธ๋“œ(.delegate(.๋งํฌ์ƒ์„ธ(content: content))):
return .send(.inner(.๋งํฌ์ƒ์„ธ_ํ™œ์„ฑํ™”(content.id)))
case let .๋ฆฌ๋งˆ์ธ๋“œ(.delegate(.๋งํฌ์ˆ˜์ •(id: contentId))):
return .send(.inner(.๋งํฌ์ˆ˜์ •_ํ™œ์„ฑํ™”(contentId)))
case .๋ฆฌ๋งˆ์ธ๋“œ:
return .none

// - MARK: ๋งํฌ๋ชฉ๋ก
case let .๋งํฌ๋ชฉ๋ก(.delegate(.๋งํฌ์ƒ์„ธ(content: content))):
return .send(.inner(.๋งํฌ์ƒ์„ธ_ํ™œ์„ฑํ™”(content.id)))
case let .๋งํฌ๋ชฉ๋ก(.delegate(.๋งํฌ์ˆ˜์ •(contentId: contentId))):
return .send(.inner(.๋งํฌ์ˆ˜์ •_ํ™œ์„ฑํ™”(contentId)))
case .๋งํฌ๋ชฉ๋ก:
return .none

// - MARK: ๋งํฌ์ถ”๊ฐ€๋ฐ์ˆ˜์ •
case .๋งํฌ์ถ”๊ฐ€(.delegate(.dismiss)):
state.columnVisibility = .doubleColumn
return .none
case .๋งํฌ์ถ”๊ฐ€(.delegate(.์ €์žฅํ•˜๊ธฐ_์™„๋ฃŒ)):
state.๋งํฌ์ถ”๊ฐ€ = .init()
return .merge(
.send(.๋ฆฌ๋งˆ์ธ๋“œ(.delegate(.์ปจํ…์ธ _์ƒ์„ธ๋ณด๊ธฐ_delegate_์œ„์ž„))),
.send(.๋งํฌ๋ชฉ๋ก(.delegate(.์ปจํ…์ธ _๋ชฉ๋ก_์กฐํšŒ)))
)
case .๋งํฌ์ถ”๊ฐ€(.delegate(.ํฌํ‚ท์ถ”๊ฐ€ํ•˜๊ธฐ)):
return .send(.inner(.ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •_ํ™œ์„ฑํ™”(nil)))
case .๋งํฌ์ถ”๊ฐ€:
return .none

// - MARK: ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •
case .ํฌํ‚ท์ถ”๊ฐ€๋ฐ์ˆ˜์ •:
return .none

// - MARK: ๊ฒ€์ƒ‰
case let .๊ฒ€์ƒ‰(_, .delegate(.๋งํฌ์ˆ˜์ •(contentId: contentId))):
return .send(.inner(.๋งํฌ์ˆ˜์ •_ํ™œ์„ฑํ™”(contentId)))
case let .๊ฒ€์ƒ‰(_, .delegate(.linkCardTapped(content: content))):
return .send(.inner(.๋งํฌ์ƒ์„ธ_ํ™œ์„ฑํ™”(content.id)))
case .๊ฒ€์ƒ‰(_, .delegate(.์ปจํ…์ธ _์‚ญ์ œ)):
return .send(.๋ฆฌ๋งˆ์ธ๋“œ(.delegate(.์ปจํ…์ธ _์ƒ์„ธ๋ณด๊ธฐ_delegate_์œ„์ž„)))
case .๊ฒ€์ƒ‰:
return .none

// - MARK: ์„ค์ •
case .์„ค์ •:
return .none

// - MARK: ๋งํฌ์ƒ์„ธ
case .๋งํฌ์ƒ์„ธ(.presented(.delegate(.์ฆ๊ฒจ์ฐพ๊ธฐ_๊ฐฑ์‹ _์™„๋ฃŒ))),
.๋งํฌ์ƒ์„ธ(.presented(.delegate(.์ปจํ…์ธ _์กฐํšŒ_์™„๋ฃŒ))),
.๋งํฌ์ƒ์„ธ(.presented(.delegate(.์ปจํ…์ธ _์‚ญ์ œ_์™„๋ฃŒ))):
var mergeEffect: [Effect<Action>] = [
.send(.๋ฆฌ๋งˆ์ธ๋“œ(.delegate(.์ปจํ…์ธ _์ƒ์„ธ๋ณด๊ธฐ_delegate_์œ„์ž„)))
]
switch state.path.last {
case .๊ฒ€์ƒ‰:
guard let id = state.path.ids.last else { break }
mergeEffect.append(
.send(.path(.element(id: id, action: .๊ฒ€์ƒ‰(.delegate(.์ปจํ…์ธ _๊ฒ€์ƒ‰)))))
)
default:
mergeEffect.append(.send(.๋งํฌ๋ชฉ๋ก(.delegate(.์ปจํ…์ธ _๋ชฉ๋ก_์กฐํšŒ))))
}
return .merge(mergeEffect)
case let .๋งํฌ์ƒ์„ธ(.presented(.delegate(.editButtonTapped(contentId: contentId)))):
return .send(.inner(.๋งํฌ์ˆ˜์ •_ํ™œ์„ฑํ™”(contentId)))
case .๋งํฌ์ƒ์„ธ:
return .none
case .categoryDetail:

// - MARK: ์•Œ๋žŒ
case let .์•Œ๋ฆผํ•จ(.presented(.delegate(.moveToContentEdit(id: contentId)))):
return .send(.inner(.๋งํฌ์ˆ˜์ •_ํ™œ์„ฑํ™”(contentId)))
case .์•Œ๋ฆผํ•จ(.presented(.delegate(.alertBoxDismiss))):
return .send(.์•Œ๋ฆผํ•จ(.dismiss))
case .์•Œ๋ฆผํ•จ:
return .none
case .contentSetting:

// - MARK: ๋งํฌ์ˆ˜์ •
case .๋งํฌ์ˆ˜์ •(.presented(.delegate(.์ €์žฅํ•˜๊ธฐ_์™„๋ฃŒ))):
var mergeEffect: [Effect<Action>] = [
.send(.๋ฆฌ๋งˆ์ธ๋“œ(.delegate(.์ปจํ…์ธ _์ƒ์„ธ๋ณด๊ธฐ_delegate_์œ„์ž„))),
.send(.๋งํฌ์ˆ˜์ •(.dismiss))
]
switch state.path.last {
case .๊ฒ€์ƒ‰:
guard let id = state.path.ids.last else { break }
mergeEffect.append(
.send(.path(.element(id: id, action: .๊ฒ€์ƒ‰(.delegate(.์ปจํ…์ธ _๊ฒ€์ƒ‰)))))
)
default:
mergeEffect.append(.send(.๋งํฌ๋ชฉ๋ก(.delegate(.์ปจํ…์ธ _๋ชฉ๋ก_์กฐํšŒ))))
}
return .merge(mergeEffect)
case .๋งํฌ์ˆ˜์ •:
return .none
}
}
Expand All @@ -152,4 +334,20 @@ private extension RemindSplitFeature {
func handleDelegateAction(_ action: Action.DelegateAction, state: inout State) -> Effect<Action> {
return .none
}

func handlePathAction(_ action: StackActionOf<Path>, state: inout State) -> Effect<Action> {
switch action {
case let .element(id: stackElementId, action: .๊ฒ€์ƒ‰(searchAction)):
return .send(.scope(.๊ฒ€์ƒ‰(stackElementId, searchAction)))
case .element, .popFrom, .push:
return .none
}
}
}

extension RemindSplitFeature {
@Reducer
public enum Path {
case ๊ฒ€์ƒ‰(PokitSearchFeature)
}
}
Loading

0 comments on commit 9f1e5fb

Please sign in to comment.