@tagea/capacitor-matrix 1.2.0 → 1.3.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.
@@ -32,6 +32,8 @@ class MatrixSDKBridge {
32
32
  private var syncStateObserver: SyncStateObserverProxy?
33
33
  private let subscriptionLock = NSLock()
34
34
  private var receiptSyncTask: Task<Void, Never>?
35
+ private var roomCreatedAtCache: [String: UInt64] = [:]
36
+ private let createdAtLock = NSLock()
35
37
  // Rooms currently being paginated by getRoomMessages — live listener suppresses events for these
36
38
  private var paginatingRooms = Set<String>()
37
39
  private let paginatingLock = NSLock()
@@ -603,11 +605,51 @@ class MatrixSDKBridge {
603
605
  }
604
606
  var result: [[String: Any]] = []
605
607
  for room in c.rooms() {
606
- result.append(try await Self.serializeRoom(room))
608
+ var dict = try await Self.serializeRoom(room)
609
+ if dict["lastEventTs"] as? UInt64 == nil {
610
+ if let ts = await fetchRoomCreatedAt(roomId: room.id()) {
611
+ dict["createdAt"] = ts
612
+ }
613
+ }
614
+ result.append(dict)
607
615
  }
608
616
  return result
609
617
  }
610
618
 
619
+ private func fetchRoomCreatedAt(roomId: String) async -> UInt64? {
620
+ createdAtLock.lock()
621
+ let cached = roomCreatedAtCache[roomId]
622
+ createdAtLock.unlock()
623
+ if let cached { return cached }
624
+ guard let session = sessionStore.load() else { return nil }
625
+ let baseUrl = session.homeserverUrl.hasSuffix("/")
626
+ ? String(session.homeserverUrl.dropLast())
627
+ : session.homeserverUrl
628
+ guard let encodedRoomId = roomId.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed),
629
+ let url = URL(string: "\(baseUrl)/_matrix/client/v3/rooms/\(encodedRoomId)/state") else {
630
+ return nil
631
+ }
632
+ var request = URLRequest(url: url)
633
+ request.httpMethod = "GET"
634
+ request.setValue("Bearer \(session.accessToken)", forHTTPHeaderField: "Authorization")
635
+ guard let (data, response) = try? await URLSession.shared.data(for: request),
636
+ let statusCode = (response as? HTTPURLResponse)?.statusCode,
637
+ statusCode >= 200, statusCode < 300,
638
+ let events = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]] else {
639
+ return nil
640
+ }
641
+ for event in events {
642
+ if event["type"] as? String == "m.room.create",
643
+ let ts = event["origin_server_ts"] as? UInt64 {
644
+ createdAtLock.lock()
645
+ roomCreatedAtCache[roomId] = ts
646
+ createdAtLock.unlock()
647
+ return ts
648
+ }
649
+ }
650
+ return nil
651
+ }
652
+
611
653
  func getRoomMembers(roomId: String) async throws -> [[String: Any]] {
612
654
  let room = try requireRoom(roomId: roomId)
613
655
  let iterator = try await room.members()
@@ -728,7 +770,9 @@ class MatrixSDKBridge {
728
770
 
729
771
  // Reset cursor on initial load
730
772
  if !isPagination {
773
+ paginatingLock.lock()
731
774
  oldestReturnedEventId.removeValue(forKey: roomId)
775
+ paginatingLock.unlock()
732
776
  }
733
777
 
734
778
  // Paginate when: first load with too few items, OR explicit pagination request
@@ -763,7 +807,11 @@ class MatrixSDKBridge {
763
807
  let allEvents = collector.events
764
808
  var events: [[String: Any]]
765
809
 
766
- if let cursorId = oldestReturnedEventId[roomId], from != nil {
810
+ paginatingLock.lock()
811
+ let cursorId = oldestReturnedEventId[roomId]
812
+ paginatingLock.unlock()
813
+
814
+ if let cursorId = cursorId, from != nil {
767
815
  if let cursorIdx = allEvents.firstIndex(where: { ($0["eventId"] as? String) == cursorId }) {
768
816
  let available = Array(allEvents.prefix(cursorIdx))
769
817
  events = Array(available.suffix(limit))
@@ -776,7 +824,9 @@ class MatrixSDKBridge {
776
824
  }
777
825
 
778
826
  if let oldest = events.first, let eid = oldest["eventId"] as? String {
827
+ paginatingLock.lock()
779
828
  oldestReturnedEventId[roomId] = eid
829
+ paginatingLock.unlock()
780
830
  }
781
831
 
782
832
  // Apply receipt watermark
@@ -1475,7 +1525,7 @@ class MatrixSDKBridge {
1475
1525
  return dir.path
1476
1526
  }
1477
1527
 
1478
- private static func serializeRoom(_ room: Room) async throws -> [String: Any] {
1528
+ static func serializeRoom(_ room: Room) async throws -> [String: Any] {
1479
1529
  let info = try await room.roomInfo()
1480
1530
  let encrypted = await room.isEncrypted()
1481
1531
  let membership: String = {
@@ -1651,6 +1701,7 @@ private func serializeEventTimelineItem(_ eventItem: EventTimelineItem, roomId:
1651
1701
 
1652
1702
  var contentDict: [String: Any] = [:]
1653
1703
  var eventType = "m.room.message"
1704
+ var stateKey: String? = nil
1654
1705
 
1655
1706
  let content = eventItem.content
1656
1707
  switch content {
@@ -1707,6 +1758,7 @@ private func serializeEventTimelineItem(_ eventItem: EventTimelineItem, roomId:
1707
1758
  }
1708
1759
  case .roomMembership(let userId, let userDisplayName, let change, _):
1709
1760
  eventType = "m.room.member"
1761
+ stateKey = userId
1710
1762
  let membership: String
1711
1763
  switch change {
1712
1764
  case .joined, .invitationAccepted:
@@ -1726,8 +1778,8 @@ private func serializeEventTimelineItem(_ eventItem: EventTimelineItem, roomId:
1726
1778
  }
1727
1779
  contentDict["membership"] = membership
1728
1780
  contentDict["displayname"] = userDisplayName ?? userId
1729
- contentDict["stateKey"] = userId
1730
- case .state(_, let stateContent):
1781
+ case .state(let sk, let stateContent):
1782
+ stateKey = sk
1731
1783
  switch stateContent {
1732
1784
  case .roomCreate:
1733
1785
  eventType = "m.room.create"
@@ -1767,7 +1819,13 @@ private func serializeEventTimelineItem(_ eventItem: EventTimelineItem, roomId:
1767
1819
  }
1768
1820
  }
1769
1821
 
1770
- return [
1822
+ // Build unsigned dict — include transaction_id when available
1823
+ var unsignedDict: [String: Any]? = nil
1824
+ if case .transactionId(let txnId) = eventItem.eventOrTransactionId {
1825
+ unsignedDict = ["transaction_id": txnId]
1826
+ }
1827
+
1828
+ var result: [String: Any] = [
1771
1829
  "eventId": eventId,
1772
1830
  "roomId": roomId,
1773
1831
  "senderId": eventItem.sender,
@@ -1777,6 +1835,13 @@ private func serializeEventTimelineItem(_ eventItem: EventTimelineItem, roomId:
1777
1835
  "status": status,
1778
1836
  "readBy": readBy as Any,
1779
1837
  ]
1838
+ if let sk = stateKey {
1839
+ result["stateKey"] = sk
1840
+ }
1841
+ if let ud = unsignedDict {
1842
+ result["unsigned"] = ud
1843
+ }
1844
+ return result
1780
1845
  }
1781
1846
 
1782
1847
  // MARK: - Live Timeline Listener (for sync subscriptions)
@@ -1796,19 +1861,14 @@ class LiveTimelineListener: TimelineListener {
1796
1861
  self.isPaginating = isPaginating
1797
1862
  }
1798
1863
 
1799
- /// Emit a room update with unread count and latest event preview.
1864
+ /// Emit a room update with full room summary.
1800
1865
  private func emitRoomUpdate() {
1801
1866
  Task {
1802
- let unreadCount: Int
1803
- if let info = try? await room.roomInfo() {
1804
- unreadCount = Int(info.numUnreadMessages ?? 0)
1867
+ let summary: [String: Any]
1868
+ if let s = try? await MatrixSDKBridge.serializeRoom(room) {
1869
+ summary = s
1805
1870
  } else {
1806
- unreadCount = 0
1807
- }
1808
- var summary: [String: Any] = ["roomId": roomId, "unreadCount": unreadCount]
1809
- let latestEvent = await room.latestEvent()
1810
- if let le = serializeLatestEventValue(latestEvent, roomId: roomId) {
1811
- summary["latestEvent"] = le
1871
+ summary = ["roomId": roomId]
1812
1872
  }
1813
1873
  onRoomUpdate(roomId, summary)
1814
1874
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tagea/capacitor-matrix",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "A capacitor plugin wrapping native matrix SDKs",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",