Skip to content

Commit

Permalink
Release/1.0.8 -> develop (#285)
Browse files Browse the repository at this point in the history
* chore: 빌드 넘버 1.0.8 (29)

* chore: DomainAuth 의존성 추가

* feat: ProfileEditView bottom ignoreSafeArea 추가

* [Feature/#277] 웹뷰 os type, 버전 파라미터 추가 (#279)

* [Feature/#278] 웹뷰 상단 safe area 무시하도록 수정 (#284)

* [Fix/#280] 로그인화면 백그라운드 이미지 비율 수정 (#281)

* [Fix/#282] 로그인 화면 로그인 버튼 vstack 하단 마진 수정 (#283)

* [Feature/#232] 커스텀 alert 적용 (#272)

* feat: SplashView bottleAlert 적용

* feat: PingPongDetailView bottleAlert 적용

* feat: ReportUserView bottleAlert 적용

* feat: SandBeachView bottleAlert 적용

* feat: 탈퇴하기 Alert  message 수정 (#274)

* [Feature/#275] 알림 권한 미허용 시 alert 추가 (#276)

* feat: AppDelegate 푸시 수신 상태 Notification 등록

* feat: UserClient 푸시 알림 허용 상태 로직 추가

* feat: 푸시 알림 허용 상태에 따른 알림설정 화면 로직 구현

* feat: UserClient 푸쉬알림허용상태 Publisher 구현

* feat: 푸쉬알림허용상태에 따른 로직 변경

* feat: 토글 버튼 binding 코드 개선

- 코드리뷰 반영

* feat: UserClient UserDefaultKeys enum 추가

* feat: 오탈자 수정

- pushNotificationSubject -> pushNotificationAllowStatusSubject

* chore: 빌드 넘버 1.0.8 (30)

---------

Co-authored-by: JongHoon <[email protected]>
  • Loading branch information
leemhyungyu and JongHoooon authored Sep 26, 2024
1 parent 97e6980 commit 1524b1a
Show file tree
Hide file tree
Showing 32 changed files with 365 additions and 194 deletions.
42 changes: 40 additions & 2 deletions Projects/App/Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
UIApplication.shared.registerForRemoteNotifications()
UNUserNotificationCenter.current().delegate = self
Messaging.messaging().delegate = self

setNotification()
application.registerForRemoteNotifications()

store.send(.appDelegate(.didFinishLunching))
return true
}
}

// MARK: - UNUserNotificationCenterDelegate
extension AppDelegate: UNUserNotificationCenterDelegate {
func messaging(
_ messaging: Messaging,
Expand All @@ -62,3 +62,41 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
return [.badge, .sound, .banner, .list]
}
}

// MARK: - objc funcs
private extension AppDelegate {
@objc func checkPushNotificationStatus() {
UNUserNotificationCenter.current()
.getNotificationSettings { [weak self] permission in
guard let self = self else { return }
DispatchQueue.main.async {
switch permission.authorizationStatus {
case .notDetermined:
self.store.send(.appDelegate(.pushNotificationAllowStatusDidChanged(isAllow: true)))
case .denied:
self.store.send(.appDelegate(.pushNotificationAllowStatusDidChanged(isAllow: false)))
case .authorized:
self.store.send(.appDelegate(.pushNotificationAllowStatusDidChanged(isAllow: true)))
case .provisional:
self.store.send(.appDelegate(.pushNotificationAllowStatusDidChanged(isAllow: false)))
case .ephemeral:
self.store.send(.appDelegate(.pushNotificationAllowStatusDidChanged(isAllow: true)))
@unknown default:
Log.error("Unknow Notification Status")
}
}
}
}
}

// MARK: - Private Methods
private extension AppDelegate {
func setNotification() {
NotificationCenter.default.addObserver(
self,
selector: #selector(checkPushNotificationStatus),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
}
}
3 changes: 2 additions & 1 deletion Projects/Domain/Auth/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ let project = Project.makeModule(
factory: .init(
dependencies: [
.domain(interface: .Auth),
.domain(interface: .Error)
.domain(interface: .Error),
.domain(implements: .User)
]
)
),
Expand Down
22 changes: 22 additions & 0 deletions Projects/Domain/User/Interface/Sources/UserClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,26 @@

import Foundation

import Combine

public struct UserClient {
private let _isLoggedIn: () -> Bool
private let _isAppDeleted: () -> Bool
private let _fetchFcmToken: () -> String?
private let updateLoginState: (Bool) -> Void
private let updateDeleteState: (Bool) -> Void
private let updateFcmToken: (String) -> Void
private let updatePushNotificationAllowStatus: (Bool) -> Void
private let _fetchAlertState: () async throws -> [UserAlertState]
private let _fetchPushNotificationAllowStatus: () -> Bool
private let updateAlertState: (UserAlertState) async throws -> Void
private let fetchContacts: () async throws -> [String]
private let updateBlockContacts: ([String]) async throws -> Void
private let pushNotificationAllowStatusSubject = CurrentValueSubject<Bool, Never>(true)

public var pushNotificationAllowStatusPublisher: AnyPublisher<Bool, Never> {
return pushNotificationAllowStatusSubject.eraseToAnyPublisher()
}

public init(
isLoggedIn: @escaping () -> Bool,
Expand All @@ -26,7 +35,9 @@ public struct UserClient {
updateLoginState: @escaping (Bool) -> Void,
updateDeleteState: @escaping (Bool) -> Void,
updateFcmToken: @escaping (String) -> Void,
updatePushNotificationAllowStatus: @escaping (Bool) -> Void,
fetchAlertState: @escaping () async throws -> [UserAlertState],
fetchPushNotificationAllowStatus: @escaping () -> Bool,
updateAlertState: @escaping (UserAlertState) async throws -> Void,
fetchContacts: @escaping () async throws -> [String],
updateBlockContacts: @escaping ([String]) async throws -> Void
Expand All @@ -37,7 +48,9 @@ public struct UserClient {
self.updateLoginState = updateLoginState
self.updateDeleteState = updateDeleteState
self.updateFcmToken = updateFcmToken
self.updatePushNotificationAllowStatus = updatePushNotificationAllowStatus
self._fetchAlertState = fetchAlertState
self._fetchPushNotificationAllowStatus = fetchPushNotificationAllowStatus
self.updateAlertState = updateAlertState
self.fetchContacts = fetchContacts
self.updateBlockContacts = updateBlockContacts
Expand Down Expand Up @@ -67,10 +80,19 @@ public struct UserClient {
updateFcmToken(fcmToken)
}

public func updatePushNotificationAllowStatus(isAllow: Bool) {
pushNotificationAllowStatusSubject.send(isAllow)
updatePushNotificationAllowStatus(isAllow)
}

public func fetchAlertState() async throws -> [UserAlertState] {
try await _fetchAlertState()
}

public func fetchPushNotificationAllowStatus() -> Bool {
_fetchPushNotificationAllowStatus()
}

public func updateAlertState(alertState: UserAlertState) async throws {
try await updateAlertState(alertState)
}
Expand Down
29 changes: 22 additions & 7 deletions Projects/Domain/User/Sources/UserClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,56 @@ import ComposableArchitecture
import Moya

extension UserClient: DependencyKey {
private enum UserDefaultsKeys: String {
case loginState
case deleteState
case fcmToken
case alertAllowState
}

static public var liveValue: UserClient = .live()

static func live() -> UserClient {
@Dependency(\.network) var networkManager

return .init(
isLoggedIn: {
return UserDefaults.standard.bool(forKey: "loginState")
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.loginState.rawValue)
},

isAppDeleted: {
return !UserDefaults.standard.bool(forKey: "deleteState")
return !UserDefaults.standard.bool(forKey: UserDefaultsKeys.deleteState.rawValue)
},

fetchFcmToken: {
return UserDefaults.standard.string(forKey: "fcmToken")
return UserDefaults.standard.string(forKey: UserDefaultsKeys.fcmToken.rawValue)
},

updateLoginState: { isLoggedIn in
UserDefaults.standard.set(isLoggedIn, forKey: "loginState")
UserDefaults.standard.set(isLoggedIn, forKey: UserDefaultsKeys.loginState.rawValue)
},

updateDeleteState: { isDelete in
UserDefaults.standard.set(!isDelete, forKey: "deleteState")
UserDefaults.standard.set(!isDelete, forKey: UserDefaultsKeys.deleteState.rawValue)
},

updateFcmToken: { fcmToken in
UserDefaults.standard.set(fcmToken, forKey: "fcmToken")
UserDefaults.standard.set(fcmToken, forKey: UserDefaultsKeys.fcmToken.rawValue)
},

updatePushNotificationAllowStatus: { isAllow in
UserDefaults.standard.set(isAllow, forKey: UserDefaultsKeys.alertAllowState.rawValue)
},

fetchAlertState: {
let responseData = try await networkManager.reqeust(api: .apiType(UserAPI.fetchAlertState), dto: [AlertStateResponseDTO].self)
return responseData.map { $0.toDomain() }

},

fetchPushNotificationAllowStatus: {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.alertAllowState.rawValue)
},

updateAlertState: { alertState in
let requestData = AlertStateRequestDTO(alertType: alertState.alertType, enabled: alertState.enabled)
try await networkManager.reqeust(api: .apiType(UserAPI.updateAlertState(reqeustData: requestData)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@

import Foundation

import DomainApplicationInterface
import DomainApplication

import CoreWebViewInterface
import CoreKeyChainStoreInterface
import CoreKeyChainStore

import Dependencies

public enum BottleWebViewType {
private var baseURL: String {
(Bundle.main.infoDictionary?["WEB_VIEW_BASE_URL"] as? String) ?? ""
Expand Down Expand Up @@ -67,11 +72,15 @@ public enum BottleWebViewType {
// MARK: - private methods
private extension BottleWebViewType {
func makeUrlWithToken(_ path: String) -> URL {
@Dependency(\.applicationClient) var applicationClient

var components = URLComponents(string: baseURL)
components?.path = "/\(path)"
components?.queryItems = [
URLQueryItem(name: "accessToken", value: KeyChainTokenStore.shared.load(property: .accessToken)),
URLQueryItem(name: "refreshToken", value: KeyChainTokenStore.shared.load(property: .refreshToken))
URLQueryItem(name: "refreshToken", value: KeyChainTokenStore.shared.load(property: .refreshToken)),
URLQueryItem(name: "device", value: "ios"),
URLQueryItem(name: "version", value: applicationClient.fetchCurrentAppVersion())
]

return (components?.url)!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public struct BottleArrivalView: View {
LoadingIndicator()
}
}
.ignoresSafeArea(.all, edges: .bottom)
.ignoresSafeArea(.all, edges: [.top, .bottom])
.toolbar(.hidden, for: .navigationBar)
.toolbar(.hidden, for: .bottomBar)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

import Foundation

import CoreLoggerInterface
import FeatureReportInterface
import DomainBottle

import CoreLoggerInterface

import ComposableArchitecture

extension PingPongDetailFeature {
Expand Down Expand Up @@ -42,12 +43,46 @@ extension PingPongDetailFeature {
imageURL: imageURL ?? "", userID: userId ?? -1, userName: userName, userAge: userAge ?? -1)
return .send(.delegate(.reportButtonDidTapped(userReportProfile)))

case .stopTalkAlertDidRequired:
state.destination = .alert(.init(
title: { TextState("중단하기") },
actions: {
ButtonState(
role: .cancel,
action: .dismiss,
label: { TextState("계속하기")})

ButtonState(
role: .destructive,
action: .confirmStopTalk,
label: { TextState("중단하기") })
},
message: { TextState("중단 시 모든 핑퐁 내용이 사라져요. 정말 중단하시겠어요?") }
))
return .none

// Destination
case let .destination(.presented(.alert(alert))):
switch alert {
case .confirmStopTalk:
return .run { [bottleID = state.bottleID] send in
try await bottleClient.stopTalk(bottleID: bottleID)
await send(.delegate(.popToRootDidRequired))
}

case .dismiss:
state.destination = nil
return .none
}

// Introduction Delegate
case let .introduction(.delegate(delegate)):
switch delegate {
case .popToRootDidRequired:
return .send(.delegate(.popToRootDidRequired))
case .stopTaskButtonTapped:
return .send(.stopTalkAlertDidRequired)
}


// QuestionAndAnswer Delegate
case let .questionAndAnswer(.delegate(delegate)):
switch delegate {
case .reloadPingPongRequired:
Expand All @@ -56,8 +91,11 @@ extension PingPongDetailFeature {
return .send(.delegate(.popToRootDidRequired))
case .refreshPingPong:
return fetchPingPong(state: &state)
case .stopTaskButtonDidTapped:
return .send(.stopTalkAlertDidRequired)
}


// Matching Delegate
case let .matching(.delegate(delegate)):
switch delegate {
case .otherBottleButtonDidTapped:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public struct PingPongDetailFeature {
var matching: MatchingFeature.State
var selectedTab: PingPongDetailViewTabType

@Presents var destination: Destination.State?

public init(
bottleID: Int,
isRead: Bool,
Expand All @@ -67,7 +69,7 @@ public struct PingPongDetailFeature {
case pingPongDidFetched(_: BottlePingPong)
case backButtonDidTapped
case reportButtonDidTapped

case stopTalkAlertDidRequired

// Delegate
case delegate(Delegate)
Expand All @@ -83,6 +85,13 @@ public struct PingPongDetailFeature {
case questionAndAnswer(QuestionAndAnswerFeature.Action)
case matching(MatchingFeature.Action)
case binding(BindingAction<State>)
case destination(PresentationAction<Destination.Action>)
// Alert
case alert(Alert)
public enum Alert: Equatable {
case confirmStopTalk
case dismiss
}
}

public var body: some ReducerOf<Self> {
Expand All @@ -97,6 +106,15 @@ public struct PingPongDetailFeature {
MatchingFeature()
}
reducer
.ifLet(\.$destination, action: \.destination)
}
}

// MARK: - Destination

extension PingPongDetailFeature {
@Reducer(state: .equatable)
public enum Destination {
case alert(AlertState<PingPongDetailFeature.Action.Alert>)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public struct PingPongDetailView: View {
}
)
.ignoresSafeArea(.all, edges: .bottom)
.bottleAlert($store.scope(state: \.destination?.alert, action: \.destination.alert))
}
}

Expand Down
Loading

0 comments on commit 1524b1a

Please sign in to comment.