Files
letro-ios/ElementX/Sources/Screens/LocationSharing/View/UserLocationCell.swift
Mauro 2e9a499fc3 LLS Sheet implementation (#5420)
* 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
2026-04-17 11:13:16 +00:00

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)
}
}