mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
* perf(mobile): answer a page's ready that arrived before its bridge host A page's ready can reach the shell before the screen has built its bridge host (the host waits on the client and the host profile read). It used to be dropped, so init waited for the page's next retry, up to 2 s later. Keep the latest ready per session and answer it once the host exists. * perf(mobile): keep one prewarmed page document while Home is shown While native Home is on screen, boot one page document for the most recently connected host with a cached build, parked invisibly in the window. Opening a page from that build adopts it instead of building a WebView, which removes the WebView start and script parse from the tap. The spare is dropped when Home is left without a claim (2 s grace), on background, memory pressure, its own failure and before the cache deletes its files. It is never kept beside a shown page, and only phones with more than 3 GiB of RAM (4 GB phones) get one. A parked page is told to stop asking for init; the adopting view answers the ready it sent. Builds on Jinwoo's spare pool and replay work in #32203 and #32204. Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com> * fix(mobile): unregister the shell's memory callbacks from the context that registered them --------- Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com>
111 lines
3.6 KiB
Swift
111 lines
3.6 KiB
Swift
import ExpoModulesCore
|
|
import QuickLook
|
|
import UIKit
|
|
|
|
public class OrcaMobileWebShellModule: Module {
|
|
private var pdfPreview: MobileWebShellPdfPreview?
|
|
private var memoryWarningObserver: NSObjectProtocol?
|
|
|
|
public func definition() -> ModuleDefinition {
|
|
Name("OrcaMobileWebShell")
|
|
|
|
// A spare is the first thing to give back: nothing is shown by it.
|
|
OnCreate {
|
|
memoryWarningObserver = NotificationCenter.default.addObserver(
|
|
forName: UIApplication.didReceiveMemoryWarningNotification,
|
|
object: nil,
|
|
queue: .main
|
|
) { _ in
|
|
MobileWebShellSparePool.shared.discardAll()
|
|
}
|
|
}
|
|
|
|
OnDestroy {
|
|
if let memoryWarningObserver {
|
|
NotificationCenter.default.removeObserver(memoryWarningObserver)
|
|
}
|
|
DispatchQueue.main.async {
|
|
MobileWebShellSparePool.shared.discardAll()
|
|
}
|
|
}
|
|
|
|
// Home boots the next one after its next foreground idle.
|
|
OnAppEntersBackground {
|
|
DispatchQueue.main.async {
|
|
MobileWebShellSparePool.shared.discardAll()
|
|
}
|
|
}
|
|
|
|
AsyncFunction("prepareSpareDocument") { (directory: String, sessionId: String) -> Bool in
|
|
MobileWebShellSparePool.shared.prepare(directory: directory, sessionId: sessionId)
|
|
}.runOnQueue(.main)
|
|
|
|
AsyncFunction("claimSpareDocument") { (directory: String) -> String? in
|
|
MobileWebShellSparePool.shared.claim(directory: directory)
|
|
}.runOnQueue(.main)
|
|
|
|
AsyncFunction("discardSpareDocument") {
|
|
MobileWebShellSparePool.shared.discardAll()
|
|
}.runOnQueue(.main)
|
|
|
|
// Home calls this once it has a paired host, so a user with nothing to open pays nothing.
|
|
Function("warmUp") {
|
|
// A second's delay keeps the warm-up off the launch's first frames.
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
MobileWebShellWarmUp.start()
|
|
}
|
|
}
|
|
|
|
AsyncFunction("presentPdf") { (url: URL) in
|
|
guard let presenter = self.appContext?.utilities?.currentViewController() else {
|
|
throw PdfPresenterUnavailableException()
|
|
}
|
|
let preview = MobileWebShellPdfPreview(url: url)
|
|
let controller = QLPreviewController()
|
|
controller.dataSource = preview
|
|
self.pdfPreview = preview
|
|
presenter.present(controller, animated: true)
|
|
}.runOnQueue(.main)
|
|
|
|
AsyncFunction("shareImage") { (url: URL, _: String) in
|
|
guard let presenter = self.appContext?.utilities?.currentViewController() else {
|
|
throw ImagePresenterUnavailableException()
|
|
}
|
|
presentImageShareSheet(url: url, from: presenter)
|
|
}.runOnQueue(.main)
|
|
|
|
AsyncFunction("saveImageToPhotos") { (url: URL, _: String, promise: Promise) in
|
|
saveImageToPhotoLibrary(url: url, promise: promise)
|
|
}
|
|
|
|
View(OrcaMobileWebShellView.self) {
|
|
Events("onLoadState", "onBridgeMessage", "onExternalNavigation", "onBackEdgeSwipe")
|
|
|
|
Prop("generationDirectory") { (view: OrcaMobileWebShellView, value: String) in
|
|
view.setGenerationDirectory(value)
|
|
}
|
|
|
|
Prop("sessionId") { (view: OrcaMobileWebShellView, value: String) in
|
|
view.setSessionId(value)
|
|
}
|
|
|
|
Prop("bridgeEnabled") { (view: OrcaMobileWebShellView, value: Bool) in
|
|
view.setBridgeEnabled(value)
|
|
}
|
|
|
|
Prop("backEdgeSwipe") { (view: OrcaMobileWebShellView, value: Bool) in
|
|
view.setBackEdgeSwipe(value)
|
|
}
|
|
|
|
AsyncFunction("postBridgeMessage") {
|
|
(view: OrcaMobileWebShellView, json: String, promise: Promise) in
|
|
try view.postBridgeMessage(json, promise: promise)
|
|
}
|
|
|
|
OnViewDidUpdateProps { (view: OrcaMobileWebShellView) in
|
|
view.propsDidUpdate()
|
|
}
|
|
}
|
|
}
|
|
}
|