@tagea/capacitor-matrix 0.0.2

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.
@@ -0,0 +1,775 @@
1
+ package de.tremaze.capacitor.matrix
2
+
3
+ import com.getcapacitor.JSArray
4
+ import com.getcapacitor.JSObject
5
+ import com.getcapacitor.Plugin
6
+ import com.getcapacitor.PluginCall
7
+ import com.getcapacitor.PluginMethod
8
+ import com.getcapacitor.annotation.CapacitorPlugin
9
+ import kotlinx.coroutines.CoroutineScope
10
+ import kotlinx.coroutines.Dispatchers
11
+ import kotlinx.coroutines.SupervisorJob
12
+ import kotlinx.coroutines.launch
13
+
14
+ @CapacitorPlugin(name = "Matrix")
15
+ class MatrixPlugin : Plugin() {
16
+
17
+ private lateinit var bridge: MatrixSDKBridge
18
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
19
+
20
+ override fun load() {
21
+ bridge = MatrixSDKBridge(context)
22
+ }
23
+
24
+ @PluginMethod
25
+ fun login(call: PluginCall) {
26
+ val homeserverUrl = call.getString("homeserverUrl") ?: return call.reject("Missing homeserverUrl")
27
+ val userId = call.getString("userId") ?: return call.reject("Missing userId")
28
+ val password = call.getString("password") ?: return call.reject("Missing password")
29
+
30
+ scope.launch {
31
+ try {
32
+ val session = bridge.login(homeserverUrl, userId, password)
33
+ call.resolve(session.toJSObject())
34
+ } catch (e: Exception) {
35
+ call.reject(e.message ?: "Login failed", e)
36
+ }
37
+ }
38
+ }
39
+
40
+ @PluginMethod
41
+ fun loginWithToken(call: PluginCall) {
42
+ val homeserverUrl = call.getString("homeserverUrl") ?: return call.reject("Missing homeserverUrl")
43
+ val accessToken = call.getString("accessToken") ?: return call.reject("Missing accessToken")
44
+ val userId = call.getString("userId") ?: return call.reject("Missing userId")
45
+ val deviceId = call.getString("deviceId") ?: return call.reject("Missing deviceId")
46
+
47
+ scope.launch {
48
+ try {
49
+ val session = bridge.loginWithToken(homeserverUrl, accessToken, userId, deviceId)
50
+ call.resolve(session.toJSObject())
51
+ } catch (e: Exception) {
52
+ call.reject(e.message ?: "Login with token failed", e)
53
+ }
54
+ }
55
+ }
56
+
57
+ @PluginMethod
58
+ fun logout(call: PluginCall) {
59
+ scope.launch {
60
+ try {
61
+ bridge.logout()
62
+ call.resolve()
63
+ } catch (e: Exception) {
64
+ call.reject(e.message ?: "Logout failed", e)
65
+ }
66
+ }
67
+ }
68
+
69
+ @PluginMethod
70
+ fun clearAllData(call: PluginCall) {
71
+ try {
72
+ bridge.clearAllData()
73
+ call.resolve()
74
+ } catch (e: Exception) {
75
+ call.reject(e.message ?: "clearAllData failed", e)
76
+ }
77
+ }
78
+
79
+ @PluginMethod
80
+ fun getSession(call: PluginCall) {
81
+ try {
82
+ val session = bridge.getSession()
83
+ if (session != null) {
84
+ call.resolve(session.toJSObject())
85
+ } else {
86
+ call.resolve(JSObject())
87
+ }
88
+ } catch (e: Exception) {
89
+ call.reject(e.message ?: "getSession failed", e)
90
+ }
91
+ }
92
+
93
+ @PluginMethod
94
+ fun startSync(call: PluginCall) {
95
+ scope.launch {
96
+ try {
97
+ bridge.startSync(
98
+ onSyncState = { state ->
99
+ notifyListeners("syncStateChange", JSObject().put("state", state))
100
+ },
101
+ onMessage = { event ->
102
+ try {
103
+ val jsEvent = mapToJSObject(event)
104
+ notifyListeners("messageReceived", JSObject().put("event", jsEvent))
105
+ } catch (_: Exception) {
106
+ // ignore serialization errors
107
+ }
108
+ },
109
+ onRoomUpdate = { roomId, summary ->
110
+ notifyListeners(
111
+ "roomUpdated",
112
+ JSObject().put("roomId", roomId).put("summary", mapToJSObject(summary)),
113
+ )
114
+ },
115
+ onReceipt = { roomId ->
116
+ notifyListeners(
117
+ "receiptReceived",
118
+ JSObject().put("roomId", roomId),
119
+ )
120
+ },
121
+ )
122
+ call.resolve()
123
+ } catch (e: Exception) {
124
+ call.reject(e.message ?: "startSync failed", e)
125
+ }
126
+ }
127
+ }
128
+
129
+ @PluginMethod
130
+ fun stopSync(call: PluginCall) {
131
+ scope.launch {
132
+ try {
133
+ bridge.stopSync()
134
+ call.resolve()
135
+ } catch (e: Exception) {
136
+ call.reject(e.message ?: "stopSync failed", e)
137
+ }
138
+ }
139
+ }
140
+
141
+ @PluginMethod
142
+ fun getSyncState(call: PluginCall) {
143
+ try {
144
+ val state = bridge.getSyncState()
145
+ call.resolve(JSObject().put("state", state))
146
+ } catch (e: Exception) {
147
+ call.reject(e.message ?: "getSyncState failed", e)
148
+ }
149
+ }
150
+
151
+ @PluginMethod
152
+ fun getRooms(call: PluginCall) {
153
+ scope.launch {
154
+ try {
155
+ val rooms = bridge.getRooms()
156
+ val jsRooms = JSArray()
157
+ rooms.forEach { room -> jsRooms.put(mapToJSObject(room)) }
158
+ call.resolve(JSObject().put("rooms", jsRooms))
159
+ } catch (e: Exception) {
160
+ call.reject(e.message ?: "getRooms failed", e)
161
+ }
162
+ }
163
+ }
164
+
165
+ @PluginMethod
166
+ fun getRoomMembers(call: PluginCall) {
167
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
168
+
169
+ scope.launch {
170
+ try {
171
+ val members = bridge.getRoomMembers(roomId)
172
+ val jsMembers = JSArray()
173
+ members.forEach { member -> jsMembers.put(mapToJSObject(member)) }
174
+ call.resolve(JSObject().put("members", jsMembers))
175
+ } catch (e: Exception) {
176
+ call.reject(e.message ?: "getRoomMembers failed", e)
177
+ }
178
+ }
179
+ }
180
+
181
+ @PluginMethod
182
+ fun joinRoom(call: PluginCall) {
183
+ val roomIdOrAlias = call.getString("roomIdOrAlias") ?: return call.reject("Missing roomIdOrAlias")
184
+
185
+ scope.launch {
186
+ try {
187
+ val roomId = bridge.joinRoom(roomIdOrAlias)
188
+ call.resolve(JSObject().put("roomId", roomId))
189
+ } catch (e: Exception) {
190
+ call.reject(e.message ?: "joinRoom failed", e)
191
+ }
192
+ }
193
+ }
194
+
195
+ @PluginMethod
196
+ fun leaveRoom(call: PluginCall) {
197
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
198
+
199
+ scope.launch {
200
+ try {
201
+ bridge.leaveRoom(roomId)
202
+ call.resolve()
203
+ } catch (e: Exception) {
204
+ call.reject(e.message ?: "leaveRoom failed", e)
205
+ }
206
+ }
207
+ }
208
+
209
+ @PluginMethod
210
+ fun forgetRoom(call: PluginCall) {
211
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
212
+
213
+ scope.launch {
214
+ try {
215
+ bridge.forgetRoom(roomId)
216
+ call.resolve()
217
+ } catch (e: Exception) {
218
+ call.reject(e.message ?: "forgetRoom failed", e)
219
+ }
220
+ }
221
+ }
222
+
223
+ @PluginMethod
224
+ fun sendMessage(call: PluginCall) {
225
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
226
+ val body = call.getString("body") ?: return call.reject("Missing body")
227
+ val msgtype = call.getString("msgtype") ?: "m.text"
228
+
229
+ scope.launch {
230
+ try {
231
+ val eventId = bridge.sendMessage(roomId, body, msgtype)
232
+ call.resolve(JSObject().put("eventId", eventId))
233
+ } catch (e: Exception) {
234
+ call.reject(e.message ?: "sendMessage failed", e)
235
+ }
236
+ }
237
+ }
238
+
239
+ @PluginMethod
240
+ fun editMessage(call: PluginCall) {
241
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
242
+ val eventId = call.getString("eventId") ?: return call.reject("Missing eventId")
243
+ val newBody = call.getString("newBody") ?: return call.reject("Missing newBody")
244
+
245
+ scope.launch {
246
+ try {
247
+ val resultEventId = bridge.editMessage(roomId, eventId, newBody)
248
+ call.resolve(JSObject().put("eventId", resultEventId))
249
+ } catch (e: Exception) {
250
+ call.reject(e.message ?: "editMessage failed", e)
251
+ }
252
+ }
253
+ }
254
+
255
+ @PluginMethod
256
+ fun sendReply(call: PluginCall) {
257
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
258
+ val body = call.getString("body") ?: return call.reject("Missing body")
259
+ val replyToEventId = call.getString("replyToEventId") ?: return call.reject("Missing replyToEventId")
260
+ val msgtype = call.getString("msgtype") ?: "m.text"
261
+
262
+ scope.launch {
263
+ try {
264
+ val resultEventId = bridge.sendReply(roomId, body, replyToEventId, msgtype)
265
+ call.resolve(JSObject().put("eventId", resultEventId))
266
+ } catch (e: Exception) {
267
+ call.reject(e.message ?: "sendReply failed", e)
268
+ }
269
+ }
270
+ }
271
+
272
+ @PluginMethod
273
+ fun getRoomMessages(call: PluginCall) {
274
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
275
+ val limit = call.getInt("limit")
276
+ val from = call.getString("from")
277
+
278
+ scope.launch {
279
+ try {
280
+ val result = bridge.getRoomMessages(roomId, limit, from)
281
+ val jsResult = JSObject()
282
+ val jsEvents = JSArray()
283
+ @Suppress("UNCHECKED_CAST")
284
+ (result["events"] as? List<Map<String, Any?>>)?.forEach { event ->
285
+ jsEvents.put(mapToJSObject(event))
286
+ }
287
+ jsResult.put("events", jsEvents)
288
+ jsResult.put("nextBatch", result["nextBatch"])
289
+ call.resolve(jsResult)
290
+ } catch (e: Exception) {
291
+ call.reject(e.message ?: "getRoomMessages failed", e)
292
+ }
293
+ }
294
+ }
295
+
296
+ @PluginMethod
297
+ fun markRoomAsRead(call: PluginCall) {
298
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
299
+ val eventId = call.getString("eventId") ?: return call.reject("Missing eventId")
300
+
301
+ scope.launch {
302
+ try {
303
+ bridge.markRoomAsRead(roomId, eventId)
304
+ call.resolve()
305
+ } catch (e: Exception) {
306
+ call.reject(e.message ?: "markRoomAsRead failed", e)
307
+ }
308
+ }
309
+ }
310
+
311
+ @PluginMethod
312
+ fun refreshEventStatuses(call: PluginCall) {
313
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
314
+ val eventIdsArray = call.getArray("eventIds") ?: return call.reject("Missing eventIds")
315
+ val eventIds = (0 until eventIdsArray.length()).map { eventIdsArray.getString(it) }
316
+
317
+ scope.launch {
318
+ try {
319
+ val events = bridge.refreshEventStatuses(roomId, eventIds)
320
+ val jsResult = JSObject()
321
+ val jsEvents = JSArray()
322
+ events.forEach { event -> jsEvents.put(mapToJSObject(event)) }
323
+ jsResult.put("events", jsEvents)
324
+ call.resolve(jsResult)
325
+ } catch (e: Exception) {
326
+ call.reject(e.message ?: "refreshEventStatuses failed", e)
327
+ }
328
+ }
329
+ }
330
+
331
+ @PluginMethod
332
+ fun createRoom(call: PluginCall) {
333
+ val name = call.getString("name")
334
+ val topic = call.getString("topic")
335
+ val isEncrypted = call.getBoolean("isEncrypted") ?: false
336
+ val isDirect = call.getBoolean("isDirect") ?: false
337
+ val preset = call.getString("preset")
338
+ val inviteArray = call.getArray("invite")
339
+ val invite = inviteArray?.let { arr ->
340
+ (0 until arr.length()).map { arr.getString(it) }
341
+ }
342
+
343
+ scope.launch {
344
+ try {
345
+ val roomId = bridge.createRoom(name, topic, isEncrypted, isDirect, invite, preset)
346
+ call.resolve(JSObject().put("roomId", roomId))
347
+ } catch (e: Exception) {
348
+ call.reject(e.message ?: "createRoom failed", e)
349
+ }
350
+ }
351
+ }
352
+
353
+ @PluginMethod
354
+ fun redactEvent(call: PluginCall) {
355
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
356
+ val eventId = call.getString("eventId") ?: return call.reject("Missing eventId")
357
+ val reason = call.getString("reason")
358
+
359
+ scope.launch {
360
+ try {
361
+ bridge.redactEvent(roomId, eventId, reason)
362
+ call.resolve()
363
+ } catch (e: Exception) {
364
+ call.reject(e.message ?: "redactEvent failed", e)
365
+ }
366
+ }
367
+ }
368
+
369
+ @PluginMethod
370
+ fun sendReaction(call: PluginCall) {
371
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
372
+ val eventId = call.getString("eventId") ?: return call.reject("Missing eventId")
373
+ val key = call.getString("key") ?: return call.reject("Missing key")
374
+
375
+ scope.launch {
376
+ try {
377
+ bridge.sendReaction(roomId, eventId, key)
378
+ call.resolve(JSObject().put("eventId", ""))
379
+ } catch (e: Exception) {
380
+ call.reject(e.message ?: "sendReaction failed", e)
381
+ }
382
+ }
383
+ }
384
+
385
+ @PluginMethod
386
+ fun searchUsers(call: PluginCall) {
387
+ val searchTerm = call.getString("searchTerm") ?: return call.reject("Missing searchTerm")
388
+ val limit = call.getInt("limit", 10)!!.toLong()
389
+
390
+ scope.launch {
391
+ try {
392
+ val result = bridge.searchUsers(searchTerm, limit)
393
+ call.resolve(mapToJSObject(result))
394
+ } catch (e: Exception) {
395
+ call.reject(e.message ?: "searchUsers failed", e)
396
+ }
397
+ }
398
+ }
399
+
400
+ @PluginMethod
401
+ fun setRoomName(call: PluginCall) {
402
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
403
+ val name = call.getString("name") ?: return call.reject("Missing name")
404
+
405
+ scope.launch {
406
+ try {
407
+ bridge.setRoomName(roomId, name)
408
+ call.resolve()
409
+ } catch (e: Exception) {
410
+ call.reject(e.message ?: "setRoomName failed", e)
411
+ }
412
+ }
413
+ }
414
+
415
+ @PluginMethod
416
+ fun setRoomTopic(call: PluginCall) {
417
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
418
+ val topic = call.getString("topic") ?: return call.reject("Missing topic")
419
+
420
+ scope.launch {
421
+ try {
422
+ bridge.setRoomTopic(roomId, topic)
423
+ call.resolve()
424
+ } catch (e: Exception) {
425
+ call.reject(e.message ?: "setRoomTopic failed", e)
426
+ }
427
+ }
428
+ }
429
+
430
+ @PluginMethod
431
+ fun setRoomAvatar(call: PluginCall) {
432
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
433
+ val mxcUrl = call.getString("mxcUrl") ?: return call.reject("Missing mxcUrl")
434
+
435
+ scope.launch {
436
+ try {
437
+ bridge.setRoomAvatar(roomId, mxcUrl)
438
+ call.resolve()
439
+ } catch (e: Exception) {
440
+ call.reject(e.message ?: "setRoomAvatar failed", e)
441
+ }
442
+ }
443
+ }
444
+
445
+ @PluginMethod
446
+ fun uploadContent(call: PluginCall) {
447
+ val fileUri = call.getString("fileUri") ?: return call.reject("Missing fileUri")
448
+ val fileName = call.getString("fileName") ?: return call.reject("Missing fileName")
449
+ val mimeType = call.getString("mimeType") ?: return call.reject("Missing mimeType")
450
+
451
+ scope.launch {
452
+ try {
453
+ val contentUri = bridge.uploadContent(fileUri, fileName, mimeType)
454
+ call.resolve(JSObject().put("contentUri", contentUri))
455
+ } catch (e: Exception) {
456
+ call.reject(e.message ?: "uploadContent failed", e)
457
+ }
458
+ }
459
+ }
460
+
461
+ @PluginMethod
462
+ fun getThumbnailUrl(call: PluginCall) {
463
+ val mxcUrl = call.getString("mxcUrl") ?: return call.reject("Missing mxcUrl")
464
+ val width = call.getInt("width") ?: return call.reject("Missing width")
465
+ val height = call.getInt("height") ?: return call.reject("Missing height")
466
+ val method = call.getString("method") ?: "scale"
467
+
468
+ try {
469
+ val httpUrl = bridge.getThumbnailUrl(mxcUrl, width, height, method)
470
+ call.resolve(JSObject().put("httpUrl", httpUrl))
471
+ } catch (e: Exception) {
472
+ call.reject(e.message ?: "getThumbnailUrl failed", e)
473
+ }
474
+ }
475
+
476
+ @PluginMethod
477
+ fun getDevices(call: PluginCall) {
478
+ scope.launch {
479
+ try {
480
+ val devices = bridge.getDevices()
481
+ val jsDevices = JSArray()
482
+ devices.forEach { device -> jsDevices.put(mapToJSObject(device)) }
483
+ call.resolve(JSObject().put("devices", jsDevices))
484
+ } catch (e: Exception) {
485
+ call.reject(e.message ?: "getDevices failed", e)
486
+ }
487
+ }
488
+ }
489
+
490
+ @PluginMethod
491
+ fun deleteDevice(call: PluginCall) {
492
+ val deviceId = call.getString("deviceId") ?: return call.reject("Missing deviceId")
493
+
494
+ scope.launch {
495
+ try {
496
+ bridge.deleteDevice(deviceId)
497
+ call.resolve()
498
+ } catch (e: Exception) {
499
+ call.reject(e.message ?: "deleteDevice failed", e)
500
+ }
501
+ }
502
+ }
503
+
504
+ @PluginMethod
505
+ fun setPusher(call: PluginCall) {
506
+ call.reject("setPusher is not yet supported on this platform")
507
+ }
508
+
509
+ @PluginMethod
510
+ fun inviteUser(call: PluginCall) {
511
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
512
+ val userId = call.getString("userId") ?: return call.reject("Missing userId")
513
+
514
+ scope.launch {
515
+ try {
516
+ bridge.inviteUser(roomId, userId)
517
+ call.resolve()
518
+ } catch (e: Exception) {
519
+ call.reject(e.message ?: "inviteUser failed", e)
520
+ }
521
+ }
522
+ }
523
+
524
+ @PluginMethod
525
+ fun kickUser(call: PluginCall) {
526
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
527
+ val userId = call.getString("userId") ?: return call.reject("Missing userId")
528
+ val reason = call.getString("reason")
529
+
530
+ scope.launch {
531
+ try {
532
+ bridge.kickUser(roomId, userId, reason)
533
+ call.resolve()
534
+ } catch (e: Exception) {
535
+ call.reject(e.message ?: "kickUser failed", e)
536
+ }
537
+ }
538
+ }
539
+
540
+ @PluginMethod
541
+ fun banUser(call: PluginCall) {
542
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
543
+ val userId = call.getString("userId") ?: return call.reject("Missing userId")
544
+ val reason = call.getString("reason")
545
+
546
+ scope.launch {
547
+ try {
548
+ bridge.banUser(roomId, userId, reason)
549
+ call.resolve()
550
+ } catch (e: Exception) {
551
+ call.reject(e.message ?: "banUser failed", e)
552
+ }
553
+ }
554
+ }
555
+
556
+ @PluginMethod
557
+ fun unbanUser(call: PluginCall) {
558
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
559
+ val userId = call.getString("userId") ?: return call.reject("Missing userId")
560
+
561
+ scope.launch {
562
+ try {
563
+ bridge.unbanUser(roomId, userId)
564
+ call.resolve()
565
+ } catch (e: Exception) {
566
+ call.reject(e.message ?: "unbanUser failed", e)
567
+ }
568
+ }
569
+ }
570
+
571
+ @PluginMethod
572
+ fun sendTyping(call: PluginCall) {
573
+ val roomId = call.getString("roomId") ?: return call.reject("Missing roomId")
574
+ val isTyping = call.getBoolean("isTyping") ?: return call.reject("Missing isTyping")
575
+
576
+ scope.launch {
577
+ try {
578
+ bridge.sendTyping(roomId, isTyping)
579
+ call.resolve()
580
+ } catch (e: Exception) {
581
+ call.reject(e.message ?: "sendTyping failed", e)
582
+ }
583
+ }
584
+ }
585
+
586
+ @PluginMethod
587
+ fun getMediaUrl(call: PluginCall) {
588
+ val mxcUrl = call.getString("mxcUrl") ?: return call.reject("mxcUrl required")
589
+ try {
590
+ val httpUrl = bridge.getMediaUrl(mxcUrl)
591
+ val ret = JSObject()
592
+ ret.put("httpUrl", httpUrl)
593
+ call.resolve(ret)
594
+ } catch (e: Exception) {
595
+ call.reject(e.message ?: "Failed to resolve media URL")
596
+ }
597
+ }
598
+
599
+ @PluginMethod
600
+ fun setPresence(call: PluginCall) {
601
+ call.reject("setPresence is not supported on this platform")
602
+ }
603
+
604
+ @PluginMethod
605
+ fun getPresence(call: PluginCall) {
606
+ call.reject("getPresence is not supported on this platform")
607
+ }
608
+
609
+ @PluginMethod
610
+ fun initializeCrypto(call: PluginCall) {
611
+ scope.launch {
612
+ try {
613
+ bridge.initializeCrypto()
614
+ call.resolve()
615
+ } catch (e: Exception) {
616
+ call.reject(e.message ?: "initializeCrypto failed", e)
617
+ }
618
+ }
619
+ }
620
+
621
+ @PluginMethod
622
+ fun bootstrapCrossSigning(call: PluginCall) {
623
+ scope.launch {
624
+ try {
625
+ bridge.bootstrapCrossSigning()
626
+ call.resolve()
627
+ } catch (e: Exception) {
628
+ call.reject(e.message ?: "bootstrapCrossSigning failed", e)
629
+ }
630
+ }
631
+ }
632
+
633
+ @PluginMethod
634
+ fun exportRoomKeys(call: PluginCall) {
635
+ call.reject("exportRoomKeys is not supported on native — use recovery key instead")
636
+ }
637
+
638
+ @PluginMethod
639
+ fun importRoomKeys(call: PluginCall) {
640
+ call.reject("importRoomKeys is not supported on native — use recovery key instead")
641
+ }
642
+
643
+ @PluginMethod
644
+ fun getEncryptionStatus(call: PluginCall) {
645
+ scope.launch {
646
+ try {
647
+ call.resolve(mapToJSObject(bridge.getEncryptionStatus()))
648
+ } catch (e: Exception) {
649
+ call.reject(e.message ?: "getEncryptionStatus failed", e)
650
+ }
651
+ }
652
+ }
653
+
654
+ @PluginMethod
655
+ fun setupKeyBackup(call: PluginCall) {
656
+ scope.launch {
657
+ try {
658
+ val result = bridge.setupKeyBackup()
659
+ call.resolve(mapToJSObject(result))
660
+ } catch (e: Exception) {
661
+ call.reject(e.message ?: "setupKeyBackup failed", e)
662
+ }
663
+ }
664
+ }
665
+
666
+ @PluginMethod
667
+ fun getKeyBackupStatus(call: PluginCall) {
668
+ scope.launch {
669
+ try {
670
+ val result = bridge.getKeyBackupStatus()
671
+ call.resolve(mapToJSObject(result))
672
+ } catch (e: Exception) {
673
+ call.reject(e.message ?: "getKeyBackupStatus failed", e)
674
+ }
675
+ }
676
+ }
677
+
678
+ @PluginMethod
679
+ fun restoreKeyBackup(call: PluginCall) {
680
+ val recoveryKey = call.getString("recoveryKey")
681
+
682
+ scope.launch {
683
+ try {
684
+ val result = bridge.restoreKeyBackup(recoveryKey)
685
+ call.resolve(mapToJSObject(result))
686
+ } catch (e: Exception) {
687
+ call.reject(e.message ?: "restoreKeyBackup failed", e)
688
+ }
689
+ }
690
+ }
691
+
692
+ @PluginMethod
693
+ fun setupRecovery(call: PluginCall) {
694
+ val passphrase = call.getString("passphrase")
695
+
696
+ scope.launch {
697
+ try {
698
+ val result = bridge.setupRecovery(passphrase)
699
+ call.resolve(mapToJSObject(result))
700
+ } catch (e: Exception) {
701
+ call.reject(e.message ?: "setupRecovery failed", e)
702
+ }
703
+ }
704
+ }
705
+
706
+ @PluginMethod
707
+ fun isRecoveryEnabled(call: PluginCall) {
708
+ scope.launch {
709
+ try {
710
+ val enabled = bridge.isRecoveryEnabled()
711
+ call.resolve(JSObject().put("enabled", enabled))
712
+ } catch (e: Exception) {
713
+ call.reject(e.message ?: "isRecoveryEnabled failed", e)
714
+ }
715
+ }
716
+ }
717
+
718
+ @PluginMethod
719
+ fun recoverAndSetup(call: PluginCall) {
720
+ val recoveryKey = call.getString("recoveryKey") ?: call.getString("passphrase")
721
+ ?: return call.reject("Missing recoveryKey or passphrase")
722
+
723
+ scope.launch {
724
+ try {
725
+ bridge.recoverAndSetup(recoveryKey)
726
+ call.resolve()
727
+ } catch (e: Exception) {
728
+ call.reject(e.message ?: "recoverAndSetup failed", e)
729
+ }
730
+ }
731
+ }
732
+
733
+ @PluginMethod
734
+ fun resetRecoveryKey(call: PluginCall) {
735
+ scope.launch {
736
+ try {
737
+ val result = bridge.resetRecoveryKey()
738
+ call.resolve(mapToJSObject(result))
739
+ } catch (e: Exception) {
740
+ call.reject(e.message ?: "resetRecoveryKey failed", e)
741
+ }
742
+ }
743
+ }
744
+
745
+ private fun SessionInfo.toJSObject(): JSObject {
746
+ return JSObject().apply {
747
+ put("accessToken", accessToken)
748
+ put("userId", userId)
749
+ put("deviceId", deviceId)
750
+ put("homeserverUrl", homeserverUrl)
751
+ }
752
+ }
753
+
754
+ @Suppress("UNCHECKED_CAST")
755
+ private fun mapToJSObject(map: Map<String, Any?>): JSObject {
756
+ val obj = JSObject()
757
+ map.forEach { (key, value) ->
758
+ when (value) {
759
+ is Map<*, *> -> obj.put(key, mapToJSObject(value as Map<String, Any?>))
760
+ is List<*> -> {
761
+ val arr = JSArray()
762
+ value.forEach { item ->
763
+ when (item) {
764
+ is Map<*, *> -> arr.put(mapToJSObject(item as Map<String, Any?>))
765
+ else -> arr.put(item)
766
+ }
767
+ }
768
+ obj.put(key, arr)
769
+ }
770
+ else -> obj.put(key, value)
771
+ }
772
+ }
773
+ return obj
774
+ }
775
+ }