2026-07-23 22:58:08 +05:30
|
|
|
import Foundation
|
|
|
|
|
import Capacitor
|
|
|
|
|
|
|
|
|
|
// Reads the files the Share Extension staged into the App Group container, so the web app can pick a
|
|
|
|
|
// conversation and send them. The extension and the app are separate processes; the App Group's shared
|
|
|
|
|
// container is the only place both can read/write, and it is NOT one of @capacitor/filesystem's known
|
|
|
|
|
// directories — hence this small bridge.
|
|
|
|
|
//
|
|
|
|
|
// getPending() → { items: [ {kind, name, path, uri, mime, size} | {kind:"text", text} ] }
|
|
|
|
|
// `uri` is a file:// URL the web layer turns into a fetchable source with Capacitor.convertFileSrc,
|
|
|
|
|
// so the existing upload path can read the bytes without base64 marshalling.
|
|
|
|
|
// clear() removes the manifest and every staged file, once the app has taken them.
|
|
|
|
|
@objc(ShareInboxPlugin)
|
|
|
|
|
public class ShareInboxPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
|
|
public let identifier = "ShareInboxPlugin"
|
|
|
|
|
public let jsName = "ShareInbox"
|
|
|
|
|
public let pluginMethods: [CAPPluginMethod] = [
|
|
|
|
|
CAPPluginMethod(name: "getPending", returnType: CAPPluginReturnPromise),
|
2026-07-24 16:33:23 +05:30
|
|
|
CAPPluginMethod(name: "clear", returnType: CAPPluginReturnPromise),
|
|
|
|
|
CAPPluginMethod(name: "setAuth", returnType: CAPPluginReturnPromise)
|
2026-07-23 22:58:08 +05:30
|
|
|
]
|
|
|
|
|
|
|
|
|
|
private let appGroup = "group.com.bizgaze.connect"
|
|
|
|
|
|
2026-07-24 16:33:23 +05:30
|
|
|
// The web app hands the extension a bearer token + API base (via /api/share/token) so the extension can
|
|
|
|
|
// list chats, upload and send on its own — no app-open needed. Stored in the App Group's shared
|
|
|
|
|
// UserDefaults, which the extension reads directly. Passing an empty token clears it (e.g. on logout).
|
|
|
|
|
@objc func setAuth(_ call: CAPPluginCall) {
|
|
|
|
|
let token = call.getString("token") ?? ""
|
|
|
|
|
let base = call.getString("base") ?? ""
|
|
|
|
|
if let d = UserDefaults(suiteName: appGroup) {
|
|
|
|
|
if token.isEmpty { d.removeObject(forKey: "bzc_token"); d.removeObject(forKey: "bzc_base") }
|
|
|
|
|
else { d.set(token, forKey: "bzc_token"); d.set(base, forKey: "bzc_base") }
|
|
|
|
|
}
|
|
|
|
|
call.resolve(["ok": true])
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-23 22:58:08 +05:30
|
|
|
@objc func getPending(_ call: CAPPluginCall) {
|
|
|
|
|
guard let dir = sharedDir() else { return call.resolve(["items": []]) }
|
|
|
|
|
let manifest = dir.appendingPathComponent("manifest.json")
|
|
|
|
|
guard let data = try? Data(contentsOf: manifest),
|
|
|
|
|
let records = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]] else {
|
|
|
|
|
return call.resolve(["items": []])
|
|
|
|
|
}
|
|
|
|
|
var items: [[String: Any]] = []
|
|
|
|
|
for r in records {
|
|
|
|
|
let kind = r["kind"] as? String ?? "file"
|
|
|
|
|
if kind == "text" {
|
|
|
|
|
if let text = r["text"] as? String { items.append(["kind": "text", "text": text]) }
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// A file record is only usable if its staged copy is still on disk.
|
|
|
|
|
guard let path = r["path"] as? String,
|
|
|
|
|
FileManager.default.fileExists(atPath: path) else { continue }
|
|
|
|
|
let url = URL(fileURLWithPath: path)
|
|
|
|
|
items.append([
|
|
|
|
|
"kind": "file",
|
|
|
|
|
"name": r["name"] as? String ?? url.lastPathComponent,
|
|
|
|
|
"path": path,
|
|
|
|
|
"uri": url.absoluteString,
|
|
|
|
|
"mime": r["mime"] as? String ?? "application/octet-stream",
|
|
|
|
|
"size": r["size"] as? Int64 ?? (r["size"] as? Int ?? 0)
|
|
|
|
|
])
|
|
|
|
|
}
|
|
|
|
|
call.resolve(["items": items])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func clear(_ call: CAPPluginCall) {
|
|
|
|
|
if let dir = sharedDir() {
|
|
|
|
|
let fm = FileManager.default
|
|
|
|
|
if let entries = try? fm.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil) {
|
|
|
|
|
for e in entries { try? fm.removeItem(at: e) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
call.resolve()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func sharedDir() -> URL? {
|
|
|
|
|
guard let base = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else { return nil }
|
|
|
|
|
return base.appendingPathComponent("Shared", isDirectory: true)
|
|
|
|
|
}
|
|
|
|
|
}
|