235 lines
7.2 KiB
Swift
235 lines
7.2 KiB
Swift
import Foundation
|
|
|
|
enum AppDeepLinkAction {
|
|
case live(roomId: Int)
|
|
case content(contentId: Int)
|
|
case series(seriesId: Int)
|
|
case community(creatorId: Int, postId: Int?)
|
|
case message
|
|
case audition
|
|
}
|
|
|
|
enum AppDeepLinkSource {
|
|
case external
|
|
case notificationList
|
|
}
|
|
|
|
enum AppDeepLinkHandler {
|
|
static func handle(url: URL, source: AppDeepLinkSource = .external) -> Bool {
|
|
guard isSupportedScheme(url) else {
|
|
return false
|
|
}
|
|
|
|
guard let action = parseAction(url: url) else {
|
|
return false
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
if case .splash = AppState.shared.rootStep {
|
|
AppState.shared.pendingDeepLinkAction = action
|
|
return
|
|
}
|
|
|
|
apply(action: action, source: source)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
static func handle(urlString: String, source: AppDeepLinkSource = .external) -> Bool {
|
|
let trimmed = urlString.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard let url = URL(string: trimmed) else {
|
|
return false
|
|
}
|
|
|
|
return handle(url: url, source: source)
|
|
}
|
|
|
|
static func apply(action: AppDeepLinkAction) {
|
|
apply(action: action, source: .external)
|
|
}
|
|
|
|
private static func apply(action: AppDeepLinkAction, source: AppDeepLinkSource) {
|
|
switch action {
|
|
case .live(let roomId):
|
|
guard roomId > 0 else { return }
|
|
AppState.shared.isPushRoomFromDeepLink = source == .external
|
|
AppState.shared.pushRoomId = 0
|
|
AppState.shared.pushRoomId = roomId
|
|
|
|
case .content(let contentId):
|
|
guard contentId > 0 else { return }
|
|
AppState.shared.setAppStep(step: .contentDetail(contentId: contentId))
|
|
|
|
case .series(let seriesId):
|
|
guard seriesId > 0 else { return }
|
|
AppState.shared.setAppStep(step: .seriesDetail(seriesId: seriesId))
|
|
|
|
case .community(let creatorId, let postId):
|
|
guard creatorId > 0 else { return }
|
|
|
|
if let postId = postId, postId > 0 {
|
|
AppState.shared.setPendingCommunityCommentDeepLink(creatorId: creatorId, postId: postId)
|
|
} else {
|
|
AppState.shared.clearPendingCommunityCommentDeepLink()
|
|
}
|
|
|
|
AppState.shared.setAppStep(step: .creatorCommunityAll(creatorId: creatorId))
|
|
|
|
case .message:
|
|
AppState.shared.setAppStep(step: .message)
|
|
|
|
case .audition:
|
|
AppState.shared.setAppStep(step: .audition)
|
|
}
|
|
}
|
|
|
|
private static func isSupportedScheme(_ url: URL) -> Bool {
|
|
guard let scheme = url.scheme?.lowercased() else {
|
|
return false
|
|
}
|
|
|
|
let appScheme = APPSCHEME.lowercased()
|
|
return scheme == appScheme || scheme == "voiceon" || scheme == "voiceon-test"
|
|
}
|
|
|
|
private static func parseAction(url: URL) -> AppDeepLinkAction? {
|
|
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
|
|
|
if let routeAction = parsePathStyle(url: url, components: components) {
|
|
return routeAction
|
|
}
|
|
|
|
return parseQueryStyle(components: components)
|
|
}
|
|
|
|
private static func parsePathStyle(url: URL, components: URLComponents?) -> AppDeepLinkAction? {
|
|
let pathComponents = url.pathComponents.filter { $0 != "/" && !$0.isEmpty }
|
|
let host = components?.host?.lowercased() ?? ""
|
|
|
|
if !host.isEmpty {
|
|
let identifier = pathComponents.first
|
|
return makeAction(route: host, identifier: identifier, components: components)
|
|
}
|
|
|
|
guard !pathComponents.isEmpty else {
|
|
return nil
|
|
}
|
|
|
|
let route = pathComponents[0].lowercased()
|
|
let identifier = pathComponents.count > 1 ? pathComponents[1] : nil
|
|
return makeAction(route: route, identifier: identifier, components: components)
|
|
}
|
|
|
|
private static func parseQueryStyle(components: URLComponents?) -> AppDeepLinkAction? {
|
|
guard let queryItems = components?.queryItems else {
|
|
return nil
|
|
}
|
|
|
|
var queryMap: [String: String] = [:]
|
|
for item in queryItems {
|
|
queryMap[item.name.lowercased()] = item.value
|
|
}
|
|
|
|
if let roomId = queryMap["room_id"], let value = Int(roomId), value > 0 {
|
|
return .live(roomId: value)
|
|
}
|
|
|
|
if let contentId = queryMap["content_id"], let value = Int(contentId), value > 0 {
|
|
return .content(contentId: value)
|
|
}
|
|
|
|
if let seriesId = queryMap["series_id"], let value = Int(seriesId), value > 0 {
|
|
return .series(seriesId: value)
|
|
}
|
|
|
|
if let communityId = queryMap["community_id"], let value = Int(communityId), value > 0 {
|
|
return .community(creatorId: value, postId: communityPostId(queryMap: queryMap))
|
|
}
|
|
|
|
if queryMap["message_id"] != nil {
|
|
return .message
|
|
}
|
|
|
|
if queryMap["audition_id"] != nil {
|
|
return .audition
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
private static func makeAction(route: String, identifier: String?, components: URLComponents?) -> AppDeepLinkAction? {
|
|
switch route {
|
|
case "live":
|
|
guard let identifier = identifier, let roomId = Int(identifier), roomId > 0 else {
|
|
return nil
|
|
}
|
|
return .live(roomId: roomId)
|
|
|
|
case "content":
|
|
guard let identifier = identifier, let contentId = Int(identifier), contentId > 0 else {
|
|
return nil
|
|
}
|
|
return .content(contentId: contentId)
|
|
|
|
case "series":
|
|
guard let identifier = identifier, let seriesId = Int(identifier), seriesId > 0 else {
|
|
return nil
|
|
}
|
|
return .series(seriesId: seriesId)
|
|
|
|
case "community":
|
|
let postId = communityPostId(queryItems: components?.queryItems)
|
|
|
|
if let identifier = identifier, let creatorId = Int(identifier), creatorId > 0 {
|
|
return .community(creatorId: creatorId, postId: postId)
|
|
}
|
|
|
|
guard let creatorId = fallbackCommunityCreatorId() else {
|
|
return nil
|
|
}
|
|
|
|
return .community(creatorId: creatorId, postId: postId)
|
|
|
|
case "message":
|
|
return .message
|
|
|
|
case "audition":
|
|
return .audition
|
|
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private static func communityPostId(queryItems: [URLQueryItem]?) -> Int? {
|
|
guard let queryItems = queryItems else {
|
|
return nil
|
|
}
|
|
|
|
var queryMap: [String: String] = [:]
|
|
for item in queryItems {
|
|
queryMap[item.name.lowercased()] = item.value
|
|
}
|
|
|
|
return communityPostId(queryMap: queryMap)
|
|
}
|
|
|
|
private static func communityPostId(queryMap: [String: String]) -> Int? {
|
|
if let postId = queryMap["postid"], let value = Int(postId), value > 0 {
|
|
return value
|
|
}
|
|
|
|
if let postId = queryMap["post_id"], let value = Int(postId), value > 0 {
|
|
return value
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
private static func fallbackCommunityCreatorId() -> Int? {
|
|
let userId = UserDefaults.int(forKey: .userId)
|
|
return userId > 0 ? userId : nil
|
|
}
|
|
}
|