Live Location Sharing Banner (#5370)

* Live Location Sharing Banner

# Conflicts:
#	ElementX.xcodeproj/project.pbxproj

* updated the top banner modifier and the top banner views

# Conflicts:
#	ElementX/Resources/Localizations/en-US.lproj/Localizable.strings
#	ElementX/Resources/Localizations/en.lproj/Localizable.strings

* improved the stopping function from the timeline item

* stop live location sharing before starting a new one.

* added some tests for LiveLocationManager

* pr suggestions
This commit is contained in:
Mauro
2026-04-10 14:50:42 +02:00
committed by GitHub
parent 148b292ca6
commit 922fcafcd5
47 changed files with 413 additions and 83 deletions
@@ -70,7 +70,7 @@ enum TimelineViewAction {
case handlePollAction(TimelineViewPollAction)
case handleAudioPlayerAction(TimelineAudioPlayerAction)
case stopLiveLocationSharing
case stopLiveLocationSharing(TimelineItemIdentifier)
/// Focus the timeline onto the specified event ID (switching to a detached timeline if needed).
case focusOnEventID(String)
@@ -143,6 +143,8 @@ struct TimelineViewState: BindableState {
var mapTilerConfiguration: MapTilerConfiguration
var enableKeyShareOnInvite: Bool
var stoppedLiveLocationIDs: Set<TimelineItemIdentifier> = []
var bindings: TimelineViewStateBindings
}
@@ -198,8 +198,9 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
handlePollAction(pollAction)
case .handleAudioPlayerAction(let audioPlayerAction):
handleAudioPlayerAction(audioPlayerAction)
case .stopLiveLocationSharing:
Task { await userSession.liveLocationManager.stopLiveLocation(roomID: state.roomID) }
case .stopLiveLocationSharing(let id):
state.stoppedLiveLocationIDs.insert(id)
Task { await stopLiveLocationSharing() }
case .focusOnEventID(let eventID):
Task { await focusOnEvent(eventID: eventID) }
case .focusLive:
@@ -276,6 +277,10 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
}
}
func stopLiveLocationSharing() async {
await userSession.liveLocationManager.stopLiveLocation(roomID: roomProxy.id)
}
func makeForwardingItem(for itemID: TimelineItemIdentifier) async -> MessageForwardingItem? {
guard let content = await timelineController.messageEventContent(for: itemID) else { return nil }
return .init(id: itemID, roomID: roomProxy.id, content: content)
@@ -18,7 +18,8 @@ protocol TimelineViewModelProtocol {
func process(composerAction: ComposerToolbarViewModelAction)
/// Updates the timeline to show and highlight the item with the corresponding event ID.
func focusOnEvent(eventID: String) async
/// Stops the current live location sharing
func stopLiveLocationSharing() async
/// Handles getting the content to forward an item given its item ID.
func makeForwardingItem(for itemID: TimelineItemIdentifier) async -> MessageForwardingItem?
}
@@ -15,10 +15,10 @@ struct LiveLocationRoomTimelineView: View {
let timelineItem: LiveLocationRoomTimelineItem
private let currentDate: Date
init(currentDate: Date = .now, timelineItem: LiveLocationRoomTimelineItem) {
init(currentDate: Date = .now, timelineItem: LiveLocationRoomTimelineItem, isStopped: Bool = false) {
self.currentDate = currentDate
self.timelineItem = timelineItem
_hasExpired = State(initialValue: currentDate >= timelineItem.content.timeoutDate)
_hasExpired = State(initialValue: isStopped || currentDate >= timelineItem.content.timeoutDate)
}
/// A publisher that fires once when the timeoutDate is reached, setting `hasExpired` to true.
@@ -191,7 +191,7 @@ struct LiveLocationRoomTimelineView: View {
private func stop() {
hasExpired = true
context?.send(viewAction: .stopLiveLocationSharing)
context?.send(viewAction: .stopLiveLocationSharing(timelineItem.id))
}
// MARK: - Constants