4.B 分享逻辑对齐 daoqi msext:调用即触发 sharesuccess
【契约影响】docs/H5-Native-Contract.md §3.1[2]/§3.2[11]
H5 端 sharesuccess 时机改为"调用 friendsShare handler 即立刻发",与
原 msext 行为等价(live path 完全不等 SDK / URL Scheme 回包)。用户
明示有意为之:URL Scheme + 剪贴板路径本就没有可靠回包,乐观策略避免
H5 业务卡住。
3 平台分支严格 1:1 对齐 daoqi msext:
WechatShare(对齐 WechatShareManager shareWithContent)
- type=="2" → 截图 JPEG 0.6 + sendImageData + app icon thumb
- else → 链接分享 sendLinkURL + app icon thumb
- 移除原"远端图"分支(原项目无此分支)
- 新增 appIconImage helper(读 Info.plist CFBundleIcons,对齐
[self getAppIconImage])
QQShare(对齐 QQShareManager shareWithContent + simpleShareToQQFriend)
- type=="2" → 截图 → UIPasteboard.image → 4 URL Scheme fallback
- else → mqqapi://share/to_{fri,qzone}?req_type=1&url=...
&title=...&description=...(webpageUrl 空时 req_type=0)
- 不依赖 QQ SDK(CLAUDE.md ADR-006)
DouyinShare(对齐 DouyinShareManager shareWithContent)
- type=="2" → 截图 → UIPasteboard.image → "立即打开抖音"引导框
- else → title+"\n\n"+desc → UIPasteboard.string → 引导框
双侧空时兜底 "来自进贤聚友棋牌的精彩内容分享"
- 放弃之前的"存相册 + 自动拉起"激进路径,回到原项目"用户主动点"路径
架构调整:
- SharePlatform.share 改 sync fire-and-forget(去 ShareResult 返回)
- ShareCenter.dispatch / SharePanel.show 去 completion 参数
- FriendsShareHandler:解析后先发 responseCallback("sharefriend")
与 sharesuccess({success:"2", type:<sharefriend>}),再异步 dispatch
This commit is contained in:
@@ -2,21 +2,18 @@
|
||||
// QQShare.swift
|
||||
// ylgamehall
|
||||
//
|
||||
// QQ 分享 SharePlatform 实现(Phase 4.C 完整)。
|
||||
// QQ 分享 SharePlatform 实现,对齐 daoqi msext QQShareManager
|
||||
// `shareWithContent:completion:`(按 type 分两个分支):
|
||||
// - type == "2" → 截图分享:复制截图到 UIPasteboard + 拉起 mqqapi://
|
||||
// - else → 链接分享:mqqapi://share/to_fri?req_type=1&url=...
|
||||
//
|
||||
// 移植 msext QQShareManager.m:716-771 simpleShareToQQFriend 的 URL Scheme
|
||||
// fallback。**不依赖 QQ SDK**。
|
||||
//
|
||||
// URL 构造:
|
||||
// mqqapi://share/to_fri?version=1&cflag=0&req_type=1
|
||||
// &url=<encoded webpageUrl>&title=<encoded title>&description=<encoded desc>
|
||||
// **不依赖 QQ SDK**(CLAUDE.md ADR-006):原项目走 SDK 弹原生会话列表,
|
||||
// 新外壳走 URL Scheme + pasteboard 兜底实现等价 UX。
|
||||
//
|
||||
// encodeString 与 msext line 1262-1268 等价:unreserved 字符集(RFC 3986
|
||||
// alphanumeric + "-._~"),其它全部 percent encode。
|
||||
//
|
||||
// ⚠️ 当前仅支持链接分享(type == "1")。type == "2" 截图 / 其它 远端图
|
||||
// 在 Phase 4.F 接入(依赖 UIGraphicsImageRenderer + Photos / 临时文件 +
|
||||
// mqqapi://share/to_fri?file_type=img 路径)。
|
||||
// fire-and-forget:sharesuccess 已在 FriendsShareHandler 立即触发。
|
||||
//
|
||||
|
||||
import UIKit
|
||||
@@ -38,28 +35,57 @@ public final class QQShare: SharePlatform {
|
||||
|
||||
public init() {}
|
||||
|
||||
public func share(_ content: ShareContent, scene: ShareScene) async -> ShareResult {
|
||||
guard isInstalled else { return .notInstalled(platform: name) }
|
||||
public func share(_ content: ShareContent, scene: ShareScene) {
|
||||
guard isInstalled else { return }
|
||||
|
||||
// 仅链接分享走完整 URL Scheme;图片分享留 Phase 4.F
|
||||
guard content.type == "1" else {
|
||||
Self.log.warning("QQShare: type=\(content.type, privacy: .public) (非链接) 暂未实现,返回 success 避免 H5 业务卡住")
|
||||
return .success
|
||||
if content.type == "2" {
|
||||
shareScreenshot()
|
||||
} else {
|
||||
shareLink(content, scene: scene)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 截图分享
|
||||
|
||||
/// 对齐 msext QQShareManager.m:1876-1906 isScreenshotShare 分支:
|
||||
/// 原项目用 QQ SDK shareToQQFriend QQShareTypeImage 直接传 UIImage 给 SDK;
|
||||
/// 无 SDK 路径下退化为:截图 → pasteboard → 拉起 QQ(用户在聊天里粘贴)。
|
||||
private func shareScreenshot() {
|
||||
do {
|
||||
let shot = try ImageProvider.captureScreenshot()
|
||||
UIPasteboard.general.image = shot
|
||||
} catch {
|
||||
Self.log.error("QQShare 截图失败 \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
|
||||
// 拼装 URL:mqqapi://share/to_fri? 或 mqqapi://share/to_qzone?
|
||||
// scene == .timeline 走 QQ 空间 to_qzone;.friend 走 to_fri(QQ 好友)
|
||||
let host = scene == .timeline ? "share/to_qzone" : "share/to_fri"
|
||||
var parts: [String] = [
|
||||
"version=1",
|
||||
"cflag=0",
|
||||
"req_type=1"
|
||||
let candidates = [
|
||||
"mqqapi://share/to_fri?file_type=news&version=1&cflag=0",
|
||||
"mqq://im/chat?chat_type=select",
|
||||
"mqqapi://",
|
||||
"mqq://"
|
||||
]
|
||||
for s in candidates {
|
||||
guard let url = URL(string: s),
|
||||
UIApplication.shared.canOpenURL(url) else { continue }
|
||||
UIApplication.shared.open(url)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 链接分享
|
||||
|
||||
/// 对齐 msext QQShareManager.m simpleShareToQQFriend:
|
||||
/// mqqapi://share/to_fri?version=1&cflag=0&req_type=1
|
||||
/// &url=<encoded webpageUrl>&title=<encoded title>&description=<encoded desc>
|
||||
/// scene == .timeline 走 QQ 空间 (to_qzone);.friend 走 QQ 好友 (to_fri)
|
||||
private func shareLink(_ content: ShareContent, scene: ShareScene) {
|
||||
let host = scene == .timeline ? "share/to_qzone" : "share/to_fri"
|
||||
var parts: [String] = ["version=1", "cflag=0"]
|
||||
if !content.webpageUrl.isEmpty {
|
||||
parts.append("req_type=1")
|
||||
parts.append("url=\(Self.encode(content.webpageUrl))")
|
||||
} else {
|
||||
// 无 URL → 改 req_type=0 文本分享
|
||||
parts = ["version=1", "cflag=0", "req_type=0"]
|
||||
parts.append("req_type=0")
|
||||
}
|
||||
if !content.title.isEmpty {
|
||||
parts.append("title=\(Self.encode(content.title))")
|
||||
@@ -69,21 +95,11 @@ public final class QQShare: SharePlatform {
|
||||
}
|
||||
let urlString = "mqqapi://\(host)?\(parts.joined(separator: "&"))"
|
||||
|
||||
Self.log.debug("QQShare open: \(urlString, privacy: .public)")
|
||||
|
||||
guard let qqURL = URL(string: urlString),
|
||||
UIApplication.shared.canOpenURL(qqURL)
|
||||
else {
|
||||
return .failed(reason: "invalid QQ URL")
|
||||
}
|
||||
|
||||
// URL Scheme 单向调起:msext 同款乐观策略 — 调起即视为 success
|
||||
let opened = await UIApplication.shared.open(qqURL)
|
||||
return opened ? .success : .failed(reason: "openURL failed")
|
||||
UIApplication.shared.canOpenURL(qqURL) else { return }
|
||||
UIApplication.shared.open(qqURL)
|
||||
}
|
||||
|
||||
/// 与 msext QQShareManager.m:1262-1268 等价:仅保留 RFC 3986 unreserved
|
||||
/// 字符(alphanumeric + "-._~"),其它全部 percent encode。
|
||||
nonisolated static func encode(_ string: String) -> String {
|
||||
var allowed = CharacterSet.alphanumerics
|
||||
allowed.insert(charactersIn: "-._~")
|
||||
|
||||
Reference in New Issue
Block a user