feat(share): send from inside the share sheet — the Teams-style in-extension picker
Replaces the wrong approach (stage + try to bounce into the app, which iOS blocks) with the one Teams/WhatsApp actually use: the picker and the send happen INSIDE the share extension, so there's no app-open at all. Tap Share → Biz Connect → pick a chat → it uploads and sends, right there in the sheet. How the extension can send without the app: it's a separate process that can't see the web app's HttpOnly cookie, so: - server: GET /api/share/token mints a bearer token for the logged-in user. - web: on every launch the app fetches that token and hands it to the extension via the App Group (ShareInbox.setAuth writes token+base to the shared UserDefaults). - extension: reads the token and calls the SAME API the native client uses — GET /api/messages/conversations to list chats, POST /api/messages/upload for each file, POST /api/messages to send. Native UITableView picker with search. Robustness: it still stages the files + writes a manifest first, so if there's no token yet (user never signed in) or the send fails, the file isn't lost — the app collects it on next open, exactly as before. On success the manifest is cleared so the app doesn't re-offer it. Server + web are live now; the token endpoint is harmless until a build ships the extension. NEEDS A NEW iOS BUILD for the picker itself. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -16,11 +16,25 @@ public class ShareInboxPlugin: CAPPlugin, CAPBridgedPlugin {
|
||||
public let jsName = "ShareInbox"
|
||||
public let pluginMethods: [CAPPluginMethod] = [
|
||||
CAPPluginMethod(name: "getPending", returnType: CAPPluginReturnPromise),
|
||||
CAPPluginMethod(name: "clear", returnType: CAPPluginReturnPromise)
|
||||
CAPPluginMethod(name: "clear", returnType: CAPPluginReturnPromise),
|
||||
CAPPluginMethod(name: "setAuth", returnType: CAPPluginReturnPromise)
|
||||
]
|
||||
|
||||
private let appGroup = "group.com.bizgaze.connect"
|
||||
|
||||
// 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])
|
||||
}
|
||||
|
||||
@objc func getPending(_ call: CAPPluginCall) {
|
||||
guard let dir = sharedDir() else { return call.resolve(["items": []]) }
|
||||
let manifest = dir.appendingPathComponent("manifest.json")
|
||||
|
||||
Reference in New Issue
Block a user