Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
source "https://rubygems.org"

gem "fastlane"
gem "multi_json"
18 changes: 16 additions & 2 deletions Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -431,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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
51 changes: 21 additions & 30 deletions Projects/Feature/Home/Sources/Home/HomeEmptyContentSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,34 @@ import FeatureHomeInterface
import SharedDesignSystem

/// `hadFirstGoal`을 읽는 빈 상태 영역입니다.
/// `emptyScrollHeight`는 로컬 `@State`로 관리해 다른 콘텐츠 section 재렌더링에 의해
/// 초기화되지 않게 합니다.
/// `goalEmptyView`의 center anchor를 기기 화면 기준 y축 중앙에 배치합니다.
struct HomeEmptyContentSection: View {
let store: StoreOf<HomeReducer>

@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)
}
}

Expand Down Expand Up @@ -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()
}
Expand Down
2 changes: 1 addition & 1 deletion Projects/Feature/Home/Sources/Home/HomeReducer+Impl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Projects/Feature/Home/Sources/Root/HomeCoordinator+Impl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)):
Expand Down
19 changes: 14 additions & 5 deletions Projects/Feature/MakeGoal/Sources/MakeGoalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,9 @@ private extension MakeGoalView {
Spacer()

if store.showPeriodCount {
dropDownButton { store.send(.view(.periodSelected)) }
dropDownButton(text: store.periodCountText) {
store.send(.view(.periodSelected))
}
}
}
}
Expand All @@ -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)
Expand All @@ -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)
}
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ public struct StatsDetailReducer {
/// ```swift
/// let state = StatsDetailReducer.State(goalId: 1)
/// ```
public init(goalId: Int64) {
public init(goalId: Int64, calendarDate: TXCalendarDate?) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사소한 포인트인데, calenderDate라는 매개변수가 무슨 뜻인가 싶더라구
그래서 생각해보니 initialMonth 정도 되겠구나 싶은걸 알았는데,
그럼 시작 월이라던지, 날짜를 굳이 지정할 생각이 없으면 nil이 가능하게 기본값을 지정해주는 것도 좋을 듯?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말한대로 네이밍 좀 헷갈리는 거 같아서 initialMonth로 바꿀게
그리고 코드 다시 보니까 nil 전달하는 경우가 없어서 옵셔널도 빼고 initialMonth를 필수 주입하도록 바꿀게
근데 기본값으로 지정할 경우 호출부에서 initialMonth를 주입해야하는데 실수로 빼먹을 경우가 있을 수 있으니 기본값 지정은 안하고 필수 주입하는거로 고칠게!

self.goalId = goalId

let currentMonth = TXCalendarDate()
let currentMonth = calendarDate ?? TXCalendarDate()
self.currentMonth = currentMonth
self.monthlyData = TXCalendarDataGenerator.generateMonthData(
for: currentMonth,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ extension StatsCoordinator {
let reducer = Reduce<State, Action> { 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))):
Expand Down
10 changes: 3 additions & 7 deletions Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down Expand Up @@ -188,7 +187,6 @@ private extension StatsDetailView {
Text(content[0])
.typography(.b4_12b)
.foregroundStyle(Color.Gray.gray500)
.lineLimit(1)

if isCompletedCount {
Text("|")
Expand All @@ -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
Expand Down Expand Up @@ -266,7 +262,7 @@ private extension StatsDetailView {
#Preview {
StatsDetailView(
store: Store(
initialState: StatsDetailReducer.State(goalId: 1),
initialState: StatsDetailReducer.State(goalId: 1, calendarDate: nil),
reducer: { StatsDetailReducer() }
)
)
Expand Down
3 changes: 2 additions & 1 deletion Projects/Feature/Stats/Sources/Stats/StatsReducer+Impl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)):
Expand Down
2 changes: 1 addition & 1 deletion Projects/Feature/Stats/Sources/Stats/StatsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,27 @@ 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 {
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)
Expand All @@ -34,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 {
Expand Down Expand Up @@ -112,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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import SwiftUI
/// ```
public struct TXButton: View {
let shape: TXButtonShape
let allowsActionWhenDisabled: Bool
let onTap: () -> Void

/// 버튼을 생성합니다.
Expand All @@ -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
}

Expand All @@ -65,6 +68,7 @@ public struct TXButton: View {
case .round:
TXRoundButton(
shape: shape,
allowsActionWhenDisabled: allowsActionWhenDisabled,
onTap: onTap
)

Expand Down
Loading
Loading