* Add LiveLocationSheet and refactor existing views to share code * Implement logic for highlighting a specific LLS from a user once selected in the sheet * Updated tests, project and added new previews for the LLS sheet. # Conflicts: # PreviewTests/Sources/__Snapshots__/PreviewTests/liveLocationRoomTimelineView.Bubbles-iPad-pseudo.png # PreviewTests/Sources/__Snapshots__/PreviewTests/liveLocationRoomTimelineView.Bubbles-iPhone-pseudo.png * add Equatable conformance to CLLocationCoordinate2D
110 lines
4.6 KiB
Swift
110 lines
4.6 KiB
Swift
//
|
|
// 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 Compound
|
|
import SwiftUI
|
|
|
|
struct UserLocationCell: View {
|
|
let profile: UserProfileProxy
|
|
let isOwnUser: Bool
|
|
let kind: Kind
|
|
var mediaProvider: MediaProviderProtocol?
|
|
|
|
var onShare: (() -> Void)?
|
|
var onStop: (() -> Void)?
|
|
|
|
enum Kind {
|
|
case `static`(isUserLocation: Bool, timestamp: Date)
|
|
case live
|
|
}
|
|
|
|
private var name: String {
|
|
isOwnUser ? L10n.commonYou : profile.displayName ?? profile.userID
|
|
}
|
|
|
|
var body: some View {
|
|
HStack(spacing: 12) {
|
|
LoadableAvatarImage(url: profile.avatarURL,
|
|
name: profile.displayName,
|
|
contentID: profile.id,
|
|
avatarSize: .user(on: .map),
|
|
mediaProvider: mediaProvider)
|
|
.accessibilityHidden(true)
|
|
|
|
HStack(spacing: 16) {
|
|
VStack(alignment: .leading, spacing: 0) {
|
|
Text(name)
|
|
.font(.compound.bodyLG)
|
|
.foregroundStyle(.compound.textPrimary)
|
|
HStack(spacing: 4) {
|
|
if case let .static(isUserLocation, timestamp) = kind {
|
|
CompoundIcon(isUserLocation ? \.locationNavigatorCentred : \.locationNavigator,
|
|
size: .xSmall,
|
|
relativeTo: .compound.bodyMD)
|
|
.foregroundStyle(.compound.iconSecondary)
|
|
.accessibilityLabel(isUserLocation ? L10n.a11ySenderLocation : L10n.a11yPinnedLocation)
|
|
Text(L10n.screenStaticLocationSheetTimestampDescription(timestamp.formatted(.relative(presentation: .named))))
|
|
.font(.compound.bodyMD)
|
|
.foregroundStyle(.compound.textSecondary)
|
|
} else {
|
|
CompoundIcon(\.locationPinSolid,
|
|
size: .xSmall,
|
|
relativeTo: .compound.bodyMD)
|
|
.foregroundStyle(.compound.iconAccentPrimary)
|
|
.accessibilityHidden(true)
|
|
Text(L10n.screenLiveLocationSheetSharingLiveLocation)
|
|
.font(.compound.bodyMD)
|
|
.foregroundStyle(.compound.textPrimary)
|
|
}
|
|
}
|
|
}
|
|
.accessibilityElement(children: .combine)
|
|
|
|
Spacer()
|
|
if case .live = kind, isOwnUser {
|
|
StopButton { onStop?() }
|
|
}
|
|
Button { onShare?() } label: {
|
|
CompoundIcon(\.shareIos)
|
|
.foregroundStyle(.compound.iconPrimary)
|
|
.padding(5)
|
|
.overlay(RoundedRectangle(cornerRadius: 99)
|
|
.inset(by: -0.5)
|
|
.stroke(.compound.borderInteractiveSecondary, lineWidth: 1))
|
|
.accessibilityLabel(L10n.actionShare)
|
|
}
|
|
}
|
|
.padding(.vertical, 12)
|
|
.rowDivider(alignment: .top)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
}
|
|
}
|
|
|
|
struct UserLocationCell_Previews: PreviewProvider, TestablePreview {
|
|
static var previews: some View {
|
|
UserLocationCell(profile: .mockDan,
|
|
isOwnUser: true,
|
|
kind: .static(isUserLocation: true, timestamp: .mock),
|
|
mediaProvider: MediaProviderMock(configuration: .init()))
|
|
.previewDisplayName("Stiatc user locaton")
|
|
.previewLayout(.sizeThatFits)
|
|
UserLocationCell(profile: .mockDan,
|
|
isOwnUser: false,
|
|
kind: .static(isUserLocation: false, timestamp: .mock),
|
|
mediaProvider: MediaProviderMock(configuration: .init()))
|
|
.previewDisplayName("Static pin location")
|
|
.previewLayout(.sizeThatFits)
|
|
UserLocationCell(profile: .mockDan,
|
|
isOwnUser: true,
|
|
kind: .live,
|
|
mediaProvider: MediaProviderMock(configuration: .init()))
|
|
.previewDisplayName("Live location")
|
|
.previewLayout(.sizeThatFits)
|
|
}
|
|
}
|