@unisim/sdk 0.123.4 → 0.124.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +103 -6
  2. package/android/build.gradle +59 -0
  3. package/android/src/main/AndroidManifest.xml +88 -0
  4. package/android/src/main/java/uk/co/unisim/sdk/SuiteAuthProvider.java +107 -0
  5. package/android/src/main/java/uk/co/unisim/sdk/SuiteAuthStore.java +179 -0
  6. package/android/src/main/java/uk/co/unisim/sdk/UnisimSuiteAuthPlugin.java +214 -0
  7. package/dist/ChangelogMenu.js +7 -0
  8. package/dist/ChangelogMenu.js.map +1 -1
  9. package/dist/SuiteSwitcher.d.ts.map +1 -1
  10. package/dist/SuiteSwitcher.js +70 -4
  11. package/dist/SuiteSwitcher.js.map +1 -1
  12. package/dist/UniversalAppsNavBar.d.ts.map +1 -1
  13. package/dist/UniversalAppsNavBar.js +92 -6
  14. package/dist/UniversalAppsNavBar.js.map +1 -1
  15. package/dist/UniversalNavBar.d.ts +25 -3
  16. package/dist/UniversalNavBar.d.ts.map +1 -1
  17. package/dist/UniversalNavBar.js +144 -61
  18. package/dist/UniversalNavBar.js.map +1 -1
  19. package/dist/UserProfile.d.ts +108 -1
  20. package/dist/UserProfile.d.ts.map +1 -1
  21. package/dist/UserProfile.js +147 -78
  22. package/dist/UserProfile.js.map +1 -1
  23. package/dist/i18n.d.ts.map +1 -1
  24. package/dist/i18n.js +7 -0
  25. package/dist/i18n.js.map +1 -1
  26. package/dist/index.d.ts +5 -3
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +6 -2
  29. package/dist/index.js.map +1 -1
  30. package/dist/provider.d.ts.map +1 -1
  31. package/dist/provider.js +94 -1
  32. package/dist/provider.js.map +1 -1
  33. package/dist/sessionStorage.d.ts +25 -7
  34. package/dist/sessionStorage.d.ts.map +1 -1
  35. package/dist/sessionStorage.js +49 -15
  36. package/dist/sessionStorage.js.map +1 -1
  37. package/dist/suiteApps.d.ts +66 -0
  38. package/dist/suiteApps.d.ts.map +1 -0
  39. package/dist/suiteApps.js +168 -0
  40. package/dist/suiteApps.js.map +1 -0
  41. package/dist/types.d.ts +1 -1
  42. package/dist/types.d.ts.map +1 -1
  43. package/dist/unisimMark.d.ts +1 -1
  44. package/dist/unisimMark.d.ts.map +1 -1
  45. package/dist/unisimMark.js +8 -5
  46. package/dist/unisimMark.js.map +1 -1
  47. package/ios/Sources/UnisimSuiteAuthPlugin/UnisimSuiteAuthPlugin.swift +63 -1
  48. package/package.json +8 -2
@@ -1,6 +1,7 @@
1
1
  import Foundation
2
2
  import Capacitor
3
3
  import Security
4
+ import UIKit
4
5
 
5
6
  /// Stores the Universal Suite session in the iOS Keychain, in a group shared by
6
7
  /// every suite app signed by the same team — so signing in on Universal Images
@@ -37,7 +38,9 @@ public class UnisimSuiteAuthPlugin: CAPPlugin, CAPBridgedPlugin {
37
38
  CAPPluginMethod(name: "get", returnType: CAPPluginReturnPromise),
38
39
  CAPPluginMethod(name: "set", returnType: CAPPluginReturnPromise),
39
40
  CAPPluginMethod(name: "remove", returnType: CAPPluginReturnPromise),
40
- CAPPluginMethod(name: "status", returnType: CAPPluginReturnPromise)
41
+ CAPPluginMethod(name: "status", returnType: CAPPluginReturnPromise),
42
+ CAPPluginMethod(name: "installedApps", returnType: CAPPluginReturnPromise),
43
+ CAPPluginMethod(name: "openApp", returnType: CAPPluginReturnPromise)
41
44
  ]
42
45
 
43
46
  /// One service for the whole suite. The `key` the JS side passes (Supabase's
@@ -238,4 +241,63 @@ public class UnisimSuiteAuthPlugin: CAPPlugin, CAPBridgedPlugin {
238
241
  call.reject(Self.describe("delete", status), String(status))
239
242
  }
