implement APIs for LLS observation

This commit is contained in:
Mauro Romito
2026-04-14 12:45:10 +02:00
committed by Mauro
parent 78240ebdee
commit facba008e7
10 changed files with 237 additions and 0 deletions
@@ -0,0 +1,29 @@
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Foundation
import MatrixRustSDK
struct LiveLocationShareProxy: Hashable {
let userID: String
let geoURI: GeoURI?
let timestamp: Date
let timeoutDate: Date
}
extension LiveLocationShareProxy {
init(liveLocationShare: LiveLocationShare) {
userID = liveLocationShare.userId
if let geoURI = liveLocationShare.lastLocation?.location.geoUri {
self.geoURI = GeoURI(string: geoURI)
} else {
geoURI = nil
}
timestamp = Date(timeIntervalSince1970: Double(liveLocationShare.startTs))
timeoutDate = timestamp.addingTimeInterval(Double(liveLocationShare.timeout) / 1000)
}
}
@@ -0,0 +1,72 @@
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Combine
import Foundation
import MatrixRustSDK
final class LiveLocationSharesService: LiveLocationSharesServiceProtocol {
// periphery:ignore - required for instance retention in the rust codebase
private let liveLocationShares: LiveLocationShares
// periphery:ignore - required for instance retention in the rust codebase
private var observationToken: TaskHandle?
private let liveLocationSharesSubject = PassthroughSubject<[LiveLocationShareProxy], Never>()
var liveLocationSharesPublisher: AnyPublisher<[LiveLocationShareProxy], Never> {
liveLocationSharesSubject.eraseToAnyPublisher()
}
private var previousLiveLocationShares: [LiveLocationShareProxy] = []
init(liveLocationShares: LiveLocationShares) {
self.liveLocationShares = liveLocationShares
observationToken = liveLocationShares
.subscribe(listener: SDKListener { [weak self] updates in
guard let self else { return }
MXLog.info("Received live location shares update")
let updatedShares = handleLiveLocationShareUpdates(updates)
liveLocationSharesSubject.send(updatedShares)
})
}
// MARK: - Private
private func handleLiveLocationShareUpdates(_ updates: [LiveLocationShareUpdate]) -> [LiveLocationShareProxy] {
var shares = previousLiveLocationShares
for update in updates {
switch update {
case .append(let values):
shares.append(contentsOf: values.map(LiveLocationShareProxy.init))
case .clear:
shares.removeAll()
case .pushFront(let value):
shares.insert(LiveLocationShareProxy(liveLocationShare: value), at: 0)
case .pushBack(let value):
shares.append(LiveLocationShareProxy(liveLocationShare: value))
case .popFront:
shares.removeFirst()
case .popBack:
shares.removeLast()
case .insert(let index, let value):
shares.insert(LiveLocationShareProxy(liveLocationShare: value), at: Int(index))
case .set(let index, let value):
shares[Int(index)] = LiveLocationShareProxy(liveLocationShare: value)
case .remove(let index):
shares.remove(at: Int(index))
case .truncate(let length):
shares.removeSubrange(Int(length)..<shares.count)
case .reset(let values):
shares = values.map(LiveLocationShareProxy.init)
}
}
previousLiveLocationShares = shares
return shares
}
}
@@ -0,0 +1,14 @@
//
// Copyright 2026 Element Creations Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
// Please see LICENSE files in the repository root for full details.
//
import Combine
import Foundation
// sourcery: AutoMockable
protocol LiveLocationSharesServiceProtocol {
var liveLocationSharesPublisher: AnyPublisher<[LiveLocationShareProxy], Never> { get }
}