diff --git a/ylgamehall/Source/Bridge/Handlers/FriendsShareHandler.swift b/ylgamehall/Source/Bridge/Handlers/FriendsShareHandler.swift index a036b57..5ab707d 100644 --- a/ylgamehall/Source/Bridge/Handlers/FriendsShareHandler.swift +++ b/ylgamehall/Source/Bridge/Handlers/FriendsShareHandler.swift @@ -5,13 +5,15 @@ // H5 → Native handler:friendsSharetypeUrlToptitleDescript — 分享 // 契约:docs/H5-Native-Contract.md §3.1 [2] + §3.2 [11]sharesuccess // -// 流程(与 msext gameController.m:575-597 等价,调用即视为成功): -// 1. 入参解析 ShareContent -// 2. responseCallback("sharefriend") -// 3. 立即 bridge.call("sharesuccess", { success:"2", type: }) -// (**有意为之**:H5 端把"调用分享接口"等同"分享成功",无须等 SDK 回包; -// URL Scheme / 剪贴板路径本身就无返回。) -// 4. 异步 fire-and-forget 调用 ShareCenter.dispatch 拉起真实分享 UI +// **页面 mode 区分**(与原 msext 两个 ViewController 各注册一份且行为不同对齐): +// - `.lobby` 对应 NewRootVC.m:直发微信,3 type 分支(链接 / 截图 / 远端图) +// - `.subGame` 对应 gameController.m 活路径:SharePanel 三选一 + 朋友圈兜底 +// +// 公共流程(无论 mode): +// 1. responseCallback("sharefriend") +// 2. 立即 bridge.call("sharesuccess", { success:"2", type: }) +// (**有意为之**:调用分享接口即视为成功;URL Scheme / 剪贴板路径本就无返回) +// 3. 按 mode 异步 fire-and-forget 拉起 UI // // 各平台 SDK 依赖: // - 微信:通过 WeChatManager(已接入 1.8.2 SDK) @@ -23,7 +25,15 @@ import Foundation @MainActor public enum FriendsShareHandler { - public static func register(on bridge: any BridgeProtocol) { + /// 注册时机的页面 mode。 + public enum Mode: Sendable { + /// 大厅页:H5 在 WebContainer 调 friendsShare 时直发微信 + case lobby + /// 子游戏页:H5 在 SubGame 调 friendsShare 时弹 SharePanel 或朋友圈兜底 + case subGame + } + + public static func register(on bridge: any BridgeProtocol, mode: Mode) { // 【2】friendsSharetypeUrlToptitleDescript // 入参:sharefriend / sharetype / type / webpageUrl / title / description // cb: "sharefriend"(msext 字面,沿用) @@ -34,9 +44,10 @@ public enum FriendsShareHandler { guard let obj = data?.asObject else { return } let content = ShareContent( - sharefriend: obj["sharefriend"]?.asString ?? "1", + // 缺失默认空串 → ShareCenter default 分支兜底(与原 msext [nil intValue]==0 等价) + sharefriend: obj["sharefriend"]?.asString ?? "", sharetype: obj["sharetype"]?.asString ?? "", - type: obj["type"]?.asString ?? "1", + type: obj["type"]?.asString ?? "", webpageUrl: obj["webpageUrl"]?.asString ?? "", title: obj["title"]?.asString ?? "", desc: obj["description"]?.asString ?? "" @@ -53,8 +64,11 @@ public enum FriendsShareHandler { callback: nil ) - // 3. fire-and-forget 拉起真实分享 UI - ShareCenter.dispatch(content) + // 3. fire-and-forget 拉起真实分享 UI(按页面 mode 选路径) + switch mode { + case .lobby: ShareCenter.dispatchLobby(content) + case .subGame: ShareCenter.dispatchSubGame(content) + } } } } diff --git a/ylgamehall/Source/Share/ShareCenter.swift b/ylgamehall/Source/Share/ShareCenter.swift index cc1acfa..6f9c0a6 100644 --- a/ylgamehall/Source/Share/ShareCenter.swift +++ b/ylgamehall/Source/Share/ShareCenter.swift @@ -2,9 +2,17 @@ // ShareCenter.swift // ylgamehall // -// H5 friendsShare 调用入口的协调中心,按 msext gameController.m:575-597 行为: -// - sharefriend == "1"(好友/私聊)→ 弹 SharePanel 三选一(微信/QQ/抖音) -// - sharefriend == "2"(朋友圈) → 直接 WechatShare(朋友圈 timeline 场景) +// H5 friendsShare 调用入口的协调中心。原 msext 同名 handler 在两个 +// ViewController 各注册一份且**行为不同**,新外壳按页面 mode 分发: +// +// • dispatchSubGame — 对齐 msext gameController.m:575-597(活路径): +// - sharefriend == "1" → SharePanel 三选一(微信/QQ/抖音) +// - 其他 → WechatShare.share() 朋友圈 timeline +// +// • dispatchLobby — 对齐 msext NewRootVC.m:453-537:直发微信,无 SharePanel +// - sharefriend == "1" → WXSceneSession(好友) +// - 其他 → WXSceneTimeline(朋友圈) +// - type 3 分支由 WechatShare.shareLobby 内部处理 // // fire-and-forget:H5 端 sharesuccess 已在 FriendsShareHandler 立即触发, // 这里只负责拉起 UI / SDK,不需要回传结果。 @@ -15,19 +23,23 @@ import Foundation @MainActor public enum ShareCenter { - public static func dispatch(_ content: ShareContent) { + /// 子游戏页(SubGameViewController)路径:SharePanel + 朋友圈兜底 + public static func dispatchSubGame(_ content: ShareContent) { switch content.sharefriend { case "1": // 好友/私聊 → SharePanel 三选一 SharePanel.show(content: content) - case "2": - // 朋友圈 → 直接 WechatShare(timeline 场景) - WechatShare.shared.share(content, scene: .timeline) - default: - // 未知 sharefriend:msext 沿用第二条分支兜底(朋友圈微信) + // sharefriend == "2" 或缺失或其他 → msext WechatShareManager + // 内部 [sharefriend intValue] != 1 → WXSceneTimeline 兜底 WechatShare.shared.share(content, scene: .timeline) } } + + /// 大厅页(WebContainerViewController)路径:直发微信,无 SharePanel + public static func dispatchLobby(_ content: ShareContent) { + let scene: ShareScene = content.sharefriend == "1" ? .friend : .timeline + WechatShare.shared.shareLobby(content, scene: scene) + } } diff --git a/ylgamehall/Source/Share/WechatShare.swift b/ylgamehall/Source/Share/WechatShare.swift index f1969c6..819d193 100644 --- a/ylgamehall/Source/Share/WechatShare.swift +++ b/ylgamehall/Source/Share/WechatShare.swift @@ -2,12 +2,20 @@ // WechatShare.swift // ylgamehall // -// 微信分享 SharePlatform 实现,1:1 对齐 daoqi msext WechatShareManager -// `shareWithContent:completion:`(仅两个分支): -// - type == "2" → 截图分享(getImageWithFullScreenshot + JPEG 0.6 + sendImageData) -// - else → 链接分享(sendLinkURL + webpageUrl + title + desc) +// 微信分享 SharePlatform 实现 + 大厅专用 shareLobby() 路径。 // -// 缩略图:app icon(与原 `[self getAppIconImage]` 同源策略,不用 sharelogo)。 +// 两条路径对应原 msext 两个 ViewController 中同名 handler 的差异行为: +// +// • share() — 对齐 daoqi msext `WechatShareManager.shareWithContent:` +// (子游戏 gameController.m 活路径)2 type 分支: +// - type == "2" → 截图分享(thumb = app icon) +// - else → 链接分享(thumb = app icon) +// +// • shareLobby() — 对齐 daoqi msext `NewRootVC.m:471-534` 3 type 分支: +// - type == "1" → 链接分享(thumb = app icon;原 msext +// 用 sharelogo.png,按用户决定 fallback 到 app icon) +// - type == "2" → 截图分享(thumb = 截图 40% 缩放) +// - 其他 → 远端图分享(NSData 直发 + thumb = 远端图 40% 缩放) // // fire-and-forget:H5 端的 sharesuccess 已在 FriendsShareHandler 立即触发, // 此处不再回传结果(与原 dispatch_after 1s completion(YES) 实质等价)。 @@ -69,6 +77,69 @@ public final class WechatShare: SharePlatform { } } + // MARK: - 大厅路径(NewRootVC.m) + + /// 大厅页 H5 调 friendsShare 的微信分享路径,对齐 msext NewRootVC.m + /// :471-534 完整 3 分支结构。直发微信无 SharePanel。 + public func shareLobby(_ content: ShareContent, scene: ShareScene) { + guard WXApi.isWXAppInstalled() else { return } + + let wxScene: WeChatManager.ShareScene = (scene == .timeline) ? .timeline : .session + let appIcon = Self.appIconImage() + + Task { @MainActor in + do { + switch content.type { + case "1": + // 链接分享:原 msext 用 sharelogo.png;用户决定 fallback 到 app icon + try await WeChatManager.shared.shareLink( + .init( + url: content.webpageUrl, + title: content.title, + desc: content.desc, + thumb: appIcon, + mediaTagName: "\(Self.appTagPrefix)游戏下载链接" + ), + scene: wxScene + ) + case "2": + // 截图分享:thumb = 截图 40% 缩放(对齐 msext imageByScalingAndCroppingForSize:0.4x) + let shot = try ImageProvider.captureScreenshot() + guard let imageData = shot.jpegData(compressionQuality: 0.6) else { return } + try await WeChatManager.shared.shareImage( + .init( + imageData: imageData, + thumb: ImageProvider.scaledThumb(shot, scale: 0.4), + messageExt: content.desc, + mediaTagName: "\(Self.appTagPrefix)游戏截图分享" + ), + scene: wxScene + ) + default: + // 远端图分享(type == "" / "3" / 其他):下载 NSData 直发 + + // thumb = 远端图 40% 缩放。messageExt/mediaTagName 都是固定 + // 字面(msext NewRootVC.m:530 字面,非 H5 desc) + guard let url = URL(string: content.webpageUrl) else { return } + let (data, _) = try await URLSession.shared.data(from: url) + guard let image = UIImage(data: data) else { return } + try await WeChatManager.shared.shareImage( + .init( + imageData: data, + thumb: ImageProvider.scaledThumb(image, scale: 0.4), + messageExt: "\(Self.appTagPrefix)公众号二维码分享", + mediaTagName: "\(Self.appTagPrefix)公众号二维码分享" + ), + scene: wxScene + ) + } + } catch { + // 与子游戏路径同样吞掉错误(sharesuccess 已乐观发出) + } + } + } + + // MARK: - 共享常量 / 工具 + /// msext WechatShareManager 字面常量 "进贤聚友棋牌"(与 daoqi /// SGDefineInfo.h gamehallname 同源),用于 WXMediaMessage.mediaTagName 拼接。 private static let appTagPrefix = "进贤聚友棋牌" diff --git a/ylgamehall/Source/WebView/SubGameViewController.swift b/ylgamehall/Source/WebView/SubGameViewController.swift index c9a5cd4..9ba7033 100644 --- a/ylgamehall/Source/WebView/SubGameViewController.swift +++ b/ylgamehall/Source/WebView/SubGameViewController.swift @@ -112,7 +112,7 @@ public final class SubGameViewController: UIViewController { ) AccreditLoginHandler.register(on: bridge) - FriendsShareHandler.register(on: bridge) + FriendsShareHandler.register(on: bridge, mode: .subGame) // 子游戏路径:SharePanel 三选一 + 朋友圈兜底(对齐 msext gameController.m 活路径) // OpenurlTitleData 在子游戏中也可能调(threeView 弹层 push 自子游戏页),保留 stub OpenurlTitleDataHandler.register(on: bridge) diff --git a/ylgamehall/Source/WebView/WebContainerViewController.swift b/ylgamehall/Source/WebView/WebContainerViewController.swift index 937b0cc..e871503 100644 --- a/ylgamehall/Source/WebView/WebContainerViewController.swift +++ b/ylgamehall/Source/WebView/WebContainerViewController.swift @@ -95,7 +95,7 @@ public final class WebContainerViewController: UIViewController { // Phase 4.A 登录 + 分享 stub(Phase 4.B 微信 SDK / Phase 4.C QQ URL Scheme 后升级) AccreditLoginHandler.register(on: bridge) // §3.1 [1]Phase 4.E 完整微信 SDK - FriendsShareHandler.register(on: bridge) // §3.1 [2]Phase 4.B 完整框架(QQ/抖音 已 4.C/4.D 落地) + FriendsShareHandler.register(on: bridge, mode: .lobby) // §3.1 [2]大厅路径:直发微信,3 type 分支(对齐 msext NewRootVC.m) // Phase 6.4 / 7 子游戏 + 弹层入口 stub(让 H5 业务期调时不报 "no handler") SwitchOverGameHandler.register(on: bridge) // §3.1 [17]Phase 6 完整实现