微信分享去sdk
This commit is contained in:
@@ -261,12 +261,11 @@ public enum RemoteAudioHandler {
|
||||
return // 参数不全 → 不触发 cb(对齐 daoqi 入参空时走到 file 不存在 return)
|
||||
}
|
||||
|
||||
// 2. 记录 lastUserId(无论是否实际播放都更新,对齐 daoqi
|
||||
// `self.User_id = userid;` 总是执行的行为)
|
||||
VoicePlayer.shared.recordIncomingUser(user)
|
||||
|
||||
// 3. 注册 VoicePlayer.onFinish(每次 mediaTypeAudio 更新,绑定当前 bridge;
|
||||
// [weak bridge] 让 bridge 释放后 onFinish 自动失效)
|
||||
// 2. 注册 VoicePlayer.onFinish(每次 mediaTypeAudio 更新,绑定当前 bridge;
|
||||
// [weak bridge] 让 bridge 释放后 onFinish 自动失效)。
|
||||
// onFinish 触发时机:自然结束 / 解码失败 / 被新一次 play 打断;
|
||||
// payload `finishedUser` 是被结束 / 被打断那条语音绑定的 user
|
||||
// (由 VoicePlayer 内部 currentUser 跟踪),不是入参里最新的 user。
|
||||
VoicePlayer.shared.onFinish = { [weak bridge] finishedUser in
|
||||
Task { @MainActor in
|
||||
bridge?.call(
|
||||
@@ -277,7 +276,7 @@ public enum RemoteAudioHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 异步下载 AMR
|
||||
// 3. 异步下载 AMR
|
||||
let amrData: Data
|
||||
do {
|
||||
let (d, _) = try await URLSession.shared.data(from: downloadURL)
|
||||
@@ -288,7 +287,7 @@ public enum RemoteAudioHandler {
|
||||
// `if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) return;`)
|
||||
}
|
||||
|
||||
// 5. 写到沙盒 .amr 临时文件
|
||||
// 4. 写到沙盒 .amr 临时文件
|
||||
let tempName = VoiceRecorder.currentTimeString()
|
||||
let docs = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
let amrLocal = docs.appendingPathComponent(tempName).appendingPathExtension("amr")
|
||||
@@ -300,7 +299,7 @@ public enum RemoteAudioHandler {
|
||||
return
|
||||
}
|
||||
|
||||
// 6. AMR → WAV 转码
|
||||
// 5. AMR → WAV 转码
|
||||
// daoqi 文件名约定:tempName + "_AmrToWav.wav"(NewRootVC.m:341)
|
||||
let wavLocal = docs
|
||||
.appendingPathComponent("\(tempName)_AmrToWav")
|
||||
@@ -317,14 +316,16 @@ public enum RemoteAudioHandler {
|
||||
transcoded = false
|
||||
}
|
||||
|
||||
// 7. voicePlaying 总开关 + 实际播放 + gameui_play_voice
|
||||
// 6. voicePlaying 总开关 + 实际播放 + gameui_play_voice
|
||||
// (仅转码成功 + 开关 enabled 时触发,对齐 daoqi
|
||||
// NewRootVC.m:343-353 if (canbofang) { play + callHandler })
|
||||
// NewRootVC.m:343-353 if (canbofang) { play + callHandler })。
|
||||
// 若当前已有正在播放的 player,VoicePlayer.play 会先停旧 player 并
|
||||
// 通过 onFinish 反向调 gameui_stop_voice 让 H5 停旧动画,再起新播放。
|
||||
let canPlay = await VoiceCenter.shared.isEnabled
|
||||
log.debug("mediaTypeAudio user=\(user, privacy: .public) transcoded=\(transcoded, privacy: .public) canPlay=\(canPlay, privacy: .public)")
|
||||
if transcoded, canPlay {
|
||||
do {
|
||||
try VoicePlayer.shared.play(wavLocal)
|
||||
try VoicePlayer.shared.play(wavLocal, user: user)
|
||||
// gameui_play_voice 数据是 user 字符串(非 dict)
|
||||
bridge.call(
|
||||
"gameui_play_voice",
|
||||
@@ -337,7 +338,7 @@ public enum RemoteAudioHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 触发 mediaTypeAudio cb(对齐 daoqi NewRootVC.m:357,**总是**触发
|
||||
// 7. 触发 mediaTypeAudio cb(对齐 daoqi NewRootVC.m:357,**总是**触发
|
||||
// 即使转码失败 / canbofang=NO 都触发;仅下载失败的 return 路径不触发)
|
||||
callback?(.string("Response from mediaTypeAudio"))
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
// WeChatManager.swift
|
||||
// ylgamehall
|
||||
//
|
||||
// 微信 SDK 持久 delegate + authorize + share 异步包装。
|
||||
// 把 WXApiDelegate 的回调 → async/await,state UUID 配对多发起方。
|
||||
// 微信 SDK 持久 delegate + authorize 异步包装。
|
||||
// 把 WXApiDelegate 的 onResp 回调 → async/await,state UUID 配对多发起方。
|
||||
//
|
||||
// Design §8.5;msext WXApiManager.m 等价。
|
||||
// 注:分享路径已改走剪贴板 + 引导框(详见 WechatShare.swift),不再依赖
|
||||
// 微信 SDK 的 SendMessageToWXReq;本类只剩登录 OAuth 链路。
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@@ -23,8 +24,6 @@ public final class WeChatManager: NSObject {
|
||||
|
||||
/// 待 resolve 的 authorize continuation(state UUID 配对)
|
||||
private var authPending: [String: CheckedContinuation<WXAuthCodePayload, Error>] = [:]
|
||||
/// 待 resolve 的 share continuation(FIFO 串行 — 微信回包不带配对 ID)
|
||||
private var sharePending: [CheckedContinuation<Void, Error>] = []
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
@@ -84,125 +83,6 @@ public final class WeChatManager: NSObject {
|
||||
return top
|
||||
}
|
||||
|
||||
// MARK: - Share
|
||||
|
||||
public enum ShareScene {
|
||||
case session // 微信好友
|
||||
case timeline // 朋友圈
|
||||
}
|
||||
|
||||
/// 链接分享载荷。`mediaTagName` 对齐 msext WXApiRequestHandler `TagName:`
|
||||
/// 参数(写入 WXMediaMessage.mediaTagName,微信内部分类用)。
|
||||
/// 含 UIImage 故无法 Sendable —— 实际使用都在 MainActor 上发起,无跨 actor 风险。
|
||||
public struct ShareLink {
|
||||
public let url: String
|
||||
public let title: String
|
||||
public let desc: String
|
||||
public let thumb: UIImage?
|
||||
public let mediaTagName: String?
|
||||
|
||||
public init(
|
||||
url: String, title: String, desc: String,
|
||||
thumb: UIImage?, mediaTagName: String? = nil
|
||||
) {
|
||||
self.url = url; self.title = title; self.desc = desc
|
||||
self.thumb = thumb; self.mediaTagName = mediaTagName
|
||||
}
|
||||
}
|
||||
|
||||
/// 图片分享载荷(type=2 截图)。
|
||||
/// - `imageData`:大图原始字节(JPEG)
|
||||
/// - `thumb`:缩略图源 — SDK `setThumbImage:` 内部压到 32KB
|
||||
/// - `messageExt`:H5 的 description;写入 `WXMediaMessage.description`
|
||||
/// 与 `.messageExt`,对齐 msext `WXApiRequestHandler sendImageData:MessageExt:`
|
||||
/// - `mediaTagName`:对齐 msext `TagName:` 参数
|
||||
public struct ShareImage {
|
||||
public let imageData: Data
|
||||
public let thumb: UIImage
|
||||
public let messageExt: String
|
||||
public let mediaTagName: String?
|
||||
|
||||
public init(
|
||||
imageData: Data, thumb: UIImage,
|
||||
messageExt: String = "", mediaTagName: String? = nil
|
||||
) {
|
||||
self.imageData = imageData
|
||||
self.thumb = thumb
|
||||
self.messageExt = messageExt
|
||||
self.mediaTagName = mediaTagName
|
||||
}
|
||||
}
|
||||
|
||||
/// 链接分享(FIFO 串行)。微信回包不携带配对 ID,连续调时按入队顺序消费。
|
||||
public func shareLink(_ link: ShareLink, scene: ShareScene) async throws {
|
||||
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
|
||||
sharePending.append(cont)
|
||||
|
||||
let media = WXWebpageObject()
|
||||
media.webpageUrl = link.url
|
||||
|
||||
let message = WXMediaMessage()
|
||||
message.title = link.title
|
||||
message.description = link.desc
|
||||
message.mediaObject = media
|
||||
if let mediaTagName = link.mediaTagName {
|
||||
message.mediaTagName = mediaTagName
|
||||
}
|
||||
if let thumb = link.thumb {
|
||||
message.setThumbImage(thumb)
|
||||
}
|
||||
|
||||
let req = SendMessageToWXReq()
|
||||
req.bText = false
|
||||
req.message = message
|
||||
switch scene {
|
||||
case .session: req.scene = Int32(WXSceneSession.rawValue)
|
||||
case .timeline: req.scene = Int32(WXSceneTimeline.rawValue)
|
||||
}
|
||||
// 1.x: sendReq BOOL 返回;分享回包通过 SceneDelegate.openURLContexts →
|
||||
// WXApi.handleOpen(url:delegate:WeChatManager.shared) → onResp 路径到达。
|
||||
// ObjC selector +sendReq:,Swift 简化为 send(_:)(去 Req 后缀)。
|
||||
// 运行时仍调 +[WXApi sendReq:]
|
||||
WXApi.send(req)
|
||||
}
|
||||
}
|
||||
|
||||
/// 图片分享(FIFO 串行,复用 sharePending)。对齐 msext WechatShareManager
|
||||
/// `sendImageData:TagName:MessageExt:Action:ThumbImage:InScene:` 的字段映射:
|
||||
/// TagName → mediaTagName
|
||||
/// MessageExt → description(接收方预览可见)+ messageExt(SDK 内部回包)
|
||||
/// ThumbImage → setThumbImage
|
||||
public func shareImage(_ img: ShareImage, scene: ShareScene) async throws {
|
||||
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
|
||||
sharePending.append(cont)
|
||||
|
||||
let media = WXImageObject()
|
||||
media.imageData = img.imageData
|
||||
|
||||
let message = WXMediaMessage()
|
||||
message.description = img.messageExt
|
||||
message.mediaObject = media
|
||||
message.setThumbImage(img.thumb)
|
||||
if !img.messageExt.isEmpty {
|
||||
message.messageExt = img.messageExt
|
||||
}
|
||||
if let mediaTagName = img.mediaTagName {
|
||||
message.mediaTagName = mediaTagName
|
||||
}
|
||||
|
||||
let req = SendMessageToWXReq()
|
||||
req.bText = false
|
||||
req.message = message
|
||||
switch scene {
|
||||
case .session: req.scene = Int32(WXSceneSession.rawValue)
|
||||
case .timeline: req.scene = Int32(WXSceneTimeline.rawValue)
|
||||
}
|
||||
// ObjC selector +sendReq:,Swift 简化为 send(_:)(去 Req 后缀)。
|
||||
// 运行时仍调 +[WXApi sendReq:]
|
||||
WXApi.send(req)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Internal: resolve callbacks
|
||||
|
||||
fileprivate func resolveAuth(state: String, code: String?, errCode: Int) {
|
||||
@@ -213,16 +93,6 @@ public final class WeChatManager: NSObject {
|
||||
cont.resume(throwing: WeChatError.authFailed(errCode))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func resolveShare(errCode: Int) {
|
||||
guard !sharePending.isEmpty else { return }
|
||||
let cont = sharePending.removeFirst()
|
||||
if errCode == 0 {
|
||||
cont.resume(returning: ())
|
||||
} else {
|
||||
cont.resume(throwing: WeChatError.shareFailed(errCode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WXApiDelegate
|
||||
@@ -242,11 +112,8 @@ extension WeChatManager: WXApiDelegate {
|
||||
Task { @MainActor in
|
||||
Self.shared.resolveAuth(state: state, code: code, errCode: errCode)
|
||||
}
|
||||
} else if resp is SendMessageToWXResp {
|
||||
Task { @MainActor in
|
||||
Self.shared.resolveShare(errCode: errCode)
|
||||
}
|
||||
}
|
||||
// 分享走剪贴板路径(WechatShare),不再有 SendMessageToWXResp 回包
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,5 +127,4 @@ public struct WXAuthCodePayload: Sendable {
|
||||
public enum WeChatError: Error, Sendable {
|
||||
case sdkNotLinked
|
||||
case authFailed(Int)
|
||||
case shareFailed(Int)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public final class SharePanel: UIView {
|
||||
|
||||
private static let log = Logger(subsystem: "ylgamehall", category: "SharePanel")
|
||||
|
||||
private static let panelHeight: CGFloat = 200
|
||||
private static let panelHeight: CGFloat = 150
|
||||
private static let buttonSize: CGFloat = 60
|
||||
private static let animationDuration: TimeInterval = 0.25
|
||||
|
||||
|
||||
@@ -104,16 +104,20 @@ public final class WechatShare: SharePlatform {
|
||||
|
||||
// MARK: - 链接分享
|
||||
|
||||
/// title + desc + url 拼字符串 → UIPasteboard.string → 弹引导框。
|
||||
/// 三字段均空时用兜底文案"来自进贤聚友棋牌的精彩内容分享"(与 DouyinShare 同款策略)。
|
||||
/// title + "\n\n" + desc 拼字符串 → UIPasteboard.string → 弹引导框。
|
||||
/// 与 DouyinShare 文本分享 1:1 同款(不含 url,避免在微信对话框出现裸链接),
|
||||
/// 双侧都空时用兜底文案"来自进贤聚友棋牌的精彩内容分享"。
|
||||
private func shareLink(_ content: ShareContent) {
|
||||
var parts: [String] = []
|
||||
if !content.title.isEmpty { parts.append(content.title) }
|
||||
if !content.desc.isEmpty { parts.append(content.desc) }
|
||||
if !content.webpageUrl.isEmpty { parts.append(content.webpageUrl) }
|
||||
let shareText = parts.isEmpty
|
||||
? "来自进贤聚友棋牌的精彩内容分享"
|
||||
: parts.joined(separator: "\n")
|
||||
var shareText = ""
|
||||
if !content.title.isEmpty {
|
||||
shareText = content.title
|
||||
}
|
||||
if !content.desc.isEmpty {
|
||||
shareText = shareText.isEmpty ? content.desc : "\(shareText)\n\n\(content.desc)"
|
||||
}
|
||||
if shareText.isEmpty {
|
||||
shareText = "来自进贤聚友棋牌的精彩内容分享"
|
||||
}
|
||||
UIPasteboard.general.string = shareText
|
||||
Self.log.debug("WechatShare 链接已复制到剪贴板")
|
||||
|
||||
@@ -121,7 +125,7 @@ public final class WechatShare: SharePlatform {
|
||||
✅ 内容已复制到剪贴板
|
||||
|
||||
🎯 分享到好友、群聊或朋友圈步骤:
|
||||
1️⃣ 打开微信,选择好友、群聊或朋友圈
|
||||
1️⃣ 打开微信,选择好友、群聊或朋友圈
|
||||
2️⃣ 在输入框长按粘贴内容
|
||||
3️⃣ 点击发送即可分享
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user