Files
ForumMobile/native-sidebar/ios/Plugin/NativeSidebarPlugin.swift
2026-06-22 04:35:01 -05:00

89 lines
2.8 KiB
Swift

import Foundation
import Capacitor
/// Hosts a native left-edge drawer (the "sidebar") that floats above the
/// Capacitor web view. The drawer's *content* is a second, transparent
/// WKWebView that renders the existing `Sidebar.js` from data pushed in via
/// `setData`. Taps inside that content web view are forwarded back to the main
/// app as an `action` event, mirroring how the NativeInput plugin works.
@objc(NativeSidebarPlugin)
public class NativeSidebarPlugin: CAPPlugin {
private var drawer: SidebarDrawer?
// MARK: Helpers
private func hostView() -> UIView? {
// Pin the drawer as a sibling of the web view inside the same parent so
// it can float above the web content (same pattern as NativeInput).
return webView?.superview ?? webView
}
private func ensureDrawer() -> SidebarDrawer? {
if let drawer = drawer { return drawer }
guard let host = hostView() else { return nil }
let drawer = SidebarDrawer(hostView: host, mainWebView: webView, parentVC: bridge?.viewController)
drawer.onAction = { [weak self] data in
self?.notifyListeners("action", data: data)
}
drawer.onStateChange = { [weak self] open in
self?.notifyListeners("stateChange", data: ["open": open])
}
drawer.attach()
self.drawer = drawer
return drawer
}
// MARK: Plugin methods
@objc func setData(_ call: CAPPluginCall) {
// Forward the whole options object (minus Capacitor's callbackId) to the
// content web view as the sidebar payload.
var payload = (call.options as? [String: Any]) ?? [:]
payload.removeValue(forKey: "callbackId")
DispatchQueue.main.async {
guard let drawer = self.ensureDrawer() else { call.reject("No host view"); return }
drawer.setData(payload)
call.resolve()
}
}
@objc func setTheme(_ call: CAPPluginCall) {
let theme = call.getString("theme") ?? ""
DispatchQueue.main.async {
self.drawer?.setTheme(theme)
call.resolve()
}
}
@objc func open(_ call: CAPPluginCall) {
DispatchQueue.main.async {
self.ensureDrawer()?.open()
call.resolve()
}
}
@objc func close(_ call: CAPPluginCall) {
DispatchQueue.main.async {
self.drawer?.close()
call.resolve()
}
}
@objc func setEnabled(_ call: CAPPluginCall) {
let enabled = call.getBool("enabled") ?? true
DispatchQueue.main.async {
self.drawer?.setEnabled(enabled)
call.resolve()
}
}
@objc func dismiss(_ call: CAPPluginCall) {
DispatchQueue.main.async {
self.drawer?.removeFromHost()
self.drawer = nil
call.resolve()
}
}
}