feat(event): 이벤트 상세 딥링크와 OneLink를 연결한다
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import Foundation
|
||||
|
||||
import AppsFlyerLib
|
||||
|
||||
enum AppDeepLinkAction {
|
||||
case live(roomId: Int)
|
||||
case userCreatorChat(roomId: Int)
|
||||
@@ -7,6 +9,7 @@ enum AppDeepLinkAction {
|
||||
case series(seriesId: Int)
|
||||
case giftDetail(applicationNo: String)
|
||||
case community(postId: Int)
|
||||
case eventDetail(eventId: Int)
|
||||
case message
|
||||
case audition
|
||||
}
|
||||
@@ -17,7 +20,14 @@ enum AppDeepLinkSource {
|
||||
}
|
||||
|
||||
enum AppDeepLinkHandler {
|
||||
private static var navigatingEventId: Int?
|
||||
|
||||
static func handle(url: URL, source: AppDeepLinkSource = .external) -> Bool {
|
||||
if isOneLink(url) {
|
||||
AppsFlyerLib.shared().performOnAppAttribution(with: url)
|
||||
return true
|
||||
}
|
||||
|
||||
guard isSupportedScheme(url) else {
|
||||
return false
|
||||
}
|
||||
@@ -26,16 +36,58 @@ enum AppDeepLinkHandler {
|
||||
return false
|
||||
}
|
||||
|
||||
dispatch(action: action, source: source)
|
||||
return true
|
||||
}
|
||||
|
||||
static func handleEventDeepLink(result: DeepLinkResult) -> Bool {
|
||||
guard result.status == .found,
|
||||
let clickEvent = result.deepLink?.clickEvent,
|
||||
clickEvent["deep_link_value"] as? String == "event-detail",
|
||||
let id = eventId(clickEvent["deep_link_sub5"] as? String) else {
|
||||
return false
|
||||
}
|
||||
|
||||
dispatch(action: .eventDetail(eventId: id))
|
||||
return true
|
||||
}
|
||||
|
||||
static func handlePushDeepLink(userInfo: [AnyHashable: Any]) -> Bool {
|
||||
if userInfo["notifly_message_type"] as? String == "push-notification" {
|
||||
if let url = userInfo["url"] as? URL, !url.absoluteString.isEmpty {
|
||||
return true
|
||||
}
|
||||
if let urlString = userInfo["url"] as? String, !urlString.isEmpty {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
let deepLinkString = (userInfo["deep_link"] as? String ?? "")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !deepLinkString.isEmpty else { return false }
|
||||
|
||||
if let url = URL(string: deepLinkString), isOneLink(url) {
|
||||
AppsFlyerLib.shared().handlePushNotification(userInfo)
|
||||
} else {
|
||||
_ = handle(urlString: deepLinkString)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private static func dispatch(action: AppDeepLinkAction, source: AppDeepLinkSource = .external) {
|
||||
DispatchQueue.main.async {
|
||||
if case .splash = AppState.shared.rootStep {
|
||||
if case .eventDetail(let eventId) = action,
|
||||
case .eventDetail(let pendingId)? = AppState.shared.pendingDeepLinkAction,
|
||||
pendingId == eventId {
|
||||
return
|
||||
}
|
||||
AppState.shared.pendingDeepLinkAction = action
|
||||
return
|
||||
}
|
||||
|
||||
apply(action: action, source: source)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
static func handle(urlString: String, source: AppDeepLinkSource = .external) -> Bool {
|
||||
@@ -78,6 +130,35 @@ enum AppDeepLinkHandler {
|
||||
guard postId > 0 else { return }
|
||||
AppState.shared.setAppStep(step: .creatorChannelCommunityPostDetail(postId: postId))
|
||||
|
||||
case .eventDetail(let eventId):
|
||||
guard eventId > 0, navigatingEventId != eventId else { return }
|
||||
navigatingEventId = eventId
|
||||
AppState.shared.requestExternalNavigation(
|
||||
navigationAction: {
|
||||
guard navigatingEventId == eventId else { return }
|
||||
AppState.shared.setAppStep(
|
||||
step: .eventDetail(eventId: eventId),
|
||||
ifCurrentStep: { currentStep in
|
||||
guard navigatingEventId == eventId else { return false }
|
||||
if case .eventDetail(let currentId) = currentStep {
|
||||
return currentId != eventId
|
||||
}
|
||||
return true
|
||||
},
|
||||
completion: {
|
||||
if navigatingEventId == eventId {
|
||||
navigatingEventId = nil
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
cancelAction: {
|
||||
if navigatingEventId == eventId {
|
||||
navigatingEventId = nil
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
case .message:
|
||||
AppState.shared.setAppStep(step: .message)
|
||||
|
||||
@@ -86,6 +167,26 @@ enum AppDeepLinkHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static func isOneLink(_ url: URL) -> Bool {
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
return false
|
||||
}
|
||||
|
||||
return components.scheme?.lowercased() == "https"
|
||||
&& components.host?.lowercased() == "voiceon.onelink.me"
|
||||
&& components.user == nil && components.password == nil
|
||||
&& (components.port == nil || components.port == 443)
|
||||
}
|
||||
|
||||
private static func eventId(_ identifier: String?) -> Int? {
|
||||
guard let identifier = identifier, !identifier.isEmpty,
|
||||
identifier.utf8.allSatisfy({ $0 >= 48 && $0 <= 57 }),
|
||||
let value = Int64(identifier), value > 0 else {
|
||||
return nil
|
||||
}
|
||||
return Int(exactly: value)
|
||||
}
|
||||
|
||||
private static func isSupportedScheme(_ url: URL) -> Bool {
|
||||
guard let scheme = url.scheme?.lowercased() else {
|
||||
return false
|
||||
@@ -100,6 +201,16 @@ enum AppDeepLinkHandler {
|
||||
let host = components?.host?.lowercased() ?? ""
|
||||
let pathRoute = url.pathComponents.first { $0 != "/" && !$0.isEmpty }?.lowercased()
|
||||
|
||||
if host == "event-detail" || (host.isEmpty && pathRoute == "event-detail") {
|
||||
let scheme = url.scheme?.lowercased()
|
||||
guard scheme == "voiceon" || scheme == "voiceon-test" else { return nil }
|
||||
let path = url.pathComponents.filter { $0 != "/" && !$0.isEmpty }
|
||||
guard path.count == (host.isEmpty ? 2 : 1), let id = eventId(path.last) else {
|
||||
return nil
|
||||
}
|
||||
return .eventDetail(eventId: id)
|
||||
}
|
||||
|
||||
if host == "community" || (host.isEmpty && pathRoute == "community") {
|
||||
return parsePathStyle(url: url, components: components)
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
AppsFlyerLib.shared().delegate = self
|
||||
AppsFlyerLib.shared().deepLinkDelegate = self
|
||||
AppsFlyerLib.shared().addPushNotificationDeepLinkPath(["deep_link"])
|
||||
}
|
||||
|
||||
private func logUtmInFirebase() {
|
||||
@@ -221,6 +222,7 @@ extension AppDelegate: DeepLinkDelegate {
|
||||
}
|
||||
|
||||
logUtmInFirebase()
|
||||
_ = AppDeepLinkHandler.handleEventDeepLink(result: result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,11 +274,7 @@ extension AppDelegate : UNUserNotificationCenterDelegate {
|
||||
Messaging.messaging().appDidReceiveMessage(userInfo)
|
||||
Notifly.userNotificationCenter(center, didReceive: response)
|
||||
|
||||
let deepLinkString = (userInfo["deep_link"] as? String ?? "")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if !deepLinkString.isEmpty {
|
||||
_ = AppDeepLinkHandler.handle(urlString: deepLinkString)
|
||||
if AppDeepLinkHandler.handlePushDeepLink(userInfo: userInfo) {
|
||||
completionHandler()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -110,8 +110,15 @@ class AppState: ObservableObject {
|
||||
routeStepMap[route]
|
||||
}
|
||||
|
||||
func setAppStep(step: AppStep) {
|
||||
func setAppStep(
|
||||
step: AppStep,
|
||||
ifCurrentStep matches: ((AppStep) -> Bool)? = nil,
|
||||
completion: @escaping () -> Void = {}
|
||||
) {
|
||||
DispatchQueue.main.async {
|
||||
defer { completion() }
|
||||
guard matches?(self.appStep) ?? true else { return }
|
||||
|
||||
switch step {
|
||||
case .splash, .main:
|
||||
self.liveDetailSheet = nil
|
||||
@@ -196,6 +203,7 @@ class AppState: ObservableObject {
|
||||
return
|
||||
}
|
||||
|
||||
pendingExternalNavigationCancelAction?()
|
||||
pendingExternalNavigationAction = navigationAction
|
||||
pendingExternalNavigationCancelAction = cancelAction
|
||||
isShowLeaveLiveNavigationDialog = true
|
||||
|
||||
Reference in New Issue
Block a user