mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Send current user location (#1272)
* add new asset for location, add logic for sharing user current location * work on default zoom and auth for location * add attribution button * fix attribution button position, fix isSharingUserLocation * Add NSLocationWhenInUseUsageDescription back * Fix initial animation * change map logo and attribution position * Add annotation just in the setup * add errors and alert for map vies * location unit test * add unit test for location sharing * add changelog * Update ElementX/Sources/Other/MapLibre/MapLibreMapView.swift Co-authored-by: Alfonso Grillo <alfogrillo@gmail.com> * add comments and minor parameters refactor * Update ElementX/Sources/Screens/LocationSharing/StaticLocationScreenCoordinator.swift Co-authored-by: Mauro <34335419+Velin92@users.noreply.github.com> * Update ElementX/Sources/Screens/LocationSharing/StaticLocationScreenViewModel.swift Co-authored-by: Mauro <34335419+Velin92@users.noreply.github.com> * Sort assets * Cleanup * Cleanup * Fix error localizations * Fix tests --------- Co-authored-by: Alfonso Grillo <alfogrillo@gmail.com> Co-authored-by: Mauro <34335419+Velin92@users.noreply.github.com>
This commit is contained in:
@@ -4,8 +4,7 @@
|
|||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES"
|
buildImplicitDependencies = "YES">
|
||||||
runPostActionsOnFailure = "NO">
|
|
||||||
<BuildActionEntries>
|
<BuildActionEntries>
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
@@ -30,12 +29,6 @@
|
|||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
codeCoverageEnabled = "YES"
|
codeCoverageEnabled = "YES"
|
||||||
onlyGenerateCoverageForSpecifiedTargets = "YES">
|
onlyGenerateCoverageForSpecifiedTargets = "YES">
|
||||||
<TestPlans>
|
|
||||||
<TestPlanReference
|
|
||||||
default = "YES"
|
|
||||||
reference = "container:UnitTests/SupportingFiles/UnitTests.xctestplan">
|
|
||||||
</TestPlanReference>
|
|
||||||
</TestPlans>
|
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
@@ -45,10 +38,6 @@
|
|||||||
ReferencedContainer = "container:ElementX.xcodeproj">
|
ReferencedContainer = "container:ElementX.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
<CommandLineArguments>
|
|
||||||
</CommandLineArguments>
|
|
||||||
<CodeCoverageTargets>
|
<CodeCoverageTargets>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
@@ -58,6 +47,12 @@
|
|||||||
ReferencedContainer = "container:ElementX.xcodeproj">
|
ReferencedContainer = "container:ElementX.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</CodeCoverageTargets>
|
</CodeCoverageTargets>
|
||||||
|
<TestPlans>
|
||||||
|
<TestPlanReference
|
||||||
|
reference = "container:UnitTests/SupportingFiles/UnitTests.xctestplan"
|
||||||
|
default = "YES">
|
||||||
|
</TestPlanReference>
|
||||||
|
</TestPlans>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@@ -79,8 +74,6 @@
|
|||||||
ReferencedContainer = "container:ElementX.xcodeproj">
|
ReferencedContainer = "container:ElementX.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<CommandLineArguments>
|
|
||||||
</CommandLineArguments>
|
|
||||||
<EnvironmentVariables>
|
<EnvironmentVariables>
|
||||||
<EnvironmentVariable
|
<EnvironmentVariable
|
||||||
key = "RUST_BACKTRACE"
|
key = "RUST_BACKTRACE"
|
||||||
@@ -120,8 +113,6 @@
|
|||||||
ReferencedContainer = "container:ElementX.xcodeproj">
|
ReferencedContainer = "container:ElementX.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<CommandLineArguments>
|
|
||||||
</CommandLineArguments>
|
|
||||||
</ProfileAction>
|
</ProfileAction>
|
||||||
<AnalyzeAction
|
<AnalyzeAction
|
||||||
buildConfiguration = "Debug">
|
buildConfiguration = "Debug">
|
||||||
|
|||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "location-pointer-full.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filename" : "location-pointer-full-dark.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+189
@@ -0,0 +1,189 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 2 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.062745 0.074510 0.090196 scn
|
||||||
|
0.000000 34.000000 m
|
||||||
|
0.000000 37.313709 2.686292 40.000000 6.000000 40.000000 c
|
||||||
|
34.000000 40.000000 l
|
||||||
|
37.313709 40.000000 40.000000 37.313709 40.000000 34.000000 c
|
||||||
|
40.000000 6.000000 l
|
||||||
|
40.000000 2.686291 37.313709 0.000000 34.000000 0.000000 c
|
||||||
|
5.999999 0.000000 l
|
||||||
|
2.686291 0.000000 0.000000 2.686291 0.000000 6.000000 c
|
||||||
|
0.000000 34.000000 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 9.000000 9.000000 cm
|
||||||
|
0.921569 0.933333 0.949020 scn
|
||||||
|
11.000000 15.000000 m
|
||||||
|
8.790000 15.000000 7.000000 13.210000 7.000000 11.000000 c
|
||||||
|
7.000000 8.790000 8.790000 7.000000 11.000000 7.000000 c
|
||||||
|
13.210000 7.000000 15.000000 8.790000 15.000000 11.000000 c
|
||||||
|
15.000000 13.210000 13.210000 15.000000 11.000000 15.000000 c
|
||||||
|
h
|
||||||
|
19.940001 12.000000 m
|
||||||
|
19.480001 16.170000 16.170000 19.480000 12.000000 19.940001 c
|
||||||
|
12.000000 21.000000 l
|
||||||
|
12.000000 21.549999 11.550000 22.000000 11.000000 22.000000 c
|
||||||
|
10.450000 22.000000 10.000000 21.549999 10.000000 21.000000 c
|
||||||
|
10.000000 19.940001 l
|
||||||
|
5.830000 19.480000 2.520000 16.170000 2.060000 12.000000 c
|
||||||
|
1.000000 12.000000 l
|
||||||
|
0.450000 12.000000 0.000000 11.550000 0.000000 11.000000 c
|
||||||
|
0.000000 10.450000 0.450000 10.000000 1.000000 10.000000 c
|
||||||
|
2.060000 10.000000 l
|
||||||
|
2.520000 5.830000 5.830000 2.519999 10.000000 2.059999 c
|
||||||
|
10.000000 1.000000 l
|
||||||
|
10.000000 0.450001 10.450000 0.000000 11.000000 0.000000 c
|
||||||
|
11.550000 0.000000 12.000000 0.450001 12.000000 1.000000 c
|
||||||
|
12.000000 2.059999 l
|
||||||
|
16.170000 2.519999 19.480001 5.830000 19.940001 10.000000 c
|
||||||
|
21.000000 10.000000 l
|
||||||
|
21.549999 10.000000 22.000000 10.450000 22.000000 11.000000 c
|
||||||
|
22.000000 11.550000 21.549999 12.000000 21.000000 12.000000 c
|
||||||
|
19.940001 12.000000 l
|
||||||
|
19.940001 12.000000 l
|
||||||
|
h
|
||||||
|
11.000000 4.000000 m
|
||||||
|
7.130000 4.000000 4.000000 7.130000 4.000000 11.000000 c
|
||||||
|
4.000000 14.870000 7.130000 18.000000 11.000000 18.000000 c
|
||||||
|
14.870000 18.000000 18.000000 14.870000 18.000000 11.000000 c
|
||||||
|
18.000000 7.130000 14.870000 4.000000 11.000000 4.000000 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
2028
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 4 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.000000 34.000000 m
|
||||||
|
0.000000 37.313709 2.686292 40.000000 6.000000 40.000000 c
|
||||||
|
34.000000 40.000000 l
|
||||||
|
37.313709 40.000000 40.000000 37.313709 40.000000 34.000000 c
|
||||||
|
40.000000 6.000000 l
|
||||||
|
40.000000 2.686291 37.313709 0.000000 34.000000 0.000000 c
|
||||||
|
5.999999 0.000000 l
|
||||||
|
2.686291 0.000000 0.000000 2.686291 0.000000 6.000000 c
|
||||||
|
0.000000 34.000000 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
468
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /XObject << /X1 1 0 R >>
|
||||||
|
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||||
|
/G 3 0 R
|
||||||
|
/S /Alpha
|
||||||
|
>>
|
||||||
|
/Type /ExtGState
|
||||||
|
>> >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Length 7 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
/E1 gs
|
||||||
|
/X1 Do
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
46
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
/Resources 5 0 R
|
||||||
|
/Contents 6 0 R
|
||||||
|
/Parent 9 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
<< /Kids [ 8 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Pages 9 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 11
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000002286 00000 n
|
||||||
|
0000002309 00000 n
|
||||||
|
0000003025 00000 n
|
||||||
|
0000003047 00000 n
|
||||||
|
0000003345 00000 n
|
||||||
|
0000003447 00000 n
|
||||||
|
0000003468 00000 n
|
||||||
|
0000003641 00000 n
|
||||||
|
0000003715 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 10 0 R
|
||||||
|
/Size 11
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3775
|
||||||
|
%%EOF
|
||||||
+189
@@ -0,0 +1,189 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 2 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
0.000000 34.000000 m
|
||||||
|
0.000000 37.313709 2.686292 40.000000 6.000000 40.000000 c
|
||||||
|
34.000000 40.000000 l
|
||||||
|
37.313709 40.000000 40.000000 37.313709 40.000000 34.000000 c
|
||||||
|
40.000000 6.000000 l
|
||||||
|
40.000000 2.686291 37.313709 0.000000 34.000000 0.000000 c
|
||||||
|
5.999999 0.000000 l
|
||||||
|
2.686291 0.000000 0.000000 2.686291 0.000000 6.000000 c
|
||||||
|
0.000000 34.000000 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 9.000000 9.000000 cm
|
||||||
|
0.105882 0.113725 0.133333 scn
|
||||||
|
11.000000 15.000000 m
|
||||||
|
8.790000 15.000000 7.000000 13.210000 7.000000 11.000000 c
|
||||||
|
7.000000 8.790000 8.790000 7.000000 11.000000 7.000000 c
|
||||||
|
13.210000 7.000000 15.000000 8.790000 15.000000 11.000000 c
|
||||||
|
15.000000 13.210000 13.210000 15.000000 11.000000 15.000000 c
|
||||||
|
h
|
||||||
|
19.940001 12.000000 m
|
||||||
|
19.480001 16.170000 16.170000 19.480000 12.000000 19.940001 c
|
||||||
|
12.000000 21.000000 l
|
||||||
|
12.000000 21.549999 11.550000 22.000000 11.000000 22.000000 c
|
||||||
|
10.450000 22.000000 10.000000 21.549999 10.000000 21.000000 c
|
||||||
|
10.000000 19.940001 l
|
||||||
|
5.830000 19.480000 2.520000 16.170000 2.060000 12.000000 c
|
||||||
|
1.000000 12.000000 l
|
||||||
|
0.450000 12.000000 0.000000 11.550000 0.000000 11.000000 c
|
||||||
|
0.000000 10.450000 0.450000 10.000000 1.000000 10.000000 c
|
||||||
|
2.060000 10.000000 l
|
||||||
|
2.520000 5.830000 5.830000 2.519999 10.000000 2.059999 c
|
||||||
|
10.000000 1.000000 l
|
||||||
|
10.000000 0.450001 10.450000 0.000000 11.000000 0.000000 c
|
||||||
|
11.550000 0.000000 12.000000 0.450001 12.000000 1.000000 c
|
||||||
|
12.000000 2.059999 l
|
||||||
|
16.170000 2.519999 19.480001 5.830000 19.940001 10.000000 c
|
||||||
|
21.000000 10.000000 l
|
||||||
|
21.549999 10.000000 22.000000 10.450000 22.000000 11.000000 c
|
||||||
|
22.000000 11.550000 21.549999 12.000000 21.000000 12.000000 c
|
||||||
|
19.940001 12.000000 l
|
||||||
|
19.940001 12.000000 l
|
||||||
|
h
|
||||||
|
11.000000 4.000000 m
|
||||||
|
7.130000 4.000000 4.000000 7.130000 4.000000 11.000000 c
|
||||||
|
4.000000 14.870000 7.130000 18.000000 11.000000 18.000000 c
|
||||||
|
14.870000 18.000000 18.000000 14.870000 18.000000 11.000000 c
|
||||||
|
18.000000 7.130000 14.870000 4.000000 11.000000 4.000000 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
2028
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 4 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.000000 34.000000 m
|
||||||
|
0.000000 37.313709 2.686292 40.000000 6.000000 40.000000 c
|
||||||
|
34.000000 40.000000 l
|
||||||
|
37.313709 40.000000 40.000000 37.313709 40.000000 34.000000 c
|
||||||
|
40.000000 6.000000 l
|
||||||
|
40.000000 2.686291 37.313709 0.000000 34.000000 0.000000 c
|
||||||
|
5.999999 0.000000 l
|
||||||
|
2.686291 0.000000 0.000000 2.686291 0.000000 6.000000 c
|
||||||
|
0.000000 34.000000 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
468
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /XObject << /X1 1 0 R >>
|
||||||
|
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||||
|
/G 3 0 R
|
||||||
|
/S /Alpha
|
||||||
|
>>
|
||||||
|
/Type /ExtGState
|
||||||
|
>> >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Length 7 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
/E1 gs
|
||||||
|
/X1 Do
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
46
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
/Resources 5 0 R
|
||||||
|
/Contents 6 0 R
|
||||||
|
/Parent 9 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
<< /Kids [ 8 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Pages 9 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 11
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000002286 00000 n
|
||||||
|
0000002309 00000 n
|
||||||
|
0000003025 00000 n
|
||||||
|
0000003047 00000 n
|
||||||
|
0000003345 00000 n
|
||||||
|
0000003447 00000 n
|
||||||
|
0000003468 00000 n
|
||||||
|
0000003641 00000 n
|
||||||
|
0000003715 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 10 0 R
|
||||||
|
/Size 11
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3775
|
||||||
|
%%EOF
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "location-pointer.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filename" : "location-pointer-dark.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+218
@@ -0,0 +1,218 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 2 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.062745 0.074510 0.090196 scn
|
||||||
|
0.000000 34.000000 m
|
||||||
|
0.000000 37.313709 2.686292 40.000000 6.000000 40.000000 c
|
||||||
|
34.000000 40.000000 l
|
||||||
|
37.313709 40.000000 40.000000 37.313709 40.000000 34.000000 c
|
||||||
|
40.000000 6.000000 l
|
||||||
|
40.000000 2.686291 37.313709 0.000000 34.000000 0.000000 c
|
||||||
|
5.999999 0.000000 l
|
||||||
|
2.686291 0.000000 0.000000 2.686291 0.000000 6.000000 c
|
||||||
|
0.000000 34.000000 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 9.049805 9.049805 cm
|
||||||
|
0.921569 0.933333 0.949020 scn
|
||||||
|
10.950000 0.000391 m
|
||||||
|
10.666667 0.000391 10.429167 0.096224 10.237500 0.287889 c
|
||||||
|
10.045834 0.479557 9.950000 0.717056 9.950000 1.000391 c
|
||||||
|
9.950000 2.000391 l
|
||||||
|
7.866667 2.233725 6.079167 3.096224 4.587500 4.587891 c
|
||||||
|
3.095833 6.079557 2.233333 7.867057 2.000000 9.950391 c
|
||||||
|
1.000000 9.950391 l
|
||||||
|
0.716667 9.950391 0.479167 10.046224 0.287500 10.237890 c
|
||||||
|
0.095833 10.429557 0.000000 10.667057 0.000000 10.950391 c
|
||||||
|
0.000000 11.233724 0.095833 11.471224 0.287500 11.662890 c
|
||||||
|
0.479167 11.854557 0.716667 11.950391 1.000000 11.950391 c
|
||||||
|
2.000000 11.950391 l
|
||||||
|
2.233333 14.033724 3.095833 15.821224 4.587500 17.312891 c
|
||||||
|
6.079167 18.804558 7.866667 19.667057 9.950000 19.900391 c
|
||||||
|
9.950000 20.900391 l
|
||||||
|
9.950000 21.183723 10.045834 21.421225 10.237500 21.612890 c
|
||||||
|
10.429167 21.804558 10.666667 21.900391 10.950000 21.900391 c
|
||||||
|
11.233334 21.900391 11.470834 21.804558 11.662500 21.612890 c
|
||||||
|
11.854167 21.421225 11.950000 21.183723 11.950000 20.900391 c
|
||||||
|
11.950000 19.900391 l
|
||||||
|
14.033334 19.667057 15.820833 18.804558 17.312500 17.312891 c
|
||||||
|
18.804167 15.821224 19.666666 14.033724 19.900000 11.950391 c
|
||||||
|
20.900000 11.950391 l
|
||||||
|
21.183334 11.950391 21.420834 11.854557 21.612501 11.662890 c
|
||||||
|
21.804167 11.471224 21.900000 11.233724 21.900000 10.950391 c
|
||||||
|
21.900000 10.667057 21.804167 10.429557 21.612501 10.237890 c
|
||||||
|
21.420834 10.046224 21.183334 9.950391 20.900000 9.950391 c
|
||||||
|
19.900000 9.950391 l
|
||||||
|
19.666666 7.867057 18.804167 6.079557 17.312500 4.587891 c
|
||||||
|
15.820833 3.096224 14.033334 2.233725 11.950000 2.000391 c
|
||||||
|
11.950000 1.000391 l
|
||||||
|
11.950000 0.717056 11.854167 0.479557 11.662500 0.287889 c
|
||||||
|
11.470834 0.096224 11.233334 0.000391 10.950000 0.000391 c
|
||||||
|
h
|
||||||
|
10.950000 3.950390 m
|
||||||
|
12.883333 3.950390 14.533334 4.633724 15.900001 6.000390 c
|
||||||
|
17.266666 7.367057 17.950001 9.017057 17.950001 10.950391 c
|
||||||
|
17.950001 12.883724 17.266666 14.533724 15.900001 15.900391 c
|
||||||
|
14.533334 17.267057 12.883333 17.950390 10.950000 17.950390 c
|
||||||
|
9.016666 17.950390 7.366667 17.267057 6.000000 15.900391 c
|
||||||
|
4.633333 14.533724 3.950000 12.883724 3.950000 10.950391 c
|
||||||
|
3.950000 9.017057 4.633333 7.367057 6.000000 6.000390 c
|
||||||
|
7.366667 4.633724 9.016666 3.950390 10.950000 3.950390 c
|
||||||
|
h
|
||||||
|
10.950000 6.950391 m
|
||||||
|
9.850000 6.950391 8.908334 7.342057 8.125000 8.125390 c
|
||||||
|
7.341667 8.908724 6.950000 9.850390 6.950000 10.950391 c
|
||||||
|
6.950000 12.050390 7.341667 12.992057 8.125000 13.775391 c
|
||||||
|
8.908334 14.558723 9.850000 14.950390 10.950000 14.950390 c
|
||||||
|
12.050000 14.950390 12.991667 14.558723 13.775001 13.775391 c
|
||||||
|
14.558333 12.992057 14.950000 12.050390 14.950000 10.950391 c
|
||||||
|
14.950000 9.850390 14.558333 8.908724 13.775001 8.125390 c
|
||||||
|
12.991667 7.342057 12.050000 6.950391 10.950000 6.950391 c
|
||||||
|
h
|
||||||
|
10.950000 8.950391 m
|
||||||
|
11.500000 8.950391 11.970834 9.146224 12.362500 9.537890 c
|
||||||
|
12.754167 9.929557 12.950000 10.400391 12.950000 10.950391 c
|
||||||
|
12.950000 11.500390 12.754167 11.971224 12.362500 12.362890 c
|
||||||
|
11.970834 12.754558 11.500000 12.950391 10.950000 12.950391 c
|
||||||
|
10.400001 12.950391 9.929167 12.754558 9.537500 12.362890 c
|
||||||
|
9.145833 11.971224 8.950000 11.500390 8.950000 10.950391 c
|
||||||
|
8.950000 10.400391 9.145833 9.929557 9.537500 9.537890 c
|
||||||
|
9.929167 9.146224 10.400001 8.950391 10.950000 8.950391 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
3685
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 4 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.000000 34.000000 m
|
||||||
|
0.000000 37.313709 2.686292 40.000000 6.000000 40.000000 c
|
||||||
|
34.000000 40.000000 l
|
||||||
|
37.313709 40.000000 40.000000 37.313709 40.000000 34.000000 c
|
||||||
|
40.000000 6.000000 l
|
||||||
|
40.000000 2.686291 37.313709 0.000000 34.000000 0.000000 c
|
||||||
|
5.999999 0.000000 l
|
||||||
|
2.686291 0.000000 0.000000 2.686291 0.000000 6.000000 c
|
||||||
|
0.000000 34.000000 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
468
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /XObject << /X1 1 0 R >>
|
||||||
|
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||||
|
/G 3 0 R
|
||||||
|
/S /Alpha
|
||||||
|
>>
|
||||||
|
/Type /ExtGState
|
||||||
|
>> >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Length 7 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
/E1 gs
|
||||||
|
/X1 Do
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
46
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
/Resources 5 0 R
|
||||||
|
/Contents 6 0 R
|
||||||
|
/Parent 9 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
<< /Kids [ 8 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Pages 9 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 11
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000003943 00000 n
|
||||||
|
0000003966 00000 n
|
||||||
|
0000004682 00000 n
|
||||||
|
0000004704 00000 n
|
||||||
|
0000005002 00000 n
|
||||||
|
0000005104 00000 n
|
||||||
|
0000005125 00000 n
|
||||||
|
0000005298 00000 n
|
||||||
|
0000005372 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 10 0 R
|
||||||
|
/Size 11
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
5432
|
||||||
|
%%EOF
|
||||||
Vendored
+218
@@ -0,0 +1,218 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 2 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
0.000000 34.000000 m
|
||||||
|
0.000000 37.313709 2.686292 40.000000 6.000000 40.000000 c
|
||||||
|
34.000000 40.000000 l
|
||||||
|
37.313709 40.000000 40.000000 37.313709 40.000000 34.000000 c
|
||||||
|
40.000000 6.000000 l
|
||||||
|
40.000000 2.686291 37.313709 0.000000 34.000000 0.000000 c
|
||||||
|
5.999999 0.000000 l
|
||||||
|
2.686291 0.000000 0.000000 2.686291 0.000000 6.000000 c
|
||||||
|
0.000000 34.000000 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 9.049805 9.049927 cm
|
||||||
|
0.105882 0.113725 0.133333 scn
|
||||||
|
10.950000 0.000025 m
|
||||||
|
10.666666 0.000025 10.429167 0.095856 10.237500 0.287523 c
|
||||||
|
10.045834 0.479191 9.950000 0.716692 9.950000 1.000025 c
|
||||||
|
9.950000 2.000025 l
|
||||||
|
7.866666 2.233358 6.079167 3.095858 4.587500 4.587524 c
|
||||||
|
3.095833 6.079191 2.233333 7.866692 2.000000 9.950025 c
|
||||||
|
1.000000 9.950025 l
|
||||||
|
0.716667 9.950025 0.479167 10.045857 0.287500 10.237524 c
|
||||||
|
0.095833 10.429191 0.000000 10.666691 0.000000 10.950025 c
|
||||||
|
0.000000 11.233358 0.095833 11.470858 0.287500 11.662524 c
|
||||||
|
0.479167 11.854191 0.716667 11.950025 1.000000 11.950025 c
|
||||||
|
2.000000 11.950025 l
|
||||||
|
2.233333 14.033358 3.095833 15.820858 4.587500 17.312525 c
|
||||||
|
6.079167 18.804192 7.866666 19.666691 9.950000 19.900024 c
|
||||||
|
9.950000 20.900024 l
|
||||||
|
9.950000 21.183357 10.045834 21.420858 10.237500 21.612524 c
|
||||||
|
10.429167 21.804192 10.666666 21.900024 10.950000 21.900024 c
|
||||||
|
11.233334 21.900024 11.470834 21.804192 11.662500 21.612524 c
|
||||||
|
11.854167 21.420858 11.950000 21.183357 11.950000 20.900024 c
|
||||||
|
11.950000 19.900024 l
|
||||||
|
14.033333 19.666691 15.820833 18.804192 17.312500 17.312525 c
|
||||||
|
18.804167 15.820858 19.666666 14.033358 19.900000 11.950025 c
|
||||||
|
20.900000 11.950025 l
|
||||||
|
21.183332 11.950025 21.420834 11.854191 21.612501 11.662524 c
|
||||||
|
21.804169 11.470858 21.900000 11.233358 21.900000 10.950025 c
|
||||||
|
21.900000 10.666691 21.804169 10.429191 21.612501 10.237524 c
|
||||||
|
21.420834 10.045857 21.183332 9.950025 20.900000 9.950025 c
|
||||||
|
19.900000 9.950025 l
|
||||||
|
19.666666 7.866692 18.804167 6.079191 17.312500 4.587524 c
|
||||||
|
15.820833 3.095858 14.033333 2.233358 11.950000 2.000025 c
|
||||||
|
11.950000 1.000025 l
|
||||||
|
11.950000 0.716692 11.854167 0.479191 11.662500 0.287523 c
|
||||||
|
11.470834 0.095856 11.233334 0.000025 10.950000 0.000025 c
|
||||||
|
h
|
||||||
|
10.950000 3.950024 m
|
||||||
|
12.883333 3.950024 14.533334 4.633358 15.900001 6.000024 c
|
||||||
|
17.266666 7.366691 17.950001 9.016691 17.950001 10.950025 c
|
||||||
|
17.950001 12.883358 17.266666 14.533358 15.900001 15.900024 c
|
||||||
|
14.533334 17.266691 12.883333 17.950024 10.950000 17.950024 c
|
||||||
|
9.016666 17.950024 7.366667 17.266691 6.000000 15.900024 c
|
||||||
|
4.633333 14.533358 3.950000 12.883358 3.950000 10.950025 c
|
||||||
|
3.950000 9.016691 4.633333 7.366691 6.000000 6.000024 c
|
||||||
|
7.366667 4.633358 9.016666 3.950024 10.950000 3.950024 c
|
||||||
|
h
|
||||||
|
10.950000 6.950025 m
|
||||||
|
9.849999 6.950025 8.908334 7.341690 8.125000 8.125024 c
|
||||||
|
7.341667 8.908358 6.950000 9.850024 6.950000 10.950025 c
|
||||||
|
6.950000 12.050025 7.341667 12.991691 8.125000 13.775024 c
|
||||||
|
8.908334 14.558357 9.849999 14.950024 10.950000 14.950024 c
|
||||||
|
12.050000 14.950024 12.991667 14.558357 13.775001 13.775024 c
|
||||||
|
14.558334 12.991691 14.950000 12.050025 14.950000 10.950025 c
|
||||||
|
14.950000 9.850024 14.558334 8.908358 13.775001 8.125024 c
|
||||||
|
12.991667 7.341690 12.050000 6.950025 10.950000 6.950025 c
|
||||||
|
h
|
||||||
|
10.950000 8.950025 m
|
||||||
|
11.500000 8.950025 11.970834 9.145858 12.362500 9.537524 c
|
||||||
|
12.754167 9.929191 12.950000 10.400024 12.950000 10.950025 c
|
||||||
|
12.950000 11.500025 12.754167 11.970858 12.362500 12.362524 c
|
||||||
|
11.970834 12.754190 11.500000 12.950025 10.950000 12.950025 c
|
||||||
|
10.400000 12.950025 9.929167 12.754190 9.537500 12.362524 c
|
||||||
|
9.145834 11.970858 8.950000 11.500025 8.950000 10.950025 c
|
||||||
|
8.950000 10.400024 9.145834 9.929191 9.537500 9.537524 c
|
||||||
|
9.929167 9.145858 10.400000 8.950025 10.950000 8.950025 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
3685
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<< /Type /XObject
|
||||||
|
/Length 4 0 R
|
||||||
|
/Group << /Type /Group
|
||||||
|
/S /Transparency
|
||||||
|
>>
|
||||||
|
/Subtype /Form
|
||||||
|
/Resources << >>
|
||||||
|
/BBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.000000 0.000000 0.000000 scn
|
||||||
|
0.000000 34.000000 m
|
||||||
|
0.000000 37.313709 2.686292 40.000000 6.000000 40.000000 c
|
||||||
|
34.000000 40.000000 l
|
||||||
|
37.313709 40.000000 40.000000 37.313709 40.000000 34.000000 c
|
||||||
|
40.000000 6.000000 l
|
||||||
|
40.000000 2.686291 37.313709 0.000000 34.000000 0.000000 c
|
||||||
|
5.999999 0.000000 l
|
||||||
|
2.686291 0.000000 0.000000 2.686291 0.000000 6.000000 c
|
||||||
|
0.000000 34.000000 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
468
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /XObject << /X1 1 0 R >>
|
||||||
|
/ExtGState << /E1 << /SMask << /Type /Mask
|
||||||
|
/G 3 0 R
|
||||||
|
/S /Alpha
|
||||||
|
>>
|
||||||
|
/Type /ExtGState
|
||||||
|
>> >>
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Length 7 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
/E1 gs
|
||||||
|
/X1 Do
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
46
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 40.000000 40.000000 ]
|
||||||
|
/Resources 5 0 R
|
||||||
|
/Contents 6 0 R
|
||||||
|
/Parent 9 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
<< /Kids [ 8 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<< /Pages 9 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 11
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000003943 00000 n
|
||||||
|
0000003966 00000 n
|
||||||
|
0000004682 00000 n
|
||||||
|
0000004704 00000 n
|
||||||
|
0000005002 00000 n
|
||||||
|
0000005104 00000 n
|
||||||
|
0000005125 00000 n
|
||||||
|
0000005298 00000 n
|
||||||
|
0000005372 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 10 0 R
|
||||||
|
/Size 11
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
5432
|
||||||
|
%%EOF
|
||||||
@@ -139,10 +139,10 @@
|
|||||||
"emoji_picker_category_places" = "Travel & Places";
|
"emoji_picker_category_places" = "Travel & Places";
|
||||||
"emoji_picker_category_symbols" = "Symbols";
|
"emoji_picker_category_symbols" = "Symbols";
|
||||||
"error_failed_creating_the_permalink" = "Failed creating the permalink";
|
"error_failed_creating_the_permalink" = "Failed creating the permalink";
|
||||||
"error_failed_loading_map" = "Element could not load the map. Please try again later.";
|
"error_failed_loading_map" = "%1$@ could not load the map. Please try again later.";
|
||||||
"error_failed_loading_messages" = "Failed loading messages";
|
"error_failed_loading_messages" = "Failed loading messages";
|
||||||
"error_failed_locating_user" = "Element could not access your location. Please try again later.";
|
"error_failed_locating_user" = "%1$@ could not access your location. Please try again later.";
|
||||||
"error_missing_location_auth" = "Element does not have permission to access your location. You can enable access in Settings > Location";
|
"error_missing_location_auth" = "%1$@ does not have permission to access your location. You can enable access in Settings > Location";
|
||||||
"error_no_compatible_app_found" = "No compatible app was found to handle this action.";
|
"error_no_compatible_app_found" = "No compatible app was found to handle this action.";
|
||||||
"error_some_messages_have_not_been_sent" = "Some messages have not been sent";
|
"error_some_messages_have_not_been_sent" = "Some messages have not been sent";
|
||||||
"error_unknown" = "Sorry, an error occurred";
|
"error_unknown" = "Sorry, an error occurred";
|
||||||
@@ -308,6 +308,8 @@
|
|||||||
"screen_room_reactions_show_more" = "Show more";
|
"screen_room_reactions_show_more" = "Show more";
|
||||||
"screen_room_retry_send_menu_send_again_action" = "Send again";
|
"screen_room_retry_send_menu_send_again_action" = "Send again";
|
||||||
"screen_room_retry_send_menu_title" = "Your message failed to send";
|
"screen_room_retry_send_menu_title" = "Your message failed to send";
|
||||||
|
"screen_room_timeline_add_reaction" = "Add emoji";
|
||||||
|
"screen_room_timeline_less_reactions" = "Show less";
|
||||||
"screen_roomlist_a11y_create_message" = "Create a new conversation or room";
|
"screen_roomlist_a11y_create_message" = "Create a new conversation or room";
|
||||||
"screen_roomlist_main_space_title" = "All Chats";
|
"screen_roomlist_main_space_title" = "All Chats";
|
||||||
"screen_server_confirmation_change_server" = "Change account provider";
|
"screen_server_confirmation_change_server" = "Change account provider";
|
||||||
|
|||||||
@@ -146,5 +146,19 @@
|
|||||||
<string>%1$d people</string>
|
<string>%1$d people</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>screen_room_timeline_more_reactions</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@COUNT@</string>
|
||||||
|
<key>COUNT</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>d</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>%1$d more</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -515,7 +515,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
_ = await self.roomProxy?.sendLocation(body: geoURI.bodyMessage,
|
_ = await self.roomProxy?.sendLocation(body: geoURI.bodyMessage,
|
||||||
geoURI: geoURI,
|
geoURI: geoURI,
|
||||||
description: nil,
|
description: nil,
|
||||||
zoomLevel: nil,
|
zoomLevel: 15,
|
||||||
assetType: isUserLocation ? .sender : .pin)
|
assetType: isUserLocation ? .sender : .pin)
|
||||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ internal enum Asset {
|
|||||||
internal static let launchLogo = ImageAsset(name: "images/launch-logo")
|
internal static let launchLogo = ImageAsset(name: "images/launch-logo")
|
||||||
internal static let locationMarker = ImageAsset(name: "images/location-marker")
|
internal static let locationMarker = ImageAsset(name: "images/location-marker")
|
||||||
internal static let locationPin = ImageAsset(name: "images/location-pin")
|
internal static let locationPin = ImageAsset(name: "images/location-pin")
|
||||||
|
internal static let locationPointerFull = ImageAsset(name: "images/location-pointer-full")
|
||||||
|
internal static let locationPointer = ImageAsset(name: "images/location-pointer")
|
||||||
internal static let timelineComposerSendMessage = ImageAsset(name: "images/timeline-composer-send-message")
|
internal static let timelineComposerSendMessage = ImageAsset(name: "images/timeline-composer-send-message")
|
||||||
internal static let waitingGradient = ImageAsset(name: "images/waiting-gradient")
|
internal static let waitingGradient = ImageAsset(name: "images/waiting-gradient")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,14 +308,20 @@ public enum L10n {
|
|||||||
public static var emojiPickerCategorySymbols: String { return L10n.tr("Localizable", "emoji_picker_category_symbols") }
|
public static var emojiPickerCategorySymbols: String { return L10n.tr("Localizable", "emoji_picker_category_symbols") }
|
||||||
/// Failed creating the permalink
|
/// Failed creating the permalink
|
||||||
public static var errorFailedCreatingThePermalink: String { return L10n.tr("Localizable", "error_failed_creating_the_permalink") }
|
public static var errorFailedCreatingThePermalink: String { return L10n.tr("Localizable", "error_failed_creating_the_permalink") }
|
||||||
/// Element could not load the map. Please try again later.
|
/// %1$@ could not load the map. Please try again later.
|
||||||
public static var errorFailedLoadingMap: String { return L10n.tr("Localizable", "error_failed_loading_map") }
|
public static func errorFailedLoadingMap(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "error_failed_loading_map", String(describing: p1))
|
||||||
|
}
|
||||||
/// Failed loading messages
|
/// Failed loading messages
|
||||||
public static var errorFailedLoadingMessages: String { return L10n.tr("Localizable", "error_failed_loading_messages") }
|
public static var errorFailedLoadingMessages: String { return L10n.tr("Localizable", "error_failed_loading_messages") }
|
||||||
/// Element could not access your location. Please try again later.
|
/// %1$@ could not access your location. Please try again later.
|
||||||
public static var errorFailedLocatingUser: String { return L10n.tr("Localizable", "error_failed_locating_user") }
|
public static func errorFailedLocatingUser(_ p1: Any) -> String {
|
||||||
/// Element does not have permission to access your location. You can enable access in Settings > Location
|
return L10n.tr("Localizable", "error_failed_locating_user", String(describing: p1))
|
||||||
public static var errorMissingLocationAuth: String { return L10n.tr("Localizable", "error_missing_location_auth") }
|
}
|
||||||
|
/// %1$@ does not have permission to access your location. You can enable access in Settings > Location
|
||||||
|
public static func errorMissingLocationAuth(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "error_missing_location_auth", String(describing: p1))
|
||||||
|
}
|
||||||
/// No compatible app was found to handle this action.
|
/// No compatible app was found to handle this action.
|
||||||
public static var errorNoCompatibleAppFound: String { return L10n.tr("Localizable", "error_no_compatible_app_found") }
|
public static var errorNoCompatibleAppFound: String { return L10n.tr("Localizable", "error_no_compatible_app_found") }
|
||||||
/// Some messages have not been sent
|
/// Some messages have not been sent
|
||||||
@@ -780,6 +786,14 @@ public enum L10n {
|
|||||||
public static var screenRoomRetrySendMenuSendAgainAction: String { return L10n.tr("Localizable", "screen_room_retry_send_menu_send_again_action") }
|
public static var screenRoomRetrySendMenuSendAgainAction: String { return L10n.tr("Localizable", "screen_room_retry_send_menu_send_again_action") }
|
||||||
/// Your message failed to send
|
/// Your message failed to send
|
||||||
public static var screenRoomRetrySendMenuTitle: String { return L10n.tr("Localizable", "screen_room_retry_send_menu_title") }
|
public static var screenRoomRetrySendMenuTitle: String { return L10n.tr("Localizable", "screen_room_retry_send_menu_title") }
|
||||||
|
/// Add emoji
|
||||||
|
public static var screenRoomTimelineAddReaction: String { return L10n.tr("Localizable", "screen_room_timeline_add_reaction") }
|
||||||
|
/// Show less
|
||||||
|
public static var screenRoomTimelineLessReactions: String { return L10n.tr("Localizable", "screen_room_timeline_less_reactions") }
|
||||||
|
/// Plural format key: "%#@COUNT@"
|
||||||
|
public static func screenRoomTimelineMoreReactions(_ p1: Int) -> String {
|
||||||
|
return L10n.tr("Localizable", "screen_room_timeline_more_reactions", p1)
|
||||||
|
}
|
||||||
/// Create a new conversation or room
|
/// Create a new conversation or room
|
||||||
public static var screenRoomlistA11yCreateMessage: String { return L10n.tr("Localizable", "screen_roomlist_a11y_create_message") }
|
public static var screenRoomlistA11yCreateMessage: String { return L10n.tr("Localizable", "screen_roomlist_a11y_create_message") }
|
||||||
/// All Chats
|
/// All Chats
|
||||||
|
|||||||
@@ -20,15 +20,20 @@ import SwiftUI
|
|||||||
|
|
||||||
struct MapLibreMapView: UIViewRepresentable {
|
struct MapLibreMapView: UIViewRepresentable {
|
||||||
struct Options {
|
struct Options {
|
||||||
/// The initial zoom level
|
/// the final zoom level used when the first user location emit
|
||||||
let zoomLevel: Double
|
let zoomLevel: Double
|
||||||
|
/// The initial zoom level used when the map it firstly loaded and the user location is not yet available, in case of annotations this property is not being used
|
||||||
|
let initialZoomLevel: Double
|
||||||
|
|
||||||
/// The initial map center
|
/// The initial map center
|
||||||
let mapCenter: CLLocationCoordinate2D?
|
let mapCenter: CLLocationCoordinate2D
|
||||||
|
|
||||||
/// Map annotations
|
/// Map annotations
|
||||||
let annotations: [LocationAnnotation]
|
let annotations: [LocationAnnotation]
|
||||||
|
|
||||||
init(zoomLevel: Double, mapCenter: CLLocationCoordinate2D? = nil, annotations: [LocationAnnotation] = []) {
|
init(zoomLevel: Double, initialZoomLevel: Double, mapCenter: CLLocationCoordinate2D, annotations: [LocationAnnotation] = []) {
|
||||||
self.zoomLevel = zoomLevel
|
self.zoomLevel = zoomLevel
|
||||||
|
self.initialZoomLevel = initialZoomLevel
|
||||||
self.mapCenter = mapCenter
|
self.mapCenter = mapCenter
|
||||||
self.annotations = annotations
|
self.annotations = annotations
|
||||||
}
|
}
|
||||||
@@ -43,14 +48,16 @@ struct MapLibreMapView: UIViewRepresentable {
|
|||||||
let options: Options
|
let options: Options
|
||||||
|
|
||||||
/// Behavior mode of the current user's location, can be hidden, only shown and shown following the user
|
/// Behavior mode of the current user's location, can be hidden, only shown and shown following the user
|
||||||
var showsUserLocationMode: ShowUserLocationMode = .hide
|
@Binding var showsUserLocationMode: ShowUserLocationMode
|
||||||
|
|
||||||
/// Bind view errors if any
|
/// Bind view errors if any
|
||||||
let error: Binding<MapLibreError?>
|
@Binding var error: MapLibreError?
|
||||||
|
|
||||||
/// Coordinate of the center of the map
|
/// Coordinate of the center of the map
|
||||||
@Binding var mapCenterCoordinate: CLLocationCoordinate2D?
|
@Binding var mapCenterCoordinate: CLLocationCoordinate2D?
|
||||||
|
|
||||||
|
@Binding var isLocationAuthorized: Bool?
|
||||||
|
|
||||||
/// Called when the user pan on the map
|
/// Called when the user pan on the map
|
||||||
var userDidPan: (() -> Void)?
|
var userDidPan: (() -> Void)?
|
||||||
|
|
||||||
@@ -59,23 +66,12 @@ struct MapLibreMapView: UIViewRepresentable {
|
|||||||
func makeUIView(context: Context) -> MGLMapView {
|
func makeUIView(context: Context) -> MGLMapView {
|
||||||
let mapView = makeMapView()
|
let mapView = makeMapView()
|
||||||
mapView.delegate = context.coordinator
|
mapView.delegate = context.coordinator
|
||||||
let panGesture = UIPanGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.didPan))
|
|
||||||
panGesture.delegate = context.coordinator
|
|
||||||
mapView.addGestureRecognizer(panGesture)
|
|
||||||
setupMap(mapView: mapView, with: options)
|
setupMap(mapView: mapView, with: options)
|
||||||
return mapView
|
return mapView
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIView(_ mapView: MGLMapView, context: Context) {
|
func updateUIView(_ mapView: MGLMapView, context: Context) {
|
||||||
mapView.removeAllAnnotations()
|
mapView.styleURL = builder.dynamicMapURL(for: .init(colorScheme))
|
||||||
mapView.addAnnotations(options.annotations)
|
|
||||||
|
|
||||||
if colorScheme == .dark {
|
|
||||||
mapView.styleURL = builder.dynamicMapURL(for: .dark)
|
|
||||||
} else {
|
|
||||||
mapView.styleURL = builder.dynamicMapURL(for: .light)
|
|
||||||
}
|
|
||||||
|
|
||||||
showUserLocation(in: mapView)
|
showUserLocation(in: mapView)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,32 +82,36 @@ struct MapLibreMapView: UIViewRepresentable {
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func setupMap(mapView: MGLMapView, with options: Options) {
|
private func setupMap(mapView: MGLMapView, with options: Options) {
|
||||||
mapView.zoomLevel = options.zoomLevel
|
mapView.addAnnotations(options.annotations)
|
||||||
if let mapCenter = options.mapCenter {
|
mapView.zoomLevel = options.annotations.isEmpty ? options.initialZoomLevel : options.zoomLevel
|
||||||
mapView.centerCoordinate = mapCenter
|
mapView.centerCoordinate = options.mapCenter
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeMapView() -> MGLMapView {
|
private func makeMapView() -> MGLMapView {
|
||||||
let mapView = MGLMapView(frame: .zero, styleURL: colorScheme == .dark ? builder.dynamicMapURL(for: .dark) : builder.dynamicMapURL(for: .light))
|
let mapView = MGLMapView(frame: .zero, styleURL: colorScheme == .dark ? builder.dynamicMapURL(for: .dark) : builder.dynamicMapURL(for: .light))
|
||||||
|
mapView.logoViewPosition = .topLeft
|
||||||
showUserLocation(in: mapView)
|
mapView.attributionButtonPosition = .topLeft
|
||||||
mapView.attributionButton.isHidden = true
|
mapView.attributionButtonMargins = .init(x: mapView.logoView.frame.maxX + 8, y: mapView.logoView.center.y / 2)
|
||||||
|
|
||||||
return mapView
|
return mapView
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showUserLocation(in mapView: MGLMapView) {
|
private func showUserLocation(in mapView: MGLMapView) {
|
||||||
switch showsUserLocationMode {
|
switch (showsUserLocationMode, options.annotations) {
|
||||||
case .showAndFollow:
|
case (.showAndFollow, _):
|
||||||
mapView.showsUserLocation = true
|
|
||||||
mapView.userTrackingMode = .follow
|
mapView.userTrackingMode = .follow
|
||||||
case .show:
|
case (.show, let annotations) where !annotations.isEmpty:
|
||||||
|
/** in the show mode, if there are annotations, we check the authorizationStatus,
|
||||||
|
if it's not determined, we wont prompt the user with a request for permissions,
|
||||||
|
because he should be able to see the annotations without sharing his location informations
|
||||||
|
**/
|
||||||
|
guard mapView.locationManager.authorizationStatus != .notDetermined else { return }
|
||||||
|
fallthrough
|
||||||
|
case (.show, _):
|
||||||
mapView.showsUserLocation = true
|
mapView.showsUserLocation = true
|
||||||
mapView.userTrackingMode = .none
|
mapView.setUserTrackingMode(.none, animated: false, completionHandler: nil)
|
||||||
case .hide:
|
case (.hide, _):
|
||||||
mapView.showsUserLocation = false
|
mapView.showsUserLocation = false
|
||||||
mapView.userTrackingMode = .none
|
mapView.setUserTrackingMode(.none, animated: false, completionHandler: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,11 +119,13 @@ struct MapLibreMapView: UIViewRepresentable {
|
|||||||
// MARK: - Coordinator
|
// MARK: - Coordinator
|
||||||
|
|
||||||
extension MapLibreMapView {
|
extension MapLibreMapView {
|
||||||
class Coordinator: NSObject, MGLMapViewDelegate, UIGestureRecognizerDelegate {
|
class Coordinator: NSObject, MGLMapViewDelegate {
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
|
|
||||||
var mapLibreView: MapLibreMapView
|
var mapLibreView: MapLibreMapView
|
||||||
|
|
||||||
|
private var previousUserLocation: MGLUserLocation?
|
||||||
|
|
||||||
// MARK: - Setup
|
// MARK: - Setup
|
||||||
|
|
||||||
init(_ mapLibreView: MapLibreMapView) {
|
init(_ mapLibreView: MapLibreMapView) {
|
||||||
@@ -140,20 +142,30 @@ extension MapLibreMapView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mapViewDidFailLoadingMap(_ mapView: MGLMapView, withError error: Error) {
|
func mapViewDidFailLoadingMap(_ mapView: MGLMapView, withError error: Error) {
|
||||||
mapLibreView.error.wrappedValue = .failedLoadingMap
|
mapLibreView.error = .failedLoadingMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapView(_ mapView: MGLMapView, didUpdate userLocation: MGLUserLocation?) { }
|
func mapView(_ mapView: MGLMapView, didUpdate userLocation: MGLUserLocation?) {
|
||||||
|
guard let userLocation else { return }
|
||||||
|
|
||||||
|
if previousUserLocation == nil, mapLibreView.options.annotations.isEmpty {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
|
mapView.setCenter(userLocation.coordinate, zoomLevel: self.mapLibreView.options.zoomLevel, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousUserLocation = userLocation
|
||||||
|
}
|
||||||
|
|
||||||
func mapView(_ mapView: MGLMapView, didChangeLocationManagerAuthorization manager: MGLLocationManager) {
|
func mapView(_ mapView: MGLMapView, didChangeLocationManagerAuthorization manager: MGLLocationManager) {
|
||||||
guard mapView.showsUserLocation else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch manager.authorizationStatus {
|
switch manager.authorizationStatus {
|
||||||
case .denied, .restricted:
|
case .denied, .restricted:
|
||||||
mapLibreView.error.wrappedValue = .invalidLocationAuthorization
|
mapLibreView.isLocationAuthorized = false
|
||||||
default:
|
case .authorizedAlways, .authorizedWhenInUse:
|
||||||
|
mapLibreView.isLocationAuthorized = true
|
||||||
|
case .notDetermined:
|
||||||
|
mapLibreView.isLocationAuthorized = nil
|
||||||
|
@unknown default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,15 +183,25 @@ extension MapLibreMapView {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UIGestureRecognizer
|
func mapView(_ mapView: MGLMapView, shouldChangeFrom oldCamera: MGLMapCamera, to newCamera: MGLMapCamera, reason: MGLCameraChangeReason) -> Bool {
|
||||||
|
// we send the userDidPan event only for the reasons that actually will change the map center, and not zoom only / rotations only events.
|
||||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
switch reason {
|
||||||
gestureRecognizer is UIPanGestureRecognizer
|
case .gesturePan,
|
||||||
}
|
.gesturePinch,
|
||||||
|
.gestureRotate:
|
||||||
@objc
|
mapLibreView.userDidPan?()
|
||||||
func didPan() {
|
case .gestureOneFingerZoom,
|
||||||
mapLibreView.userDidPan?()
|
.gestureTilt,
|
||||||
|
.gestureZoomIn,
|
||||||
|
.gestureZoomOut,
|
||||||
|
.programmatic,
|
||||||
|
.resetNorth,
|
||||||
|
.transitionCancelled:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,3 +216,16 @@ private extension MGLMapView {
|
|||||||
removeAnnotations(annotations)
|
removeAnnotations(annotations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension MapTilerStyle {
|
||||||
|
init(_ colorScheme: ColorScheme) {
|
||||||
|
switch colorScheme {
|
||||||
|
case .light:
|
||||||
|
self = .light
|
||||||
|
case .dark:
|
||||||
|
self = .dark
|
||||||
|
@unknown default:
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ enum MapTilerStyle {
|
|||||||
enum MapLibreError: Error {
|
enum MapLibreError: Error {
|
||||||
case failedLoadingMap
|
case failedLoadingMap
|
||||||
case failedLocatingUser
|
case failedLocatingUser
|
||||||
case invalidLocationAuthorization
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MapTilerAttributionPlacement: String {
|
enum MapTilerAttributionPlacement: String {
|
||||||
|
|||||||
@@ -18,12 +18,13 @@ import CoreLocation
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum LocationSharingViewError: Error, Hashable {
|
enum LocationSharingViewError: Error, Hashable {
|
||||||
case failedSharingLocation
|
case missingAuthorization
|
||||||
case mapError(MapLibreError)
|
case mapError(MapLibreError)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum StaticLocationScreenViewModelAction {
|
enum StaticLocationScreenViewModelAction {
|
||||||
case close
|
case close
|
||||||
|
case openSystemSettings
|
||||||
case sendLocation(GeoURI, isUserLocation: Bool)
|
case sendLocation(GeoURI, isUserLocation: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,47 +34,36 @@ enum StaticLocationInteractionMode: Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct StaticLocationScreenViewState: BindableState {
|
struct StaticLocationScreenViewState: BindableState {
|
||||||
init(interactionMode: StaticLocationInteractionMode, isSharingUserLocation: Bool = false, showsUserLocationMode: ShowUserLocationMode = .hide) {
|
init(interactionMode: StaticLocationInteractionMode) {
|
||||||
self.interactionMode = interactionMode
|
self.interactionMode = interactionMode
|
||||||
self.isSharingUserLocation = isSharingUserLocation
|
|
||||||
self.showsUserLocationMode = showsUserLocationMode
|
|
||||||
|
|
||||||
switch interactionMode {
|
switch interactionMode {
|
||||||
case .picker:
|
case .picker:
|
||||||
bindings = .init()
|
bindings.showsUserLocationMode = .showAndFollow
|
||||||
case .viewOnly(let geoURI, _):
|
case .viewOnly:
|
||||||
bindings = .init(mapCenterLocation: .init(latitude: geoURI.latitude, longitude: geoURI.longitude))
|
bindings.showsUserLocationMode = .show
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let interactionMode: StaticLocationInteractionMode
|
let interactionMode: StaticLocationInteractionMode
|
||||||
/// Indicates whether the user is sharing his current location
|
/// Indicates whether the user is sharing his current location
|
||||||
var isSharingUserLocation: Bool
|
var isSharingUserLocation: Bool {
|
||||||
/// Behavior mode of the current user's location, can be hidden, only shown and shown following the user
|
bindings.isLocationAuthorized == true && bindings.showsUserLocationMode == .showAndFollow
|
||||||
var showsUserLocationMode: ShowUserLocationMode
|
|
||||||
|
|
||||||
var bindings = StaticLocationScreenBindings()
|
|
||||||
|
|
||||||
var showBottomToolbar: Bool {
|
|
||||||
interactionMode == .picker
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var mapAnnotationCoordinate: CLLocationCoordinate2D? {
|
var bindings = StaticLocationScreenBindings(showsUserLocationMode: .hide)
|
||||||
|
|
||||||
|
var initialMapCenter: CLLocationCoordinate2D {
|
||||||
switch interactionMode {
|
switch interactionMode {
|
||||||
case .picker:
|
case .picker:
|
||||||
return nil
|
// middle point in Europe, to be used if the users location is not yet known
|
||||||
|
return .init(latitude: 49.843, longitude: 9.902056)
|
||||||
case .viewOnly(let geoURI, _):
|
case .viewOnly(let geoURI, _):
|
||||||
return .init(latitude: geoURI.latitude, longitude: geoURI.longitude)
|
return .init(latitude: geoURI.latitude, longitude: geoURI.longitude)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var isLocationPickerMode: Bool {
|
var isLocationPickerMode: Bool {
|
||||||
switch interactionMode {
|
interactionMode == .picker
|
||||||
case .picker:
|
|
||||||
return true
|
|
||||||
case .viewOnly:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var navigationTitle: String {
|
var navigationTitle: String {
|
||||||
@@ -95,9 +85,13 @@ struct StaticLocationScreenViewState: BindableState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var zoomLevel: Double {
|
var zoomLevel: Double {
|
||||||
|
15.0
|
||||||
|
}
|
||||||
|
|
||||||
|
var initialZoomLevel: Double {
|
||||||
switch interactionMode {
|
switch interactionMode {
|
||||||
case .picker:
|
case .picker:
|
||||||
return 5.0
|
return 2.7
|
||||||
case .viewOnly:
|
case .viewOnly:
|
||||||
return 15.0
|
return 15.0
|
||||||
}
|
}
|
||||||
@@ -115,6 +109,10 @@ struct StaticLocationScreenViewState: BindableState {
|
|||||||
|
|
||||||
struct StaticLocationScreenBindings {
|
struct StaticLocationScreenBindings {
|
||||||
var mapCenterLocation: CLLocationCoordinate2D?
|
var mapCenterLocation: CLLocationCoordinate2D?
|
||||||
|
|
||||||
|
var showsUserLocationMode: ShowUserLocationMode
|
||||||
|
|
||||||
|
var isLocationAuthorized: Bool?
|
||||||
|
|
||||||
/// Information describing the currently displayed alert.
|
/// Information describing the currently displayed alert.
|
||||||
var mapError: MapLibreError? {
|
var mapError: MapLibreError? {
|
||||||
@@ -125,7 +123,7 @@ struct StaticLocationScreenBindings {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
alertInfo = newValue.map { AlertInfo(id: .mapError($0)) }
|
alertInfo = newValue.map { AlertInfo(locationSharingViewError: .mapError($0)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,5 +136,33 @@ struct StaticLocationScreenBindings {
|
|||||||
enum StaticLocationScreenViewAction {
|
enum StaticLocationScreenViewAction {
|
||||||
case close
|
case close
|
||||||
case selectLocation
|
case selectLocation
|
||||||
|
case centerToUser
|
||||||
case userDidPan
|
case userDidPan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension AlertInfo where T == LocationSharingViewError {
|
||||||
|
init(locationSharingViewError error: LocationSharingViewError,
|
||||||
|
primaryButton: AlertButton = AlertButton(title: L10n.actionOk, action: nil),
|
||||||
|
secondaryButton: AlertButton? = nil) {
|
||||||
|
switch error {
|
||||||
|
case .missingAuthorization:
|
||||||
|
self.init(id: error,
|
||||||
|
title: "",
|
||||||
|
message: L10n.errorMissingLocationAuth(InfoPlistReader.main.bundleDisplayName),
|
||||||
|
primaryButton: primaryButton,
|
||||||
|
secondaryButton: secondaryButton)
|
||||||
|
case .mapError(.failedLoadingMap):
|
||||||
|
self.init(id: error,
|
||||||
|
title: "",
|
||||||
|
message: L10n.errorFailedLoadingMap(InfoPlistReader.main.bundleDisplayName),
|
||||||
|
primaryButton: primaryButton,
|
||||||
|
secondaryButton: secondaryButton)
|
||||||
|
case .mapError(.failedLocatingUser):
|
||||||
|
self.init(id: error,
|
||||||
|
title: "",
|
||||||
|
message: L10n.errorFailedLocatingUser(InfoPlistReader.main.bundleDisplayName),
|
||||||
|
primaryButton: primaryButton,
|
||||||
|
secondaryButton: secondaryButton)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,12 @@ final class StaticLocationScreenCoordinator: CoordinatorProtocol {
|
|||||||
switch action {
|
switch action {
|
||||||
case .close:
|
case .close:
|
||||||
actionsSubject.send(.close)
|
actionsSubject.send(.close)
|
||||||
|
case .openSystemSettings:
|
||||||
|
guard let url = URL(string: UIApplication.openSettingsURLString),
|
||||||
|
UIApplication.shared.canOpenURL(url) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
UIApplication.shared.open(url)
|
||||||
case .sendLocation(let geoURI, let isUserLocation):
|
case .sendLocation(let geoURI, let isUserLocation):
|
||||||
actionsSubject.send(.selectedLocation(geoURI, isUserLocation: isUserLocation))
|
actionsSubject.send(.selectedLocation(geoURI, isUserLocation: isUserLocation))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,17 @@ class StaticLocationScreenViewModel: StaticLocationScreenViewModelType, StaticLo
|
|||||||
guard let coordinate = state.bindings.mapCenterLocation else { return }
|
guard let coordinate = state.bindings.mapCenterLocation else { return }
|
||||||
actionsSubject.send(.sendLocation(.init(coordinate: coordinate), isUserLocation: state.isSharingUserLocation))
|
actionsSubject.send(.sendLocation(.init(coordinate: coordinate), isUserLocation: state.isSharingUserLocation))
|
||||||
case .userDidPan:
|
case .userDidPan:
|
||||||
state.showsUserLocationMode = .hide
|
state.bindings.showsUserLocationMode = .show
|
||||||
state.isSharingUserLocation = false
|
case .centerToUser:
|
||||||
|
switch state.bindings.isLocationAuthorized {
|
||||||
|
case .some(true), .none:
|
||||||
|
state.bindings.showsUserLocationMode = .showAndFollow
|
||||||
|
case .some(false):
|
||||||
|
let action: () -> Void = { [weak self] in self?.actionsSubject.send(.openSystemSettings) }
|
||||||
|
state.bindings.alertInfo = .init(locationSharingViewError: .missingAuthorization,
|
||||||
|
primaryButton: .init(title: L10n.actionNotNow, role: .cancel, action: nil),
|
||||||
|
secondaryButton: .init(title: L10n.commonSettings, action: action))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,17 +44,20 @@ struct StaticLocationScreen: View {
|
|||||||
ZStack(alignment: .center) {
|
ZStack(alignment: .center) {
|
||||||
MapLibreMapView(builder: builder,
|
MapLibreMapView(builder: builder,
|
||||||
options: mapOptions,
|
options: mapOptions,
|
||||||
showsUserLocationMode: .hide,
|
showsUserLocationMode: $context.showsUserLocationMode,
|
||||||
error: $context.mapError,
|
error: $context.mapError,
|
||||||
mapCenterCoordinate: $context.mapCenterLocation,
|
mapCenterCoordinate: $context.mapCenterLocation,
|
||||||
userDidPan: {
|
isLocationAuthorized: $context.isLocationAuthorized) {
|
||||||
context.send(viewAction: .userDidPan)
|
context.send(viewAction: .userDidPan)
|
||||||
})
|
}
|
||||||
|
.ignoresSafeArea(.all, edges: mapSafeAreaEdges)
|
||||||
if context.viewState.isLocationPickerMode {
|
if context.viewState.isLocationPickerMode {
|
||||||
LocationMarkerView()
|
LocationMarkerView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoresSafeArea(.all, edges: mapSafeAreaEdges)
|
.overlay(alignment: .bottomTrailing) {
|
||||||
|
centerToUserLocationButton
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
@@ -72,7 +75,7 @@ struct StaticLocationScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.viewState.showBottomToolbar {
|
if context.viewState.isLocationPickerMode {
|
||||||
ToolbarItemGroup(placement: .bottomBar) {
|
ToolbarItemGroup(placement: .bottomBar) {
|
||||||
selectLocationButton
|
selectLocationButton
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -81,19 +84,22 @@ struct StaticLocationScreen: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var mapOptions: MapLibreMapView.Options {
|
private var mapOptions: MapLibreMapView.Options {
|
||||||
guard let coordinate = context.viewState.mapAnnotationCoordinate else {
|
var annotations: [LocationAnnotation] = []
|
||||||
return .init(zoomLevel: context.viewState.zoomLevel)
|
if context.viewState.isLocationPickerMode == false {
|
||||||
|
let annotation = LocationAnnotation(coordinate: context.viewState.initialMapCenter, anchorPoint: .bottomCenter) {
|
||||||
|
LocationMarkerView()
|
||||||
|
}
|
||||||
|
annotations.append(annotation)
|
||||||
}
|
}
|
||||||
|
|
||||||
return .init(zoomLevel: context.viewState.zoomLevel,
|
return .init(zoomLevel: context.viewState.zoomLevel,
|
||||||
mapCenter: coordinate,
|
initialZoomLevel: context.viewState.initialZoomLevel,
|
||||||
annotations: [LocationAnnotation(coordinate: coordinate, anchorPoint: .bottomCenter) {
|
mapCenter: context.viewState.initialMapCenter,
|
||||||
LocationMarkerView()
|
annotations: annotations)
|
||||||
}])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var mapSafeAreaEdges: Edge.Set {
|
private var mapSafeAreaEdges: Edge.Set {
|
||||||
context.viewState.showBottomToolbar ? .horizontal : [.horizontal, .bottom]
|
context.viewState.isLocationPickerMode ? .horizontal : [.horizontal, .bottom]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ScaledMetric private var shareMarkerSize: CGFloat = 28
|
@ScaledMetric private var shareMarkerSize: CGFloat = 28
|
||||||
@@ -111,6 +117,15 @@ struct StaticLocationScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var centerToUserLocationButton: some View {
|
||||||
|
Button {
|
||||||
|
context.send(viewAction: .centerToUser)
|
||||||
|
} label: {
|
||||||
|
Image(asset: context.viewState.isSharingUserLocation ? Asset.Images.locationPointerFull : Asset.Images.locationPointer)
|
||||||
|
}
|
||||||
|
.padding(16)
|
||||||
|
}
|
||||||
|
|
||||||
private var closeButton: some View {
|
private var closeButton: some View {
|
||||||
Button(L10n.actionCancel) {
|
Button(L10n.actionCancel) {
|
||||||
context.send(viewAction: .close)
|
context.send(viewAction: .close)
|
||||||
@@ -127,14 +142,13 @@ struct StaticLocationScreen: View {
|
|||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var shareSheet: some View {
|
private var shareSheet: some View {
|
||||||
if let location = context.viewState.mapAnnotationCoordinate {
|
let location = context.viewState.initialMapCenter
|
||||||
let locationDescription = context.viewState.locationDescription
|
let locationDescription = context.viewState.locationDescription
|
||||||
AppActivityView(activityItems: [ShareToMapsAppActivity.MapsAppType.apple.activityURL(for: location, locationDescription: locationDescription)],
|
AppActivityView(activityItems: [ShareToMapsAppActivity.MapsAppType.apple.activityURL(for: location, locationDescription: locationDescription)],
|
||||||
applicationActivities: ShareToMapsAppActivity.MapsAppType.allCases.map { ShareToMapsAppActivity(type: $0, location: location, locationDescription: locationDescription) })
|
applicationActivities: ShareToMapsAppActivity.MapsAppType.allCases.map { ShareToMapsAppActivity(type: $0, location: location, locationDescription: locationDescription) })
|
||||||
.edgesIgnoringSafeArea(.bottom)
|
.edgesIgnoringSafeArea(.bottom)
|
||||||
.presentationDetents([.medium, .large])
|
.presentationDetents([.medium, .large])
|
||||||
.presentationDragIndicator(.hidden)
|
.presentationDragIndicator(.hidden)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -238,7 +238,8 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable:next cyclomatic_complexity function_body_length
|
||||||
private func updateTimelineItems() {
|
private func updateTimelineItems() {
|
||||||
var newTimelineItems = [RoomTimelineItemProtocol]()
|
var newTimelineItems = [RoomTimelineItemProtocol]()
|
||||||
var canBackPaginate = true
|
var canBackPaginate = true
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
<false/>
|
<false/>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>To take pictures or videos and send them as a message $(APP_DISPLAY_NAME) needs access to the camera.</string>
|
<string>To take pictures or videos and send them as a message $(APP_DISPLAY_NAME) needs access to the camera.</string>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string>When you share your location to people, $(APP_DISPLAY_NAME) needs access to show them a map.</string>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<string>To take videos with audio and send them as a message $(APP_DISPLAY_NAME) needs access to the microphone.</string>
|
<string>To take videos with audio and send them as a message $(APP_DISPLAY_NAME) needs access to the microphone.</string>
|
||||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ targets:
|
|||||||
NSCameraUsageDescription: To take pictures or videos and send them as a message $(APP_DISPLAY_NAME) needs access to the camera.
|
NSCameraUsageDescription: To take pictures or videos and send them as a message $(APP_DISPLAY_NAME) needs access to the camera.
|
||||||
NSMicrophoneUsageDescription: To take videos with audio and send them as a message $(APP_DISPLAY_NAME) needs access to the microphone.
|
NSMicrophoneUsageDescription: To take videos with audio and send them as a message $(APP_DISPLAY_NAME) needs access to the microphone.
|
||||||
NSPhotoLibraryAddUsageDescription: Allows saving photos and videos to your library.
|
NSPhotoLibraryAddUsageDescription: Allows saving photos and videos to your library.
|
||||||
|
NSLocationWhenInUseUsageDescription: When you share your location to people, $(APP_DISPLAY_NAME) needs access to show them a map.
|
||||||
UIBackgroundModes: [
|
UIBackgroundModes: [
|
||||||
fetch
|
fetch
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -32,13 +32,48 @@ class StaticLocationScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
let viewModel = StaticLocationScreenViewModel(interactionMode: .picker)
|
let viewModel = StaticLocationScreenViewModel(interactionMode: .picker)
|
||||||
viewModel.state.isSharingUserLocation = true
|
viewModel.state.bindings.isLocationAuthorized = true
|
||||||
self.viewModel = viewModel
|
self.viewModel = viewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func testUserDidPan() async throws {
|
func testUserDidPan() async throws {
|
||||||
XCTAssertTrue(context.viewState.isSharingUserLocation)
|
XCTAssertTrue(context.viewState.isSharingUserLocation)
|
||||||
|
XCTAssertEqual(context.showsUserLocationMode, .showAndFollow)
|
||||||
context.send(viewAction: .userDidPan)
|
context.send(viewAction: .userDidPan)
|
||||||
XCTAssertFalse(context.viewState.isSharingUserLocation)
|
XCTAssertFalse(context.viewState.isSharingUserLocation)
|
||||||
|
XCTAssertEqual(context.showsUserLocationMode, .show)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCenterOnUser() async throws {
|
||||||
|
XCTAssertTrue(context.viewState.isSharingUserLocation)
|
||||||
|
context.showsUserLocationMode = .show
|
||||||
|
XCTAssertFalse(context.viewState.isSharingUserLocation)
|
||||||
|
context.send(viewAction: .centerToUser)
|
||||||
|
XCTAssertTrue(context.viewState.isSharingUserLocation)
|
||||||
|
XCTAssertEqual(context.showsUserLocationMode, .showAndFollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCenterOnUserWithoutAuth() async throws {
|
||||||
|
context.showsUserLocationMode = .hide
|
||||||
|
context.isLocationAuthorized = nil
|
||||||
|
context.send(viewAction: .centerToUser)
|
||||||
|
XCTAssertEqual(context.showsUserLocationMode, .showAndFollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCenterOnUserWithDeniedAuth() async throws {
|
||||||
|
context.isLocationAuthorized = false
|
||||||
|
context.showsUserLocationMode = .hide
|
||||||
|
context.send(viewAction: .centerToUser)
|
||||||
|
XCTAssertNotEqual(context.showsUserLocationMode, .showAndFollow)
|
||||||
|
XCTAssertNotNil(context.alertInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testErrorMapping() async throws {
|
||||||
|
let mapError = AlertInfo(locationSharingViewError: .mapError(.failedLoadingMap))
|
||||||
|
XCTAssertEqual(mapError.message, L10n.errorFailedLoadingMap(InfoPlistReader.main.bundleDisplayName))
|
||||||
|
let locationError = AlertInfo(locationSharingViewError: .mapError(.failedLocatingUser))
|
||||||
|
XCTAssertEqual(locationError.message, L10n.errorFailedLocatingUser(InfoPlistReader.main.bundleDisplayName))
|
||||||
|
let authorizationError = AlertInfo(locationSharingViewError: .missingAuthorization)
|
||||||
|
XCTAssertEqual(authorizationError.message, L10n.errorMissingLocationAuth(InfoPlistReader.main.bundleDisplayName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Send current user location
|
||||||
Reference in New Issue
Block a user