fix(deeplink): 커뮤니티 링크를 게시글 상세에 연결한다
This commit is contained in:
@@ -6,7 +6,7 @@ enum AppDeepLinkAction {
|
|||||||
case content(contentId: Int)
|
case content(contentId: Int)
|
||||||
case series(seriesId: Int)
|
case series(seriesId: Int)
|
||||||
case giftDetail(applicationNo: String)
|
case giftDetail(applicationNo: String)
|
||||||
case community(creatorId: Int, postId: Int?)
|
case community(postId: Int)
|
||||||
case message
|
case message
|
||||||
case audition
|
case audition
|
||||||
}
|
}
|
||||||
@@ -74,16 +74,9 @@ enum AppDeepLinkHandler {
|
|||||||
case .giftDetail(let applicationNo):
|
case .giftDetail(let applicationNo):
|
||||||
AppState.shared.setAppStep(step: .giftDetail(applicationNo: applicationNo))
|
AppState.shared.setAppStep(step: .giftDetail(applicationNo: applicationNo))
|
||||||
|
|
||||||
case .community(let creatorId, let postId):
|
case .community(let postId):
|
||||||
guard creatorId > 0 else { return }
|
guard postId > 0 else { return }
|
||||||
|
AppState.shared.setAppStep(step: .creatorChannelCommunityPostDetail(postId: postId))
|
||||||
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:
|
case .message:
|
||||||
AppState.shared.setAppStep(step: .message)
|
AppState.shared.setAppStep(step: .message)
|
||||||
@@ -104,6 +97,12 @@ enum AppDeepLinkHandler {
|
|||||||
|
|
||||||
private static func parseAction(url: URL) -> AppDeepLinkAction? {
|
private static func parseAction(url: URL) -> AppDeepLinkAction? {
|
||||||
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
||||||
|
let host = components?.host?.lowercased() ?? ""
|
||||||
|
let pathRoute = url.pathComponents.first { $0 != "/" && !$0.isEmpty }?.lowercased()
|
||||||
|
|
||||||
|
if host == "community" || (host.isEmpty && pathRoute == "community") {
|
||||||
|
return parsePathStyle(url: url, components: components)
|
||||||
|
}
|
||||||
|
|
||||||
if let routeAction = parsePathStyle(url: url, components: components) {
|
if let routeAction = parsePathStyle(url: url, components: components) {
|
||||||
return routeAction
|
return routeAction
|
||||||
@@ -160,7 +159,10 @@ enum AppDeepLinkHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let communityId = queryMap["community_id"], let value = Int(communityId), value > 0 {
|
if let communityId = queryMap["community_id"], let value = Int(communityId), value > 0 {
|
||||||
return .community(creatorId: value, postId: communityPostId(queryMap: queryMap))
|
guard let postId = communityPostId(queryMap: queryMap) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return .community(postId: postId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if queryMap["message_id"] != nil {
|
if queryMap["message_id"] != nil {
|
||||||
@@ -208,17 +210,10 @@ enum AppDeepLinkHandler {
|
|||||||
return .giftDetail(applicationNo: applicationNo)
|
return .giftDetail(applicationNo: applicationNo)
|
||||||
|
|
||||||
case "community":
|
case "community":
|
||||||
let postId = communityPostId(queryItems: components?.queryItems)
|
guard let postId = communityPostId(queryItems: components?.queryItems) else {
|
||||||
|
|
||||||
if let identifier = identifier, let creatorId = Int(identifier), creatorId > 0 {
|
|
||||||
return .community(creatorId: creatorId, postId: postId)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let creatorId = fallbackCommunityCreatorId() else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return .community(postId: postId)
|
||||||
return .community(creatorId: creatorId, postId: postId)
|
|
||||||
|
|
||||||
case "message":
|
case "message":
|
||||||
return .message
|
return .message
|
||||||
@@ -256,8 +251,4 @@ enum AppDeepLinkHandler {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func fallbackCommunityCreatorId() -> Int? {
|
|
||||||
let userId = UserDefaults.int(forKey: .userId)
|
|
||||||
return userId > 0 ? userId : nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ enum AppStep: Equatable {
|
|||||||
case contentDetail(contentId: Int)
|
case contentDetail(contentId: Int)
|
||||||
case seriesDetail(seriesId: Int)
|
case seriesDetail(seriesId: Int)
|
||||||
case creatorCommunityAll(creatorId: Int)
|
case creatorCommunityAll(creatorId: Int)
|
||||||
|
case creatorChannelCommunityPostDetail(postId: Int)
|
||||||
case message
|
case message
|
||||||
case audition
|
case audition
|
||||||
case giftDetail(applicationNo: String)
|
case giftDetail(applicationNo: String)
|
||||||
@@ -14,7 +15,8 @@ enum AppStep: Equatable {
|
|||||||
|
|
||||||
enum UserDefaultsKey { case userId }
|
enum UserDefaultsKey { case userId }
|
||||||
enum UserDefaults {
|
enum UserDefaults {
|
||||||
static func int(forKey: UserDefaultsKey) -> Int { 0 }
|
static var userId = 0
|
||||||
|
static func int(forKey: UserDefaultsKey) -> Int { userId }
|
||||||
}
|
}
|
||||||
|
|
||||||
final class AppState {
|
final class AppState {
|
||||||
@@ -24,9 +26,10 @@ final class AppState {
|
|||||||
var appliedStep: AppStep?
|
var appliedStep: AppStep?
|
||||||
var isPushRoomFromDeepLink = false
|
var isPushRoomFromDeepLink = false
|
||||||
var pushRoomId = 0
|
var pushRoomId = 0
|
||||||
|
var pendingCommentPostId: Int?
|
||||||
func setAppStep(step: AppStep) { appliedStep = step }
|
func setAppStep(step: AppStep) { appliedStep = step }
|
||||||
func setPendingCommunityCommentDeepLink(creatorId: Int, postId: Int) {}
|
func setPendingCommunityCommentDeepLink(creatorId: Int, postId: Int) { pendingCommentPostId = postId }
|
||||||
func clearPendingCommunityCommentDeepLink() {}
|
func clearPendingCommunityCommentDeepLink() { pendingCommentPostId = nil }
|
||||||
}
|
}
|
||||||
|
|
||||||
let APPSCHEME = "soda"
|
let APPSCHEME = "soda"
|
||||||
@@ -104,6 +107,67 @@ enum GiftDetailDeepLinkCheck {
|
|||||||
check(AppDeepLinkHandler.handle(urlString: "voiceon://?content_id=42&deepLinkValue=GIFT_DETAIL&deepLinkId=GIFT-2026-007"), "existing content route accepted")
|
check(AppDeepLinkHandler.handle(urlString: "voiceon://?content_id=42&deepLinkValue=GIFT_DETAIL&deepLinkId=GIFT-2026-007"), "existing content route accepted")
|
||||||
settle()
|
settle()
|
||||||
check(AppState.shared.appliedStep == .contentDetail(contentId: 42), "existing content route still applies")
|
check(AppState.shared.appliedStep == .contentDetail(contentId: 42), "existing content route still applies")
|
||||||
|
checkCommunityLinks()
|
||||||
print("PASS AppDeepLinkGiftDetailCheck assertions=\(assertions)")
|
print("PASS AppDeepLinkGiftDetailCheck assertions=\(assertions)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func checkCommunityLinks() {
|
||||||
|
for userId in [0, 42] {
|
||||||
|
UserDefaults.userId = userId
|
||||||
|
for source in [AppDeepLinkSource.external, .notificationList] {
|
||||||
|
for url in [
|
||||||
|
"voiceon://community?postId=12345",
|
||||||
|
"voiceon-test://community?post_id=12345",
|
||||||
|
"voiceon:///community?POSTID=12345",
|
||||||
|
"voiceon://community/77?postId=12345",
|
||||||
|
"voiceon://?community_id=77&postId=12345"
|
||||||
|
] {
|
||||||
|
AppState.shared.appliedStep = nil
|
||||||
|
AppState.shared.pendingCommentPostId = nil
|
||||||
|
check(AppDeepLinkHandler.handle(urlString: url, source: source), "community link accepted: \(url), userId=\(userId)")
|
||||||
|
settle()
|
||||||
|
check(AppState.shared.appliedStep == .creatorChannelCommunityPostDetail(postId: 12345), "community link opens requested post detail: \(url)")
|
||||||
|
check(AppState.shared.pendingCommentPostId == nil, "detail link does not queue legacy comment sheet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for rootStep in [AppStep.main, .splash] {
|
||||||
|
AppState.shared.rootStep = rootStep
|
||||||
|
for route in ["voiceon://community", "voiceon:///community", "voiceon://community/77", "voiceon://?community_id=77"] {
|
||||||
|
for parameter in ["", "postId=", "postId=abc", "postId=0", "postId=-1", "postId=1.5", "postId=999999999999999999999999", "post_id=0"] {
|
||||||
|
let separator = route.contains("?") ? "&" : "?"
|
||||||
|
let url = route + separator + parameter
|
||||||
|
AppState.shared.appliedStep = .contentDetail(contentId: 42)
|
||||||
|
AppState.shared.pendingDeepLinkAction = .content(contentId: 99)
|
||||||
|
check(!AppDeepLinkHandler.handle(urlString: url), "invalid community link rejected: \(url)")
|
||||||
|
settle()
|
||||||
|
check(AppState.shared.appliedStep == .contentDetail(contentId: 42), "invalid postId leaves current route unchanged")
|
||||||
|
if case .content(let contentId)? = AppState.shared.pendingDeepLinkAction {
|
||||||
|
check(contentId == 99, "invalid postId preserves existing deferred action")
|
||||||
|
} else {
|
||||||
|
check(false, "invalid postId preserves existing deferred action")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check(!AppDeepLinkHandler.handle(urlString: "voiceon://community?postId=bad&content_id=42"), "invalid community route does not fall through to content")
|
||||||
|
check(!AppDeepLinkHandler.handle(urlString: "voiceon:///community?message_id=42"), "missing postId does not fall through to message")
|
||||||
|
|
||||||
|
AppState.shared.rootStep = .splash
|
||||||
|
AppState.shared.appliedStep = nil
|
||||||
|
AppState.shared.pendingDeepLinkAction = nil
|
||||||
|
check(AppDeepLinkHandler.handle(urlString: "voiceon://community?postId=12345"), "splash accepts community detail")
|
||||||
|
settle()
|
||||||
|
check(AppState.shared.appliedStep == nil, "splash defers detail navigation")
|
||||||
|
guard let action = AppState.shared.pendingDeepLinkAction else {
|
||||||
|
check(false, "splash stores community detail action")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
AppState.shared.rootStep = .main
|
||||||
|
AppState.shared.pendingDeepLinkAction = nil
|
||||||
|
AppDeepLinkHandler.apply(action: action)
|
||||||
|
check(AppState.shared.appliedStep == .creatorChannelCommunityPostDetail(postId: 12345), "deferred community link opens requested post detail")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user