mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:06:00 +00:00
Refactor and redesign Location Sharing Screen (#5155)
* refactored the static location screen to the location sharing screen # Conflicts: # UnitTests/Sources/LocationSharingScreenViewModelTests.swift # Conflicts: # ElementX.xcodeproj/project.pbxproj * implemented a custom pin with an overlayable view * implemented the render of the user when the location is sender instead of the pin type * removed description and body they are not used at all. * reimplemented single button for sharing this or user location + implemented an experimental way to update annotations * removed unnecessary @Suite description * implemented a way to display the alert on top of the sheet and added a spinner to the center user location button * fixed alerts strings * fixed a failing test * improved preview tests
This commit is contained in:
@@ -10,28 +10,84 @@ import Compound
|
||||
import SwiftUI
|
||||
|
||||
struct LocationMarkerView: View {
|
||||
private let pinColor: Color = .compound.iconOnSolidPrimary
|
||||
private let pinInsets = EdgeInsets(top: 13, leading: 12, bottom: 15, trailing: 12)
|
||||
var userProfile: UserProfileProxy?
|
||||
var fillColor = Color.compound.bgCanvasDefault
|
||||
var strokeColor = Color.compound.iconSecondaryAlpha
|
||||
var dotColor = Color.compound.iconPrimary
|
||||
@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 {
|
||||
CompoundIcon(\.locationPinSolid)
|
||||
.dynamicTypeSize(.large)
|
||||
.foregroundStyle(pinColor)
|
||||
.padding(pinInsets)
|
||||
.background {
|
||||
backgroundShape
|
||||
.shadow(color: .black.opacity(0.2), radius: 4.1129, x: 0, y: 4.93548)
|
||||
// Generated from the SVG
|
||||
Canvas { context, canvasSize in
|
||||
let scaleX = canvasSize.width / 42
|
||||
let scaleY = canvasSize.height / 50
|
||||
|
||||
let pinPath = Path { p in
|
||||
p.move(to: CGPoint(x: 21 * scaleX, y: 49 * scaleY))
|
||||
p.addCurve(to: CGPoint(x: 17.6875 * scaleX, y: 47.7581 * scaleY),
|
||||
control1: CGPoint(x: 19.25 * scaleX, y: 49 * scaleY),
|
||||
control2: CGPoint(x: 18.1458 * scaleX, y: 48.4825 * scaleY))
|
||||
p.addCurve(to: CGPoint(x: 5.28125 * scaleX, y: 33.6313 * scaleY),
|
||||
control1: CGPoint(x: 12.5833 * scaleX, y: 42.8525 * scaleY),
|
||||
control2: CGPoint(x: 8.41667 * scaleX, y: 38.1332 * scaleY))
|
||||
p.addCurve(to: CGPoint(x: 1 * scaleX, y: 21.3674 * scaleY),
|
||||
control1: CGPoint(x: 2.82292 * scaleX, y: 29.2846 * scaleY),
|
||||
control2: CGPoint(x: 1 * scaleX, y: 25.1863 * scaleY))
|
||||
p.addCurve(to: CGPoint(x: 21 * scaleX, y: 1 * scaleY),
|
||||
control1: CGPoint(x: 1 * scaleX, y: 10.2109 * scaleY),
|
||||
control2: CGPoint(x: 9.5 * scaleX, y: 1 * scaleY))
|
||||
p.addCurve(to: CGPoint(x: 41 * scaleX, y: 21.3674 * scaleY),
|
||||
control1: CGPoint(x: 32.5 * scaleX, y: 1 * scaleY),
|
||||
control2: CGPoint(x: 41 * scaleX, y: 10.2109 * scaleY))
|
||||
p.addCurve(to: CGPoint(x: 36.7188 * scaleX, y: 33.6313 * scaleY),
|
||||
control1: CGPoint(x: 41 * scaleX, y: 25.1863 * scaleY),
|
||||
control2: CGPoint(x: 39.1771 * scaleX, y: 29.2846 * scaleY))
|
||||
p.addCurve(to: CGPoint(x: 24.3125 * scaleX, y: 47.7581 * scaleY),
|
||||
control1: CGPoint(x: 33.5833 * scaleX, y: 38.1332 * scaleY),
|
||||
control2: CGPoint(x: 29.4167 * scaleX, y: 42.8525 * scaleY))
|
||||
p.addCurve(to: CGPoint(x: 21 * scaleX, y: 49 * scaleY),
|
||||
control1: CGPoint(x: 23.3333 * scaleX, y: 48.4825 * scaleY),
|
||||
control2: CGPoint(x: 21.5833 * scaleX, y: 49 * scaleY))
|
||||
p.closeSubpath()
|
||||
}
|
||||
.alignmentGuide(VerticalAlignment.center) { dimensions in
|
||||
dimensions[.bottom]
|
||||
|
||||
context.stroke(pinPath, with: .color(strokeColor), lineWidth: 2 * scaleX)
|
||||
context.fill(pinPath, with: .color(fillColor))
|
||||
|
||||
// Dot
|
||||
let dotPath = Path(ellipseIn: CGRect(x: (circleCenter.x - circleRadius) * scaleX,
|
||||
y: (circleCenter.y - circleRadius) * scaleY,
|
||||
width: circleRadius * 2 * scaleX,
|
||||
height: circleRadius * 2 * scaleY))
|
||||
context.fill(dotPath, with: .color(dotColor))
|
||||
|
||||
// Draw resolved symbol centered on the circle
|
||||
if 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)
|
||||
}
|
||||
}
|
||||
|
||||
var backgroundShape: some View {
|
||||
Image(asset: Asset.Images.locationMarkerShape)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.foregroundStyle(.compound.iconPrimary)
|
||||
} symbols: {
|
||||
if let 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)
|
||||
}
|
||||
.tag(0)
|
||||
}
|
||||
}
|
||||
.frame(width: size, height: size * 50 / 42)
|
||||
.alignmentGuide(VerticalAlignment.center) { dimensions in
|
||||
dimensions[.bottom]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +95,19 @@ struct LocationMarkerView_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
VStack(spacing: 30) {
|
||||
LocationMarkerView()
|
||||
|
||||
|
||||
LocationMarkerView()
|
||||
.colorScheme(.dark)
|
||||
|
||||
LocationMarkerView(userProfile: UserProfileProxy.mockDan,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||
|
||||
LocationMarkerView(userProfile: UserProfileProxy.mockDan,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||
.colorScheme(.dark)
|
||||
}
|
||||
.padding(16)
|
||||
.background(Color(red: 0.9, green: 0.85, blue: 0.8))
|
||||
.previewLayout(.sizeThatFits)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user