Files
youle_app_ios_v2/ylgamehall/Source/Share/QQShare.swift
T
joywayer 9bd5a81e6c 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
2026-06-24 07:34:03 +08:00

109 lines
3.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// QQShare.swift
// ylgamehall
//
// QQ 分享 SharePlatform 实现,对齐 daoqi msext QQShareManager
// `shareWithContent:completion:`(按 type 分两个分支):
// - type == "2" → 截图分享:复制截图到 UIPasteboard + 拉起 mqqapi://
// - else → 链接分享:mqqapi://share/to_fri?req_type=1&url=...
//
// **不依赖 QQ SDK**CLAUDE.md ADR-006):原项目走 SDK 弹原生会话列表,
// 新外壳走 URL Scheme + pasteboard 兜底实现等价 UX。
//
// encodeString 与 msext line 1262-1268 等价:unreserved 字符集(RFC 3986
// alphanumeric + "-._~"),其它全部 percent encode。
//
// fire-and-forgetsharesuccess 已在 FriendsShareHandler 立即触发。
//
import UIKit
import os.log
@MainActor
public final class QQShare: SharePlatform {
public static let shared = QQShare()
private static let log = Logger(subsystem: "ylgamehall", category: "QQShare")
public let name = "QQ"
public var isInstalled: Bool {
guard let url = URL(string: "mqqapi://") else { return false }
return UIApplication.shared.canOpenURL(url)
}
public init() {}
public func share(_ content: ShareContent, scene: ShareScene) {
guard isInstalled else { return }
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)")
}
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 {
parts.append("req_type=0")
}
if !content.title.isEmpty {
parts.append("title=\(Self.encode(content.title))")
}
if !content.desc.isEmpty {
parts.append("description=\(Self.encode(content.desc))")
}
let urlString = "mqqapi://\(host)?\(parts.joined(separator: "&"))"
guard let qqURL = URL(string: urlString),
UIApplication.shared.canOpenURL(qqURL) else { return }
UIApplication.shared.open(qqURL)
}
nonisolated static func encode(_ string: String) -> String {
var allowed = CharacterSet.alphanumerics
allowed.insert(charactersIn: "-._~")
return string.addingPercentEncoding(withAllowedCharacters: allowed) ?? ""
}
}