From f5bb5825cb6c74ab2d3b5201dbb841f72ebee2a6 Mon Sep 17 00:00:00 2001 From: jihun Date: Tue, 2 Jun 2026 20:36:32 +0900 Subject: [PATCH 01/11] =?UTF-8?q?fix:=20=EC=9D=B8=EC=A6=9D=EC=83=B7=20?= =?UTF-8?q?=EC=83=81=ED=95=98=20=EC=8A=A4=EC=99=80=EC=9D=B4=ED=94=84=20?= =?UTF-8?q?=EC=95=88=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-?= =?UTF-8?q?=20#337?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Detail/GoalDetailView.swift | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift b/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift index e329fdb3..f748f95f 100644 --- a/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift +++ b/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift @@ -188,8 +188,22 @@ private extension GoalDetailView { cardOffset = repeatedCardOffset(for: width) isCrossingDuringDrag = shouldCrossCards(for: width) } - .onEnded { _ in + .onEnded { value in guard !store.isEditing else { return } + + let translation = value.translation + let width = resistedDragWidth( + for: translation.width, + velocity: value.velocity.width + ) + + guard abs(width) >= abs(translation.height) else { + withAnimation(.spring(response: 0.2, dampingFraction: 0.94)) { + resetDragState() + } + return + } + withAnimation(.spring(response: 0.2, dampingFraction: 0.94)) { resetDragState() store.send(.view(.cardSwiped)) From 8c82d677e82c814f526fc05571d4143639e9ce24 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 14:06:56 +0900 Subject: [PATCH 02/11] =?UTF-8?q?fix:=20disable=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=95=A1=EC=85=98=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=20?= =?UTF-8?q?-=20#337?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Button/Round/TXRoundButton.swift | 10 +++++++++- .../Sources/Components/Button/TXButton.swift | 4 ++++ .../Sources/Components/Card/Goal/GoalCardView.swift | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift b/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift index ea895461..0266469e 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift @@ -9,12 +9,20 @@ import SwiftUI struct TXRoundButton: View { let shape: TXButtonShape + let allowsActionWhenDisabled: Bool let onTap: () -> Void public var body: some View { if case let .round(style, size, state) = shape { Button { - onTap() + switch state { + case .disabled: + if allowsActionWhenDisabled { + onTap() + } + case .standard: + onTap() + } } label: { ZStack { Capsule() diff --git a/Projects/Shared/DesignSystem/Sources/Components/Button/TXButton.swift b/Projects/Shared/DesignSystem/Sources/Components/Button/TXButton.swift index 6af90249..cf8d27e7 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Button/TXButton.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Button/TXButton.swift @@ -24,6 +24,7 @@ import SwiftUI /// ``` public struct TXButton: View { let shape: TXButtonShape + let allowsActionWhenDisabled: Bool let onTap: () -> Void /// 버튼을 생성합니다. @@ -41,9 +42,11 @@ public struct TXButton: View { /// ``` public init( shape: TXButtonShape, + allowsActionWhenDisabled: Bool = false, onTap: @escaping () -> Void ) { self.shape = shape + self.allowsActionWhenDisabled = allowsActionWhenDisabled self.onTap = onTap } @@ -65,6 +68,7 @@ public struct TXButton: View { case .round: TXRoundButton( shape: shape, + allowsActionWhenDisabled: allowsActionWhenDisabled, onTap: onTap ) diff --git a/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift b/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift index 524cb235..3bddb7d2 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift @@ -169,6 +169,7 @@ private extension GoalCardView { size: .s, state: isButtonDisabled ? .disabled : .standard ), + allowsActionWhenDisabled: true, onTap: { buttonAction?() } ) .padding(.bottom, 14) From a3bb2ab60caf10ac5ef007ed80a63ec6e3dff083 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 14:09:28 +0900 Subject: [PATCH 03/11] =?UTF-8?q?fix:=20dropdownButton=20=ED=85=8D?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A3=BC=EC=9E=85=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20-=20#337?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MakeGoal/Sources/MakeGoalView.swift | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Projects/Feature/MakeGoal/Sources/MakeGoalView.swift b/Projects/Feature/MakeGoal/Sources/MakeGoalView.swift index 147d9888..7f94a323 100644 --- a/Projects/Feature/MakeGoal/Sources/MakeGoalView.swift +++ b/Projects/Feature/MakeGoal/Sources/MakeGoalView.swift @@ -177,7 +177,9 @@ private extension MakeGoalView { Spacer() if store.showPeriodCount { - dropDownButton { store.send(.view(.periodSelected)) } + dropDownButton(text: store.periodCountText) { + store.send(.view(.periodSelected)) + } } } } @@ -190,7 +192,9 @@ private extension MakeGoalView { Spacer() - dropDownButton { store.send(.view(.startDateTapped)) } + dropDownButton(text: store.startDateText) { + store.send(.view(.startDateTapped)) + } } .frame(height: 32) .padding(.vertical, 16) @@ -214,7 +218,9 @@ private extension MakeGoalView { Spacer() - dropDownButton { store.send(.view(.endDateTapped)) } + dropDownButton(text: store.endDateText) { + store.send(.view(.endDateTapped)) + } } .padding(.vertical, 21.5) } @@ -236,9 +242,12 @@ private extension MakeGoalView { .padding(.vertical, -1) } - func dropDownButton(_ action: @escaping () -> Void) -> some View { + func dropDownButton( + text: String, + action: @escaping () -> Void + ) -> some View { HStack(spacing: 0) { - Text(store.startDateText) + Text(text) .typography(.b2_14r) .foregroundStyle(Color.Gray.gray500) Image.Icon.Symbol.arrow2Down From c9ff5f47ff8783d4c6c5d0181ee8e2a852df3710 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 14:13:39 +0900 Subject: [PATCH 04/11] =?UTF-8?q?fix:=20=EC=9D=B8=EC=A6=9D=EC=83=B7=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=EB=9E=99=EC=85=98=20=EC=8B=9C=20=EB=93=9C?= =?UTF-8?q?=EB=9E=98=EA=B7=B8=20=EC=9C=84=EC=B9=98=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EC=BD=94=EB=A9=98=ED=8A=B8=20offset=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-?= =?UTF-8?q?=20#337?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift b/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift index f748f95f..75bd83c3 100644 --- a/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift +++ b/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift @@ -445,7 +445,7 @@ private extension GoalDetailView { .padding(.bottom, 26) .frame(width: rectFrame.width, height: rectFrame.height, alignment: .bottom) .rotationEffect(frontCardRotation) - .offset(x: posX, y: posY - keyboardInset) + .offset(x: posX + cardOffset, y: posY - keyboardInset) .animation(.easeOut(duration: 0.25), value: keyboardInset) } } From 117147ac94f43e86a69efe4bf0dececd7d7e2709 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 14:31:20 +0900 Subject: [PATCH 05/11] =?UTF-8?q?fix:=20=ED=86=B5=EA=B3=84=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EB=84=A4=EB=B9=84=EB=B0=94=20=EC=95=88=EC=9E=98?= =?UTF-8?q?=EB=A6=AC=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-=20#338?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Feature/Stats/Sources/Detail/StatsDetailView.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift b/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift index 92f52b5c..0ce3b6df 100644 --- a/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift +++ b/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift @@ -156,8 +156,7 @@ private extension StatsDetailView { summaryTitle(for: summary.title) summartyContent(content: summary.content, isCompletedCount: summary.isCompletedCount) .layoutPriority(1) - - Spacer() + .frame(maxWidth: .infinity, alignment: .leading) } } } @@ -188,7 +187,6 @@ private extension StatsDetailView { Text(content[0]) .typography(.b4_12b) .foregroundStyle(Color.Gray.gray500) - .lineLimit(1) if isCompletedCount { Text("|") @@ -199,11 +197,9 @@ private extension StatsDetailView { Text(content[1]) .typography(.b4_12b) .foregroundStyle(Color.Gray.gray500) - .lineLimit(1) } } - .lineLimit(1) - .fixedSize(horizontal: true, vertical: false) + .frame(maxWidth: .infinity, alignment: .leading) } @ViewBuilder From b64e23e69819429fc9c5e54936aa12f1c5631938 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 20:10:23 +0900 Subject: [PATCH 06/11] =?UTF-8?q?fix:=20=ED=99=88=20=EB=AA=A9=ED=91=9C=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=20=EB=95=8C=20UI=20=EC=88=98=EC=A0=95=20-=20?= =?UTF-8?q?#339=20-=20emptyView=20y=EC=B6=95=20=EA=B0=80=EC=9A=B4=EB=8D=B0?= =?UTF-8?q?=20=EC=A0=95=EB=A0=AC=20-=20=EC=B2=AB=20=EB=AA=A9=ED=91=9C=20?= =?UTF-8?q?=EC=9D=B4=ED=9B=84=20=ED=99=94=EC=82=B4=ED=91=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/HomeEmptyContentSection.swift | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/Projects/Feature/Home/Sources/Home/HomeEmptyContentSection.swift b/Projects/Feature/Home/Sources/Home/HomeEmptyContentSection.swift index 6212798f..e056ef9b 100644 --- a/Projects/Feature/Home/Sources/Home/HomeEmptyContentSection.swift +++ b/Projects/Feature/Home/Sources/Home/HomeEmptyContentSection.swift @@ -10,42 +10,34 @@ import FeatureHomeInterface import SharedDesignSystem /// `hadFirstGoal`을 읽는 빈 상태 영역입니다. -/// `emptyScrollHeight`는 로컬 `@State`로 관리해 다른 콘텐츠 section 재렌더링에 의해 -/// 초기화되지 않게 합니다. +/// `goalEmptyView`의 center anchor를 기기 화면 기준 y축 중앙에 배치합니다. struct HomeEmptyContentSection: View { let store: StoreOf - @State private var emptyScrollHeight: CGFloat = 0 - var body: some View { VStack(spacing: 0) { - HomeHeaderRow(store: store) - .padding(.horizontal, 20) - .padding(.top, 16) + GeometryReader { geo in + let frame = geo.frame(in: .global) + let deviceHeight = UIScreen.main.bounds.height + let deviceCenterYInSection = max(0, deviceHeight / 2 - frame.minY) - ScrollView { - goalEmptyView - // 실제 가시 영역 기준으로 중앙 정렬되도록 탭바 높이만큼 차감 - .frame(maxWidth: .infinity, minHeight: max(0, emptyScrollHeight - 58)) - .padding(.bottom, 58) - } - .scrollIndicators(.hidden) - .refreshable { - store.send(.view(.refreshPulled)) - } - .overlay(alignment: .bottomTrailing) { - emptyArrow - } - .frame(maxHeight: .infinity) - .background { - GeometryReader { geo in - Color.clear - .onAppear { emptyScrollHeight = geo.size.height } - .onChange(of: geo.size.height) { _, newValue in - emptyScrollHeight = newValue - } + ScrollView { + goalEmptyView + .frame(width: geo.size.width) + .position(x: geo.size.width / 2, y: deviceCenterYInSection) + } + .scrollIndicators(.hidden) + .refreshable { + store.send(.view(.refreshPulled)) } + .overlay(alignment: .bottomTrailing) { + if store.hadFirstGoal == false { + emptyArrow + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) } + .frame(maxHeight: .infinity) } } @@ -79,12 +71,11 @@ struct HomeEmptyContentSection: View { } } } - .frame(maxWidth: .infinity, maxHeight: .infinity) } var emptyArrow: some View { Image.Illustration.arrow - .padding(.bottom, 71 + 58) + .padding(.bottom, 71 + TXTabBarLayout.height) .padding(.trailing, 86) .ignoresSafeArea() } From c74ef33adb32cbe0c2409742e9770b4353d5d047 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 21:20:03 +0900 Subject: [PATCH 07/11] =?UTF-8?q?fix:=20TXRoundButton=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20UI=20=EC=88=98=EC=A0=95=20-=20#339?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Button/Round/TXRoundButton.swift | 28 ++++++------------- .../Components/Card/Goal/GoalCardView.swift | 2 +- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift b/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift index 0266469e..b72bdaf1 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift @@ -24,12 +24,12 @@ struct TXRoundButton: View { onTap() } } label: { - ZStack { + ZStack(alignment: .top) { Capsule() .fill(style.backgroundColor(state: state)) .frame(maxWidth: size.frameWidth) - .frame(height: size.backgroundHeight(state: state)) - .padding(.top, size.bottomYOffset(state: state)) + .frame(height: size.backgroundHeight) + .padding(.top, size.backgroundYOffset) Text(style.text) .typography(size.typography) @@ -42,8 +42,8 @@ struct TXRoundButton: View { lineWidth: size.borderWidth ) .background(style.foregroundColor(state: state), in: .capsule) + .padding(.top, size.foregroundYOffset(state: state)) } - .padding(.top, size.topYOffset(state: state)) } .buttonStyle(.plain) } else { @@ -120,29 +120,19 @@ private extension TXButtonShape.TXRoundSize { } } - func backgroundHeight(state: TXButtonShape.TXRoundState) -> CGFloat { + var backgroundHeight: CGFloat { switch self { case .l, .m: 70 - case .s: - switch state { - case .standard: 31 - case .disabled: 28 - } + case .s: 28 } } - func bottomYOffset(state: TXButtonShape.TXRoundState) -> CGFloat { - switch self { - case .s, .l, .m: - switch state { - case .standard: 4 - case .disabled: 1 - } - } + var backgroundYOffset: CGFloat { + 4 } - func topYOffset(state: TXButtonShape.TXRoundState) -> CGFloat { + func foregroundYOffset(state: TXButtonShape.TXRoundState) -> CGFloat { switch self { case .s, .l, .m: switch state { diff --git a/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift b/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift index 3bddb7d2..ae498178 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift @@ -180,7 +180,7 @@ private extension GoalCardView { .padding(.bottom, 10) } } - .frame(maxHeight: .infinity) + .frame(maxHeight: .infinity, alignment: .center) } func emojiImage(emoji: Image) -> some View { From a2afb1be9fb65cda1226ddfb1810461be473ba77 Mon Sep 17 00:00:00 2001 From: jihun Date: Fri, 5 Jun 2026 16:40:23 +0900 Subject: [PATCH 08/11] =?UTF-8?q?fix:=20=ED=99=88,=20=ED=86=B5=EA=B3=84?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=86=B5=EA=B3=84=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EB=B7=B0=20=EC=A7=84=EC=9E=85=EC=8B=9C=20=EB=B3=B4=EA=B3=A0?= =?UTF-8?q?=EC=9E=88=EB=8D=98=20=EC=9B=94=EC=97=90=20=ED=95=B4=EB=8B=B9?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B3=B4?= =?UTF-8?q?=EC=97=AC=EC=A3=BC=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-?= =?UTF-8?q?=20#350?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Feature/Home/Interface/Sources/Home/HomeReducer.swift | 2 +- Projects/Feature/Home/Sources/Home/HomeReducer+Impl.swift | 2 +- Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift | 4 ++-- .../Stats/Interface/Sources/Detail/StatsDetailReducer.swift | 4 ++-- .../Feature/Stats/Interface/Sources/Stats/StatsReducer.swift | 2 +- .../Stats/Sources/Coordinator/StatsCoordinator+Impl.swift | 4 ++-- Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift | 2 +- Projects/Feature/Stats/Sources/Stats/StatsReducer+Impl.swift | 3 ++- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Projects/Feature/Home/Interface/Sources/Home/HomeReducer.swift b/Projects/Feature/Home/Interface/Sources/Home/HomeReducer.swift index a9a47266..439d2af5 100644 --- a/Projects/Feature/Home/Interface/Sources/Home/HomeReducer.swift +++ b/Projects/Feature/Home/Interface/Sources/Home/HomeReducer.swift @@ -241,7 +241,7 @@ public struct HomeReducer { /// 홈 화면에서 외부로 전달하는 이벤트입니다. public enum Delegate { case goToGoalDetail(id: Int64, owner: GoalDetail.Owner, verificationDate: String) - case goToStatsDetail(id: Int64) + case goToStatsDetail(id: Int64, calendarDate: TXCalendarDate) case goToMakeGoal(GoalCategory) case goToEditGoalList(date: TXCalendarDate) case goToSettings diff --git a/Projects/Feature/Home/Sources/Home/HomeReducer+Impl.swift b/Projects/Feature/Home/Sources/Home/HomeReducer+Impl.swift index 14df2ed1..89bc05da 100644 --- a/Projects/Feature/Home/Sources/Home/HomeReducer+Impl.swift +++ b/Projects/Feature/Home/Sources/Home/HomeReducer+Impl.swift @@ -286,7 +286,7 @@ extension HomeReducer { return .send(.delegate(.goToGoalDetail(id: card.id, owner: .mySelf, verificationDate: verificationDate))) case let .view(.headerTapped(card)): - return .send(.delegate(.goToStatsDetail(id: card.id))) + return .send(.delegate(.goToStatsDetail(id: card.id, calendarDate: state.calendarDate))) case .view(.floatingButtonTapped): state.isAddGoalPresented = true diff --git a/Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift b/Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift index 813e71b7..8b877a91 100644 --- a/Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift +++ b/Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift @@ -58,9 +58,9 @@ extension HomeCoordinator { state.notification = .init() return .none - case let .home(.delegate(.goToStatsDetail(id))): + case let .home(.delegate(.goToStatsDetail(id, date))): state.routes.append(.statsDetail) - state.statsDetail = .init(goalId: id) + state.statsDetail = .init(goalId: id, calendarDate: date) return .none case .statsDetail(.delegate(.navigateBack)): diff --git a/Projects/Feature/Stats/Interface/Sources/Detail/StatsDetailReducer.swift b/Projects/Feature/Stats/Interface/Sources/Detail/StatsDetailReducer.swift index 72fee817..031dc1f7 100644 --- a/Projects/Feature/Stats/Interface/Sources/Detail/StatsDetailReducer.swift +++ b/Projects/Feature/Stats/Interface/Sources/Detail/StatsDetailReducer.swift @@ -80,10 +80,10 @@ public struct StatsDetailReducer { /// ```swift /// let state = StatsDetailReducer.State(goalId: 1) /// ``` - public init(goalId: Int64) { + public init(goalId: Int64, calendarDate: TXCalendarDate?) { self.goalId = goalId - let currentMonth = TXCalendarDate() + let currentMonth = calendarDate ?? TXCalendarDate() self.currentMonth = currentMonth self.monthlyData = TXCalendarDataGenerator.generateMonthData( for: currentMonth, diff --git a/Projects/Feature/Stats/Interface/Sources/Stats/StatsReducer.swift b/Projects/Feature/Stats/Interface/Sources/Stats/StatsReducer.swift index 58e21b3f..19239028 100644 --- a/Projects/Feature/Stats/Interface/Sources/Stats/StatsReducer.swift +++ b/Projects/Feature/Stats/Interface/Sources/Stats/StatsReducer.swift @@ -109,7 +109,7 @@ public struct StatsReducer { /// StatsReducer가 상위 Coordinator로 전달하는 이벤트입니다. public enum Delegate { - case goToStatsDetail(goalId: Int64) + case goToStatsDetail(goalId: Int64, calendarDate: TXCalendarDate) } case view(View) diff --git a/Projects/Feature/Stats/Sources/Coordinator/StatsCoordinator+Impl.swift b/Projects/Feature/Stats/Sources/Coordinator/StatsCoordinator+Impl.swift index 0b284b33..a98f3e91 100644 --- a/Projects/Feature/Stats/Sources/Coordinator/StatsCoordinator+Impl.swift +++ b/Projects/Feature/Stats/Sources/Coordinator/StatsCoordinator+Impl.swift @@ -33,9 +33,9 @@ extension StatsCoordinator { let reducer = Reduce { state, action in switch action { // MARK: - Child Action - case let .stats(.delegate(.goToStatsDetail(goalId))): + case let .stats(.delegate(.goToStatsDetail(goalId, calendarDate))): state.routes.append(.statsDetail) - state.statsDetail = .init(goalId: goalId) + state.statsDetail = .init(goalId: goalId, calendarDate: calendarDate) return .none case let .statsDetail(.delegate(.goToGoalDetail(goalId, isCompletedPartner, date))): diff --git a/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift b/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift index 0ce3b6df..3872b5ed 100644 --- a/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift +++ b/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift @@ -262,7 +262,7 @@ private extension StatsDetailView { #Preview { StatsDetailView( store: Store( - initialState: StatsDetailReducer.State(goalId: 1), + initialState: StatsDetailReducer.State(goalId: 1, calendarDate: nil), reducer: { StatsDetailReducer() } ) ) diff --git a/Projects/Feature/Stats/Sources/Stats/StatsReducer+Impl.swift b/Projects/Feature/Stats/Sources/Stats/StatsReducer+Impl.swift index 349dac0a..cf073353 100644 --- a/Projects/Feature/Stats/Sources/Stats/StatsReducer+Impl.swift +++ b/Projects/Feature/Stats/Sources/Stats/StatsReducer+Impl.swift @@ -49,7 +49,8 @@ extension StatsReducer { return .send(.internal(.fetchStats)) case let .view(.statsCardTapped(goalId)): - return .send(.delegate(.goToStatsDetail(goalId: goalId))) + let date = state.isOngoing ? state.currentMonth : TXCalendarDate() + return .send(.delegate(.goToStatsDetail(goalId: goalId, calendarDate: date))) // MARK: - Update State case let .presentation(.showToast(toast)): From a2352f625fedd8074ff00c8e94f68e4967dddd2d Mon Sep 17 00:00:00 2001 From: jihun Date: Mon, 8 Jun 2026 18:46:54 +0900 Subject: [PATCH 09/11] =?UTF-8?q?fix:=20empty=20=EB=AC=B8=EA=B5=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20#350?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Feature/Stats/Sources/Stats/StatsView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/Feature/Stats/Sources/Stats/StatsView.swift b/Projects/Feature/Stats/Sources/Stats/StatsView.swift index b5fa40fb..2c072863 100644 --- a/Projects/Feature/Stats/Sources/Stats/StatsView.swift +++ b/Projects/Feature/Stats/Sources/Stats/StatsView.swift @@ -114,7 +114,7 @@ private extension StatsView { if store.isOngoing { VStack(spacing: 8) { Image.Illustration.scare - Text("아직 목표가 없어요!") + Text("이 달은 목표가 없어요!") .typography(.t2_16b) .foregroundStyle(Color.Gray.gray400) } From 2cffae4febf9450811244c93d0d1779c1961c412 Mon Sep 17 00:00:00 2001 From: jihun Date: Mon, 8 Jun 2026 21:20:36 +0900 Subject: [PATCH 10/11] =?UTF-8?q?fix:=20CI=20Fastlane=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20-=20#350?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 7a118b49..b2a45d3e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,4 @@ source "https://rubygems.org" gem "fastlane" +gem "multi_json" From 39ec2a497ab6118498507c056449c4fa38b95e9c Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 11 Jun 2026 13:09:27 +0900 Subject: [PATCH 11/11] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20-=20#350?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Root/HomeCoordinator+Impl.swift | 2 +- .../Sources/Detail/StatsDetailReducer.swift | 19 ++++++++++++------- .../Coordinator/StatsCoordinator+Impl.swift | 2 +- .../Sources/Detail/StatsDetailView.swift | 5 ++++- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift b/Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift index 8b877a91..88025022 100644 --- a/Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift +++ b/Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift @@ -60,7 +60,7 @@ extension HomeCoordinator { case let .home(.delegate(.goToStatsDetail(id, date))): state.routes.append(.statsDetail) - state.statsDetail = .init(goalId: id, calendarDate: date) + state.statsDetail = .init(goalId: id, initialMonth: date) return .none case .statsDetail(.delegate(.navigateBack)): diff --git a/Projects/Feature/Stats/Interface/Sources/Detail/StatsDetailReducer.swift b/Projects/Feature/Stats/Interface/Sources/Detail/StatsDetailReducer.swift index 031dc1f7..7fdc9cf4 100644 --- a/Projects/Feature/Stats/Interface/Sources/Detail/StatsDetailReducer.swift +++ b/Projects/Feature/Stats/Interface/Sources/Detail/StatsDetailReducer.swift @@ -18,7 +18,10 @@ import SharedDesignSystem /// ## 사용 예시 /// ```swift /// let store = Store( -/// initialState: StatsDetailReducer.State() +/// initialState: StatsDetailReducer.State( +/// goalId: 1, +/// initialMonth: TXCalendarDate() +/// ) /// ) { /// StatsDetailReducer(reducer: Reduce { _, _ in .none }) /// } @@ -78,15 +81,17 @@ public struct StatsDetailReducer { /// /// ## 사용 예시 /// ```swift - /// let state = StatsDetailReducer.State(goalId: 1) + /// let state = StatsDetailReducer.State( + /// goalId: 1, + /// initialMonth: TXCalendarDate() + /// ) /// ``` - public init(goalId: Int64, calendarDate: TXCalendarDate?) { + public init(goalId: Int64, initialMonth: TXCalendarDate) { self.goalId = goalId - - let currentMonth = calendarDate ?? TXCalendarDate() - self.currentMonth = currentMonth + + self.currentMonth = initialMonth self.monthlyData = TXCalendarDataGenerator.generateMonthData( - for: currentMonth, + for: initialMonth, hideAdjacentDates: true ) } diff --git a/Projects/Feature/Stats/Sources/Coordinator/StatsCoordinator+Impl.swift b/Projects/Feature/Stats/Sources/Coordinator/StatsCoordinator+Impl.swift index a98f3e91..9e964d3a 100644 --- a/Projects/Feature/Stats/Sources/Coordinator/StatsCoordinator+Impl.swift +++ b/Projects/Feature/Stats/Sources/Coordinator/StatsCoordinator+Impl.swift @@ -35,7 +35,7 @@ extension StatsCoordinator { // MARK: - Child Action case let .stats(.delegate(.goToStatsDetail(goalId, calendarDate))): state.routes.append(.statsDetail) - state.statsDetail = .init(goalId: goalId, calendarDate: calendarDate) + state.statsDetail = .init(goalId: goalId, initialMonth: calendarDate) return .none case let .statsDetail(.delegate(.goToGoalDetail(goalId, isCompletedPartner, date))): diff --git a/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift b/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift index 3872b5ed..c49c6fb8 100644 --- a/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift +++ b/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift @@ -262,7 +262,10 @@ private extension StatsDetailView { #Preview { StatsDetailView( store: Store( - initialState: StatsDetailReducer.State(goalId: 1, calendarDate: nil), + initialState: StatsDetailReducer.State( + goalId: 1, + initialMonth: TXCalendarDate() + ), reducer: { StatsDetailReducer() } ) )