Render the live location timeline item (#5209)

* created the data for the timeline item

# Conflicts:
#	ElementX/Sources/Other/Logging/Tracing.swift
#	ElementX/Sources/Services/Authentication/AuthenticationService.swift
#	ElementX/Sources/Services/Authentication/LinkNewDeviceService.swift

* Implemented the LLS timeline item

* updated preview tests

* pr and design suggestions

* previewscrollview is debug only
This commit is contained in:
Mauro
2026-03-24 10:34:00 +01:00
committed by GitHub
parent 610c296125
commit b8d337e4ce
51 changed files with 799 additions and 93 deletions
@@ -10,16 +10,10 @@ import Compound
import SwiftUI
struct LocationMarkerView: View {
var userProfile: UserProfileProxy?
var fillColor = Color.compound.bgCanvasDefault
var strokeColor = Color.compound.iconSecondaryAlpha
var dotColor = Color.compound.iconPrimary
var kind: LocationMarkerKind
@ScaledMetric var size: CGFloat = 42
var mediaProvider: MediaProviderProtocol?
private let circleCenter = CGPoint(x: 21, y: 21) // in SVG space
private let circleRadius: CGFloat = 6 // in SVG space
var body: some View {
// Generated from the SVG
Canvas { context, canvasSize in
@@ -55,7 +49,7 @@ struct LocationMarkerView: View {
p.closeSubpath()
}
context.stroke(pinPath, with: .color(strokeColor), lineWidth: 2 * scaleX)
context.stroke(pinPath, with: .color(externalStrokeColor), lineWidth: 2 * scaleX)
context.fill(pinPath, with: .color(fillColor))
// Dot
@@ -66,20 +60,20 @@ struct LocationMarkerView: View {
context.fill(dotPath, with: .color(dotColor))
// Draw resolved symbol centered on the circle
if userProfile != nil, let symbol = context.resolveSymbol(id: 0) {
if kind.userProfile != nil, let symbol = context.resolveSymbol(id: 0) {
let center = CGPoint(x: circleCenter.x * scaleX,
y: circleCenter.y * scaleY)
context.draw(symbol, at: center, anchor: .center)
}
} symbols: {
if let userProfile {
if let userProfile = kind.userProfile {
LoadableAvatarImage(url: userProfile.avatarURL,
name: userProfile.displayName,
contentID: userProfile.userID,
avatarSize: .user(on: .map),
mediaProvider: mediaProvider)
.overlay {
Circle().inset(by: 0.5).stroke(strokeColor)
Circle().inset(by: 0.5).stroke(internalStrokeColor)
}
.tag(0)
}
@@ -89,22 +83,77 @@ struct LocationMarkerView: View {
dimensions[.bottom]
}
}
private let circleCenter = CGPoint(x: 21, y: 21) // in SVG space
private let circleRadius: CGFloat = 6 // in SVG space
private var fillColor: Color {
switch kind {
case .pin, .staticUser:
.compound.bgCanvasDefault
case .liveUser:
.compound.iconAccentPrimary
case .placeholder:
.compound.bgSubtleSecondary
}
}
private var externalStrokeColor: Color {
switch kind {
case .pin, .staticUser:
.compound.iconSecondaryAlpha
case .liveUser:
.compound.iconAccentPrimary
case .placeholder:
.compound.iconDisabled
}
}
private var internalStrokeColor: Color {
switch kind {
case .pin, .staticUser:
.compound.iconSecondaryAlpha
case .liveUser:
.compound.bgCanvasDefault
case .placeholder:
.compound.iconDisabled
}
}
private var dotColor: Color {
switch kind {
case .placeholder:
.compound.iconDisabled
default:
.compound.iconPrimary
}
}
}
struct LocationMarkerView_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
VStack(spacing: 30) {
LocationMarkerView()
// Placeholder
LocationMarkerView(kind: .placeholder)
LocationMarkerView()
.colorScheme(.dark)
// Pin (no user)
LocationMarkerView(kind: .pin)
LocationMarkerView(userProfile: UserProfileProxy.mockDan,
// Static user with avatar
LocationMarkerView(kind: .staticUser(.mockDan),
mediaProvider: MediaProviderMock(configuration: .init()))
LocationMarkerView(userProfile: UserProfileProxy.mockDan,
// Static user without avatar
LocationMarkerView(kind: .staticUser(.init(userID: "@someone:matrix.org",
displayName: "Someone")))
// Live user with avatar
LocationMarkerView(kind: .liveUser(.mockDan),
mediaProvider: MediaProviderMock(configuration: .init()))
.colorScheme(.dark)
// Live user without avatar
LocationMarkerView(kind: .liveUser(.init(userID: "@someone:matrix.org",
displayName: "Someone")))
}
.padding(16)
.background(Color(red: 0.9, green: 0.85, blue: 0.8))