@tagea/capacitor-matrix 0.3.2 → 1.0.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.
- package/Package.swift +1 -1
- package/android/build.gradle +1 -1
- package/ios/Sources/CapMatrixPlugin/CapMatrix.swift +136 -132
- package/package.json +1 -1
package/Package.swift
CHANGED
|
@@ -11,7 +11,7 @@ let package = Package(
|
|
|
11
11
|
],
|
|
12
12
|
dependencies: [
|
|
13
13
|
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
|
|
14
|
-
.package(url: "https://github.com/matrix-org/matrix-rust-components-swift.git", exact: "
|
|
14
|
+
.package(url: "https://github.com/matrix-org/matrix-rust-components-swift.git", exact: "26.01.04")
|
|
15
15
|
],
|
|
16
16
|
targets: [
|
|
17
17
|
.target(
|
package/android/build.gradle
CHANGED
|
@@ -62,7 +62,7 @@ dependencies {
|
|
|
62
62
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
63
63
|
implementation "androidx.core:core-ktx:$androidxCoreKTXVersion"
|
|
64
64
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
|
|
65
|
-
implementation "org.matrix.rustcomponents:sdk-android:26.03.
|
|
65
|
+
implementation "org.matrix.rustcomponents:sdk-android:26.03.23"
|
|
66
66
|
implementation "androidx.security:security-crypto:1.1.0-alpha06"
|
|
67
67
|
testImplementation "junit:junit:$junitVersion"
|
|
68
68
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
@@ -127,7 +127,7 @@ class MatrixSDKBridge {
|
|
|
127
127
|
func logout() async throws {
|
|
128
128
|
receiptSyncTask?.cancel()
|
|
129
129
|
receiptSyncTask = nil
|
|
130
|
-
|
|
130
|
+
await syncService?.stop()
|
|
131
131
|
syncService = nil
|
|
132
132
|
syncStateHandle = nil
|
|
133
133
|
timelineListenerHandles.removeAll()
|
|
@@ -172,11 +172,14 @@ class MatrixSDKBridge {
|
|
|
172
172
|
|
|
173
173
|
// Enable Rust SDK tracing to diagnose sync errors
|
|
174
174
|
let tracingConfig = TracingConfiguration(
|
|
175
|
-
|
|
175
|
+
logLevel: .warn,
|
|
176
|
+
traceLogPacks: [],
|
|
177
|
+
extraTargets: ["matrix_sdk", "matrix_sdk_ui"],
|
|
176
178
|
writeToStdoutOrSystem: true,
|
|
177
|
-
writeToFiles: nil
|
|
179
|
+
writeToFiles: nil,
|
|
180
|
+
sentryDsn: nil
|
|
178
181
|
)
|
|
179
|
-
|
|
182
|
+
try? initPlatform(config: tracingConfig, useLightweightTokioRuntime: false)
|
|
180
183
|
|
|
181
184
|
print("[CapMatrix] startSync: building sync service...")
|
|
182
185
|
let service = try await c.syncService().finish()
|
|
@@ -456,7 +459,7 @@ class MatrixSDKBridge {
|
|
|
456
459
|
subscriptionLock.lock()
|
|
457
460
|
timelineListenerHandles.append(handle)
|
|
458
461
|
subscriptionLock.unlock()
|
|
459
|
-
print("[CapMatrix] room \(roomId): listener added
|
|
462
|
+
print("[CapMatrix] room \(roomId): listener added")
|
|
460
463
|
} catch {
|
|
461
464
|
print("[CapMatrix] room \(roomId): FAILED: \(error)")
|
|
462
465
|
}
|
|
@@ -464,7 +467,7 @@ class MatrixSDKBridge {
|
|
|
464
467
|
}
|
|
465
468
|
|
|
466
469
|
func stopSync() async throws {
|
|
467
|
-
|
|
470
|
+
await syncService?.stop()
|
|
468
471
|
syncStateHandle = nil
|
|
469
472
|
subscribedRoomIds.removeAll()
|
|
470
473
|
timelineListenerHandles.removeAll()
|
|
@@ -585,7 +588,9 @@ class MatrixSDKBridge {
|
|
|
585
588
|
func editMessage(roomId: String, eventId: String, newBody: String) async throws -> String {
|
|
586
589
|
let room = try requireRoom(roomId: roomId)
|
|
587
590
|
let content = messageEventContentFromMarkdown(md: newBody)
|
|
588
|
-
|
|
591
|
+
let editContent = EditedContent.roomMessage(content: content)
|
|
592
|
+
let timeline = try await getOrCreateTimeline(room: room)
|
|
593
|
+
try await timeline.edit(eventOrTransactionId: .eventId(eventId: eventId), newContent: editContent)
|
|
589
594
|
return ""
|
|
590
595
|
}
|
|
591
596
|
|
|
@@ -730,24 +735,14 @@ class MatrixSDKBridge {
|
|
|
730
735
|
|
|
731
736
|
func redactEvent(roomId: String, eventId: String, reason: String?) async throws {
|
|
732
737
|
let room = try requireRoom(roomId: roomId)
|
|
733
|
-
try await room
|
|
738
|
+
let timeline = try await getOrCreateTimeline(room: room)
|
|
739
|
+
try await timeline.redactEvent(eventOrTransactionId: .eventId(eventId: eventId), reason: reason)
|
|
734
740
|
}
|
|
735
741
|
|
|
736
742
|
func sendReaction(roomId: String, eventId: String, key: String) async throws {
|
|
737
743
|
let room = try requireRoom(roomId: roomId)
|
|
738
744
|
let timeline = try await getOrCreateTimeline(room: room)
|
|
739
|
-
|
|
740
|
-
// toggleReaction needs the timeline item's uniqueId, not the eventId
|
|
741
|
-
// addListener immediately fires a Reset diff with current items
|
|
742
|
-
let collector = TimelineItemCollector(roomId: roomId)
|
|
743
|
-
let handle = await timeline.addListener(listener: collector)
|
|
744
|
-
await collector.waitForUpdate()
|
|
745
|
-
handle.cancel()
|
|
746
|
-
|
|
747
|
-
guard let uniqueId = collector.uniqueIdForEvent(eventId) else {
|
|
748
|
-
throw MatrixBridgeError.notSupported("Could not find timeline item for event \(eventId)")
|
|
749
|
-
}
|
|
750
|
-
try await timeline.toggleReaction(uniqueId: uniqueId, key: key)
|
|
745
|
+
_ = try await timeline.toggleReaction(itemId: .eventId(eventId: eventId), key: key)
|
|
751
746
|
}
|
|
752
747
|
|
|
753
748
|
// MARK: - User Discovery
|
|
@@ -1002,6 +997,7 @@ class MatrixSDKBridge {
|
|
|
1002
997
|
let listener = NoopEnableRecoveryProgressListener()
|
|
1003
998
|
let key = try await c.encryption().enableRecovery(
|
|
1004
999
|
waitForBackupsToUpload: false,
|
|
1000
|
+
passphrase: passphrase,
|
|
1005
1001
|
progressListener: listener
|
|
1006
1002
|
)
|
|
1007
1003
|
return ["recoveryKey": key]
|
|
@@ -1054,7 +1050,7 @@ class MatrixSDKBridge {
|
|
|
1054
1050
|
|
|
1055
1051
|
private static func serializeRoom(_ room: Room) async throws -> [String: Any] {
|
|
1056
1052
|
let info = try await room.roomInfo()
|
|
1057
|
-
let encrypted =
|
|
1053
|
+
let encrypted = await room.isEncrypted()
|
|
1058
1054
|
let membership: String = {
|
|
1059
1055
|
switch room.membership() {
|
|
1060
1056
|
case .joined: return "join"
|
|
@@ -1098,6 +1094,18 @@ class MatrixSDKBridge {
|
|
|
1098
1094
|
|
|
1099
1095
|
// MARK: - Timeline Serialization Helpers
|
|
1100
1096
|
|
|
1097
|
+
/// Extract the event ID string from an EventOrTransactionId enum.
|
|
1098
|
+
private func extractEventId(_ eventOrTxnId: EventOrTransactionId) -> String? {
|
|
1099
|
+
switch eventOrTxnId {
|
|
1100
|
+
case .eventId(let eventId):
|
|
1101
|
+
return eventId
|
|
1102
|
+
case .transactionId(let transactionId):
|
|
1103
|
+
return transactionId
|
|
1104
|
+
@unknown default:
|
|
1105
|
+
return nil
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1101
1109
|
private func extractMediaUrl(source: MediaSource, into contentDict: inout [String: Any]) {
|
|
1102
1110
|
let url = source.url()
|
|
1103
1111
|
if !url.isEmpty {
|
|
@@ -1120,24 +1128,20 @@ private func serializeTimelineItem(_ item: TimelineItem, roomId: String) -> [Str
|
|
|
1120
1128
|
}
|
|
1121
1129
|
|
|
1122
1130
|
private func serializeEventTimelineItem(_ eventItem: EventTimelineItem, roomId: String) -> [String: Any]? {
|
|
1123
|
-
let eventId
|
|
1124
|
-
if let eid = eventItem.eventId() {
|
|
1125
|
-
eventId = eid
|
|
1126
|
-
} else if let tid = eventItem.transactionId() {
|
|
1127
|
-
eventId = tid
|
|
1128
|
-
} else {
|
|
1131
|
+
guard let eventId = extractEventId(eventItem.eventOrTransactionId) else {
|
|
1129
1132
|
return nil
|
|
1130
1133
|
}
|
|
1131
1134
|
|
|
1132
1135
|
var contentDict: [String: Any] = [:]
|
|
1133
1136
|
var eventType = "m.room.message"
|
|
1134
1137
|
|
|
1135
|
-
let content = eventItem.content
|
|
1136
|
-
switch content
|
|
1137
|
-
case .
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1138
|
+
let content = eventItem.content
|
|
1139
|
+
switch content {
|
|
1140
|
+
case .msgLike(let msgLikeContent):
|
|
1141
|
+
switch msgLikeContent.kind {
|
|
1142
|
+
case .message(let messageContent):
|
|
1143
|
+
contentDict["body"] = messageContent.body
|
|
1144
|
+
switch messageContent.msgType {
|
|
1141
1145
|
case .text:
|
|
1142
1146
|
contentDict["msgtype"] = "m.text"
|
|
1143
1147
|
case .image(let imgContent):
|
|
@@ -1162,39 +1166,41 @@ private func serializeEventTimelineItem(_ eventItem: EventTimelineItem, roomId:
|
|
|
1162
1166
|
default:
|
|
1163
1167
|
contentDict["msgtype"] = "m.text"
|
|
1164
1168
|
}
|
|
1169
|
+
case .unableToDecrypt:
|
|
1170
|
+
contentDict["body"] = "Unable to decrypt message"
|
|
1171
|
+
contentDict["msgtype"] = "m.text"
|
|
1172
|
+
contentDict["encrypted"] = true
|
|
1173
|
+
case .redacted:
|
|
1174
|
+
eventType = "m.room.redaction"
|
|
1175
|
+
contentDict["body"] = "Message deleted"
|
|
1176
|
+
default:
|
|
1177
|
+
eventType = "m.room.unknown"
|
|
1165
1178
|
}
|
|
1166
|
-
case .unableToDecrypt:
|
|
1167
|
-
contentDict["body"] = "Unable to decrypt message"
|
|
1168
|
-
contentDict["msgtype"] = "m.text"
|
|
1169
|
-
contentDict["encrypted"] = true
|
|
1170
|
-
case .redactedMessage:
|
|
1171
|
-
eventType = "m.room.redaction"
|
|
1172
|
-
contentDict["body"] = "Message deleted"
|
|
1173
|
-
default:
|
|
1174
|
-
eventType = "m.room.unknown"
|
|
1175
|
-
}
|
|
1176
1179
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1180
|
+
// Reactions from MsgLikeContent
|
|
1181
|
+
let reactions = msgLikeContent.reactions
|
|
1182
|
+
if !reactions.isEmpty {
|
|
1183
|
+
contentDict["reactions"] = reactions.map { r in
|
|
1184
|
+
[
|
|
1185
|
+
"key": r.key,
|
|
1186
|
+
"count": r.senders.count,
|
|
1187
|
+
"senders": r.senders.map { $0.senderId },
|
|
1188
|
+
] as [String: Any]
|
|
1189
|
+
}
|
|
1186
1190
|
}
|
|
1191
|
+
default:
|
|
1192
|
+
eventType = "m.room.unknown"
|
|
1187
1193
|
}
|
|
1188
1194
|
|
|
1189
1195
|
// Delivery/read status
|
|
1190
1196
|
var status: String = "sent"
|
|
1191
|
-
if let sendState = eventItem.localSendState
|
|
1197
|
+
if let sendState = eventItem.localSendState {
|
|
1192
1198
|
switch sendState {
|
|
1193
1199
|
case .notSentYet:
|
|
1194
1200
|
status = "sending"
|
|
1195
|
-
case .sendingFailed
|
|
1201
|
+
case .sendingFailed:
|
|
1196
1202
|
status = "sending"
|
|
1197
|
-
case .sent
|
|
1203
|
+
case .sent:
|
|
1198
1204
|
// Check read receipts below
|
|
1199
1205
|
break
|
|
1200
1206
|
default:
|
|
@@ -1203,12 +1209,12 @@ private func serializeEventTimelineItem(_ eventItem: EventTimelineItem, roomId:
|
|
|
1203
1209
|
}
|
|
1204
1210
|
|
|
1205
1211
|
var readBy: [String]? = nil
|
|
1206
|
-
let receipts = eventItem.readReceipts
|
|
1212
|
+
let receipts = eventItem.readReceipts
|
|
1207
1213
|
if !receipts.isEmpty {
|
|
1208
|
-
print("[CapMatrix] readReceipts for \(eventId): \(receipts.keys) sender=\(eventItem.sender
|
|
1214
|
+
print("[CapMatrix] readReceipts for \(eventId): \(receipts.keys) sender=\(eventItem.sender)")
|
|
1209
1215
|
}
|
|
1210
1216
|
if status == "sent" {
|
|
1211
|
-
let others = receipts.keys.filter { $0 != eventItem.sender
|
|
1217
|
+
let others = receipts.keys.filter { $0 != eventItem.sender }
|
|
1212
1218
|
if !others.isEmpty {
|
|
1213
1219
|
status = "read"
|
|
1214
1220
|
readBy = Array(others)
|
|
@@ -1218,10 +1224,10 @@ private func serializeEventTimelineItem(_ eventItem: EventTimelineItem, roomId:
|
|
|
1218
1224
|
return [
|
|
1219
1225
|
"eventId": eventId,
|
|
1220
1226
|
"roomId": roomId,
|
|
1221
|
-
"senderId": eventItem.sender
|
|
1227
|
+
"senderId": eventItem.sender,
|
|
1222
1228
|
"type": eventType,
|
|
1223
1229
|
"content": contentDict,
|
|
1224
|
-
"originServerTs": eventItem.timestamp
|
|
1230
|
+
"originServerTs": eventItem.timestamp,
|
|
1225
1231
|
"status": status,
|
|
1226
1232
|
"readBy": readBy as Any,
|
|
1227
1233
|
]
|
|
@@ -1243,11 +1249,8 @@ class LiveTimelineListener: TimelineListener {
|
|
|
1243
1249
|
func onUpdate(diff: [TimelineDiff]) {
|
|
1244
1250
|
print("[CapMatrix] LiveTimelineListener onUpdate for \(roomId): \(diff.count) diffs")
|
|
1245
1251
|
for d in diff {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
switch change {
|
|
1249
|
-
case .reset:
|
|
1250
|
-
let items = d.reset() ?? []
|
|
1252
|
+
switch d {
|
|
1253
|
+
case .reset(let items):
|
|
1251
1254
|
print("[CapMatrix] Reset: \(items.count) items")
|
|
1252
1255
|
items.forEach { item in
|
|
1253
1256
|
if let event = serializeTimelineItem(item, roomId: roomId) {
|
|
@@ -1256,8 +1259,7 @@ class LiveTimelineListener: TimelineListener {
|
|
|
1256
1259
|
}
|
|
1257
1260
|
}
|
|
1258
1261
|
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1259
|
-
case .append:
|
|
1260
|
-
let items = d.append() ?? []
|
|
1262
|
+
case .append(let items):
|
|
1261
1263
|
print("[CapMatrix] Append: \(items.count) items")
|
|
1262
1264
|
items.forEach { item in
|
|
1263
1265
|
if let event = serializeTimelineItem(item, roomId: roomId) {
|
|
@@ -1266,44 +1268,45 @@ class LiveTimelineListener: TimelineListener {
|
|
|
1266
1268
|
}
|
|
1267
1269
|
}
|
|
1268
1270
|
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1269
|
-
case .pushBack:
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1271
|
+
case .pushBack(let item):
|
|
1272
|
+
let isLocalEcho = item.asEvent().map { extractEventId($0.eventOrTransactionId) } != nil
|
|
1273
|
+
&& item.asEvent()?.eventOrTransactionId is EventOrTransactionId
|
|
1274
|
+
// Check if this is a local echo (transaction ID, no event ID yet)
|
|
1275
|
+
let eventItem = item.asEvent()
|
|
1276
|
+
var skipLocalEcho = false
|
|
1277
|
+
if let ei = eventItem {
|
|
1278
|
+
if case .transactionId = ei.eventOrTransactionId {
|
|
1279
|
+
skipLocalEcho = true
|
|
1276
1280
|
}
|
|
1277
|
-
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1278
1281
|
}
|
|
1279
|
-
|
|
1280
|
-
if let
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
onMessage(event)
|
|
1284
|
-
}
|
|
1285
|
-
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1282
|
+
print("[CapMatrix] PushBack: localEcho=\(skipLocalEcho)")
|
|
1283
|
+
if !skipLocalEcho, let event = serializeTimelineItem(item, roomId: roomId) {
|
|
1284
|
+
print("[CapMatrix] PushBack item: \(event["eventId"] ?? "nil") type=\(event["type"] ?? "nil")")
|
|
1285
|
+
onMessage(event)
|
|
1286
1286
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
// If this event has readBy data, trigger roomUpdated
|
|
1293
|
-
// so the app can refresh receipt status for all messages
|
|
1294
|
-
if let rb = event["readBy"] as? [String], !rb.isEmpty {
|
|
1295
|
-
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1287
|
+
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1288
|
+
case .pushFront(let item):
|
|
1289
|
+
if let event = serializeTimelineItem(item, roomId: roomId) {
|
|
1290
|
+
print("[CapMatrix] PushFront item: \(event["eventId"] ?? "nil")")
|
|
1291
|
+
onMessage(event)
|
|
1298
1292
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1293
|
+
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1294
|
+
case .set(let index, let item):
|
|
1295
|
+
if let event = serializeTimelineItem(item, roomId: roomId) {
|
|
1296
|
+
print("[CapMatrix] Set item: \(event["eventId"] ?? "nil") type=\(event["type"] ?? "nil") status=\(event["status"] ?? "nil") readBy=\(event["readBy"] ?? "nil")")
|
|
1297
|
+
onMessage(event)
|
|
1298
|
+
// If this event has readBy data, trigger roomUpdated
|
|
1299
|
+
// so the app can refresh receipt status for all messages
|
|
1300
|
+
if let rb = event["readBy"] as? [String], !rb.isEmpty {
|
|
1301
|
+
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1304
1302
|
}
|
|
1305
|
-
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1306
1303
|
}
|
|
1304
|
+
case .insert(let index, let item):
|
|
1305
|
+
if let event = serializeTimelineItem(item, roomId: roomId) {
|
|
1306
|
+
print("[CapMatrix] Insert item: \(event["eventId"] ?? "nil")")
|
|
1307
|
+
onMessage(event)
|
|
1308
|
+
}
|
|
1309
|
+
onRoomUpdate(roomId, ["roomId": roomId])
|
|
1307
1310
|
case .remove:
|
|
1308
1311
|
break // Index-based removal, handled by JS layer
|
|
1309
1312
|
default:
|
|
@@ -1401,46 +1404,50 @@ class TimelineItemCollector: TimelineListener {
|
|
|
1401
1404
|
var continuation: CheckedContinuation<Bool, Never>?
|
|
1402
1405
|
lock.lock()
|
|
1403
1406
|
for d in diff {
|
|
1404
|
-
switch d
|
|
1405
|
-
case .reset:
|
|
1407
|
+
switch d {
|
|
1408
|
+
case .reset(let items):
|
|
1406
1409
|
_items.removeAll()
|
|
1407
1410
|
_uniqueIdMap.removeAll()
|
|
1408
|
-
|
|
1409
|
-
trackUniqueId(item)
|
|
1410
|
-
_items.append(serializeTimelineItem(item, roomId: roomId))
|
|
1411
|
-
}
|
|
1412
|
-
case .append:
|
|
1413
|
-
d.append()?.forEach { item in
|
|
1411
|
+
items.forEach { item in
|
|
1414
1412
|
trackUniqueId(item)
|
|
1415
1413
|
_items.append(serializeTimelineItem(item, roomId: roomId))
|
|
1416
1414
|
}
|
|
1417
|
-
case .
|
|
1418
|
-
|
|
1415
|
+
case .append(let items):
|
|
1416
|
+
items.forEach { item in
|
|
1419
1417
|
trackUniqueId(item)
|
|
1420
1418
|
_items.append(serializeTimelineItem(item, roomId: roomId))
|
|
1421
1419
|
}
|
|
1422
|
-
case .
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
case .insert:
|
|
1436
|
-
if let data = d.insert() {
|
|
1437
|
-
trackUniqueId(data.item)
|
|
1438
|
-
let idx = min(Int(data.index), _items.count)
|
|
1439
|
-
_items.insert(serializeTimelineItem(data.item, roomId: roomId), at: idx)
|
|
1420
|
+
case .pushBack(let item):
|
|
1421
|
+
trackUniqueId(item)
|
|
1422
|
+
_items.append(serializeTimelineItem(item, roomId: roomId))
|
|
1423
|
+
case .pushFront(let item):
|
|
1424
|
+
trackUniqueId(item)
|
|
1425
|
+
_items.insert(serializeTimelineItem(item, roomId: roomId), at: 0)
|
|
1426
|
+
case .set(let index, let item):
|
|
1427
|
+
trackUniqueId(item)
|
|
1428
|
+
let idx = Int(index)
|
|
1429
|
+
if idx >= 0 && idx < _items.count {
|
|
1430
|
+
_items[idx] = serializeTimelineItem(item, roomId: roomId)
|
|
1440
1431
|
}
|
|
1432
|
+
case .insert(let index, let item):
|
|
1433
|
+
trackUniqueId(item)
|
|
1434
|
+
let idx = min(Int(index), _items.count)
|
|
1435
|
+
_items.insert(serializeTimelineItem(item, roomId: roomId), at: idx)
|
|
1441
1436
|
case .clear:
|
|
1442
1437
|
_items.removeAll()
|
|
1443
1438
|
_uniqueIdMap.removeAll()
|
|
1439
|
+
case .remove(let index):
|
|
1440
|
+
let idx = Int(index)
|
|
1441
|
+
if idx >= 0 && idx < _items.count {
|
|
1442
|
+
_items.remove(at: idx)
|
|
1443
|
+
}
|
|
1444
|
+
case .truncate(let length):
|
|
1445
|
+
let len = Int(length)
|
|
1446
|
+
while _items.count > len { _items.removeLast() }
|
|
1447
|
+
case .popBack:
|
|
1448
|
+
if !_items.isEmpty { _items.removeLast() }
|
|
1449
|
+
case .popFront:
|
|
1450
|
+
if !_items.isEmpty { _items.removeFirst() }
|
|
1444
1451
|
default:
|
|
1445
1452
|
break
|
|
1446
1453
|
}
|
|
@@ -1454,13 +1461,10 @@ class TimelineItemCollector: TimelineListener {
|
|
|
1454
1461
|
|
|
1455
1462
|
private func trackUniqueId(_ item: TimelineItem) {
|
|
1456
1463
|
guard let eventItem = item.asEvent() else { return }
|
|
1457
|
-
let uniqueId = item.uniqueId()
|
|
1458
|
-
if let eid = eventItem.
|
|
1464
|
+
let uniqueId = item.uniqueId().id
|
|
1465
|
+
if let eid = extractEventId(eventItem.eventOrTransactionId) {
|
|
1459
1466
|
_uniqueIdMap[eid] = uniqueId
|
|
1460
1467
|
}
|
|
1461
|
-
if let tid = eventItem.transactionId() {
|
|
1462
|
-
_uniqueIdMap[tid] = uniqueId
|
|
1463
|
-
}
|
|
1464
1468
|
}
|
|
1465
1469
|
}
|
|
1466
1470
|
|