diff --git a/Projects/App/xcconfigs/SeeYouAgain.shared.xcconfig b/Projects/App/xcconfigs/SeeYouAgain.shared.xcconfig index cd7c708e..40253378 100644 --- a/Projects/App/xcconfigs/SeeYouAgain.shared.xcconfig +++ b/Projects/App/xcconfigs/SeeYouAgain.shared.xcconfig @@ -1,2 +1,2 @@ -MARKETING_VERSION=1.4.0 +MARKETING_VERSION=1.5.0 DEVELOPMENT_TEAM=JJPXPHW2TN diff --git a/Projects/Features/Scene/LongStorageScene/LongStorageNewsList/LongStorageNewsListView.swift b/Projects/Features/Scene/LongStorageScene/LongStorageNewsList/LongStorageNewsListView.swift index 27a546cc..a12bf767 100644 --- a/Projects/Features/Scene/LongStorageScene/LongStorageNewsList/LongStorageNewsListView.swift +++ b/Projects/Features/Scene/LongStorageScene/LongStorageNewsList/LongStorageNewsListView.swift @@ -34,6 +34,7 @@ public struct LongStorageNewsListView: View { } else { viewStore.send(.deleteButtonTapped) } + UIImpactFeedbackGenerator(style: .light).impactOccurred() } ) diff --git a/Projects/Features/Scene/MyPageScene/MyPage/Achievements/MyAchievementsView.swift b/Projects/Features/Scene/MyPageScene/MyPage/Achievements/MyAchievementsView.swift index 179be9e4..7b7d43c2 100644 --- a/Projects/Features/Scene/MyPageScene/MyPage/Achievements/MyAchievementsView.swift +++ b/Projects/Features/Scene/MyPageScene/MyPage/Achievements/MyAchievementsView.swift @@ -62,6 +62,7 @@ fileprivate struct AchievementsGridView: View { AchievementBadgeView(achievement: achievement) .onTapGesture { viewStore.send(.achievementBadgeTapped(achievement)) + UIImpactFeedbackGenerator(style: .light).impactOccurred() } } } diff --git a/Projects/Features/Scene/MyPageScene/MyPage/MyPageView.swift b/Projects/Features/Scene/MyPageScene/MyPage/MyPageView.swift index 20a6741a..aa2885f4 100644 --- a/Projects/Features/Scene/MyPageScene/MyPage/MyPageView.swift +++ b/Projects/Features/Scene/MyPageScene/MyPage/MyPageView.swift @@ -27,6 +27,7 @@ public struct MyPageView: View { rightIcon: DesignSystem.Icons.iconSetting, rightIconButtonAction: { viewStore.send(.settingButtonTapped(viewStore.state.nickname)) + UIImpactFeedbackGenerator(style: .light).impactOccurred() } ) diff --git a/Projects/Features/Scene/MyPageScene/MyPage/Statistics/WeeklyStatistics/WeeklyStatisticsCore.swift b/Projects/Features/Scene/MyPageScene/MyPage/Statistics/WeeklyStatistics/WeeklyStatisticsCore.swift index 8534fbf1..7f3ef1e0 100644 --- a/Projects/Features/Scene/MyPageScene/MyPage/Statistics/WeeklyStatistics/WeeklyStatisticsCore.swift +++ b/Projects/Features/Scene/MyPageScene/MyPage/Statistics/WeeklyStatistics/WeeklyStatisticsCore.swift @@ -18,6 +18,7 @@ public struct WeeklyStatisticsState: Equatable { var weeklyShortsCountList: [(key: String, value: Int)] // 주차, 각 주차 당 읽은 숏스 수 var weeklyShortsCountDifference: Int = 0 // 지난 주와 이번 주 숏스 읽은 수 비교 값 + var weeklyShortsCountDifferenceString: String = "" var weeklyShortsPercentageList: [Double] = Array(repeating: 0.0, count: 4) // 4주간 읽은 숏스 비율을 담은 리스트 var shortsCountOfThisWeek = 0 // 이번주 읽은 숏스 카운트 var totalOfFourWeeksShortsCount = 0 // 4주 간의 숏스 데이터 더한 값 @@ -37,6 +38,7 @@ public enum WeeklyStatisticsAction { // MARK: - Inner SetState Action case _setWeeklyShortsCountDifference // 지난 주와 이번 주 숏스 읽은 수 비교 값 계산 + case _setWeeklyShortsCountDifferenceString case _setWeeklyShortsPercentageList // 4주 간의 숏스 데이터를 퍼센테이지 비율로 저장 case _setShortsCountOfThisWeek // 이번주 읽은 숏스 카운트 case _setTotalOfFourWeeksShortsCount(Int) // 4주 간의 숏스 데이터 계산 @@ -85,6 +87,16 @@ public let weeklyStatisticsReducer = Reducer< case ._setWeeklyShortsCountDifference: state.weeklyShortsCountDifference = state.weeklyShortsCountList[3].value - state.weeklyShortsCountList[2].value + return Effect(value: ._setWeeklyShortsCountDifferenceString) + + case ._setWeeklyShortsCountDifferenceString: + if state.weeklyShortsCountDifference > 0 { // 이번주 > 저번주 + state.weeklyShortsCountDifferenceString = "\(state.weeklyShortsCountDifference)개 더" + } else if state.weeklyShortsCountDifference == 0 { // 이번주랑 저번주랑 동일 + state.weeklyShortsCountDifferenceString = "" + } else { // 이번주 < 저번주 + state.weeklyShortsCountDifferenceString = "\(-state.weeklyShortsCountDifference)개 덜" + } return .none case ._setShortsCountOfThisWeek: diff --git a/Projects/Features/Scene/MyPageScene/MyPage/Statistics/WeeklyStatistics/WeeklyStatisticsDescriptionView.swift b/Projects/Features/Scene/MyPageScene/MyPage/Statistics/WeeklyStatistics/WeeklyStatisticsDescriptionView.swift index 4a146711..06bb7e89 100644 --- a/Projects/Features/Scene/MyPageScene/MyPage/Statistics/WeeklyStatistics/WeeklyStatisticsDescriptionView.swift +++ b/Projects/Features/Scene/MyPageScene/MyPage/Statistics/WeeklyStatistics/WeeklyStatisticsDescriptionView.swift @@ -56,17 +56,23 @@ private struct WeeklyStatisticsActualDescriptionView: View { .frame(height: 9) HStack(spacing: 0) { - Text("저번 주보다 ") - .font(.r14) - .foregroundColor(DesignSystem.Colors.grey60) - - Text("\(viewStore.state.weeklyShortsCountDifference)개 더") - .font(.b14) - .foregroundColor(DesignSystem.Colors.grey60) - - Text(" 읽었어요") - .font(.r14) - .foregroundColor(DesignSystem.Colors.grey60) + if viewStore.state.weeklyShortsCountDifferenceString.isEmpty { + Text("읽은 숏스 수가 지난주와 같아요.") + .font(.r14) + .foregroundColor(DesignSystem.Colors.grey60) + } else { + Text("저번 주보다 ") + .font(.r14) + .foregroundColor(DesignSystem.Colors.grey60) + + Text("\(viewStore.state.weeklyShortsCountDifferenceString)") + .font(.b14) + .foregroundColor(DesignSystem.Colors.grey60) + + Text(" 읽었어요") + .font(.r14) + .foregroundColor(DesignSystem.Colors.grey60) + } Spacer() } diff --git a/Projects/Features/Scene/NewsCardScene/NewsList/NewsListView.swift b/Projects/Features/Scene/NewsCardScene/NewsList/NewsListView.swift index d1ee3612..561afc1e 100644 --- a/Projects/Features/Scene/NewsCardScene/NewsList/NewsListView.swift +++ b/Projects/Features/Scene/NewsCardScene/NewsList/NewsListView.swift @@ -77,6 +77,7 @@ public struct NewsListView: View { BottomButton(title: "키워드별 뉴스에 저장") { viewStore.send(.saveButtonTapped) + UIImpactFeedbackGenerator(style: .light).impactOccurred() } } } diff --git a/Projects/Features/Scene/ShortStorageScene/ShortStorageNewsList/ShortStorageNewsListView.swift b/Projects/Features/Scene/ShortStorageScene/ShortStorageNewsList/ShortStorageNewsListView.swift index ddf53559..68a163b6 100644 --- a/Projects/Features/Scene/ShortStorageScene/ShortStorageNewsList/ShortStorageNewsListView.swift +++ b/Projects/Features/Scene/ShortStorageScene/ShortStorageNewsList/ShortStorageNewsListView.swift @@ -33,6 +33,7 @@ public struct ShortStorageNewsListView: View { } else { viewStore.send(.deleteButtonTapped) } + UIImpactFeedbackGenerator(style: .light).impactOccurred() } ) diff --git a/Projects/Features/Scene/TabBarScene/BottomSheet/CategoryBottomSheetContent.swift b/Projects/Features/Scene/TabBarScene/BottomSheet/CategoryBottomSheetContent.swift index 91da1a4f..21b196e3 100644 --- a/Projects/Features/Scene/TabBarScene/BottomSheet/CategoryBottomSheetContent.swift +++ b/Projects/Features/Scene/TabBarScene/BottomSheet/CategoryBottomSheetContent.swift @@ -35,6 +35,7 @@ struct CategoryBottomSheet: ViewModifier { disabled: viewStore.selectedCategories.isEmpty, action: { viewStore.send(.updateButtonTapped) + UIImpactFeedbackGenerator(style: .light).impactOccurred() } ) } diff --git a/Projects/Features/Scene/TabBarScene/BottomSheet/CategoryBottomSheetCore.swift b/Projects/Features/Scene/TabBarScene/BottomSheet/CategoryBottomSheetCore.swift index 8801fdc9..67bea33c 100644 --- a/Projects/Features/Scene/TabBarScene/BottomSheet/CategoryBottomSheetCore.swift +++ b/Projects/Features/Scene/TabBarScene/BottomSheet/CategoryBottomSheetCore.swift @@ -17,7 +17,8 @@ public struct CategoryBottomSheetState: Equatable { var allCategories: [CategoryType] = CategoryType.allCases var selectedCategories: [CategoryType] = [] public var isPresented: Bool = false - var toastMessage: String? + var successToastMessage: String? + var failureToastMessage: String? public init() {} } @@ -30,13 +31,16 @@ public enum CategoryBottomSheetAction { // MARK: - Inner Business Action case _updateCategoires([String]) case _categoriesIsUpdated - case _presentToast(String) - case _hideToast + case _presentSuccessToast(String) + case _presentFailureToast(String) + case _hideSuccessToast + case _hideFailureToast // MARK: - Inner SetState Action case _setSelectedCategories([CategoryType]) case _setIsPresented(Bool) - case _setToastMessage(String?) + case _setSuccessToastMessage(String?) + case _SetFailureToastMessage(String?) } public struct CategoryBottomSheetEnvironment { @@ -52,16 +56,14 @@ public struct CategoryBottomSheetEnvironment { } } -enum CategoryBottomSheetID: Hashable { - case _updateCategoires - case _setCategoryToast -} - public let categoryBottomSheetReducer: Reducer< CategoryBottomSheetState, CategoryBottomSheetAction, CategoryBottomSheetEnvironment > = Reducer { state, action, env in + struct SuccessToastCancelID: Hashable {} + struct FailureToastCancelID: Hashable {} + switch action { case let .categoryTapped(category): if state.selectedCategories.contains(category) { @@ -82,10 +84,13 @@ public let categoryBottomSheetReducer: Reducer< .flatMapLatest { result -> Effect in switch result { case .success: - return Effect(value: ._categoriesIsUpdated) + return Effect.concatenate([ + Effect(value: ._categoriesIsUpdated), + Effect(value: ._presentSuccessToast("관심 키워드가 변경되었어요.")) + ]) case .failure: - return Effect(value: ._presentToast("인터넷이 불안정해서 변경되지 못했어요.")) + return Effect(value: ._presentFailureToast("인터넷이 불안정해서 변경되지 못했어요.")) } } .eraseToEffect() @@ -93,18 +98,31 @@ public let categoryBottomSheetReducer: Reducer< case ._categoriesIsUpdated: return Effect(value: ._setIsPresented(false)) - case let ._presentToast(toastMessage): + case let ._presentSuccessToast(toastMessage): return .concatenate( - Effect(value: ._setToastMessage(toastMessage)), - Effect.cancel(id: CategoryBottomSheetID._setCategoryToast), - Effect(value: ._hideToast) + Effect(value: ._setSuccessToastMessage(toastMessage)), + Effect.cancel(id: SuccessToastCancelID()), + Effect(value: ._hideSuccessToast) .delay(for: 2, scheduler: env.mainQueue) .eraseToEffect() - .cancellable(id: CategoryBottomSheetID._setCategoryToast, cancelInFlight: true) + .cancellable(id: SuccessToastCancelID(), cancelInFlight: true) ) - case ._hideToast: - return Effect(value: ._setToastMessage(nil)) + case let ._presentFailureToast(toastMessage): + return .concatenate( + Effect(value: ._SetFailureToastMessage(toastMessage)), + Effect.cancel(id: FailureToastCancelID()), + Effect(value: ._hideFailureToast) + .delay(for: 2, scheduler: env.mainQueue) + .eraseToEffect() + .cancellable(id: FailureToastCancelID(), cancelInFlight: true) + ) + + case ._hideSuccessToast: + return Effect(value: ._setSuccessToastMessage(nil)) + + case ._hideFailureToast: + return Effect(value: ._SetFailureToastMessage(nil)) case let ._setSelectedCategories(categories): state.selectedCategories = categories @@ -114,8 +132,12 @@ public let categoryBottomSheetReducer: Reducer< state.isPresented = isPresented return .none - case let ._setToastMessage(message): - state.toastMessage = message + case let ._setSuccessToastMessage(message): + state.successToastMessage = message + return .none + + case let ._SetFailureToastMessage(message): + state.failureToastMessage = message return .none } } diff --git a/Projects/Features/Scene/TabBarScene/TabBar/ShortsTabBarView.swift b/Projects/Features/Scene/TabBarScene/TabBar/ShortsTabBarView.swift index 281942c4..41ea048d 100644 --- a/Projects/Features/Scene/TabBarScene/TabBar/ShortsTabBarView.swift +++ b/Projects/Features/Scene/TabBarScene/TabBar/ShortsTabBarView.swift @@ -77,6 +77,7 @@ extension ShortsTabBarView { .onTapGesture { withAnimation(.linear(duration: 0.2)) { selection = tab + UIImpactFeedbackGenerator(style: .soft).impactOccurred() } } } diff --git a/Projects/Features/Scene/TabBarScene/TabBar/TabBarView.swift b/Projects/Features/Scene/TabBarScene/TabBar/TabBarView.swift index 04edaae7..281c496b 100644 --- a/Projects/Features/Scene/TabBarScene/TabBar/TabBarView.swift +++ b/Projects/Features/Scene/TabBarScene/TabBar/TabBarView.swift @@ -84,7 +84,16 @@ public struct TabBarView: View { } }) .apply(content: { view in - WithViewStore(store.scope(state: \.categoryBottomSheet.toastMessage)) { toastMessageViewStore in + WithViewStore(store.scope(state: \.categoryBottomSheet.successToastMessage)) { toastMessageViewStore in + view.toast( + text: toastMessageViewStore.state, + toastType: .info, + toastOffset: -38 + ) + } + }) + .apply(content: { view in + WithViewStore(store.scope(state: \.categoryBottomSheet.failureToastMessage)) { toastMessageViewStore in view.toast( text: toastMessageViewStore.state, toastType: .warning, diff --git a/Projects/Features/Scene/WebScene/Web/WebView.swift b/Projects/Features/Scene/WebScene/Web/WebView.swift index f563fabe..308d8087 100644 --- a/Projects/Features/Scene/WebScene/Web/WebView.swift +++ b/Projects/Features/Scene/WebScene/Web/WebView.swift @@ -26,7 +26,10 @@ public struct WebView: View { leftIcon: DesignSystem.Icons.iconNavigationLeft, leftIconButtonAction: { viewStore.send(.backButtonTapped(viewStore.source)) }, rightText: "저장", - rightIconButtonAction: { viewStore.send(.saveButtonTapped) }, + rightIconButtonAction: { + viewStore.send(.saveButtonTapped) + UIImpactFeedbackGenerator(style: .light).impactOccurred() + }, isRightButtonActive: viewStore.binding( get: \.saveButtonDisabled, send: { WebAction._setSaveButtonDisabled($0) }