Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/1.0.8 -> develop #285

Merged
merged 11 commits into from
Sep 26, 2024
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