Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.0.0] TCA기반의 메인 공지화면 리스트 구현 #34

Merged
merged 9 commits into from
Nov 26, 2023
36 changes: 28 additions & 8 deletions KuringApp/KuringApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,23 @@
A9B4F0142ABCA86500354C00 /* NoticeApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B4F0132ABCA86500354C00 /* NoticeApp.swift */; };
A9B4F0162ABCA93400354C00 /* NoticeApp.Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B4F0152ABCA93400354C00 /* NoticeApp.Path.swift */; };
A9B4F0182ABCA9AF00354C00 /* NoticeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B4F0172ABCA9AF00354C00 /* NoticeDetailView.swift */; };
A9B4F01A2ABCAF9800354C00 /* NoticeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B4F0192ABCAF9800354C00 /* NoticeList.swift */; };
A9B4F01A2ABCAF9800354C00 /* NoticeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B4F0192ABCAF9800354C00 /* NoticeContentView.swift */; };
A9B4F01D2ABCB4CE00354C00 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B4F01C2ABCB4CE00354C00 /* SearchView.swift */; };
A9DAFA542AB1F04B0064F748 /* KuringApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DAFA532AB1F04B0064F748 /* KuringApp.swift */; };
A9DAFA562AB1F04B0064F748 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DAFA552AB1F04B0064F748 /* ContentView.swift */; };
A9DAFA582AB1F04C0064F748 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A9DAFA572AB1F04C0064F748 /* Assets.xcassets */; };
A9DAFA5B2AB1F04C0064F748 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A9DAFA5A2AB1F04C0064F748 /* Preview Assets.xcassets */; };
B11DBDCC2ACB370500501CA8 /* StaffRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B11DBDCB2ACB370500501CA8 /* StaffRow.swift */; };
B1CBFA662AC7113C00C1E0ED /* NoticeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CBFA652AC7113C00C1E0ED /* NoticeRow.swift */; };
CA52DF392AD58F4B009B9272 /* NoticeAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA52DF382AD58F4B009B9272 /* NoticeAppTests.swift */; };
CA640C192AD7FEFD002836E0 /* SubscriptionSegment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA640C182AD7FEFD002836E0 /* SubscriptionSegment.swift */; };
CA640C1B2AD8064B002836E0 /* NoticeProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA640C1A2AD8064B002836E0 /* NoticeProvider.swift */; };
CA640C1D2AD84E10002836E0 /* SubscriptionViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA640C1C2AD84E10002836E0 /* SubscriptionViewTests.swift */; };
CA640C1F2AD8525D002836E0 /* SubscriptionAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA640C1E2AD8525D002836E0 /* SubscriptionAppTests.swift */; };
CAD5A4272B10723500DED0D5 /* NoticeTypePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD5A4262B10723500DED0D5 /* NoticeTypePicker.swift */; };
CAD5A4292B10724100DED0D5 /* NoticeTypeColumn.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD5A4282B10724100DED0D5 /* NoticeTypeColumn.swift */; };
CAD5A42B2B10750800DED0D5 /* DepartmentSelectorLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD5A42A2B10750800DED0D5 /* DepartmentSelectorLink.swift */; };
CAD5A42D2B1077C200DED0D5 /* NoticeContentView.NoDepartment.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD5A42C2B1077C200DED0D5 /* NoticeContentView.NoDepartment.swift */; };
CAD5A42F2B11DBAF00DED0D5 /* NoticeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD5A42E2B11DBAF00DED0D5 /* NoticeList.swift */; };
DF331DAB2AC917E100D0BB08 /* kuring_app.png in Resources */ = {isa = PBXBuildFile; fileRef = DF331DA72AC917E000D0BB08 /* kuring_app.png */; };
DF331DAC2AC917E100D0BB08 /* kuring_app_classic.png in Resources */ = {isa = PBXBuildFile; fileRef = DF331DA82AC917E000D0BB08 /* kuring_app_classic.png */; };
DF331DAD2AC917E100D0BB08 /* kuring_app_sketch.png in Resources */ = {isa = PBXBuildFile; fileRef = DF331DA92AC917E000D0BB08 /* kuring_app_sketch.png */; };
Expand Down Expand Up @@ -63,7 +68,7 @@
A9B4F0132ABCA86500354C00 /* NoticeApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeApp.swift; sourceTree = "<group>"; };
A9B4F0152ABCA93400354C00 /* NoticeApp.Path.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeApp.Path.swift; sourceTree = "<group>"; };
A9B4F0172ABCA9AF00354C00 /* NoticeDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeDetailView.swift; sourceTree = "<group>"; };
A9B4F0192ABCAF9800354C00 /* NoticeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeList.swift; sourceTree = "<group>"; };
A9B4F0192ABCAF9800354C00 /* NoticeContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeContentView.swift; sourceTree = "<group>"; };
A9B4F01C2ABCB4CE00354C00 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
A9DAFA502AB1F04B0064F748 /* KuringApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KuringApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
A9DAFA532AB1F04B0064F748 /* KuringApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KuringApp.swift; sourceTree = "<group>"; };
Expand All @@ -72,12 +77,17 @@
A9DAFA5A2AB1F04C0064F748 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
A9DAFA662AB1F09B0064F748 /* KuringModulePackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = KuringModulePackage; path = ../KuringModulePackage; sourceTree = "<group>"; };
B11DBDCB2ACB370500501CA8 /* StaffRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaffRow.swift; sourceTree = "<group>"; };
B1CBFA652AC7113C00C1E0ED /* NoticeRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRow.swift; sourceTree = "<group>"; };
CA52DF2F2AD58DF2009B9272 /* KuringAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KuringAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
CA52DF382AD58F4B009B9272 /* NoticeAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeAppTests.swift; sourceTree = "<group>"; };
CA640C182AD7FEFD002836E0 /* SubscriptionSegment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionSegment.swift; sourceTree = "<group>"; };
CA640C1A2AD8064B002836E0 /* NoticeProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeProvider.swift; sourceTree = "<group>"; };
CA640C1C2AD84E10002836E0 /* SubscriptionViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionViewTests.swift; sourceTree = "<group>"; };
CA640C1E2AD8525D002836E0 /* SubscriptionAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionAppTests.swift; sourceTree = "<group>"; };
CAD5A4262B10723500DED0D5 /* NoticeTypePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeTypePicker.swift; sourceTree = "<group>"; };
CAD5A4282B10724100DED0D5 /* NoticeTypeColumn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeTypeColumn.swift; sourceTree = "<group>"; };
CAD5A42A2B10750800DED0D5 /* DepartmentSelectorLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DepartmentSelectorLink.swift; sourceTree = "<group>"; };
CAD5A42C2B1077C200DED0D5 /* NoticeContentView.NoDepartment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeContentView.NoDepartment.swift; sourceTree = "<group>"; };
CAD5A42E2B11DBAF00DED0D5 /* NoticeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeList.swift; sourceTree = "<group>"; };
DF062D4A2AC87B6D00FC48C0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
DF331DA72AC917E000D0BB08 /* kuring_app.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = kuring_app.png; sourceTree = "<group>"; };
DF331DA82AC917E000D0BB08 /* kuring_app_classic.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = kuring_app_classic.png; sourceTree = "<group>"; };
Expand Down Expand Up @@ -135,7 +145,6 @@
A965B7A82AC074BD0026ECDC /* WillBeRemoved */ = {
isa = PBXGroup;
children = (
CA640C1A2AD8064B002836E0 /* NoticeProvider.swift */,
);
path = WillBeRemoved;
sourceTree = "<group>";
Expand Down Expand Up @@ -172,7 +181,13 @@
isa = PBXGroup;
children = (
A9B4F0172ABCA9AF00354C00 /* NoticeDetailView.swift */,
A9B4F0192ABCAF9800354C00 /* NoticeList.swift */,
A9B4F0192ABCAF9800354C00 /* NoticeContentView.swift */,
CAD5A42E2B11DBAF00DED0D5 /* NoticeList.swift */,
CAD5A42C2B1077C200DED0D5 /* NoticeContentView.NoDepartment.swift */,
B1CBFA652AC7113C00C1E0ED /* NoticeRow.swift */,
CAD5A4262B10723500DED0D5 /* NoticeTypePicker.swift */,
CAD5A4282B10724100DED0D5 /* NoticeTypeColumn.swift */,
CAD5A42A2B10750800DED0D5 /* DepartmentSelectorLink.swift */,
);
path = NoticeList;
sourceTree = "<group>";
Expand Down Expand Up @@ -375,15 +390,20 @@
A965B7AB2AC0750E0026ECDC /* SubscriptionView.swift in Sources */,
A91455002ACF3A3D00B82A8E /* SetttingsApp.Path.swift in Sources */,
A91455062ACF451E00B82A8E /* AppIcons.swift in Sources */,
A9B4F01A2ABCAF9800354C00 /* NoticeList.swift in Sources */,
A9B4F01A2ABCAF9800354C00 /* NoticeContentView.swift in Sources */,
A9DAFA562AB1F04B0064F748 /* ContentView.swift in Sources */,
CA640C1B2AD8064B002836E0 /* NoticeProvider.swift in Sources */,
CAD5A42F2B11DBAF00DED0D5 /* NoticeList.swift in Sources */,
A9B4F01D2ABCB4CE00354C00 /* SearchView.swift in Sources */,
CAD5A42D2B1077C200DED0D5 /* NoticeContentView.NoDepartment.swift in Sources */,
CAD5A4272B10723500DED0D5 /* NoticeTypePicker.swift in Sources */,
A965B7A32AC013060026ECDC /* DepartmentEditor.swift in Sources */,
A965B7A52AC013BF0026ECDC /* DepartmentSelector.swift in Sources */,
B11DBDCC2ACB370500501CA8 /* StaffRow.swift in Sources */,
A9B4F0162ABCA93400354C00 /* NoticeApp.Path.swift in Sources */,
CAD5A42B2B10750800DED0D5 /* DepartmentSelectorLink.swift in Sources */,
A9B4F0182ABCA9AF00354C00 /* NoticeDetailView.swift in Sources */,
CAD5A4292B10724100DED0D5 /* NoticeTypeColumn.swift in Sources */,
B1CBFA662AC7113C00C1E0ED /* NoticeRow.swift in Sources */,
A965B7AF2AC084D20026ECDC /* SubscriptionApp.swift in Sources */,
A91455032ACF44CE00B82A8E /* KuringIcon.swift in Sources */,
CA640C192AD7FEFD002836E0 /* SubscriptionSegment.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion KuringApp/KuringApp/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct ContentView: View {
NoticeAppView(
store: Store(
initialState: NoticeAppFeature.State(
noticeList: NoticeListFeature.State(notices: [.random])
noticeList: NoticeListFeature.State()
),
reducer: { NoticeAppFeature() }
)
Expand Down
67 changes: 36 additions & 31 deletions KuringApp/KuringApp/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,43 @@
<plist version="1.0">
<dict>
<key>CFBundleIcons</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>kuring_app</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>kuring_app</string>
</array>
</dict>
<key>kuring_app_classic</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>kuring_app_classic</string>
</array>
</dict>
<key>kuring_app_blueprint</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>kuring_app_blueprint</string>
</array>
</dict>
<key>kuring_app_sketch</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>kuring_app_sketch</string>
</array>
</dict>
</dict>
</dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>kuring_app</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>kuring_app</string>
</array>
</dict>
<key>kuring_app_classic</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>kuring_app_classic</string>
</array>
</dict>
<key>kuring_app_blueprint</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>kuring_app_blueprint</string>
</array>
</dict>
<key>kuring_app_sketch</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>kuring_app_sketch</string>
</array>
</dict>
</dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>
81 changes: 71 additions & 10 deletions KuringApp/KuringApp/NoticeApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,33 @@

import Model
import SwiftUI
import NoticeListFeature
import ComposableArchitecture

struct NoticeAppFeature: Reducer {
struct State: Equatable {
var path = StackState<Path.State>()
// MARK: 네비게이션

/// 루트
var noticeList = NoticeListFeature.State()
/// 스택 네비게이션
var path = StackState<Path.State>()
/// 트리 네비게이션 - ``SubscriptionAppFeature``
@PresentationState var changeSubscription: SubscriptionAppFeature.State?
}

enum Action {
case path(StackAction<Path.State, Path.Action>)
/// 루트(``NoticeListFeature``) 액션
case noticeList(NoticeListFeature.Action)

/// 스택 네비게이션 액션 (``NoticeAppFeature/Path``)
case path(StackAction<Path.State, Path.Action>)

/// 구독 변경 버튼을 탭한 경우
case changeSubscriptionButtonTapped

/// ``SubscriptionAppFeature`` 의 Presentation 액션
case changeSubscription(PresentationAction<SubscriptionAppFeature.Action>)
}

var body: some ReducerOf<Self> {
Expand Down Expand Up @@ -46,13 +62,25 @@ struct NoticeAppFeature: Reducer {
return .none
}

case .noticeList:
case .changeSubscription(.presented(.subscriptionView(.subscriptionResponse))):
/// ``SubscriptionAppFeature`` 액션
state.changeSubscription = nil
return .none

case .changeSubscriptionButtonTapped:
state.changeSubscription = SubscriptionAppFeature.State()
return .none

case .noticeList, .changeSubscription:
return .none
}
}
.forEach(\.path, action: /Action.path) {
Path()
}
.ifLet(\.$changeSubscription, action: /Action.changeSubscription) {
SubscriptionAppFeature()
}
}
}

Expand All @@ -61,12 +89,46 @@ struct NoticeAppView: View {

var body: some View {
NavigationStackStore(self.store.scope(state: \.path, action: { .path($0) })) {
NoticeList(
store: self.store.scope(
state: \.noticeList,
action: { .noticeList($0) }
WithViewStore(self.store, observe: { $0 }) { viewStore in
NoticeContentView(
store: self.store.scope(
state: \.noticeList,
action: { .noticeList($0) }
)
)
)
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
Image("appIconLabel", bundle: Bundle.noticeList)
}

ToolbarItemGroup(placement: .navigationBarTrailing) {
Button {
// TODO: - to SearchView
} label: {
Image(systemName: "magnifyingglass")
.foregroundStyle(Color.black)
}
}

ToolbarItemGroup(placement: .navigationBarTrailing) {
// MARK: 푸시 알림 선택 진입
Button {
viewStore.send(.changeSubscriptionButtonTapped)
} label: {
Image(systemName: "bell")
.foregroundStyle(.black)
}
}
}
.sheet(
store: self.store.scope(
state: \.$changeSubscription,
action: { .changeSubscription($0) }
)
) { store in
SubscriptionApp(store: store)
}
}
} destination: { state in
switch state {
case .detail:
Expand Down Expand Up @@ -97,15 +159,14 @@ struct NoticeAppView: View {
}
}
}

}
}

#Preview {
NoticeAppView(
store: Store(
initialState: NoticeAppFeature.State(
noticeList: NoticeListFeature.State(notices: [.random])
noticeList: NoticeListFeature.State()
),
reducer: { NoticeAppFeature() }
)
Expand Down
47 changes: 47 additions & 0 deletions KuringApp/KuringApp/NoticeList/DepartmentSelectorLink.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// DepartmentSelectorLink.swift
// KuringApp
//
// Created by 이재성 on 11/24/23.
//

import Model
import SwiftUI

struct DepartmentSelectorLink: View {
let department: NoticeProvider
@Binding var isLoading: Bool
let action: () -> Void

var body: some View {
HStack {
Text(department.korName)
.font(.system(size: 18, weight: .semibold))
.foregroundStyle(Color.black.opacity(0.8))

Spacer()

if isLoading {
ProgressView()
} else {
Image(systemName: "chevron.right")
}
}
.contentShape(Rectangle())
.padding(.horizontal, 20)
.padding(.vertical, 16)
.onTapGesture {
guard !isLoading else { return }
action()
x-0o0 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

#Preview {
DepartmentSelectorLink(
department: .init(name: "산업디자인학과", hostPrefix: "kuid", korName: "산업디자인학과", category: .학과),
isLoading: .constant(false)
) {
// 액션 정의. 예) `viewStore.send(.changeDepartmentButtonTapped)`
}
}
Loading