240
243
  }
244
+
245
+ // ──────────────────────────────────────────────────────────────────────
246
+ // Opening a sibling suite app
247
+ //
248
+ // Not auth, and it knows it. These live on the auth plugin because
249
+ // Capacitor resolves plugins through a `capacitor.plugins.json` that is
250
+ // generated by `npx cap sync` and committed in each app: a NEW plugin class
251
+ // would be missing from every app until each one re-synced, and missing
252
+ // presents as nothing at all — the switcher would just go on opening the
253
+ // website in the apps that had not caught up. This class is already in all
254
+ // thirteen files. See `src/suiteApps.ts`, which holds the app registry.
255
+ // ──────────────────────────────────────────────────────────────────────
256
+
257
+ /// The suite apps installed alongside this one, by the JS side's product id.
258
+ ///
259
+ /// ⚠️ **`canOpenURL` answers `false` for any scheme this app has not
260
+ /// declared in `LSApplicationQueriesSchemes`**, and says so only in a
261
+ /// console line nobody reads on a device. An app missing an entry does not
262
+ /// look broken — it looks like the sibling is not installed, which is a
263
+ /// perfectly ordinary thing to be. So the plists are GENERATED from the
264
+ /// registry by `scripts/sync-native-app-links.mjs`; do not hand-edit them.
265
+ @objc func installedApps(_ call: CAPPluginCall) {
266
+ let apps = call.getArray("apps", JSObject.self) ?? []
267
+
268
+ // canOpenURL is a UIApplication call and must be made on the main
269
+ // thread; the plugin call arrives on a background queue.
270
+ DispatchQueue.main.async {
271
+ var installed: [String] = []
272
+ for app in apps {
273
+ guard let id = app["id"] as? String,
274
+ let scheme = app["scheme"] as? String,
275
+ let url = URL(string: "\(scheme)://") else { continue }
276
+ if UIApplication.shared.canOpenURL(url) {
277
+ installed.append(id)
278
+ }
279
+ }
280
+ call.resolve(["installed": installed])
281
+ }
282
+ }
283
+
284
+ /// Bring one suite app to the front.
285
+ ///
286
+ /// Resolves `{ opened: false }` rather than rejecting when iOS declines —
287
+ /// the app was removed since the probe, or its scheme is undeclared here.
288
+ /// The JS side falls back to the https link on false, so a refusal costs
289
+ /// the user a web page rather than a dead tap, and an error would have to
290
+ /// be unpicked at the call site to reach the same place.
291
+ @objc func openApp(_ call: CAPPluginCall) {
292
+ guard let scheme = call.getString("scheme"), let url = URL(string: "\(scheme)://") else {
293
+ call.reject("scheme is required")
294
+ return
295
+ }
296
+
297
+ DispatchQueue.main.async {
298
+ UIApplication.shared.open(url, options: [:]) { opened in
299
+ call.resolve(["opened": opened])
300
+ }
301
+ }
302
+ }
241
303
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unisim/sdk",
3
- "version": "0.123.4",
4
- "description": "Shared React SDK for the Universal Suite \u2014 auth, entitlements, usage telemetry, changelog, org admin.",
3
+ "version": "0.124.0",
4
+ "description": "Shared React SDK for the Universal Suite — auth, entitlements, usage telemetry, changelog, org admin.",
5
5
  "license": "MIT",
6
6
  "author": "Universal Simulation Ltd",
7
7
  "keywords": [
@@ -31,6 +31,9 @@
31
31
  "capacitor": {
32
32
  "ios": {
33
33
  "src": "ios"
34
+ },
35
+ "android": {
36
+ "src": "android"
34
37
  }
35
38
  },
36
39
  "exports": {
@@ -45,6 +48,7 @@
45
48
  "files": [
46
49
  "dist",
47
50
  "electron",
51
+ "android",
48
52
  "ios/Sources",
49
53
  "Package.swift",
50
54
  "UnisimSdk.podspec",
@@ -55,6 +59,8 @@
55
59
  "dev": "tsc -p tsconfig.build.json --watch",
56
60
  "typecheck": "tsc --noEmit",
57
61
  "test:session-storage": "npm run build && node tests/session-storage.browser.mjs",
62
+ "test:shared-signout": "node tests/shared-signout.mjs",
63
+ "test:suite-apps": "npm run build && node tests/suite-apps.mjs",
58
64
  "prepublishOnly": "npm run typecheck && npm run build"
59
65
  },
60
66
  "dependencies": {