diff --git a/Qapple/Qapple/SourceCode/Entity/AcademyEventFor4th.swift b/Qapple/Qapple/SourceCode/Entity/AcademyEventFor4th.swift index 5b0f4ac7..08a2da1f 100644 --- a/Qapple/Qapple/SourceCode/Entity/AcademyEventFor4th.swift +++ b/Qapple/Qapple/SourceCode/Entity/AcademyEventFor4th.swift @@ -99,6 +99,11 @@ enum AcademyEventFor4th: CaseIterable { let (startDate, endDate) = period return daysBetween(startDate, endDate) + 1 } + + /// 현재 이벤트의 남은 일수를 반환합니다. + var leftDays: Int { + daysBetween(.now, self.period.1) + 1 + } } // MARK: - Helper diff --git a/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowFeature.swift b/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowFeature.swift index fb8c0271..22314066 100644 --- a/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowFeature.swift @@ -238,7 +238,7 @@ extension MainFlowFeature { case bulletinBoard(BulletinBoardFeature) case bulletinBoardSearch(BulletinBoardSearchFeature) case bulletinBoardPost(BulletinBoardPostFeature) - case comment(CommentFeature) + case comment(BoardCommentFeature) case profileEdit(ProfileEditFeature) case myAnswerList(MyAnswerListFeature) case peopleWhoMadeQapple diff --git a/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowView.swift b/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowView.swift index 47d449c7..c89e6d20 100644 --- a/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowView.swift +++ b/Qapple/Qapple/SourceCode/Feature/1.MainFlow/MainFlowView.swift @@ -52,7 +52,7 @@ struct MainFlowView: View { case let .bulletinBoard(store): BulletinBoardView(store: store) case let .bulletinBoardSearch(store): BulletinBoardSearchView(store: store) case let .bulletinBoardPost(store): BulletinBoardPostView(store: store) - case let .comment(store): CommentView(store: store) + case let .comment(store): BoardCommentView(store: store) case let .profileEdit(store): ProfileEditView(store: store) case let .myAnswerList(store): MyAnswerListView(store: store) case .peopleWhoMadeQapple: PeopleWhoMadeQappleView() diff --git a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionFeature.swift b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionFeature.swift index 28b1f2d9..0480c398 100644 --- a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionFeature.swift @@ -28,6 +28,7 @@ struct TodayQuestionFeature { enum Action { case onAppear case onDisappear + case active case refresh case mainQuestionResponse(Question) case answerListResponse([Answer]) @@ -58,7 +59,7 @@ struct TodayQuestionFeature { var body: some ReducerOf { Reduce { state, action in switch action { - case .onAppear, .refresh: + case .onAppear, .active, .refresh: return .run { [isFirstLaunch = state.isFirstLaunch] send in if isFirstLaunch { await send(.toggleLoading(true), animation: .bouncy) } do { diff --git a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionView.swift b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionView.swift index bc334f7c..411f7727 100644 --- a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionView.swift +++ b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-2.TodayQuestion/TodayQuestionView.swift @@ -10,6 +10,8 @@ import SwiftUI struct TodayQuestionView: View { + @Environment(\.scenePhase) private var scenePhase + @Bindable var store: StoreOf var body: some View { @@ -26,6 +28,11 @@ struct TodayQuestionView: View { } .background(.second) .scrollIndicators(.hidden) + .onChange(of: scenePhase) { _, newPhase in + if newPhase == .active { + store.send(.active) + } + } .onAppear { store.send(.onAppear) } diff --git a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-7.AnswerCommentList/AnswerCommentView.swift b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-7.AnswerCommentList/AnswerCommentView.swift index 32d51eca..1b3385a5 100644 --- a/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-7.AnswerCommentList/AnswerCommentView.swift +++ b/Qapple/Qapple/SourceCode/Feature/2.QuestionTab/2-7.AnswerCommentList/AnswerCommentView.swift @@ -85,51 +85,63 @@ private struct CommentListView: View { var body: some View { ZStack { - ScrollView { - LazyVStack(spacing: 0) { - seperator - // TODO: 4/14 데이터 연결 필요 -// ForEach(Array(self.store.commentList.enumerated()), id: \.offset) { index, comment in -// AnswerCommentCell( -// comment: comment, -// like: { -// store.send(.likeCommentButtonTapped(comment)) -// }, -// delete: { -// store.send(.deleteCommentButtonTapped(comment)) -// }, -// report: { -// store.send(.reportButtonTapped(comment)) -// } -// ) -// .configurePagination( -// store.commentList, -// currentIndex: index, -// hasNext: store.paginationInfo.hasNext, -// pagination: { -// store.send(.pagination) -// } -// ) -// .disabled(store.isLoading) -// -// seperator -// } - - ForEach(AnswerCommentFeature.sampleComment) { comment in - AnswerCommentCell( - comment: comment, - like: { - store.send(.likeCommentButtonTapped(comment)) - }, - delete: { - store.send(.deleteCommentButtonTapped(comment)) - }, - report: { - store.send(.reportButtonTapped(comment)) - } - ) + VStack { + seperator + + HStack { + Text("댓글") + .pretendard(.medium, 14) + .foregroundStyle(.sub3) + Spacer() + } + .padding(.top, 12) + .padding(.horizontal, 20) + + ScrollView { + LazyVStack(spacing: 0) { + // TODO: 4/14 데이터 연결 필요 + // ForEach(Array(self.store.commentList.enumerated()), id: \.offset) { index, comment in + // AnswerCommentCell( + // comment: comment, + // like: { + // store.send(.likeCommentButtonTapped(comment)) + // }, + // delete: { + // store.send(.deleteCommentButtonTapped(comment)) + // }, + // report: { + // store.send(.reportButtonTapped(comment)) + // } + // ) + // .configurePagination( + // store.commentList, + // currentIndex: index, + // hasNext: store.paginationInfo.hasNext, + // pagination: { + // store.send(.pagination) + // } + // ) + // .disabled(store.isLoading) + // + // seperator + // } - seperator + ForEach(AnswerCommentFeature.sampleComment) { comment in + AnswerCommentCell( + comment: comment, + like: { + store.send(.likeCommentButtonTapped(comment)) + }, + delete: { + store.send(.deleteCommentButtonTapped(comment)) + }, + report: { + store.send(.reportButtonTapped(comment)) + } + ) + + seperator + } } } } diff --git a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardCell.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardCell.swift index 142f4afd..8fac5022 100644 --- a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardCell.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardCell.swift @@ -60,10 +60,11 @@ private struct NormalBoardCell: View { board: board, like: like ) + .padding(.top, 16) .padding(.horizontal, 16) Divider() - .padding(.top, 16) + .padding(.top, 24) } .background(Background.first) } @@ -158,7 +159,7 @@ private struct ContentView: View { board: board, like: like ) - .padding(.top, 12) + .padding(.top, 16) .disabled(board.isReported) } } diff --git a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift index 7a68c4da..333b9916 100644 --- a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardFeature.swift @@ -16,6 +16,7 @@ struct BulletinBoardFeature { @Presents var alert: AlertState? var bulletinBoardList: [BulletinBoard] = [] var todayQuestion: Question = .initialState + var event: AcademyEventFor4th = .fourthStart var paginationInfo = QappleAPI.PaginationInfo(threshold: "", hasNext: false) var isLoading: Bool = false var isFirstLaunch = true @@ -23,6 +24,7 @@ struct BulletinBoardFeature { enum Action { case onAppear + case active case refresh case pagination case bulletinBoardListResponse(Question, [BulletinBoard], QappleAPI.PaginationInfo) @@ -61,9 +63,12 @@ struct BulletinBoardFeature { @Dependency(\.bulletinBoardRepository) var bulletinBoardRepository var body: some ReducerOf { - Reduce { state,action in + Reduce { state, action in switch action { - case .onAppear, .refresh: + case .onAppear, .active, .refresh: + if let currentEvent = AcademyEventFor4th.currentEvent { + state.event = currentEvent + } return .run { [isFirstLaunch = state.isFirstLaunch] send in if isFirstLaunch { await send(.toggleLoading(true), animation: .bouncy) } do { diff --git a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardView.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardView.swift index 7c57d84d..eca01ef6 100644 --- a/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardView.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-1.BulletinBoard/BulletinBoardView.swift @@ -12,6 +12,8 @@ import ComposableArchitecture struct BulletinBoardView: View { + @Environment(\.scenePhase) private var scenePhase + @Bindable var store: StoreOf var body: some View { @@ -22,7 +24,11 @@ struct BulletinBoardView: View { .padding(.bottom, 20) } .background(.first) - + .onChange(of: scenePhase) { _, newPhase in + if newPhase == .active { + store.send(.active) + } + } .onAppear{ store.send(.onAppear) } @@ -67,7 +73,7 @@ private struct BulletinBoardContentView: View { Button { store.send(.academyDayCounterTapped) } label: { - QPAcademyDayCounter() + QPAcademyDayCounter(event: store.event) .padding(.top, 8) .padding(.horizontal, 16) } diff --git a/Qapple/Qapple/SourceCode/Feature/4.Comment/CommentCell.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-4.BoardComment/BoardCommentCell.swift similarity index 99% rename from Qapple/Qapple/SourceCode/Feature/4.Comment/CommentCell.swift rename to Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-4.BoardComment/BoardCommentCell.swift index 22babc82..fee573b4 100644 --- a/Qapple/Qapple/SourceCode/Feature/4.Comment/CommentCell.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-4.BoardComment/BoardCommentCell.swift @@ -1,5 +1,5 @@ // -// CommentCell.swift +// BoardCommentCell.swift // Qapple // // Created by 문인범 on 1/21/25. @@ -7,7 +7,7 @@ import SwiftUI -struct CommentCell: View { +struct BoardCommentCell: View { let comment: BoardComment let like: () -> Void let delete: () -> Void @@ -272,7 +272,7 @@ private struct CommentReportButton: View { anonymityId: 2 ) - CommentCell( + BoardCommentCell( comment: comment, like: {}, delete: {}, diff --git a/Qapple/Qapple/SourceCode/Feature/4.Comment/CommentFeature.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-4.BoardComment/BoardCommentFeature.swift similarity index 98% rename from Qapple/Qapple/SourceCode/Feature/4.Comment/CommentFeature.swift rename to Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-4.BoardComment/BoardCommentFeature.swift index 46d62baa..b2c3dce1 100644 --- a/Qapple/Qapple/SourceCode/Feature/4.Comment/CommentFeature.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-4.BoardComment/BoardCommentFeature.swift @@ -1,5 +1,5 @@ // -// CommentFeature.swift +// BoardCommentFeature.swift // Qapple // // Created by 문인범 on 1/21/25. @@ -9,7 +9,7 @@ import Foundation import ComposableArchitecture @Reducer -struct CommentFeature { +struct BoardCommentFeature { @ObservableState struct State: Equatable { var board: BulletinBoard @@ -273,7 +273,7 @@ struct CommentFeature { // MARK: - BulletinBoardSheet -extension CommentFeature { +extension BoardCommentFeature { @Reducer(state: .equatable) enum Sheet { case seeMore(SeeMoreSheetFeature) @@ -282,7 +282,7 @@ extension CommentFeature { // MARK: - CommentAlert -extension AlertState where Action == CommentFeature.Action.Alert { +extension AlertState where Action == BoardCommentFeature.Action.Alert { static func confirmDeletion(_ boardCommentId: Int) -> Self { return Self { TextState("정말로 댓글을 삭제하시겠습니까?") @@ -305,7 +305,7 @@ extension AlertState where Action == CommentFeature.Action.Alert { } } -extension CommentFeature { +extension BoardCommentFeature { // 이름을 익명화 해주는 method private func anonymizeCommentList(_ BoardWriterId: Int, _ commentList: [BoardComment]) -> [BoardComment] { var anonymousArray: [Int: Int] = [:] diff --git a/Qapple/Qapple/SourceCode/Feature/4.Comment/CommentView.swift b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-4.BoardComment/BoardCommentView.swift similarity index 68% rename from Qapple/Qapple/SourceCode/Feature/4.Comment/CommentView.swift rename to Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-4.BoardComment/BoardCommentView.swift index 07a6aecb..04b5ecef 100644 --- a/Qapple/Qapple/SourceCode/Feature/4.Comment/CommentView.swift +++ b/Qapple/Qapple/SourceCode/Feature/3.BulletinBoard/3-4.BoardComment/BoardCommentView.swift @@ -1,5 +1,5 @@ // -// CommentView.swift +// BoardCommentView.swift // Qapple // // Created by 문인범 on 1/21/25. @@ -8,8 +8,8 @@ import SwiftUI import ComposableArchitecture -struct CommentView: View { - @Bindable var store: StoreOf +struct BoardCommentView: View { + @Bindable var store: StoreOf private let screenWidth: CGFloat = UIScreen.main.bounds.width @@ -44,7 +44,7 @@ struct CommentView: View { AddCommentView(store: store) .frame(width: screenWidth) .padding(.bottom, 8) - + } .background(Color.bk) .onTapGesture { @@ -78,38 +78,49 @@ struct CommentView: View { // MARK: - CommentListView private struct CommentListView: View { - let store: StoreOf + let store: StoreOf var body: some View { ZStack { - ScrollView { - LazyVStack(spacing: 0) { - seperator - - ForEach(Array(self.store.commentList.enumerated()), id: \.offset) { index, comment in - CommentCell( - comment: comment, - like: { - store.send(.likeCommentButtonTapped(comment)) - }, - delete: { - store.send(.deleteCommentButtonTapped(comment)) - }, - report: { - store.send(.reportButtonTapped(comment)) - } - ) - .configurePagination( - store.commentList, - currentIndex: index, - hasNext: store.paginationInfo.hasNext, - pagination: { - store.send(.pagination) - } - ) - .disabled(store.isLoading) - - seperator + VStack { + seperator + + HStack { + Text("댓글") + .pretendard(.medium, 14) + .foregroundStyle(.sub3) + Spacer() + } + .padding(.top, 12) + .padding(.horizontal, 20) + + ScrollView { + LazyVStack(spacing: 0) { + ForEach(Array(self.store.commentList.enumerated()), id: \.offset) { index, comment in + BoardCommentCell( + comment: comment, + like: { + store.send(.likeCommentButtonTapped(comment)) + }, + delete: { + store.send(.deleteCommentButtonTapped(comment)) + }, + report: { + store.send(.reportButtonTapped(comment)) + } + ) + .configurePagination( + store.commentList, + currentIndex: index, + hasNext: store.paginationInfo.hasNext, + pagination: { + store.send(.pagination) + } + ) + .disabled(store.isLoading) + + seperator + } } } } @@ -140,7 +151,7 @@ private struct CommentListView: View { // MARK: - AddCommentView private struct AddCommentView: View { - @Bindable var store: StoreOf + @Bindable var store: StoreOf var body: some View { HStack(alignment: .bottom) { @@ -173,9 +184,9 @@ private struct AddCommentView: View { // MARK: - Preview #Preview { - CommentView( + BoardCommentView( store: Store( - initialState: CommentFeature.State( + initialState: BoardCommentFeature.State( board: BulletinBoard( id: 1, writerId: 1, @@ -191,6 +202,6 @@ private struct AddCommentView: View { ) ) ){ - CommentFeature() - }) + BoardCommentFeature() + }) } diff --git a/Qapple/Qapple/SourceCode/UIComponent/QPAcademyDayCounter.swift b/Qapple/Qapple/SourceCode/UIComponent/QPAcademyDayCounter.swift index f7794f5b..744b1415 100644 --- a/Qapple/Qapple/SourceCode/UIComponent/QPAcademyDayCounter.swift +++ b/Qapple/Qapple/SourceCode/UIComponent/QPAcademyDayCounter.swift @@ -9,67 +9,7 @@ import SwiftUI struct QPAcademyDayCounter: View { - var body: some View { - Group { - if let currentEvent = AcademyEventFor4th.currentEvent { - ContentView( - event: currentEvent, - dayLeft: currentEvent.period.1.dayLeft - ) - } else { - if let nextEvent = AcademyEventFor4th.nextEvent { - Header( - event: nextEvent, - dayLeft: nextEvent.period.0.dayLeft, - isCurrentEvent: false - ) - } - } - } - .padding(.vertical, 14) - .padding(.horizontal, 20) - .background(.second) - .clipShape(RoundedRectangle(cornerRadius: 12)) - } -} - -// MARK: - ContentView - -private struct ContentView: View { - - let event: AcademyEventFor4th - let dayLeft: Int - - var body: some View { - VStack(spacing: 0) { - Header(event: event, dayLeft: dayLeft, isCurrentEvent: true) - -// HStack { -// Text(event.period.0.formatting(.mdKorean)) -// Spacer() -// Text(event.period.1.formatting(.mdKorean)) -// } -// .padding(.top, 16) -// .padding(.horizontal, 2) -// .foregroundStyle(.main).opacity(0.6) -// .pretendard(.semiBold, 14) - -// ProgressBar( -// event: event, -// dayLeft: dayLeft -// ) -// .padding(.top, 8) - } - } -} - -// MARK: - Header - -private struct Header: View { - let event: AcademyEventFor4th - let dayLeft: Int - let isCurrentEvent: Bool var body: some View { HStack(spacing: 8) { @@ -88,52 +28,23 @@ private struct Header: View { .foregroundStyle(.main).opacity(0.8) .pretendard(.semiBold, 17) } + .padding(.vertical, 14) + .padding(.horizontal, 20) + .background(.second) + .clipShape(RoundedRectangle(cornerRadius: 12)) } private var dayLeftText: String { - if dayLeft == 0 { + if event.leftDays == 0 { "마지막 날" } else { - "\(dayLeft)\(isCurrentEvent ? "일 남음" : "일 후 시작")" + "\(event.leftDays)일 남음" } } } -// MARK: - ProgressBar - -private struct ProgressBar: View { - - let event: AcademyEventFor4th - let dayLeft: Int - - var body: some View { - GeometryReader { proxy in - ZStack(alignment: .leading) { - RoundedRectangle(cornerRadius: 10) - .foregroundStyle(TextLabel.ph) - .frame(width: proxy.size.width) - .clipShape(RoundedRectangle(cornerRadius: 10)) - - RoundedRectangle(cornerRadius: 10) - .foregroundStyle(.button) - .frame(width: proxy.size.width * progress) - } - } - .frame(height: 16) - } - - /// Progress 값을 반환합니다. - private var progress: Double { - let (startDate, endDate) = event.period - let total = Calendar.utc - .dateComponents([.day], from: startDate, to: endDate) - .day ?? 0 - return Double(total - dayLeft) / Double(total) - } -} - // MARK: - Preview #Preview { - QPAcademyDayCounter() + QPAcademyDayCounter(event: .currentEvent!) }