Send pin-drop location (#1179)
* add location sharing action in room, and open location sharing screen * add pin location sharing * fix asset, add tests for location viewModel, add send location request * fix map zoom level, fix assets for location * add feature flag for location sharing * hide attribution button
This commit is contained in:
@@ -415,6 +415,7 @@
|
||||
9AFEE46B03B7E995B3E1A53D /* WaitlistScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80C4927D09099497233E9980 /* WaitlistScreen.swift */; };
|
||||
9B582B3EEFEA615D4A6FBF1A /* TimelineReactionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 351E89CE2ED9B73C5CC47955 /* TimelineReactionsView.swift */; };
|
||||
9B872FF37DBE6BE054903831 /* MediaUploadPreviewScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54E12B98252F6C527E31FEE /* MediaUploadPreviewScreenViewModelProtocol.swift */; };
|
||||
9BB91CABB10D8FE90C491BCD /* StaticLocationScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C833673B334A0651AB46F30B /* StaticLocationScreenViewModelTests.swift */; };
|
||||
9BD3A773186291560DF92B62 /* RoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */; };
|
||||
9C45CE85325CD591DADBC4CA /* ElementXTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEAC3AC691CBB84983E275 /* ElementXTests.swift */; };
|
||||
9C5A07E7C33F3F40287D7861 /* SettingsScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EC57A32ABC80D774CC663DB /* SettingsScreenUITests.swift */; };
|
||||
@@ -496,6 +497,7 @@
|
||||
B45F20A1C3F1CE19D5B8BA74 /* InvitesScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F61A0DD8243B395499C99A2 /* InvitesScreenUITests.swift */; };
|
||||
B4A0C69370E6008A971463E7 /* BugReportScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C89820BB2B88D4EA28131C /* BugReportScreenViewModelProtocol.swift */; };
|
||||
B4AAB3257A83B73F53FB2689 /* StateStoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */; };
|
||||
B5479997ECC516C121E6625E /* LocationMarkerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFECCE59967018204876D0A5 /* LocationMarkerView.swift */; };
|
||||
B5903E48CF43259836BF2DBF /* EncryptedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56C1BCB9E83B09A45387FCA2 /* EncryptedRoomTimelineView.swift */; };
|
||||
B5BD05558DC2C3091905E14A /* FilePreviewScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 714977AF906461C8F6F16ABA /* FilePreviewScreenCoordinator.swift */; };
|
||||
B5E455C9689EA600EDB3E9E0 /* NavigationRootCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA28F29C9F93E93CC3C2C715 /* NavigationRootCoordinator.swift */; };
|
||||
@@ -580,7 +582,6 @@
|
||||
CF4044A8EED5C41BC0ED6ABE /* SoftLogoutScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D316BB02636AF2174F2580E6 /* SoftLogoutScreenViewModelProtocol.swift */; };
|
||||
CF82143AA4A4F7BD11D22946 /* RoomTimelineViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACB6C5E4950B6C9842F35A38 /* RoomTimelineViewProvider.swift */; };
|
||||
D02AA6208C7ACB9BE6332394 /* UNNotificationContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE148A4FFEE853C5A281500C /* UNNotificationContent.swift */; };
|
||||
D04B93C644A0BE4A4C5D0A1B /* LocationPinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA10BE264552E23946FF0499 /* LocationPinView.swift */; };
|
||||
D181AC8FF236B7F91C0A8C28 /* MapTiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23AA3F4B285570805CB0CCDD /* MapTiler.swift */; };
|
||||
D2A15D03F81342A09340BD56 /* AnalyticsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */; };
|
||||
D2D70B5DB1A5E4AF0CD88330 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 033DB41C51865A2E83174E87 /* target.yml */; };
|
||||
@@ -1270,6 +1271,7 @@
|
||||
C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
|
||||
C796FC1DFDBCDD5573D0360F /* WaitlistScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
C830A64609CBD152F06E0457 /* NotificationConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationConstants.swift; sourceTree = "<group>"; };
|
||||
C833673B334A0651AB46F30B /* StaticLocationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
C843CF833BF6485B64AC87E1 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
|
||||
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||
@@ -1313,7 +1315,6 @@
|
||||
D8E057FB1F07A5C201C89061 /* MockServerSelectionScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockServerSelectionScreenState.swift; sourceTree = "<group>"; };
|
||||
D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
D8F5F9E02B1AB5350B1815E7 /* TimelineStartRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStartRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
DA10BE264552E23946FF0499 /* LocationPinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPinView.swift; sourceTree = "<group>"; };
|
||||
DA2AEC1AB349A341FE13DEC1 /* StartChatScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenUITests.swift; sourceTree = "<group>"; };
|
||||
DB06F22CFA34885B40976061 /* RoomDetailsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreen.swift; sourceTree = "<group>"; };
|
||||
DBA8DC95C079805B0B56E8A9 /* SharedUserDefaultsKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedUserDefaultsKeys.swift; sourceTree = "<group>"; };
|
||||
@@ -1402,6 +1403,7 @@
|
||||
FD1275D9CE0FFBA6E8E85426 /* UserIndicatorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorController.swift; sourceTree = "<group>"; };
|
||||
FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerTests.swift; sourceTree = "<group>"; };
|
||||
FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsScreen.swift; sourceTree = "<group>"; };
|
||||
FFECCE59967018204876D0A5 /* LocationMarkerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationMarkerView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -1795,7 +1797,7 @@
|
||||
0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */,
|
||||
B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */,
|
||||
C352359663A0E52BA20761EE /* LoadableImage.swift */,
|
||||
DA10BE264552E23946FF0499 /* LocationPinView.swift */,
|
||||
FFECCE59967018204876D0A5 /* LocationMarkerView.swift */,
|
||||
50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */,
|
||||
648DD1C10E4957CB791FE0B8 /* OverridableAvatarImage.swift */,
|
||||
C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */,
|
||||
@@ -2400,6 +2402,7 @@
|
||||
3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */,
|
||||
32C5DAA1773F57653BF1C4F9 /* SoftLogoutViewModelTests.swift */,
|
||||
6DF438EAFC732D2D95D34BF6 /* StartChatViewModelTests.swift */,
|
||||
C833673B334A0651AB46F30B /* StaticLocationScreenViewModelTests.swift */,
|
||||
2CEBCB9676FCD1D0F13188DD /* StringTests.swift */,
|
||||
2AB2C848BB9A7A9B618B7B89 /* TextBasedRoomTimelineTests.swift */,
|
||||
1734A445A58ED855B977A0A8 /* TracingConfigurationTests.swift */,
|
||||
@@ -3944,6 +3947,7 @@
|
||||
206F0DBAB6AF042CA1FF2C0D /* SettingsViewModelTests.swift in Sources */,
|
||||
09AAF04B27732046C755D914 /* SoftLogoutViewModelTests.swift in Sources */,
|
||||
6189B4ABD535CE526FA1107B /* StartChatViewModelTests.swift in Sources */,
|
||||
9BB91CABB10D8FE90C491BCD /* StaticLocationScreenViewModelTests.swift in Sources */,
|
||||
1FEC0A4EC6E6DF693C16B32A /* StringTests.swift in Sources */,
|
||||
E75CE800B3E64D0F7F8E228D /* TemplateScreenViewModelTests.swift in Sources */,
|
||||
3A7DD0D13B0FB8876D69D829 /* TextBasedRoomTimelineTests.swift in Sources */,
|
||||
@@ -4151,7 +4155,7 @@
|
||||
6E47D126DD7585E8F8237CE7 /* LoadableAvatarImage.swift in Sources */,
|
||||
D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */,
|
||||
256D76972BA3254F7CB7F88B /* LocationAnnotation.swift in Sources */,
|
||||
D04B93C644A0BE4A4C5D0A1B /* LocationPinView.swift in Sources */,
|
||||
B5479997ECC516C121E6625E /* LocationMarkerView.swift in Sources */,
|
||||
D46C33F8B61B55F0C8C2D15F /* LocationRoomTimelineItem.swift in Sources */,
|
||||
854E82E064BA53CD0BC45600 /* LocationRoomTimelineItemContent.swift in Sources */,
|
||||
D9473FC9B077A6EDB7A12001 /* LocationRoomTimelineView.swift in Sources */,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
22
ElementX/Resources/Assets.xcassets/Images/Location/location-marker.imageset/Contents.json
vendored
Normal file
22
ElementX/Resources/Assets.xcassets/Images/Location/location-marker.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "location-marker.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "location-marker-dark.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
78
ElementX/Resources/Assets.xcassets/Images/Location/location-pin.imageset/location-pin-dark.pdf
vendored
Normal file
78
ElementX/Resources/Assets.xcassets/Images/Location/location-pin.imageset/location-pin-dark.pdf
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 -0.123291 cm
|
||||
0.921569 0.933333 0.949020 scn
|
||||
7.000000 20.123291 m
|
||||
3.130000 20.123291 0.000000 16.908089 0.000000 12.932741 c
|
||||
0.000000 8.649229 4.420000 2.742706 6.240000 0.493092 c
|
||||
6.640000 0.000025 7.370000 0.000025 7.770000 0.493092 c
|
||||
9.580000 2.742706 14.000000 8.649229 14.000000 12.932741 c
|
||||
14.000000 16.908089 10.870000 20.123291 7.000000 20.123291 c
|
||||
h
|
||||
7.000000 10.364689 m
|
||||
5.620000 10.364689 4.500000 11.515176 4.500000 12.932741 c
|
||||
4.500000 14.350307 5.620000 15.500795 7.000000 15.500795 c
|
||||
8.380000 15.500795 9.500000 14.350307 9.500000 12.932741 c
|
||||
9.500000 11.515176 8.380000 10.364689 7.000000 10.364689 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
699
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 14.000000 20.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000000789 00000 n
|
||||
0000000811 00000 n
|
||||
0000000984 00000 n
|
||||
0000001058 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1117
|
||||
%%EOF
|
||||
147
ElementX/Resources/Assets.xcassets/Images/Location/location-pin.imageset/location-pin.pdf
vendored
Normal file
147
ElementX/Resources/Assets.xcassets/Images/Location/location-pin.imageset/location-pin.pdf
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
%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 20.000000 20.000000 ]
|
||||
>>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 3.000000 -0.123291 cm
|
||||
0.105882 0.113725 0.133333 scn
|
||||
7.000000 20.123291 m
|
||||
3.130000 20.123291 0.000000 16.908089 0.000000 12.932741 c
|
||||
0.000000 8.649229 4.420000 2.742706 6.240000 0.493092 c
|
||||
6.640000 0.000025 7.370000 0.000025 7.770000 0.493092 c
|
||||
9.580000 2.742706 14.000000 8.649229 14.000000 12.932741 c
|
||||
14.000000 16.908089 10.870000 20.123291 7.000000 20.123291 c
|
||||
h
|
||||
7.000000 10.364689 m
|
||||
5.620000 10.364689 4.500000 11.515176 4.500000 12.932741 c
|
||||
4.500000 14.350307 5.620000 15.500795 7.000000 15.500795 c
|
||||
8.380000 15.500795 9.500000 14.350307 9.500000 12.932741 c
|
||||
9.500000 11.515176 8.380000 10.364689 7.000000 10.364689 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
699
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
<< /Type /XObject
|
||||
/Length 4 0 R
|
||||
/Group << /Type /Group
|
||||
/S /Transparency
|
||||
>>
|
||||
/Subtype /Form
|
||||
/Resources << >>
|
||||
/BBox [ 0.000000 0.000000 20.000000 20.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 20.000000 m
|
||||
20.000000 20.000000 l
|
||||
20.000000 0.000000 l
|
||||
0.000000 0.000000 l
|
||||
0.000000 20.000000 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
232
|
||||
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 20.000000 20.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
|
||||
0000000957 00000 n
|
||||
0000000979 00000 n
|
||||
0000001459 00000 n
|
||||
0000001481 00000 n
|
||||
0000001779 00000 n
|
||||
0000001881 00000 n
|
||||
0000001902 00000 n
|
||||
0000002075 00000 n
|
||||
0000002149 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 10 0 R
|
||||
/Size 11
|
||||
>>
|
||||
startxref
|
||||
2209
|
||||
%%EOF
|
||||
@@ -30,6 +30,7 @@ final class AppSettings {
|
||||
case userSuggestionsEnabled
|
||||
case readReceiptsEnabled
|
||||
case locationEventsEnabled
|
||||
case shareLocationEnabled
|
||||
}
|
||||
|
||||
private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier
|
||||
@@ -194,4 +195,7 @@ final class AppSettings {
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.locationEventsEnabled, defaultValue: false, storageType: .userDefaults(store))
|
||||
var locationEventsEnabled
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.shareLocationEnabled, defaultValue: false, storageType: .userDefaults(store))
|
||||
var shareLocationEnabled
|
||||
}
|
||||
|
||||
@@ -150,7 +150,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
return .messageForwarding(roomID: roomID, itemID: itemID)
|
||||
case (.dismissMessageForwarding, .messageForwarding(let roomID, _)):
|
||||
return .room(roomID: roomID)
|
||||
|
||||
case (.presentLocationPicker, .room(let roomID)):
|
||||
return .locationPicker(roomID: roomID)
|
||||
case (.dismissLocationPicker, .locationPicker(let roomID)):
|
||||
return .room(roomID: roomID)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -218,7 +221,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
presentMessageForwarding(for: eventID)
|
||||
case (.messageForwarding, .dismissMessageForwarding, .room):
|
||||
break
|
||||
|
||||
case (.room, .presentLocationPicker, .locationPicker):
|
||||
presentLocationPicker()
|
||||
case (.locationPicker, .dismissLocationPicker, .room):
|
||||
break
|
||||
default:
|
||||
fatalError("Unknown transition: \(context)")
|
||||
}
|
||||
@@ -312,6 +318,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: url))
|
||||
case .presentEmojiPicker(let itemID):
|
||||
stateMachine.tryEvent(.presentEmojiPicker(itemID: itemID))
|
||||
case .presentLocationPicker:
|
||||
stateMachine.tryEvent(.presentLocationPicker)
|
||||
case .presentRoomMemberDetails(member: let member):
|
||||
stateMachine.tryEvent(.presentRoomMemberDetails(member: .init(value: member)))
|
||||
case .presentMessageForwarding(let itemID):
|
||||
@@ -519,6 +527,33 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
private func presentLocationPicker() {
|
||||
let locationPickerNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let params = StaticLocationScreenCoordinatorParameters()
|
||||
let coordinator = StaticLocationScreenCoordinator(parameters: params)
|
||||
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .selectedLocation(let geoURI):
|
||||
Task {
|
||||
_ = await self.roomProxy?.sendLocation(body: geoURI.bodyMessage, geoURI: geoURI)
|
||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
case .close:
|
||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
locationPickerNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(locationPickerNavigationStackCoordinator) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissLocationPicker)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentRoomMemberDetails(member: RoomMemberProxyProtocol) {
|
||||
let params = RoomMemberDetailsScreenCoordinatorParameters(roomMemberProxy: member, mediaProvider: userSession.mediaProvider)
|
||||
let coordinator = RoomMemberDetailsScreenCoordinator(parameters: params)
|
||||
@@ -611,6 +646,7 @@ private extension RoomFlowCoordinator {
|
||||
case mediaUploadPicker(roomID: String, source: MediaPickerScreenSource)
|
||||
case mediaUploadPreview(roomID: String, fileURL: URL)
|
||||
case emojiPicker(roomID: String, itemID: String)
|
||||
case locationPicker(roomID: String)
|
||||
case roomMemberDetails(roomID: String, member: HashableRoomMemberWrapper)
|
||||
case messageForwarding(roomID: String, itemID: String)
|
||||
}
|
||||
@@ -642,6 +678,9 @@ private extension RoomFlowCoordinator {
|
||||
case presentEmojiPicker(itemID: String)
|
||||
case dismissEmojiPicker
|
||||
|
||||
case presentLocationPicker
|
||||
case dismissLocationPicker
|
||||
|
||||
case presentRoomMemberDetails(member: HashableRoomMemberWrapper)
|
||||
case dismissRoomMemberDetails
|
||||
|
||||
@@ -649,3 +688,9 @@ private extension RoomFlowCoordinator {
|
||||
case dismissMessageForwarding
|
||||
}
|
||||
}
|
||||
|
||||
private extension GeoURI {
|
||||
var bodyMessage: String {
|
||||
String(format: "Location was shared at %@ as of %@", string, Date().description)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ internal enum Asset {
|
||||
internal static let backgroundColor = ColorAsset(name: "colors/background-color")
|
||||
}
|
||||
internal enum Images {
|
||||
internal static let locationMarker = ImageAsset(name: "images/location-marker")
|
||||
internal static let locationPin = ImageAsset(name: "images/location-pin")
|
||||
internal static let appLogo = ImageAsset(name: "images/app-logo")
|
||||
internal static let serverSelectionIcon = ImageAsset(name: "images/server-selection-icon")
|
||||
internal static let closeCircle = ImageAsset(name: "images/close-circle")
|
||||
@@ -37,7 +39,6 @@ internal enum Asset {
|
||||
internal static let encryptionWarning = ImageAsset(name: "images/encryption-warning")
|
||||
internal static let launchBackground = ImageAsset(name: "images/launch-background")
|
||||
internal static let launchLogo = ImageAsset(name: "images/launch-logo")
|
||||
internal static let locationPin = ImageAsset(name: "images/location-pin")
|
||||
internal static let timelineComposerSendMessage = ImageAsset(name: "images/timeline-composer-send-message")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ struct MapLibreMapView: UIViewRepresentable {
|
||||
|
||||
private enum Constants {
|
||||
static let mapZoomLevel = 15.0
|
||||
static let mapZoomLevelWithoutPermission = 5.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
@@ -36,12 +37,22 @@ struct MapLibreMapView: UIViewRepresentable {
|
||||
|
||||
/// Bind view errors if any
|
||||
let error: Binding<MapLibreError?>
|
||||
|
||||
/// Coordinate of the center of the map
|
||||
@Binding var mapCenterCoordinate: CLLocationCoordinate2D?
|
||||
|
||||
/// Called when the user pan on the map
|
||||
var userDidPan: (() -> Void)?
|
||||
|
||||
// MARK: - UIViewRepresentable
|
||||
|
||||
func makeUIView(context: Context) -> MGLMapView {
|
||||
let mapView = makeMapView()
|
||||
mapView.delegate = context.coordinator
|
||||
let panGesture = UIPanGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.didPan))
|
||||
panGesture.delegate = context.coordinator
|
||||
mapView.addGestureRecognizer(panGesture)
|
||||
mapView.zoomLevel = Constants.mapZoomLevelWithoutPermission
|
||||
return mapView
|
||||
}
|
||||
|
||||
@@ -65,12 +76,9 @@ struct MapLibreMapView: UIViewRepresentable {
|
||||
|
||||
private func makeMapView() -> MGLMapView {
|
||||
let mapView = MGLMapView(frame: .zero, styleURL: colorScheme == .dark ? builder.dynamicMapURL(for: .dark) : builder.dynamicMapURL(for: .light))
|
||||
|
||||
mapView.logoView.isHidden = true
|
||||
mapView.attributionButton.isHidden = true
|
||||
mapView.zoomLevel = Constants.mapZoomLevel
|
||||
|
||||
showUserLocation(in: mapView)
|
||||
mapView.attributionButton.isHidden = true
|
||||
|
||||
return mapView
|
||||
}
|
||||
@@ -134,7 +142,9 @@ extension MapLibreMapView {
|
||||
}
|
||||
}
|
||||
|
||||
func mapView(_ mapView: MGLMapView, regionDidChangeAnimated animated: Bool) { }
|
||||
func mapView(_ mapView: MGLMapView, regionDidChangeAnimated animated: Bool) {
|
||||
mapLibreView.mapCenterCoordinate = mapView.centerCoordinate
|
||||
}
|
||||
|
||||
// MARK: Callout
|
||||
|
||||
@@ -147,6 +157,11 @@ extension MapLibreMapView {
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
gestureRecognizer is UIPanGestureRecognizer
|
||||
}
|
||||
|
||||
@objc
|
||||
func didPan() {
|
||||
mapLibreView.userDidPan?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LocationPinView: View {
|
||||
struct LocationMarkerView: View {
|
||||
var body: some View {
|
||||
Image(Asset.Images.locationPin.name)
|
||||
Image(Asset.Images.locationMarker.name)
|
||||
.alignmentGuide(VerticalAlignment.center) { dimensions in
|
||||
dimensions[.bottom]
|
||||
}
|
||||
@@ -26,12 +26,12 @@ struct LocationPinView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct LocationPinView_Previews: PreviewProvider {
|
||||
struct LocationMarkerView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack(spacing: 30) {
|
||||
LocationPinView()
|
||||
LocationMarkerView()
|
||||
|
||||
LocationPinView()
|
||||
LocationMarkerView()
|
||||
.colorScheme(.dark)
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import CoreLocation
|
||||
import Foundation
|
||||
|
||||
enum LocationSharingViewError: Error, Hashable {
|
||||
@@ -21,13 +22,23 @@ enum LocationSharingViewError: Error, Hashable {
|
||||
case mapError(MapLibreError)
|
||||
}
|
||||
|
||||
enum StaticLocationScreenViewModelAction { }
|
||||
enum StaticLocationScreenViewModelAction {
|
||||
case close
|
||||
case sendLocation(GeoURI)
|
||||
}
|
||||
|
||||
struct StaticLocationScreenViewState: BindableState {
|
||||
/// Indicates whether the user has moved around the map to drop a pin somewhere other than their current location
|
||||
var isPinDropSharing = true
|
||||
/// Behavior mode of the current user's location, can be hidden, only shown and shown following the user
|
||||
var showsUserLocationMode: ShowUserLocationMode = .hide
|
||||
|
||||
var bindings = StaticLocationScreenBindings()
|
||||
}
|
||||
|
||||
struct StaticLocationScreenBindings {
|
||||
var mapCenterLocation: CLLocationCoordinate2D?
|
||||
|
||||
/// Information describing the currently displayed alert.
|
||||
var mapError: MapLibreError? {
|
||||
get {
|
||||
@@ -45,4 +56,8 @@ struct StaticLocationScreenBindings {
|
||||
var alertInfo: AlertInfo<LocationSharingViewError>?
|
||||
}
|
||||
|
||||
enum StaticLocationScreenViewAction { }
|
||||
enum StaticLocationScreenViewAction {
|
||||
case close
|
||||
case selectLocation
|
||||
case userDidPan
|
||||
}
|
||||
|
||||
@@ -19,12 +19,22 @@ import SwiftUI
|
||||
|
||||
struct StaticLocationScreenCoordinatorParameters { }
|
||||
|
||||
enum StaticLocationScreenCoordinatorAction { }
|
||||
enum StaticLocationScreenCoordinatorAction {
|
||||
case close
|
||||
case selectedLocation(GeoURI)
|
||||
}
|
||||
|
||||
final class StaticLocationScreenCoordinator: CoordinatorProtocol {
|
||||
let parameters: StaticLocationScreenCoordinatorParameters
|
||||
let viewModel: StaticLocationScreenViewModelProtocol
|
||||
|
||||
private let actionsSubject: PassthroughSubject<StaticLocationScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
|
||||
var actions: AnyPublisher<StaticLocationScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: StaticLocationScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
@@ -33,6 +43,19 @@ final class StaticLocationScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
viewModel.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .close:
|
||||
actionsSubject.send(.close)
|
||||
case .sendLocation(let geoURI):
|
||||
actionsSubject.send(.selectedLocation(geoURI))
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(StaticLocationScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import Combine
|
||||
import Foundation
|
||||
|
||||
typealias StaticLocationScreenViewModelType = StateStoreViewModel<StaticLocationScreenViewState, StaticLocationScreenViewModelAction>
|
||||
typealias StaticLocationScreenViewModelType = StateStoreViewModel<StaticLocationScreenViewState, StaticLocationScreenViewAction>
|
||||
|
||||
class StaticLocationScreenViewModel: StaticLocationScreenViewModelType, StaticLocationScreenViewModelProtocol {
|
||||
private let actionsSubject: PassthroughSubject<StaticLocationScreenViewModelAction, Never> = .init()
|
||||
@@ -29,4 +29,17 @@ class StaticLocationScreenViewModel: StaticLocationScreenViewModelType, StaticLo
|
||||
init() {
|
||||
super.init(initialViewState: StaticLocationScreenViewState())
|
||||
}
|
||||
|
||||
override func process(viewAction: StaticLocationScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .close:
|
||||
actionsSubject.send(.close)
|
||||
case .selectLocation:
|
||||
guard let coordinate = state.bindings.mapCenterLocation else { return }
|
||||
actionsSubject.send(.sendLocation(.init(coordinate: coordinate)))
|
||||
case .userDidPan:
|
||||
state.showsUserLocationMode = .hide
|
||||
state.isPinDropSharing = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,18 +22,62 @@ struct StaticLocationScreen: View {
|
||||
private let builder = MapTilerStyleBuilder(appSettings: ServiceLocator.shared.settings)
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
mapView
|
||||
.ignoresSafeArea(.all, edges: [.bottom])
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.alert(item: $context.alertInfo)
|
||||
mapView
|
||||
.ignoresSafeArea(.all, edges: .horizontal)
|
||||
.navigationTitle(L10n.screenShareLocationTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { toolbar }
|
||||
.alert(item: $context.alertInfo)
|
||||
}
|
||||
|
||||
private var mapView: some View {
|
||||
ZStack(alignment: .center) {
|
||||
MapLibreMapView(builder: builder,
|
||||
showsUserLocationMode: .hide,
|
||||
error: $context.mapError,
|
||||
mapCenterCoordinate: $context.mapCenterLocation,
|
||||
userDidPan: {
|
||||
context.send(viewAction: .userDidPan)
|
||||
})
|
||||
if context.viewState.isPinDropSharing {
|
||||
LocationMarkerView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var mapView: MapLibreMapView {
|
||||
MapLibreMapView(builder: builder,
|
||||
showsUserLocationMode: .follow,
|
||||
error: $context.mapError)
|
||||
@ToolbarContentBuilder
|
||||
private var toolbar: some ToolbarContent {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
closeButton
|
||||
}
|
||||
|
||||
ToolbarItemGroup(placement: .bottomBar) {
|
||||
shareLocationButton
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
@ScaledMetric private var shareMarkerSize: CGFloat = 28
|
||||
private var shareLocationButton: some View {
|
||||
Button {
|
||||
context.send(viewAction: .selectLocation)
|
||||
} label: {
|
||||
HStack(spacing: 8) {
|
||||
Image(asset: Asset.Images.locationMarker)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: shareMarkerSize, height: shareMarkerSize)
|
||||
Text(context.viewState.isPinDropSharing ? L10n.screenShareThisLocationAction : L10n.screenShareMyLocationAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var closeButton: some View {
|
||||
Button(L10n.actionCancel, action: close)
|
||||
}
|
||||
|
||||
private func close() {
|
||||
context.send(viewAction: .close)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ enum RoomScreenCoordinatorAction {
|
||||
case presentMediaUploadPicker(MediaPickerScreenSource)
|
||||
case presentMediaUploadPreviewScreen(URL)
|
||||
case presentRoomDetails
|
||||
case presentLocationPicker
|
||||
case presentEmojiPicker(itemID: String)
|
||||
case presentRoomMemberDetails(member: RoomMemberProxyProtocol)
|
||||
case presentMessageForwarding(itemID: String)
|
||||
@@ -78,6 +79,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
actionsSubject.send(.presentMediaUploadPicker(.photoLibrary))
|
||||
case .displayDocumentPicker:
|
||||
actionsSubject.send(.presentMediaUploadPicker(.documents))
|
||||
case .displayLocationPicker:
|
||||
actionsSubject.send(.presentLocationPicker)
|
||||
case .displayMediaUploadPreviewScreen(let url):
|
||||
actionsSubject.send(.presentMediaUploadPreviewScreen(url))
|
||||
case .displayRoomMemberDetails(let member):
|
||||
|
||||
@@ -26,6 +26,7 @@ enum RoomScreenViewModelAction {
|
||||
case displayCameraPicker
|
||||
case displayMediaPicker
|
||||
case displayDocumentPicker
|
||||
case displayLocationPicker
|
||||
case displayMediaUploadPreviewScreen(url: URL)
|
||||
case displayRoomMemberDetails(member: RoomMemberProxyProtocol)
|
||||
case displayMessageForwarding(itemID: String)
|
||||
@@ -68,6 +69,7 @@ enum RoomScreenViewAction {
|
||||
case displayCameraPicker
|
||||
case displayMediaPicker
|
||||
case displayDocumentPicker
|
||||
case displayLocationPicker
|
||||
|
||||
case handlePasteOrDrop(provider: NSItemProvider)
|
||||
case tappedOnUser(userID: String)
|
||||
|
||||
@@ -109,6 +109,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
callback?(.displayMediaPicker)
|
||||
case .displayDocumentPicker:
|
||||
callback?(.displayDocumentPicker)
|
||||
case .displayLocationPicker:
|
||||
callback?(.displayLocationPicker)
|
||||
case .handlePasteOrDrop(let provider):
|
||||
handlePasteOrDrop(provider)
|
||||
case .tappedOnUser(userID: let userID):
|
||||
|
||||
@@ -66,7 +66,7 @@ struct TimelineReplyView: View {
|
||||
ReplyView(sender: sender,
|
||||
plainBody: L10n.commonSharedLocation,
|
||||
formattedBody: nil,
|
||||
icon: .init(kind: .icon(Asset.Images.locationPin.name), cornerRadii: iconCornerRadii))
|
||||
icon: .init(kind: .icon(Asset.Images.locationMarker.name), cornerRadii: iconCornerRadii))
|
||||
}
|
||||
default:
|
||||
LoadingReplyView()
|
||||
|
||||
@@ -37,21 +37,30 @@ struct RoomAttachmentPicker: View {
|
||||
context.showAttachmentPopover = false
|
||||
context.send(viewAction: .displayMediaPicker)
|
||||
} label: {
|
||||
PickerLabel(title: L10n.screenRoomAttachmentSourceGallery, systemImageName: "photo.fill")
|
||||
PickerLabel(title: L10n.screenRoomAttachmentSourceGallery, icon: Image(systemName: "photo.fill"))
|
||||
}
|
||||
|
||||
Button {
|
||||
context.showAttachmentPopover = false
|
||||
context.send(viewAction: .displayDocumentPicker)
|
||||
} label: {
|
||||
PickerLabel(title: L10n.screenRoomAttachmentSourceFiles, systemImageName: "paperclip")
|
||||
PickerLabel(title: L10n.screenRoomAttachmentSourceFiles, icon: Image(systemName: "paperclip"))
|
||||
}
|
||||
|
||||
Button {
|
||||
context.showAttachmentPopover = false
|
||||
context.send(viewAction: .displayCameraPicker)
|
||||
} label: {
|
||||
PickerLabel(title: L10n.screenRoomAttachmentSourceCamera, systemImageName: "camera.fill")
|
||||
PickerLabel(title: L10n.screenRoomAttachmentSourceCamera, icon: Image(systemName: "camera.fill"))
|
||||
}
|
||||
|
||||
if ServiceLocator.shared.settings.shareLocationEnabled {
|
||||
Button {
|
||||
context.showAttachmentPopover = false
|
||||
context.send(viewAction: .displayLocationPicker)
|
||||
} label: {
|
||||
PickerLabel(title: L10n.screenRoomAttachmentSourceLocation, icon: Image(asset: Asset.Images.locationPin))
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.top, isPresented ? 20 : 0)
|
||||
@@ -73,14 +82,23 @@ struct RoomAttachmentPicker: View {
|
||||
|
||||
private struct PickerLabel: View {
|
||||
let title: String
|
||||
let systemImageName: String
|
||||
let icon: Image
|
||||
|
||||
init(title: String, icon: Image) {
|
||||
self.title = title
|
||||
self.icon = icon
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Label(title, systemImage: systemImageName)
|
||||
.labelStyle(FixedIconSizeLabelStyle())
|
||||
.multilineTextAlignment(.leading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(16)
|
||||
Label {
|
||||
Text(title)
|
||||
} icon: {
|
||||
icon
|
||||
}
|
||||
.labelStyle(FixedIconSizeLabelStyle())
|
||||
.multilineTextAlignment(.leading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(16)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ struct LocationRoomTimelineView: View {
|
||||
if let geoURI = timelineItem.content.geoURI {
|
||||
let mapSize: CGSize = .init(width: 292, height: 188)
|
||||
MapLibreStaticMapView(geoURI: geoURI, size: mapSize) {
|
||||
LocationPinView()
|
||||
LocationMarkerView()
|
||||
}
|
||||
.frame(width: mapSize.width, height: mapSize.height)
|
||||
} else {
|
||||
|
||||
@@ -30,6 +30,7 @@ struct DeveloperOptionsScreenViewStateBindings {
|
||||
var readReceiptsEnabled: Bool
|
||||
var isEncryptionSyncEnabled: Bool
|
||||
var locationEventsEnabled: Bool
|
||||
var shareLocationEnabled: Bool
|
||||
}
|
||||
|
||||
enum DeveloperOptionsScreenViewAction {
|
||||
@@ -38,5 +39,6 @@ enum DeveloperOptionsScreenViewAction {
|
||||
case changedReadReceiptsEnabled
|
||||
case changedIsEncryptionSyncEnabled
|
||||
case changedLocationEventsEnabled
|
||||
case changedShareLocationEnabled
|
||||
case clearCache
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve
|
||||
userSuggestionsEnabled: appSettings.userSuggestionsEnabled,
|
||||
readReceiptsEnabled: appSettings.readReceiptsEnabled,
|
||||
isEncryptionSyncEnabled: appSettings.isEncryptionSyncEnabled,
|
||||
locationEventsEnabled: appSettings.locationEventsEnabled)
|
||||
locationEventsEnabled: appSettings.locationEventsEnabled,
|
||||
shareLocationEnabled: appSettings.shareLocationEnabled)
|
||||
let state = DeveloperOptionsScreenViewState(bindings: bindings)
|
||||
|
||||
super.init(initialViewState: state)
|
||||
@@ -52,6 +53,8 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve
|
||||
appSettings.isEncryptionSyncEnabled = state.bindings.isEncryptionSyncEnabled
|
||||
case .changedLocationEventsEnabled:
|
||||
appSettings.locationEventsEnabled = state.bindings.locationEventsEnabled
|
||||
case .changedShareLocationEnabled:
|
||||
appSettings.shareLocationEnabled = state.bindings.shareLocationEnabled
|
||||
case .clearCache:
|
||||
callback?(.clearCache)
|
||||
}
|
||||
|
||||
@@ -59,6 +59,13 @@ struct DeveloperOptionsScreen: View {
|
||||
.onChange(of: context.locationEventsEnabled) { _ in
|
||||
context.send(viewAction: .changedLocationEventsEnabled)
|
||||
}
|
||||
|
||||
Toggle(isOn: $context.shareLocationEnabled) {
|
||||
Text("Show share location action")
|
||||
}
|
||||
.onChange(of: context.shareLocationEnabled) { _ in
|
||||
context.send(viewAction: .changedShareLocationEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import CoreLocation
|
||||
import Foundation
|
||||
|
||||
/// A structure that parses a geo URI (i.e. geo:53.99803101552848,-8.25347900390625;u=10) and constructs their constituent parts.
|
||||
@@ -71,3 +72,9 @@ private typealias RegexGeoURI = Regex<(Substring, latitude: Substring, longitude
|
||||
private extension RegexGeoURI {
|
||||
static let standard: Self = /geo:(?<latitude>-?\d+(?:\.\d+)?),(?<longitude>-?\d+(?:\.\d+)?)(?:;u=(?<uncertainty>\d+(?:\.\d+)?))?/
|
||||
}
|
||||
|
||||
extension GeoURI {
|
||||
init(coordinate: CLLocationCoordinate2D) {
|
||||
self.init(latitude: coordinate.latitude, longitude: coordinate.longitude)
|
||||
}
|
||||
}
|
||||
|
||||
44
UnitTests/Sources/StaticLocationScreenViewModelTests.swift
Normal file
44
UnitTests/Sources/StaticLocationScreenViewModelTests.swift
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import XCTest
|
||||
|
||||
@testable import ElementX
|
||||
|
||||
@MainActor
|
||||
class StaticLocationScreenViewModelTests: XCTestCase {
|
||||
var viewModel: StaticLocationScreenViewModelProtocol!
|
||||
|
||||
private let usersSubject = CurrentValueSubject<[UserProfileProxy], Never>([])
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
|
||||
var context: StaticLocationScreenViewModel.Context {
|
||||
viewModel.context
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
let viewModel = StaticLocationScreenViewModel()
|
||||
viewModel.state.isPinDropSharing = false
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
func testUserDidPan() async throws {
|
||||
XCTAssertFalse(context.viewState.isPinDropSharing)
|
||||
context.send(viewAction: .userDidPan)
|
||||
XCTAssertTrue(context.viewState.isPinDropSharing)
|
||||
}
|
||||
}
|
||||
1
changelog.d/1179.feature
Normal file
1
changelog.d/1179.feature
Normal file
@@ -0,0 +1 @@
|
||||
Send pin-drop location
|
||||
Reference in New Issue
Block a user