Files
xuetang/TUICallKit-Vue3/native-adapter/tang-detective/utils/tangPage.js
T
2026-09-09 14:47:29 +08:00

146 lines
5.2 KiB
JavaScript

// Keep the native Page lifecycle, but hydrate the correct account before any
// story page reads/writes storage. This also covers cold starts from shares.
const bridge = require('./platformBridge')
const LIFECYCLE_METHODS = new Set(['onLoad', 'onShow', 'onReady', 'onHide', 'onUnload'])
const CLEANUP_METHOD = /^(?:destroy|clear|unbind|pause|invalidate|beginPauseLock)/
module.exports = function registerTangPage(options) {
function isCurrent(page) {
return !page.__tangDead && !page.__tangOriginalUnloaded
&& page.__tangVisible && page.__tangScope === bridge.getScope()
}
function returnHome(page) {
if (page.__tangRedirected || page.__tangDead) return
page.__tangRedirected = true
wx.reLaunch({ url: '/tang-detective/pages/home/home' })
}
function cleanUp(page, name) {
if (!page.__tangLoaded || typeof options[name] !== 'function') return
if (name !== 'onUnload' && page.__tangOriginalUnloaded) return
if (name === 'onUnload') {
if (page.__tangOriginalUnloaded) return
page.__tangOriginalUnloaded = true
}
page.__tangCleaning = true
try {
options[name].call(page)
} finally {
// This exception is synchronous only; pending timers and audio callbacks
// regain the normal visible/account/dead guards as soon as cleanup ends.
page.__tangCleaning = false
}
}
function deliverReady(page) {
if (!isCurrent(page) || !page.__tangLoaded || !page.__tangHasShown
|| !page.__tangReadyRequested || page.__tangReadyDelivered) return
page.__tangReadyDelivered = true
if (typeof options.onReady === 'function') options.onReady.call(page)
}
const guarded = { ...options }
Object.keys(options).forEach(key => {
// Event handlers such as onAudioTimeUpdate are ordinary guarded methods,
// even though their names begin with "on".
if (typeof options[key] !== 'function' || LIFECYCLE_METHODS.has(key)) return
guarded[key] = function (...args) {
if (this.__tangCleaning) {
// Release resources across an account change, but do not run helpers
// such as saveCurrentAudioTime against the newly selected account.
if (this.__tangScope === bridge.getScope() || CLEANUP_METHOD.test(key)) {
return options[key].apply(this, args)
}
return
}
if (this.__tangDead || this.__tangOriginalUnloaded || !this.__tangLoaded || !this.__tangVisible) return
if (this.__tangScope && this.__tangScope !== bridge.getScope()) {
returnHome(this)
return
}
return options[key].apply(this, args)
}
})
Page({
...guarded,
data: { ...(options.data || {}), tangBootPending: true },
tangIgnoreBootTap() {},
onLoad(query) {
this.__tangQuery = query
this.__tangScope = bridge.getScope()
this.__tangDead = false
this.__tangVisible = false
this.__tangLoaded = false
this.__tangCleaning = false
this.__tangOriginalUnloaded = false
this.__tangRedirected = false
this.__tangShowGeneration = 0
this.__tangReadyRequested = false
this.__tangReadyDelivered = false
this.__tangHasShown = false
const nativeSetData = this.setData
this.setData = function (...args) {
if (this.__tangScope !== bridge.getScope()) return
if (!this.__tangCleaning && !isCurrent(this)) return
return nativeSetData.apply(this, args)
}
this.__tangBoot = bridge.open()
},
onShow() {
if (this.__tangDead) return
this.__tangVisible = true
const generation = ++this.__tangShowGeneration
const scope = bridge.getScope()
if (this.__tangOriginalUnloaded || scope !== this.__tangScope) {
returnHome(this)
return
}
Promise.resolve(this.__tangBoot).then(() => {
if (this.__tangDead || !this.__tangVisible || generation !== this.__tangShowGeneration) return
if (bridge.getScope() !== scope) {
returnHome(this)
return
}
if (!this.__tangLoaded) {
this.__tangLoaded = true
if (options.onLoad) options.onLoad.call(this, this.__tangQuery)
if (!isCurrent(this)) return
this.setData({ tangBootPending: false })
}
if (options.onShow) options.onShow.call(this)
this.__tangHasShown = true
deliverReady(this)
}).catch(() => {
if (isCurrent(this) && generation === this.__tangShowGeneration) {
wx.showToast({ title: '故事暂时未能打开,请返回重试', icon: 'none' })
}
})
},
onReady() {
this.__tangReadyRequested = true
deliverReady(this)
},
onHide() {
this.__tangVisible = false
++this.__tangShowGeneration
try {
cleanUp(this, 'onHide')
} finally {
// A hidden page from an obsolete account must not retain contexts with
// callbacks that directly reference storage instead of guarded methods.
try {
if (this.__tangScope !== bridge.getScope()) cleanUp(this, 'onUnload')
} finally {
bridge.flush()
}
}
},
onUnload() {
this.__tangDead = true
this.__tangVisible = false
++this.__tangShowGeneration
try {
cleanUp(this, 'onUnload')
} finally {
bridge.flush()
}
},
})
}