diff --git a/.gitmodules b/.gitmodules index a9f9d67495ca6f98f5a611842ae41f2f86353448..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "Ring/WhirlyGlobeMaply"] - path = Ring/WhirlyGlobeMaply - url = https://github.com/mousebird/WhirlyGlobe.git diff --git a/Ring/Ring.xcodeproj/project.pbxproj b/Ring/Ring.xcodeproj/project.pbxproj index a82ab9143c23f06dc4520d24a0ef285f2383baaa..7660830fcd48ccb57be93d9219aa55558ecaa99f 100644 --- a/Ring/Ring.xcodeproj/project.pbxproj +++ b/Ring/Ring.xcodeproj/project.pbxproj @@ -208,8 +208,6 @@ 26074FD924F7FF9500374570 /* PreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26074FD824F7FF9500374570 /* PreviewViewController.swift */; }; 26074FDB24F7FFC100374570 /* PreviewContollerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26074FDA24F7FFC100374570 /* PreviewContollerModel.swift */; }; 26074FDD24F7FFF500374570 /* PreviewViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 26074FDC24F7FFF500374570 /* PreviewViewController.storyboard */; }; - 2607B1CE293192BF00F0107C /* WhirlyGlobe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6452143D24B4AB44007203D5 /* WhirlyGlobe.framework */; }; - 2607B1CF293192BF00F0107C /* WhirlyGlobe.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6452143D24B4AB44007203D5 /* WhirlyGlobe.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 260C73F129196B66005C513F /* MessageHistoryVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260C73F029196B66005C513F /* MessageHistoryVM.swift */; }; 260C73F329196C6C005C513F /* MessageStackVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260C73F229196C6C005C513F /* MessageStackVM.swift */; }; 263B7158246D9390007044C4 /* SmartListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263B7157246D9390007044C4 /* SmartListCell.swift */; }; @@ -459,17 +457,12 @@ 62E55B6F1F793ADE00D3FEF4 /* AvatarsColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E55B6E1F793ADE00D3FEF4 /* AvatarsColors.swift */; }; 642AD48424EC64CE00521127 /* CopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 642AD48324EC64CE00521127 /* CopyableLabel.swift */; }; 6452144424B4ACA7007203D5 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6452144324B4ACA7007203D5 /* CoreLocation.framework */; }; - 645BDD7724B7415A009129B1 /* MessageCellLocationSharingSent.xib in Resources */ = {isa = PBXBuildFile; fileRef = 645BDD6C24B7415A009129B1 /* MessageCellLocationSharingSent.xib */; }; - 645BDD7B24B7415A009129B1 /* MessageCellLocationSharingSent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 645BDD7024B7415A009129B1 /* MessageCellLocationSharingSent.swift */; }; 645BDD8124B74BCB009129B1 /* LocationSharingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 645BDD8024B74BCB009129B1 /* LocationSharingService.swift */; }; 648AF76D24ED7CA90004D727 /* UITextView+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648AF76C24ED7CA90004D727 /* UITextView+Helpers.swift */; }; 649AD3C324B4CFC700A0236D /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6452144724B4ACDE007203D5 /* libsqlite3.tbd */; }; 649AD3C624B4CFD500A0236D /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6452144524B4ACC8007203D5 /* libxml2.tbd */; }; 649AD3C724B4D00100A0236D /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6452144124B4AC9F007203D5 /* libc++.tbd */; }; 64DBCD2224DB3CF600CB5CA2 /* UserSearchResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 64DBCD2124DB3CF600CB5CA2 /* UserSearchResponse.m */; }; - 64F8127724B8AA5200A7DE6A /* MessageCellLocationSharingReceived.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F8127324B8AA5200A7DE6A /* MessageCellLocationSharingReceived.swift */; }; - 64F8127824B8AA5200A7DE6A /* MessageCellLocationSharingReceived.xib in Resources */ = {isa = PBXBuildFile; fileRef = 64F8127624B8AA5200A7DE6A /* MessageCellLocationSharingReceived.xib */; }; - 64F8127A24BBC19C00A7DE6A /* MessageCellLocationSharing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F8127924BBC19C00A7DE6A /* MessageCellLocationSharing.swift */; }; 6613A612214AFF4700B497D1 /* ScanViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6613A611214AFF4700B497D1 /* ScanViewController.storyboard */; }; 66266FC021557D2F002757A6 /* ScanViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66266FBF21557D2F002757A6 /* ScanViewModel.swift */; }; 66266FC4215C18F8002757A6 /* Emoji+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66266FC3215C18F8002757A6 /* Emoji+Helpers.swift */; }; @@ -477,16 +470,18 @@ 66ACB430214AE28C00A94162 /* ScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66ACB42F214AE28C00A94162 /* ScanViewController.swift */; }; 66E6381221764C2C005EA2B0 /* GrowingTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66E6381121764C2C005EA2B0 /* GrowingTextView.swift */; }; 66F295DE2166A5930044ED6F /* Devices+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F295DD2166A5930044ED6F /* Devices+Helpers.swift */; }; - BB1E8C7129159DFC005AE1D6 /* SwarmInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1E8C6C29159DEE005AE1D6 /* SwarmInfoView.swift */; }; BB1E8C7329159DFC005AE1D6 /* MembersList.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1E8C6E29159DF6005AE1D6 /* MembersList.swift */; }; BB1E8C7529159DFC005AE1D6 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1E8C7029159DFC005AE1D6 /* SettingsView.swift */; }; BB1E8C7729159E1F005AE1D6 /* SwarmInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1E8C7629159E1F005AE1D6 /* SwarmInfoViewController.swift */; }; BB1E8C7A29159E3E005AE1D6 /* SwarmInfoVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1E8C7929159E3E005AE1D6 /* SwarmInfoVM.swift */; }; BB3AB06C29316FA6006906BA /* ViewDidLoadModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3AB06929316FA0006906BA /* ViewDidLoadModifier.swift */; }; + BB3B0A412971AEE30083CAD8 /* LocationSharingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3B0A402971AEE30083CAD8 /* LocationSharingView.swift */; }; BB3E5815291C138600E85BEA /* SwarmInfoViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB3E5814291C138600E85BEA /* SwarmInfoViewController.storyboard */; }; BB4C6E2629229131001C901A /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4C6E2529229131001C901A /* ColorExtension.swift */; }; BB90263528F0918700B85859 /* PaddingTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB90263428F0918700B85859 /* PaddingTextField.swift */; }; BB91FAAD292EA36200BB41C1 /* EventData.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB91FAA9292EA26900BB41C1 /* EventData.swift */; }; + BBB76E412966062C00A42DF5 /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB76E402966062C00A42DF5 /* MapView.swift */; }; + BBD3D751297B4CA00098DB02 /* SwarmInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBD3D750297B4CA00098DB02 /* SwarmInfoView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -511,20 +506,6 @@ remoteGlobalIDString = 26A88C03266FFFC800888EED; remoteInfo = jamiNotificationExtension; }; - 6452143C24B4AB44007203D5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6452143724B4AB43007203D5 /* WhirlyGlobeMaplyComponent.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2BE536FF1D2499E500B60FAD; - remoteInfo = WhirlyGlobeMaplyComponent; - }; - 6452143E24B4AB44007203D5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6452143724B4AB43007203D5 /* WhirlyGlobeMaplyComponent.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2BE537091D2499E500B60FAD; - remoteInfo = WhirlyGlobeMaplyComponentTests; - }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -551,7 +532,6 @@ 44DFB3B5288704910023878C /* RxRelay.xcframework in Embed Frameworks */, 44DFB39D2887048F0023878C /* RxSwift.xcframework in Embed Frameworks */, 44DFB3A9288704900023878C /* Differentiator.xcframework in Embed Frameworks */, - 2607B1CF293192BF00F0107C /* WhirlyGlobe.framework in Embed Frameworks */, 44DFB3A12887048F0023878C /* RxRealm.xcframework in Embed Frameworks */, 44DFB3B9288704920023878C /* RealmSwift.xcframework in Embed Frameworks */, 44DFB39B2887048E0023878C /* PKHUD.xcframework in Embed Frameworks */, @@ -1102,20 +1082,14 @@ 62E55B6C1F758E6F00D3FEF4 /* String+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Helpers.swift"; sourceTree = "<group>"; }; 62E55B6E1F793ADE00D3FEF4 /* AvatarsColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarsColors.swift; sourceTree = "<group>"; }; 642AD48324EC64CE00521127 /* CopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableLabel.swift; sourceTree = "<group>"; }; - 6452143724B4AB43007203D5 /* WhirlyGlobeMaplyComponent.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = WhirlyGlobeMaplyComponent.xcodeproj; path = "WhirlyGlobeMaply/ios/library/WhirlyGlobe-MaplyComponent/WhirlyGlobeMaplyComponent.xcodeproj"; sourceTree = "<group>"; }; 6452144124B4AC9F007203D5 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; 6452144324B4ACA7007203D5 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; 6452144524B4ACC8007203D5 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; }; 6452144724B4ACDE007203D5 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; - 645BDD6C24B7415A009129B1 /* MessageCellLocationSharingSent.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessageCellLocationSharingSent.xib; sourceTree = "<group>"; }; - 645BDD7024B7415A009129B1 /* MessageCellLocationSharingSent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCellLocationSharingSent.swift; sourceTree = "<group>"; }; 645BDD8024B74BCB009129B1 /* LocationSharingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSharingService.swift; sourceTree = "<group>"; }; 648AF76C24ED7CA90004D727 /* UITextView+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Helpers.swift"; sourceTree = "<group>"; }; 64DBCD1E24DB3CA900CB5CA2 /* UserSearchResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserSearchResponse.h; sourceTree = "<group>"; }; 64DBCD2124DB3CF600CB5CA2 /* UserSearchResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserSearchResponse.m; sourceTree = "<group>"; }; - 64F8127324B8AA5200A7DE6A /* MessageCellLocationSharingReceived.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCellLocationSharingReceived.swift; sourceTree = "<group>"; }; - 64F8127624B8AA5200A7DE6A /* MessageCellLocationSharingReceived.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessageCellLocationSharingReceived.xib; sourceTree = "<group>"; }; - 64F8127924BBC19C00A7DE6A /* MessageCellLocationSharing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCellLocationSharing.swift; sourceTree = "<group>"; }; 6613A611214AFF4700B497D1 /* ScanViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ScanViewController.storyboard; sourceTree = "<group>"; }; 66266FBF21557D2F002757A6 /* ScanViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanViewModel.swift; sourceTree = "<group>"; }; 66266FC3215C18F8002757A6 /* Emoji+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Emoji+Helpers.swift"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; @@ -1123,16 +1097,18 @@ 66ACB42F214AE28C00A94162 /* ScanViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanViewController.swift; sourceTree = "<group>"; }; 66E6381121764C2C005EA2B0 /* GrowingTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrowingTextView.swift; sourceTree = "<group>"; }; 66F295DD2166A5930044ED6F /* Devices+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Devices+Helpers.swift"; sourceTree = "<group>"; }; - BB1E8C6C29159DEE005AE1D6 /* SwarmInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwarmInfoView.swift; sourceTree = "<group>"; }; BB1E8C6E29159DF6005AE1D6 /* MembersList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MembersList.swift; sourceTree = "<group>"; }; BB1E8C7029159DFC005AE1D6 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; }; BB1E8C7629159E1F005AE1D6 /* SwarmInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwarmInfoViewController.swift; sourceTree = "<group>"; }; BB1E8C7929159E3E005AE1D6 /* SwarmInfoVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwarmInfoVM.swift; sourceTree = "<group>"; }; BB3AB06929316FA0006906BA /* ViewDidLoadModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewDidLoadModifier.swift; sourceTree = "<group>"; }; + BB3B0A402971AEE30083CAD8 /* LocationSharingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSharingView.swift; sourceTree = "<group>"; }; BB3E5814291C138600E85BEA /* SwarmInfoViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SwarmInfoViewController.storyboard; sourceTree = "<group>"; }; BB4C6E2529229131001C901A /* ColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExtension.swift; sourceTree = "<group>"; }; BB90263428F0918700B85859 /* PaddingTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaddingTextField.swift; sourceTree = "<group>"; }; BB91FAA9292EA26900BB41C1 /* EventData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventData.swift; sourceTree = "<group>"; }; + BBB76E402966062C00A42DF5 /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = "<group>"; }; + BBD3D750297B4CA00098DB02 /* SwarmInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwarmInfoView.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1154,7 +1130,6 @@ 269DA07428D0D366007D51D6 /* libspeexdsp.a in Frameworks */, 269DA06C28D0D366007D51D6 /* libpjsip-simple.a in Frameworks */, 6452144424B4ACA7007203D5 /* CoreLocation.framework in Frameworks */, - 2607B1CE293192BF00F0107C /* WhirlyGlobe.framework in Frameworks */, 269DA05C28D0D366007D51D6 /* libgmp.a in Frameworks */, 269DA05328D0D366007D51D6 /* libargon2.a in Frameworks */, 44DFB3AC288704900023878C /* GSKStretchyHeaderView.xcframework in Frameworks */, @@ -1396,7 +1371,6 @@ 6452144524B4ACC8007203D5 /* libxml2.tbd */, 6452144324B4ACA7007203D5 /* CoreLocation.framework */, 6452144124B4AC9F007203D5 /* libc++.tbd */, - 6452143724B4AB43007203D5 /* WhirlyGlobeMaplyComponent.xcodeproj */, 0E4A51CB23282BCC00357AFC /* libhttp_parser.a */, 0ECB4E2922B2D4BB0097CD7B /* CallKit.framework */, 0E639459224AB32200C0890A /* Contacts.framework */, @@ -2088,11 +2062,6 @@ 62AD58472056DA6800AF0701 /* MessageCellDataTransferReceived.xib */, 62AD58492056DADF00AF0701 /* MessageCellDataTransferReceived.swift */, 62AD584B2056DB2700AF0701 /* MessageCellDataTransferSent.swift */, - 64F8127924BBC19C00A7DE6A /* MessageCellLocationSharing.swift */, - 645BDD7024B7415A009129B1 /* MessageCellLocationSharingSent.swift */, - 645BDD6C24B7415A009129B1 /* MessageCellLocationSharingSent.xib */, - 64F8127324B8AA5200A7DE6A /* MessageCellLocationSharingReceived.swift */, - 64F8127624B8AA5200A7DE6A /* MessageCellLocationSharingReceived.xib */, ); path = Cells; sourceTree = "<group>"; @@ -2205,6 +2174,8 @@ children = ( 26EF35EE29075A5300D97E14 /* MessageStackView.swift */, 269DA09C28E23F57007D51D6 /* MessagesListView.swift */, + BB3B0A402971AEE30083CAD8 /* LocationSharingView.swift */, + BBB76E402966062C00A42DF5 /* MapView.swift */, 26EF35E628E3401800D97E14 /* ReplyHistory.swift */, 26EF35E828E3847100D97E14 /* MessageContentView.swift */, 269DA09828E23D37007D51D6 /* MessageRowView.swift */, @@ -2350,15 +2321,6 @@ name = Cells; sourceTree = "<group>"; }; - 6452143824B4AB43007203D5 /* Products */ = { - isa = PBXGroup; - children = ( - 6452143D24B4AB44007203D5 /* WhirlyGlobe.framework */, - 6452143F24B4AB44007203D5 /* WhirlyGlobeMaplyComponentTests.xctest */, - ); - name = Products; - sourceTree = "<group>"; - }; 6613A610214AF8B100B497D1 /* QRCode */ = { isa = PBXGroup; children = ( @@ -2386,8 +2348,8 @@ children = ( BB1E8C6E29159DF6005AE1D6 /* MembersList.swift */, BB1E8C7029159DFC005AE1D6 /* SettingsView.swift */, + BBD3D750297B4CA00098DB02 /* SwarmInfoView.swift */, BB3AB06929316FA0006906BA /* ViewDidLoadModifier.swift */, - BB1E8C6C29159DEE005AE1D6 /* SwarmInfoView.swift */, 1DF75AC5296E0C2A0055EA87 /* AddMoreParticipantsInSwarm.swift */, ); path = View; @@ -2585,12 +2547,6 @@ mainGroup = 043999EA1D1C2D9D00E99CD9; productRefGroup = 043999F41D1C2D9D00E99CD9 /* Products */; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 6452143824B4AB43007203D5 /* Products */; - ProjectRef = 6452143724B4AB43007203D5 /* WhirlyGlobeMaplyComponent.xcodeproj */; - }, - ); projectRoot = ""; targets = ( 043999F21D1C2D9D00E99CD9 /* Ring */, @@ -2602,23 +2558,6 @@ }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - 6452143D24B4AB44007203D5 /* WhirlyGlobe.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = WhirlyGlobe.framework; - remoteRef = 6452143C24B4AB44007203D5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 6452143F24B4AB44007203D5 /* WhirlyGlobeMaplyComponentTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = WhirlyGlobeMaplyComponentTests.xctest; - remoteRef = 6452143E24B4AB44007203D5 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ 043999F11D1C2D9D00E99CD9 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -2663,9 +2602,7 @@ 446FAF192373424700519C4F /* SendFileViewController.storyboard in Resources */, BB3E5815291C138600E85BEA /* SwarmInfoViewController.storyboard in Resources */, 0EF49AA323828CD00064CD98 /* ConferenceParticipantView.xib in Resources */, - 645BDD7724B7415A009129B1 /* MessageCellLocationSharingSent.xib in Resources */, 0E72374A20460320006B0C7D /* ProfileHeaderView.xib in Resources */, - 64F8127824B8AA5200A7DE6A /* MessageCellLocationSharingReceived.xib in Resources */, 4430A66D236CBC5900747177 /* ContactPickerViewController.storyboard in Resources */, 0E96ED75225D06250016C07D /* GeneralSettingsViewController.storyboard in Resources */, 0EB1A5CF1F8EBE03009923E2 /* DeviceCell.xib in Resources */, @@ -2801,7 +2738,7 @@ 0E49096A1FEAB156005CAA50 /* CallsAdapter.mm in Sources */, 1A2D18A61F27F7A400B2C785 /* UIViewController+Rx.swift in Sources */, 26BC15EE29302B00003FC8D1 /* ContactMessageVM.swift in Sources */, - 64F8127724B8AA5200A7DE6A /* MessageCellLocationSharingReceived.swift in Sources */, + BB3B0A412971AEE30083CAD8 /* LocationSharingView.swift in Sources */, 66E6381221764C2C005EA2B0 /* GrowingTextView.swift in Sources */, 267AD77E252B979F00047593 /* ConferenceParticipant.swift in Sources */, 0E7CF4DB20164B6700CD967D /* ButtonsContainerView.swift in Sources */, @@ -2860,6 +2797,7 @@ 1ABE07E21F0D924700D36361 /* Strings.swift in Sources */, 0E6F544D223BFE3E00ECC3CE /* DisposableCell.swift in Sources */, 1DE93595291B119900E426CF /* SwarmCreationViewController.swift in Sources */, + BBD3D751297B4CA00098DB02 /* SwarmInfoView.swift in Sources */, 621231FB1F8D6FEE009B86F0 /* MessageCell.swift in Sources */, 2662FC7D246B78E800FA7782 /* IncognitoSmartListViewController.swift in Sources */, 56AC650E1E85694D00EA1AA9 /* DesignableTextField.swift in Sources */, @@ -2882,7 +2820,6 @@ BB1E8C7729159E1F005AE1D6 /* SwarmInfoViewController.swift in Sources */, 0E403F811F7D797300C80BC2 /* MessageCellGenerated.swift in Sources */, 2659F65827483656009107F1 /* VideoManager.swift in Sources */, - 64F8127A24BBC19C00A7DE6A /* MessageCellLocationSharing.swift in Sources */, 0E6D959C2407116E00996A28 /* LinkToAccountManagerViewModel.swift in Sources */, 62AD584C2056DB2700AF0701 /* MessageCellDataTransferSent.swift in Sources */, 0ECA5683243394960055D31E /* MigrateAccountViewController.swift in Sources */, @@ -2903,6 +2840,7 @@ 0E36979D20322D75009A68CA /* BlockListViewModel.swift in Sources */, 1A2D18C21F29180700B2C785 /* AccountCredentialsModel.swift in Sources */, 1A2D18FF1F29352D00B2C785 /* MeViewModel.swift in Sources */, + BBB76E412966062C00A42DF5 /* MapView.swift in Sources */, 62A88D391F6C323500F8AB18 /* PresenceAdapter.mm in Sources */, 1DF75AC6296E0C2A0055EA87 /* AddMoreParticipantsInSwarm.swift in Sources */, 1A2D18B71F29164700B2C785 /* SmartlistViewModel.swift in Sources */, @@ -2936,7 +2874,6 @@ 0E3697A1203235EA009A68CA /* BannedContactItem.swift in Sources */, 265DFAF32929C01400834B97 /* MessageRowVM.swift in Sources */, 56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */, - 645BDD7B24B7415A009129B1 /* MessageCellLocationSharingSent.swift in Sources */, BB1E8C7A29159E3E005AE1D6 /* SwarmInfoVM.swift in Sources */, 66F295DE2166A5930044ED6F /* Devices+Helpers.swift in Sources */, 1A2041911F1FD46300C08435 /* DesignableView.swift in Sources */, @@ -2945,7 +2882,6 @@ 1A3D28A91F0EBF0200B524EE /* UIView+Ring.swift in Sources */, 1A2041881F1EA1EA00C08435 /* CreateAccountViewModel.swift in Sources */, 62E55B6D1F758E6F00D3FEF4 /* String+Helpers.swift in Sources */, - BB1E8C7129159DFC005AE1D6 /* SwarmInfoView.swift in Sources */, 1ABE07D21F0D8FE800D36361 /* Images.swift in Sources */, 0273C3081E0C68BF00CF00BA /* DesignableButton.swift in Sources */, 1A5DC0321F3566140075E8EF /* ConversationSection.swift in Sources */, @@ -3253,10 +3189,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Ring/RingPrefixHeader.pch; - HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/../fat/include", - "$(SRCROOT)/WhirlyGlobeMaply/ios/library/WhirlyGlobe-MaplyComponent/include/**", - ); + HEADER_SEARCH_PATHS = "$(SRCROOT)/../fat/include"; INFOPLIST_FILE = Ring/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = ( @@ -3294,10 +3227,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Ring/RingPrefixHeader.pch; - HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/../fat/include", - "$(SRCROOT)/WhirlyGlobeMaply/ios/library/WhirlyGlobe-MaplyComponent/include/**", - ); + HEADER_SEARCH_PATHS = "$(SRCROOT)/../fat/include"; INFOPLIST_FILE = Ring/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = ( @@ -3325,7 +3255,6 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/../fat/include", "$(SRCROOT)/../../daemon/contrib/native-arm64/ffmpeg", - "$(SRCROOT)/WhirlyGlobeMaply/ios/library/WhirlyGlobe-MaplyComponent/include/**", ); INFOPLIST_FILE = RingTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -3351,7 +3280,6 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/../fat/include", "$(SRCROOT)/../../daemon/contrib/native-arm64/ffmpeg", - "$(SRCROOT)/WhirlyGlobeMaply/ios/library/WhirlyGlobe-MaplyComponent/include/**", ); INFOPLIST_FILE = RingTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -3631,10 +3559,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Ring/RingPrefixHeader.pch; - HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/../fat/include", - "$(SRCROOT)/WhirlyGlobeMaply/ios/library/WhirlyGlobe-MaplyComponent/include/**", - ); + HEADER_SEARCH_PATHS = "$(SRCROOT)/../fat/include"; INFOPLIST_FILE = Ring/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.5; LD_RUNPATH_SEARCH_PATHS = ( @@ -3663,7 +3588,6 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/../fat/include", "$(SRCROOT)/../../daemon/contrib/native-arm64/ffmpeg", - "$(SRCROOT)/WhirlyGlobeMaply/ios/library/WhirlyGlobe-MaplyComponent/include/**", ); INFOPLIST_FILE = RingTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Ring/Ring/Bridging/Ring-Bridging-Header.h b/Ring/Ring/Bridging/Ring-Bridging-Header.h index 2fa398e91f3514dfa3458c3f73476f5a97fe89b2..26bece8cad82dde793d1d7e1e7f7d96f92a94cba 100644 --- a/Ring/Ring/Bridging/Ring-Bridging-Header.h +++ b/Ring/Ring/Bridging/Ring-Bridging-Header.h @@ -47,6 +47,7 @@ #import <UserNotifications/UserNotifications.h> #import <GSKStretchyHeaderView/GSKStretchyHeaderView.h> #import "DataTransferAdapter.h" -#import "../../WhirlyGlobeMaply/ios/library/WhirlyGlobe-MaplyComponent/include/MaplyBridge.h" #import "ObjCHandler.h" +#import <CoreLocation/CoreLocation.h> +#import <MapKit/MapKit.h> #import "LinkPresentation/LinkPresentation.h" diff --git a/Ring/Ring/Extensions/UIImage+Helpers.swift b/Ring/Ring/Extensions/UIImage+Helpers.swift index 8e53bfd6d89b33da5b36a862fdb5c9fdb1782e14..d4ad761644976930dc3eb88bbb950fbc08162b19 100644 --- a/Ring/Ring/Extensions/UIImage+Helpers.swift +++ b/Ring/Ring/Extensions/UIImage+Helpers.swift @@ -265,7 +265,8 @@ extension UIImage { return image } - class func defaultJamiAvatarFor(profileName: String?, account: AccountModel) -> UIImage { + class func defaultJamiAvatarFor(profileName: String?, account: AccountModel?) -> UIImage { + guard let account = account else { return UIImage(asset: Asset.icContactPicture)! } let image = UIImage(asset: Asset.icContactPicture)! .withAlignmentRectInsets(UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)) var name: String? = (profileName != nil) ? profileName : @@ -298,7 +299,7 @@ extension UIImage { let width = spacing + leftImage.size.width + rightImage.size.width let size = CGSize(width: width, height: height) - UIGraphicsBeginImageContextWithOptions(size, false, 0.0) + UIGraphicsBeginImageContextWithOptions(size, false, 0) leftImage.draw(in: CGRect(x: 0, y: 0, width: leftImage.size.width, height: height)) rightImage.draw(in: CGRect(x: spacing + leftImage.size.width, y: 0, width: rightImage.size.width, height: height)) diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharing.swift b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharing.swift deleted file mode 100644 index ffbb592de930222677221c917472ad5ae621410b..0000000000000000000000000000000000000000 --- a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharing.swift +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright (C) 2020 Savoir-faire Linux Inc. - * - * Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -import UIKit -import Reusable -import RxCocoa - -class MessageCellLocationSharing: MessageCell { - - typealias MarkerAndComponentObject = (marker: MaplyScreenMarker, componentObject: MaplyComponentObject?) - - private static let osmCopyrightAndLicenseURL = "https://www.openstreetmap.org/copyright" - private static let remoteTileSourceBaseUrl = MessageCellLocationSharing.getBaseURL() - - @IBOutlet weak var locationSharingMessageTextView: UITextView! - @IBOutlet weak var bubbleHeight: NSLayoutConstraint! - var loader: MaplyQuadImageLoader! - var fetcher: MaplyRemoteTileFetcher! - - var xButton: UIButton? - var myPositionButton: UIButton? - - let locationTapped = BehaviorRelay<(Bool, Bool)>(value: (false, false)) // (shouldAnimate, expanding) - - var maplyViewController: MaplyBaseViewController? // protected in Swift? - /// The usage of this variable allows for the view to not be refreshed on reuse (e.g. when scrolling) - private var preventUnnecessaryReuseCounter = 0 - - override func willRemoveSubview(_ subview: UIView) { - super.willRemoveSubview(subview) - self.preventUnnecessaryReuseCounter = 0 - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - NotificationCenter.default.addObserver(self, selector: #selector(shrink), name: UIResponder.keyboardWillShowNotification, object: nil) - } - - override func configureFromItem(_ conversationViewModel: ConversationViewModel, _ items: [MessageViewModel]?, cellForRowAt indexPath: IndexPath) { - super.configureFromItem(conversationViewModel, items, cellForRowAt: indexPath) - - self.shrink() - - if self.maplyViewController as? MaplyViewController == nil || preventUnnecessaryReuseCounter < 2 { - self.setupMaply() - self.displayMapTile() - - self.configureTapGesture() - self.setupOSMCopyrightButton() - let name = (conversationViewModel.displayName.value != nil && !conversationViewModel.displayName.value!.isEmpty) ? - conversationViewModel.displayName.value! : conversationViewModel.userName.value - self.setUplocationSharingMessageTextView(username: name) - preventUnnecessaryReuseCounter += 1 - } - } - - func setUplocationSharingMessageTextView(username: String) { - self.locationSharingMessageTextView.isEditable = false - self.locationSharingMessageTextView.textColor = UIColor.jamiTextBlue - self.locationSharingMessageTextView.backgroundColor = UIColor.jamiBackgroundColor.withAlphaComponent(0.75) - self.bubble.addSubview(self.locationSharingMessageTextView) - } - - override func configureTapGesture() { - self.bubble.isUserInteractionEnabled = true - self.tapGestureRecognizer = UITapGestureRecognizer() - self.tapGestureRecognizer!.rx.event.bind(onNext: { [weak self] _ in self?.onTapGesture() }).disposed(by: self.disposeBag) - self.bubble.addGestureRecognizer(tapGestureRecognizer!) - } - - override func onTapGesture() { - if !locationTapped.value.1 { - self.expandOrShrink() - } - } - - private func removeTapDefaultGestureFromMaply() { - if let whirlyKitEAGLView = (self.maplyViewController as? MaplyViewController)?.view.subviews[0], - let gesture = whirlyKitEAGLView.gestureRecognizers?.first(where: { (gesture) -> Bool in gesture is UITapGestureRecognizer }) { - whirlyKitEAGLView.removeGestureRecognizer(gesture) - } - } - - private func setupMaply() { - self.maplyViewController?.view.removeFromSuperview() - - self.maplyViewController = MaplyViewController(mapType: .typeFlat) - self.removeTapDefaultGestureFromMaply() - - self.bubble.addSubview(self.maplyViewController!.view) - self.maplyViewController!.view.frame = self.bubble.bounds - } - - lazy var samplingParams: MaplySamplingParams = { - let samplingParams = MaplySamplingParams() - samplingParams.coverPoles = true - samplingParams.edgeMatching = false - samplingParams.singleLevel = false - samplingParams.coverPoles = false - return samplingParams - }() - - private func displayMapTile() { - // TODO: implement location map with a new API - self.maplyViewController!.clearColor = UIColor.white - - // thirty fps if we can get it - self.maplyViewController!.frameInterval = 2 - let baseCacheDir = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0] - let tilesCacheDir = "\(baseCacheDir)/openstreetmap/" - let maxZoom = Int32(19) - let info = MaplyRemoteTileFetchInfo() - let request = URLRequest(url: URL(string: MessageCellLocationSharing.remoteTileSourceBaseUrl)!) - info.urlReq = request as URLRequest - let tileSource = MaplyRemoteTileInfoNew(baseURL: MessageCellLocationSharing.remoteTileSourceBaseUrl, - minZoom: 0, - maxZoom: maxZoom) - tileSource.cacheDir = tilesCacheDir - fetcher = MaplyRemoteTileFetcher(name: "fetcher", connections: 2) - loader = MaplyQuadImageLoader(params: samplingParams, tileInfo: tileSource, viewC: maplyViewController!) - loader?.setTileFetcher(fetcher) - - if let mapViewC = self.maplyViewController as? MaplyViewController { - self.toggleMaplyGesture(false) - mapViewC.height = 0.0001 - } - } - - private func toggleMaplyGesture(_ value: Bool) { - if let mapViewC = self.maplyViewController as? MaplyViewController { - mapViewC.panGesture = value - mapViewC.pinchGesture = value - mapViewC.rotateGesture = value - mapViewC.twoFingerTapGesture = value - mapViewC.doubleTapDragGesture = value - mapViewC.doubleTapZoomGesture = value - } - } - - private static func getBaseURL() -> String { - // OpenStreetMap Tiles, © OpenStreetMap contributors - let urls = ["https://a.tile.openstreetmap.org/", - "https://b.tile.openstreetmap.org/", - "https://c.tile.openstreetmap.org/"] - let rngIndex = Int.random(in: 0 ..< 3) - - return urls[rngIndex] - } -} - -// For children -extension MessageCellLocationSharing { - func updateLocationAndMarker(location: CLLocationCoordinate2D, - imageData: Data?, - username: String?, - marker: MaplyScreenMarker, - markerDump: MaplyComponentObject?, - tryToAnimateToMarker: Bool = true) -> MaplyComponentObject? { - // only the first time - if markerDump == nil { - marker.layoutImportance = MAXFLOAT - if let imageData = imageData, let circledImage = UIImage(data: imageData)?.circleMasked { - marker.image = circledImage - } else { - marker.image = AvatarView(profileImageData: nil, username: username ?? "", size: 24).convertToImage() - } - marker.size = CGSize(width: 24, height: 24) - } - - let maplyCoordonate = MaplyCoordinateMakeWithDegrees(Float(location.longitude), Float(location.latitude)) - - marker.loc.x = maplyCoordonate.x - marker.loc.y = maplyCoordonate.y - - var dumpToReturn: MaplyComponentObject? - - if let mapViewC = self.maplyViewController as? MaplyViewController { - if markerDump != nil { - self.maplyViewController!.remove(markerDump!) - } - dumpToReturn = self.maplyViewController!.addScreenMarkers([marker], desc: nil) - - if tryToAnimateToMarker && !locationTapped.value.1 { - mapViewC.animate(toPosition: maplyCoordonate, time: 0.1) - } - } - return dumpToReturn - } -} - -// For OSM Copyrights -extension MessageCellLocationSharing { - private func setupOSMCopyrightButton() { - let infoButton = UIButton(type: .detailDisclosure) - infoButton.backgroundColor = UIColor.init(white: 0.75, alpha: 0.25) - infoButton.cornerRadius = infoButton.frame.height / 2.0 - self.bubble.addSubview(infoButton) - - infoButton.translatesAutoresizingMaskIntoConstraints = false - let constraintX = NSLayoutConstraint(item: infoButton, - attribute: NSLayoutConstraint.Attribute.centerX, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: self.bubble, - attribute: NSLayoutConstraint.Attribute.right, - multiplier: 1, - constant: -28) - let constraintY = NSLayoutConstraint(item: infoButton, - attribute: NSLayoutConstraint.Attribute.centerY, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: self.bubble, - attribute: NSLayoutConstraint.Attribute.bottom, - multiplier: 1, - constant: -28) - NSLayoutConstraint.activate([constraintX, constraintY]) - infoButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside) - } - - @objc - func buttonAction(sender: UIButton!) { - let alert = UIAlertController.init(title: L10n.Alerts.mapInformation, - message: L10n.Alerts.openStreetMapCopyright, - preferredStyle: .alert) - alert.addAction(.init(title: L10n.Alerts.openStreetMapCopyrightMoreInfo, style: UIAlertAction.Style.default, handler: { (_) in - if let url = URL(string: MessageCellLocationSharing.osmCopyrightAndLicenseURL), UIApplication.shared.canOpenURL(url) { - UIApplication.shared.open(url, completionHandler: nil) - } - })) - alert.addAction(.init(title: L10n.Global.ok, style: UIAlertAction.Style.cancel)) - - self.window?.rootViewController?.present(alert, animated: true, completion: nil) - } -} - -// For bigger map -extension MessageCellLocationSharing { - @objc - private func shrink() { - if self.bubbleHeight.constant > 220 { - self.expandOrShrink() - } - } - - private func expandOrShrink() { - let shouldExpand = !self.locationTapped.value.1 - - self.updateHeight(shouldExpand) - // self.updateWidth(shouldExpand) now in controller, for animation - self.toggleMaplyGesture(shouldExpand) - - if shouldExpand { - self.setupXButton() - self.setupMyPositionButton() - } else { - self.removeXButton() - self.removeMyPositionButton() - } - - self.locationTapped.accept((true, shouldExpand)) - } - - private func updateHeight(_ shouldExpand: Bool, extendedHeight: CGFloat = 350) { - let normalHeight: CGFloat = 220 - if shouldExpand { - self.bubbleHeight.constant = extendedHeight - } else { - self.bubbleHeight.constant = normalHeight - } - } - - @objc - func updateWidth(_ shouldExpand: Bool) { - fatalError("Must override this function") - } - - func expandHeight(_ shouldExpand: Bool, _ height: CGFloat) { - if shouldExpand { - let percentage: CGFloat = self.hasTopNotch() ? 0.88 : 0.91 - - self.updateHeight(shouldExpand, - extendedHeight: (height * percentage) - self.bubbleTopConstraint.constant) - } - } - - func hasTopNotch() -> Bool { - return UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.safeAreaInsets.top ?? 0 > 20 - } -} - -extension MessageCellLocationSharing { - private func setupXButton() { - self.setUpLocationSharingMessageTextViewInset(expanding: true) - - self.xButton = UIButton() - let xButton = self.xButton! - xButton.setBackgroundImage(UIImage(asset: Asset.closeIcon)!, for: UIControl.State.normal) - xButton.tintColor = UIColor.jamiTextBlue - self.bubble.addSubview(xButton) - - xButton.translatesAutoresizingMaskIntoConstraints = false - let constraintX = NSLayoutConstraint(item: xButton, - attribute: NSLayoutConstraint.Attribute.centerX, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: self.bubble, - attribute: NSLayoutConstraint.Attribute.left, - multiplier: 1, - constant: 24) - let constraintY = NSLayoutConstraint(item: xButton, - attribute: NSLayoutConstraint.Attribute.centerY, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: self.bubble, - attribute: NSLayoutConstraint.Attribute.top, - multiplier: 1, - constant: 24) - let height = NSLayoutConstraint(item: xButton, - attribute: NSLayoutConstraint.Attribute.height, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: nil, - attribute: NSLayoutConstraint.Attribute.notAnAttribute, - multiplier: 1, - constant: 32) - let width = NSLayoutConstraint(item: xButton, - attribute: NSLayoutConstraint.Attribute.width, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: nil, - attribute: NSLayoutConstraint.Attribute.notAnAttribute, - multiplier: 1, - constant: 32) - NSLayoutConstraint.activate([constraintX, constraintY, height, width]) - xButton.addTarget(self, action: #selector(XButtonAction), for: .touchUpInside) - } - - private func removeXButton() { - self.setUpLocationSharingMessageTextViewInset(expanding: false) - - self.xButton?.removeFromSuperview() - self.xButton = nil - } - - @objc - func XButtonAction(sender: UIButton!) { - self.expandOrShrink() - } - - func setUpLocationSharingMessageTextViewInset(expanding: Bool) { - if expanding { - self.locationSharingMessageTextView.textContainerInset.left = 48 - self.locationSharingMessageTextView.textAlignment = .left - } else { - self.locationSharingMessageTextView.textContainerInset.left = self.locationSharingMessageTextView.textContainerInset.right - self.locationSharingMessageTextView.textAlignment = .center - } - } - - func onAnimationCompletion() { - self.locationSharingMessageTextView.adjustHeightFromContentSize(minHeight: 48) - } -} - -extension MessageCellLocationSharing { - private func setupMyPositionButton() { - if self as? MessageCellLocationSharingSent != nil { - self.myPositionButton = UIButton() - let myLocation = self.myPositionButton! - myLocation.setImage(UIImage(asset: Asset.myLocation)!, for: .normal) - myLocation.tintColor = UIColor.jamiTextBlue - myLocation.backgroundColor = UIColor.jamiBackgroundColor.withAlphaComponent(0.75) - myLocation.cornerRadius = 16 - self.bubble.addSubview(myLocation) - - myLocation.translatesAutoresizingMaskIntoConstraints = false - let constraintX = NSLayoutConstraint(item: myLocation, - attribute: NSLayoutConstraint.Attribute.centerX, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: self.bubble, - attribute: NSLayoutConstraint.Attribute.right, - multiplier: 1, - constant: -28) - let constraintY = NSLayoutConstraint(item: myLocation, - attribute: NSLayoutConstraint.Attribute.centerY, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: self.bubble, - attribute: NSLayoutConstraint.Attribute.bottom, - multiplier: 1, - constant: -70) - let height = NSLayoutConstraint(item: myLocation, - attribute: NSLayoutConstraint.Attribute.height, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: nil, - attribute: NSLayoutConstraint.Attribute.notAnAttribute, - multiplier: 1, - constant: 32) - let width = NSLayoutConstraint(item: myLocation, - attribute: NSLayoutConstraint.Attribute.width, - relatedBy: NSLayoutConstraint.Relation.equal, - toItem: nil, - attribute: NSLayoutConstraint.Attribute.notAnAttribute, - multiplier: 1, - constant: 32) - NSLayoutConstraint.activate([constraintX, constraintY, height, width]) - myLocation.addTarget(self, action: #selector(myPositionButtonAction), for: .touchUpInside) - } - } - - private func removeMyPositionButton() { - self.myPositionButton?.removeFromSuperview() - self.myPositionButton = nil - } - - @objc - func myPositionButtonAction(sender: UIButton!) { - fatalError("Must override this function") - } -} diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingReceived.swift b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingReceived.swift deleted file mode 100644 index e76f0332cc38f696d9dd36d467394c75a49188f5..0000000000000000000000000000000000000000 --- a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingReceived.swift +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2020 Savoir-faire Linux Inc. - * - * Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -import UIKit -import Reusable - -class MessageCellLocationSharingReceived: MessageCellLocationSharing { - - /// Primary location - private var myContactsLocation: MarkerAndComponentObject = (marker: MaplyScreenMarker(), componentObject: nil) - /// Secondary location - private var myLocation: MarkerAndComponentObject = (marker: MaplyScreenMarker(), componentObject: nil) - - @IBOutlet weak var receivedBubbleLeading: NSLayoutConstraint! - @IBOutlet weak var receivedBubbleTrailling: NSLayoutConstraint! - - override func configureFromItem(_ conversationViewModel: ConversationViewModel, _ items: [MessageViewModel]?, cellForRowAt indexPath: IndexPath) { - super.configureFromItem(conversationViewModel, items, cellForRowAt: indexPath) - - // Primary location - conversationViewModel.myContactsLocation - .subscribe(onNext: { [weak self, weak conversationViewModel] location in - guard let self = self, let location = location else { return } - - self.myContactsLocation.componentObject = self.updateLocationAndMarker(location: location, - imageData: conversationViewModel?.profileImageData.value, - username: conversationViewModel?.userName.value, - marker: self.myContactsLocation.marker, - markerDump: self.myContactsLocation.componentObject) - }) - .disposed(by: self.disposeBag) - - // Secondary location - conversationViewModel.myLocation - .subscribe(onNext: { [weak self, weak conversationViewModel] location in - guard let self = self else { return } - - if let location = location?.coordinate { - self.myLocation.componentObject = self.updateLocationAndMarker(location: location, - imageData: conversationViewModel?.myOwnProfileImageData, - username: conversationViewModel?.userName.value, - marker: self.myLocation.marker, - markerDump: self.myLocation.componentObject, - tryToAnimateToMarker: false) - } else if let componentObject = self.myLocation.componentObject, - let maplyViewController = self.maplyViewController { - maplyViewController.remove(componentObject) - self.myLocation.componentObject = nil - } - }) - .disposed(by: self.disposeBag) - } - - override func setUplocationSharingMessageTextView(username: String) { - super.setUplocationSharingMessageTextView(username: username) - self.locationSharingMessageTextView.text = L10n.Conversation.explanationReceivingLocationFrom + username - self.locationSharingMessageTextView.adjustHeightFromContentSize() - } - - override func updateWidth(_ shouldExpand: Bool) { - let normalValue: CGFloat = 116 - let extendedValue: CGFloat = 16 - if shouldExpand { - self.receivedBubbleTrailling.constant = extendedValue - self.receivedBubbleLeading.constant = extendedValue - } else { - self.receivedBubbleTrailling.constant = normalValue - self.receivedBubbleLeading.constant = 64 - } - } -} diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingReceived.xib b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingReceived.xib deleted file mode 100644 index 019f45b2de43a31ae69e3eb02ea623f2bde190be..0000000000000000000000000000000000000000 --- a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingReceived.xib +++ /dev/null @@ -1,106 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> - <device id="retina4_7" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> - </dependencies> - <objects> - <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> - <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="253" id="3QB-g7-MaS" userLabel="Message Cell Location Sharing Received" customClass="MessageCellLocationSharingReceived" customModule="Ring" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="510" height="240"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> - <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" tableViewCell="3QB-g7-MaS" id="Dkz-SA-3Af"> - <rect key="frame" x="0.0" y="0.0" width="510" height="240"/> - <autoresizingMask key="autoresizingMask"/> - <subviews> - <view clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="xVQ-Jk-Sxy" customClass="MessageBubble" customModule="Ring" customModuleProvider="target"> - <rect key="frame" x="64" y="8" width="330" height="220"/> - <subviews> - <textView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" fixedFrame="YES" scrollEnabled="NO" editable="NO" textAlignment="center" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bBn-eM-Hsx"> - <rect key="frame" x="0.0" y="0.0" width="330" height="56"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> - <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> - <color key="textColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/> - <fontDescription key="fontDescription" type="system" pointSize="14"/> - <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> - </textView> - </subviews> - <color key="backgroundColor" systemColor="systemGray4Color" red="0.81960784310000001" green="0.81960784310000001" blue="0.83921568629999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstAttribute="height" constant="220" id="hS0-Te-OEU"/> - </constraints> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> - <integer key="value" value="15"/> - </userDefinedRuntimeAttribute> - </userDefinedRuntimeAttributes> - </view> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="11/14/2016 12:34PM" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4OM-U1-teG" userLabel="Message Time"> - <rect key="frame" x="178" y="9" width="154" height="20.5"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <fontDescription key="fontDescription" type="system" pointSize="17"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Wm5-ce-Sf6" userLabel="Left Divider"> - <rect key="frame" x="31" y="19" width="131" height="1"/> - <color key="backgroundColor" red="0.94117647059999998" green="0.94117647059999998" blue="0.94117647059999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstAttribute="height" constant="1" id="dEi-Ni-etd"/> - </constraints> - </view> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WgT-u7-Mgl" userLabel="Right Divider"> - <rect key="frame" x="348" y="19" width="131" height="1"/> - <color key="backgroundColor" red="0.94117647059999998" green="0.94117647059999998" blue="0.94117647059999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstAttribute="height" constant="1" id="9kZ-1u-mwB"/> - </constraints> - </view> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aRL-o9-tZc" userLabel="Avatar View"> - <rect key="frame" x="16" y="194" width="32" height="32"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="height" constant="32" id="JKg-Rh-evV"/> - <constraint firstAttribute="width" constant="32" id="NQC-fC-Mw5"/> - </constraints> - </view> - </subviews> - <color key="tintColor" name="controlColor" catalog="System" colorSpace="catalog"/> - <constraints> - <constraint firstAttribute="trailingMargin" secondItem="WgT-u7-Mgl" secondAttribute="trailing" constant="16" id="6Dc-L8-sJC"/> - <constraint firstItem="WgT-u7-Mgl" firstAttribute="centerY" secondItem="4OM-U1-teG" secondAttribute="centerY" id="ALc-pa-7ZW"/> - <constraint firstItem="4OM-U1-teG" firstAttribute="top" secondItem="Dkz-SA-3Af" secondAttribute="topMargin" constant="-2" id="CbK-m1-TUR"/> - <constraint firstItem="aRL-o9-tZc" firstAttribute="trailing" secondItem="xVQ-Jk-Sxy" secondAttribute="leading" constant="-16" id="DIQ-qi-aUb"/> - <constraint firstItem="Wm5-ce-Sf6" firstAttribute="leading" secondItem="Dkz-SA-3Af" secondAttribute="leadingMargin" constant="16" id="Faa-N7-gPP"/> - <constraint firstItem="WgT-u7-Mgl" firstAttribute="leading" secondItem="4OM-U1-teG" secondAttribute="trailing" constant="16" id="Foe-Zm-1oU"/> - <constraint firstItem="Wm5-ce-Sf6" firstAttribute="centerY" secondItem="4OM-U1-teG" secondAttribute="centerY" id="Q4u-AX-3D6"/> - <constraint firstAttribute="bottom" secondItem="xVQ-Jk-Sxy" secondAttribute="bottom" constant="8" id="Qbn-zO-KWj"/> - <constraint firstItem="xVQ-Jk-Sxy" firstAttribute="top" secondItem="Dkz-SA-3Af" secondAttribute="top" constant="8" id="R6Q-PY-A3m"/> - <constraint firstItem="aRL-o9-tZc" firstAttribute="bottom" secondItem="xVQ-Jk-Sxy" secondAttribute="bottom" constant="-2" id="aM1-qu-Iys"/> - <constraint firstItem="Wm5-ce-Sf6" firstAttribute="trailing" secondItem="4OM-U1-teG" secondAttribute="leading" constant="-16" id="dlX-Gh-ImE"/> - <constraint firstItem="xVQ-Jk-Sxy" firstAttribute="leading" secondItem="Dkz-SA-3Af" secondAttribute="leading" constant="64" id="qxE-d6-psE"/> - <constraint firstAttribute="trailing" secondItem="xVQ-Jk-Sxy" secondAttribute="trailing" constant="116" id="uTe-7R-qUz"/> - <constraint firstItem="4OM-U1-teG" firstAttribute="centerX" secondItem="Dkz-SA-3Af" secondAttribute="centerX" id="zxy-XJ-QAl"/> - </constraints> - </tableViewCellContentView> - <connections> - <outlet property="avatarView" destination="aRL-o9-tZc" id="ROC-7p-3of"/> - <outlet property="bubble" destination="xVQ-Jk-Sxy" id="dRd-NH-FPh"/> - <outlet property="bubbleBottomConstraint" destination="Qbn-zO-KWj" id="hKY-TA-wId"/> - <outlet property="bubbleHeight" destination="hS0-Te-OEU" id="G9h-6Z-A7m"/> - <outlet property="bubbleTopConstraint" destination="R6Q-PY-A3m" id="IQA-QC-eV0"/> - <outlet property="leftDivider" destination="Wm5-ce-Sf6" id="EaQ-1G-8Db"/> - <outlet property="locationSharingMessageTextView" destination="bBn-eM-Hsx" id="LPV-Lc-9rj"/> - <outlet property="messageLabelMarginConstraint" destination="CbK-m1-TUR" id="8qD-tP-8QW"/> - <outlet property="receivedBubbleLeading" destination="qxE-d6-psE" id="aaK-f3-9Nx"/> - <outlet property="receivedBubbleTrailling" destination="uTe-7R-qUz" id="RXi-mN-T16"/> - <outlet property="rightDivider" destination="WgT-u7-Mgl" id="k10-3V-ZLw"/> - <outlet property="timeLabel" destination="4OM-U1-teG" id="ub4-Z8-CsM"/> - </connections> - <point key="canvasLocation" x="-411.19999999999999" y="-45.877061469265371"/> - </tableViewCell> - </objects> -</document> diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingSent.swift b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingSent.swift deleted file mode 100644 index 8e494f4fb056b9d6dae5139e4d89ee169c49f047..0000000000000000000000000000000000000000 --- a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingSent.swift +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2020 Savoir-faire Linux Inc. - * - * Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -import UIKit -import Reusable -import RxCocoa - -class MessageCellLocationSharingSent: MessageCellLocationSharing { - - /// Primary location - private var myLocation: MarkerAndComponentObject = (marker: MaplyScreenMarker(), componentObject: nil) - /// Secondary location - private var myContactsLocation: MarkerAndComponentObject = (marker: MaplyScreenMarker(), componentObject: nil) - - @IBOutlet weak var sentBubbleLeading: NSLayoutConstraint! - - @IBOutlet weak var stopSharingButton: UIButton! - - @IBAction func stopSharingButton(_ sender: Any) { - self.delete(sender) - } - - override func configureFromItem(_ conversationViewModel: ConversationViewModel, _ items: [MessageViewModel]?, cellForRowAt indexPath: IndexPath) { - super.configureFromItem(conversationViewModel, items, cellForRowAt: indexPath) - - // Primary location - conversationViewModel.myLocation - .subscribe(onNext: { [weak self, weak conversationViewModel] location in - guard let self = self, let location = location?.coordinate else { return } - - self.myLocation.componentObject = self.updateLocationAndMarker(location: location, - imageData: conversationViewModel?.myOwnProfileImageData, - username: conversationViewModel?.userName.value, - marker: self.myLocation.marker, - markerDump: self.myLocation.componentObject) - }) - .disposed(by: self.disposeBag) - - // Secondary location - conversationViewModel.myContactsLocation - .subscribe(onNext: { [weak self, weak conversationViewModel] location in - guard let self = self else { return } - - if let location = location { - self.myContactsLocation.componentObject = self.updateLocationAndMarker(location: location, - imageData: conversationViewModel?.profileImageData.value, - username: conversationViewModel?.userName.value, - marker: self.myContactsLocation.marker, - markerDump: self.myContactsLocation.componentObject, - tryToAnimateToMarker: false) - } else if let componentObject = self.myContactsLocation.componentObject, - let maplyViewController = self.maplyViewController { - maplyViewController.remove(componentObject) - self.myContactsLocation.componentObject = nil - } - }) - .disposed(by: self.disposeBag) - - self.setupStopSharingButton() - } - - override func setUplocationSharingMessageTextView(username: String) { - super.setUplocationSharingMessageTextView(username: username) - self.locationSharingMessageTextView.text = L10n.Conversation.explanationSendingLocationTo + username - self.locationSharingMessageTextView.adjustHeightFromContentSize() - } - - private func setupStopSharingButton() { - self.stopSharingButton.setTitle(L10n.Actions.stopLocationSharing, for: .normal) - self.stopSharingButton.backgroundColor = UIColor.red - self.stopSharingButton.setTitleColor(UIColor.white, for: .normal) - self.bubble.addSubview(stopSharingButton) - } - - override func myPositionButtonAction(sender: UIButton!) { - if let mapViewC = self.maplyViewController as? MaplyViewController { - mapViewC.animate(toPosition: self.myLocation.marker.loc, time: 0.5) - } - } - - override func updateWidth(_ shouldExpand: Bool) { - let normalValue: CGFloat = 164 - let extendedValue: CGFloat = 16 - if shouldExpand { - self.sentBubbleLeading.constant = extendedValue - } else { - self.sentBubbleLeading.constant = normalValue - } - } -} diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingSent.xib b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingSent.xib deleted file mode 100644 index aa413cb8462f43dcb68fdda78fc4b1c8796868ae..0000000000000000000000000000000000000000 --- a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellLocationSharingSent.xib +++ /dev/null @@ -1,117 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> - <device id="retina4_7" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> - </dependencies> - <objects> - <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> - <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="253" id="3QB-g7-MaS" userLabel="Message Cell Location Sharing Sent" customClass="MessageCellLocationSharingSent" customModule="Ring" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="510" height="240"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> - <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" tableViewCell="3QB-g7-MaS" id="Dkz-SA-3Af"> - <rect key="frame" x="0.0" y="0.0" width="510" height="240"/> - <autoresizingMask key="autoresizingMask"/> - <subviews> - <view clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="xVQ-Jk-Sxy" customClass="MessageBubble" customModule="Ring" customModuleProvider="target"> - <rect key="frame" x="164" y="8" width="330" height="220"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PYf-Lj-ehe"> - <rect key="frame" x="134" y="175" width="62" height="34"/> - <inset key="contentEdgeInsets" minX="8" minY="8" maxX="8" maxY="8"/> - <state key="normal" title="Button"> - <color key="titleColor" systemColor="systemRedColor" red="1" green="0.23137254900000001" blue="0.18823529410000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - </state> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> - <real key="value" value="15"/> - </userDefinedRuntimeAttribute> - </userDefinedRuntimeAttributes> - <connections> - <action selector="stopSharingButton:" destination="3QB-g7-MaS" eventType="touchUpInside" id="9px-PV-vvz"/> - </connections> - </button> - <textView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" fixedFrame="YES" scrollEnabled="NO" editable="NO" textAlignment="center" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wd9-VO-GSo"> - <rect key="frame" x="0.0" y="0.0" width="330" height="56"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/> - <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> - <color key="textColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/> - <fontDescription key="fontDescription" type="system" pointSize="14"/> - <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> - <real key="value" value="0.0"/> - </userDefinedRuntimeAttribute> - </userDefinedRuntimeAttributes> - </textView> - </subviews> - <color key="backgroundColor" systemColor="systemGray4Color" red="0.81960784310000001" green="0.81960784310000001" blue="0.83921568629999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstItem="PYf-Lj-ehe" firstAttribute="centerY" secondItem="xVQ-Jk-Sxy" secondAttribute="bottom" constant="-28" id="SPy-c9-P2P"/> - <constraint firstItem="PYf-Lj-ehe" firstAttribute="centerX" secondItem="xVQ-Jk-Sxy" secondAttribute="centerX" id="hQD-IF-ddy"/> - <constraint firstAttribute="height" constant="220" id="hS0-Te-OEU"/> - </constraints> - <userDefinedRuntimeAttributes> - <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> - <integer key="value" value="15"/> - </userDefinedRuntimeAttribute> - </userDefinedRuntimeAttributes> - </view> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="11/14/2016 12:34PM" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4OM-U1-teG" userLabel="Message Time"> - <rect key="frame" x="178" y="9" width="154" height="21"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <fontDescription key="fontDescription" type="system" pointSize="17"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Wm5-ce-Sf6" userLabel="Left Divider"> - <rect key="frame" x="31" y="19" width="131" height="1"/> - <color key="backgroundColor" red="0.94117647059999998" green="0.94117647059999998" blue="0.94117647059999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstAttribute="height" constant="1" id="dEi-Ni-etd"/> - </constraints> - </view> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WgT-u7-Mgl" userLabel="Right Divider"> - <rect key="frame" x="348" y="19" width="131" height="1"/> - <color key="backgroundColor" red="0.94117647059999998" green="0.94117647059999998" blue="0.94117647059999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstAttribute="height" constant="1" id="9kZ-1u-mwB"/> - </constraints> - </view> - </subviews> - <color key="tintColor" name="controlColor" catalog="System" colorSpace="catalog"/> - <constraints> - <constraint firstAttribute="trailingMargin" secondItem="WgT-u7-Mgl" secondAttribute="trailing" constant="16" id="6Dc-L8-sJC"/> - <constraint firstItem="WgT-u7-Mgl" firstAttribute="centerY" secondItem="4OM-U1-teG" secondAttribute="centerY" id="ALc-pa-7ZW"/> - <constraint firstItem="4OM-U1-teG" firstAttribute="top" secondItem="Dkz-SA-3Af" secondAttribute="topMargin" constant="-2" id="CbK-m1-TUR"/> - <constraint firstItem="Wm5-ce-Sf6" firstAttribute="leading" secondItem="Dkz-SA-3Af" secondAttribute="leadingMargin" constant="16" id="Faa-N7-gPP"/> - <constraint firstItem="WgT-u7-Mgl" firstAttribute="leading" secondItem="4OM-U1-teG" secondAttribute="trailing" constant="16" id="Foe-Zm-1oU"/> - <constraint firstItem="Wm5-ce-Sf6" firstAttribute="centerY" secondItem="4OM-U1-teG" secondAttribute="centerY" id="Q4u-AX-3D6"/> - <constraint firstAttribute="bottom" secondItem="xVQ-Jk-Sxy" secondAttribute="bottom" constant="8" id="Qbn-zO-KWj"/> - <constraint firstItem="xVQ-Jk-Sxy" firstAttribute="top" secondItem="Dkz-SA-3Af" secondAttribute="top" constant="8" id="R6Q-PY-A3m"/> - <constraint firstItem="Wm5-ce-Sf6" firstAttribute="trailing" secondItem="4OM-U1-teG" secondAttribute="leading" constant="-16" id="dlX-Gh-ImE"/> - <constraint firstItem="xVQ-Jk-Sxy" firstAttribute="leading" secondItem="Dkz-SA-3Af" secondAttribute="leading" constant="164" id="qxE-d6-psE"/> - <constraint firstAttribute="trailing" secondItem="xVQ-Jk-Sxy" secondAttribute="trailing" constant="16" id="uTe-7R-qUz"/> - <constraint firstItem="4OM-U1-teG" firstAttribute="centerX" secondItem="Dkz-SA-3Af" secondAttribute="centerX" id="zxy-XJ-QAl"/> - </constraints> - </tableViewCellContentView> - <connections> - <outlet property="bubble" destination="xVQ-Jk-Sxy" id="dRd-NH-FPh"/> - <outlet property="bubbleBottomConstraint" destination="Qbn-zO-KWj" id="hKY-TA-wId"/> - <outlet property="bubbleHeight" destination="hS0-Te-OEU" id="oJg-9j-oa8"/> - <outlet property="bubbleTopConstraint" destination="R6Q-PY-A3m" id="IQA-QC-eV0"/> - <outlet property="leftDivider" destination="Wm5-ce-Sf6" id="EaQ-1G-8Db"/> - <outlet property="locationSharingMessageTextView" destination="Wd9-VO-GSo" id="Eoh-Sh-rsG"/> - <outlet property="messageLabelMarginConstraint" destination="CbK-m1-TUR" id="8qD-tP-8QW"/> - <outlet property="rightDivider" destination="WgT-u7-Mgl" id="k10-3V-ZLw"/> - <outlet property="sentBubbleLeading" destination="qxE-d6-psE" id="wIa-XC-C9H"/> - <outlet property="stopSharingButton" destination="PYf-Lj-ehe" id="qXH-bz-cAH"/> - <outlet property="timeLabel" destination="4OM-U1-teG" id="ub4-Z8-CsM"/> - </connections> - <point key="canvasLocation" x="-411.19999999999999" y="-45.877061469265371"/> - </tableViewCell> - </objects> -</document> diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift index 879df0805267e50c9271f5d037fa8553283a5e59..7958b6fe93383504e880ec04060f47801b84efe8 100644 --- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift +++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift @@ -281,10 +281,7 @@ class ConversationViewController: UIViewController, alert.addAction(recordVideoAction) alert.addAction(recordAudioAction) alert.addAction(documentsAction) - // TODO: fix location sharing with a new API - if false { - alert.addAction(locationSharingAction()) - } + alert.addAction(locationSharingAction()) alert.addAction(cancelAction) alert.popoverPresentationController?.sourceView = self.view alert.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection() @@ -837,12 +834,6 @@ extension ConversationViewController { .subscribe(onNext: { [weak self, weak cell] (shouldDelete) in guard shouldDelete, let self = self, let cell = cell, let messageId = cell.messageId else { return } - if cell as? MessageCellLocationSharing != nil { - self.tableView.isScrollEnabled = true - if cell as? MessageCellLocationSharingSent != nil { - self.viewModel.stopSendingLocation() - } - } self.isExecutingDeleteMessage = true self.viewModel.deleteLocationMessage(messageId: messageId) }) @@ -866,42 +857,6 @@ extension ConversationViewController { }) .disposed(by: cell.disposeBag) } - - private func locationCellSetup(_ item: MessageViewModel, _ cell: MessageCell) { - guard item.isLocationSharingBubble, let cell = cell as? MessageCellLocationSharing else { return } - - cell.locationTapped - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self, weak cell] (locationTapped) in - guard locationTapped.0, let self = self, let cell = cell else { return } - - let expanding = locationTapped.1 - - if let index = self.tableView.indexPath(for: cell) { - cell.expandHeight(expanding, - self.tableView.frame.height - self.tableView.contentInset.top - self.tableView.contentInset.bottom) - self.tableView.performBatchUpdates({ - self.tableView.updateConstraintsIfNeeded() - UIView.animate(withDuration: 0.4) { - cell.updateWidth(expanding) - cell.layoutIfNeeded() - } - }, completion: { [weak cell] _ in cell?.onAnimationCompletion() }) - - if expanding { - self.tableView.scrollToRow(at: index, at: UITableView.ScrollPosition.top, animated: true) - } - self.tableView.isScrollEnabled = !expanding - - cell.locationTapped.accept((false, expanding)) - } else { - self.log.warning("[ConversationViewController] locationCellSetup, something went weird, let's retry") - self.tableView.isScrollEnabled = true - cell.locationTapped.accept((true, expanding)) // retry - } - }) - .disposed(by: cell.disposeBag) - } } // MARK: Location sharing diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift index b6ef69f9f9ad5c5f6d2366b308f9a7b82c4c8707..f4954bceadd955e0a0cad4af7c79e8711a0d3d51 100644 --- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift +++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift @@ -27,7 +27,7 @@ import RxSwift import RxCocoa import SwiftyBeaver -// swiftlint:disable file_length +// swiftlint:disable file_length, type_body_length class ConversationViewModel: Stateable, ViewModel { /// Logger @@ -151,7 +151,6 @@ class ConversationViewModel: Stateable, ViewModel { var conversation: BehaviorRelay<ConversationModel>! { didSet { self.subscribeUnreadMessages() - self.subscribeLocationServiceLocationReceived() self.subscribeProfileServiceMyPhoto() guard let account = self.accountService.getAccount(fromAccountId: self.conversation.value.accountId) else { return } @@ -473,8 +472,6 @@ class ConversationViewModel: Stateable, ViewModel { // self.messages.accept(conversationsMsg) } - var myLocation: Observable<CLLocation?> { return self.locationSharingService.currentLocation.asObservable() } - var myContactsLocation = BehaviorSubject<CLLocationCoordinate2D?>(value: nil) let shouldDismiss = BehaviorRelay<Bool>(value: false) func openFullScreenPreview(parentView: UIViewController, viewModel: PlayerViewModel?, image: UIImage?, initialFrame: CGRect, delegate: PreviewViewControllerDelegate) { @@ -518,21 +515,6 @@ class ConversationViewModel: Stateable, ViewModel { // MARK: Conversation didSet functions extension ConversationViewModel { - private func subscribeLocationServiceLocationReceived() { - self.locationSharingService - .peerUriAndLocationReceived - .subscribe(onNext: { [weak self] tuple in - guard let self = self, let peerUri = tuple.0, let conversation = self.conversation, - let jamiId = conversation.value.getParticipants().first?.jamiId else { return } - let coordinates = tuple.1 - let hash = JamiURI(from: peerUri).hash - if hash == jamiId { - self.myContactsLocation.onNext(coordinates) - } - }) - .disposed(by: self.disposeBag) - } - private func subscribeProfileServiceMyPhoto() { guard let account = self.accountService.currentAccount else { return } self.profileService @@ -633,8 +615,8 @@ extension ConversationViewModel { func isAlreadySharingLocation() -> Bool { guard let account = self.accountService.currentAccount, let jamiId = self.conversation.value.getParticipants().first?.jamiId else { return true } - return self.locationSharingService.isAlreadySharing(accountId: account.id, - contactUri: jamiId) + return self.locationSharingService.isAlreadySharingMyLocation(accountId: account.id, + contactUri: jamiId) } func startSendingLocation(duration: TimeInterval) { diff --git a/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/ViewModels/MessagesListVM.swift b/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/ViewModels/MessagesListVM.swift index e86a608e483568bf73c3d168f3fac1274dc79743..2cbcfc7a2ac44e66ef685846a2f6b9f426522079 100644 --- a/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/ViewModels/MessagesListVM.swift +++ b/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/ViewModels/MessagesListVM.swift @@ -5,6 +5,7 @@ * Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> * Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com> + * Author: Alireza Toghiani Khorasgani alireza.toghiani@savoirfairelinux.com * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +25,7 @@ import Foundation import RxSwift import RxRelay +import RxCocoa enum MessageInfo: State { case updateAvatar(jamiId: String) @@ -53,10 +55,19 @@ class MessagesListVM: ObservableObject { } } @Published var numberOfNewMessages: Int = 0 + @Published var shouldShowMap: Bool = false + @Published var coordinates = [(CLLocationCoordinate2D, UIImage)]() + @Published var isMapOpened = false + var contactAvatar: UIImage = UIImage() + var currentAccountAvatar: UIImage = UIImage() + var myLocation: Observable<CLLocation?> { return self.locationSharingService.currentLocation.asObservable() } + var myContactsLocation: CLLocationCoordinate2D? + var myCoordinate: CLLocationCoordinate2D? var accountService: AccountsService var profileService: ProfilesService var dataTransferService: DataTransferService + var locationSharingService: LocationSharingService var conversationService: ConversationsService var contactsService: ContactsService var nameService: NameService @@ -138,6 +149,61 @@ class MessagesListVM: ObservableObject { self.contactsService = injectionBag.contactsService self.nameService = injectionBag.nameService self.transferHelper = transferHelper + self.locationSharingService = injectionBag.locationSharingService + self.subscribeLocationEvents() + } + + func subscribeLocationEvents() { + self.locationSharingService + .peerUriAndLocationReceived + .subscribe(onNext: { [weak self] tuple in + guard let self = self else { return } + DispatchQueue.main.async { + if let coordinates = tuple.1 { + self.myContactsLocation = coordinates + } else { + self.myContactsLocation = nil + } + self.updateCoordinatesList() + } + }) + .disposed(by: self.disposeBag) + + self.myLocation + .subscribe(onNext: { [weak self] myCurrentLocation in + guard let self = self else { return } + if let myCurrentLocation = myCurrentLocation { + self.myCoordinate = myCurrentLocation.coordinate + } else { + self.myCoordinate = nil + } + self.updateCoordinatesList() + }) + .disposed(by: self.disposeBag) + self.subscribeUserAvatarForLocationSharing() + } + + func subscribeUserAvatarForLocationSharing() { + profileService.getAccountProfile(accountId: self.conversation.accountId) + .subscribe(onNext: { [weak self] profile in + guard let self = self else { return } + let account = self.accountService.getAccount(fromAccountId: self.conversation.accountId) + self.currentAccountAvatar = UIImage.defaultJamiAvatarFor(profileName: profile.alias, account: account) + self.updateCoordinatesList() + }) + .disposed(by: self.disposeBag) + } + + private func updateCoordinatesList() { + var coordinates = [(CLLocationCoordinate2D, UIImage)]() + if let myContactsLocation = self.myContactsLocation { + coordinates.append((myContactsLocation, self.contactAvatar)) + } + if let myLocation = self.myCoordinate { + coordinates.append((myLocation, self.currentAccountAvatar)) + } + self.coordinates = coordinates + self.shouldShowMap = self.isAlreadySharingLocation() } private func insert(newMessage: MessageModel) -> Bool { @@ -468,6 +534,7 @@ class MessagesListVM: ObservableObject { private func updateAvatar(image: UIImage, id: String, message: MessageContainerModel) { self.avatars.set(value: image, for: id) + self.updateContacLocationSharingImage() message.updateAvatar(image: image) if var lastReadAvatars = self.lastRead.get(key: message.id) as? [String: UIImage] { if var _ = lastReadAvatars[id] { @@ -504,7 +571,7 @@ class MessagesListVM: ObservableObject { self.updateAvatar(image: image, id: id, message: message) } }) - .disposed(by: message.disposeBag) + .disposed(by: disposeBag) self.nameService.lookupAddress(withAccount: self.conversation.accountId, nameserver: "", address: id) } @@ -565,3 +632,76 @@ class MessagesListVM: ObservableObject { messageModel.updateRead(avatars: newValue) } } + +// MARK: Location sharing +// swiftlint:disable body_length +extension MessagesListVM { + + func updateContacLocationSharingImage() { + if let jamiId = self.conversation.getParticipants().first?.jamiId { + if let avatar = self.avatars.get(key: jamiId) as? UIImage { + DispatchQueue.main.async { [weak self] in + self?.contactAvatar = avatar + self?.updateCoordinatesList() + } + } + } + } + + func isAlreadySharingLocation() -> Bool { + guard let account = self.accountService.currentAccount, + let jamiId = self.conversation.getParticipants().first?.jamiId else { return true } + return self.locationSharingService.isAlreadySharing(accountId: account.id, + contactUri: jamiId) || self.locationSharingService.isAlreadySharingMyLocation(accountId: account.id, contactUri: jamiId) + } + + func isAlreadySharingMyLocation() -> Bool { + guard let account = self.accountService.currentAccount, + let jamiId = self.conversation.getParticipants().first?.jamiId else { return true } + return self.locationSharingService.isAlreadySharingMyLocation(accountId: account.id, contactUri: jamiId) + } + + func startSendingLocation(duration: TimeInterval) { + guard let account = self.accountService.currentAccount, + let jamiId = self.conversation.getParticipants().first?.jamiId else { return } + self.locationSharingService.startSharingLocation(from: account.id, + to: jamiId, + duration: duration) + } + + func stopSendingLocation() { + guard let account = self.accountService.currentAccount, + let jamiId = self.conversation.getParticipants().first?.jamiId else { return } + self.locationSharingService.stopSharingLocation(accountId: account.id, + contactUri: jamiId) + } + + func getMyLocationSharingRemainedTime() -> Int { + guard let account = self.accountService.currentAccount, + let jamiId = self.conversation.getParticipants().first?.jamiId else { return 0} + return self.locationSharingService.getMyLocationSharingRemainedTime(accountId: account.id, contactUri: jamiId) + } + + func getMyLocationSharingRemainedTimeText() -> String { + let remainingTime = getMyLocationSharingRemainedTime() + let hours = remainingTime / 60 + let minutes = remainingTime % 60 + + let hourString = hours == 1 ? "hour" : "hours" + let minuteString = minutes == 1 ? "minute" : "minutes" + + if hours == 0 { + if minutes == 0 { + return "0 minutes" + } else { + return "\(minutes) \(minuteString)" + } + } else { + if minutes == 0 { + return "\(hours) \(hourString)" + } else { + return "\(hours) \(hourString), \(minutes) \(minuteString)" + } + } + } +} diff --git a/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/LocationSharingView.swift b/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/LocationSharingView.swift new file mode 100644 index 0000000000000000000000000000000000000000..d8889d4abca8023f4adb1b36e918eeb3c9f54c44 --- /dev/null +++ b/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/LocationSharingView.swift @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2022-2023 Savoir-faire Linux Inc. + * + * Author: Alireza Toghiani Khorasgani alireza.toghiani@savoirfairelinux.com * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import SwiftUI + +// swiftlint:disable closure_body_length +struct LocationSharingView: View { + + @StateObject var model: MessagesListVM + @Binding var coordinates: [(CLLocationCoordinate2D, UIImage)] + @SwiftUI.State private var showCopyrightAlert = false + @SwiftUI.State private var viewCornerRadius: CGFloat = 15 + + var navigationBarHeight: CGFloat { + UINavigationController.navBarHeight() + ( UIApplication.shared.windows.first?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0) + 80 + } + + var body: some View { + HStack { + if model.isMapOpened { + VStack { + ZStack(alignment: .center) { + VStack(spacing: 0) { + HStack { + Spacer() + .frame(width: 20) + Button { + model.isMapOpened = false + } label: { + Image(systemName: "xmark") + .foregroundColor(.white) + .font(.system(size: 20, weight: .semibold)) + } + Spacer() + .frame(width: 20) + Text("Location Sharing") + .fontWeight(.semibold) + .font(.title3) + .foregroundColor(.white) + Spacer() + } + .frame(width: UIScreen.main.bounds.size.width, height: 60) + .background(Color(UIColor.darkGray)) + .cornerRadius(radius: viewCornerRadius, corners: [.topLeft, .topRight]) + ZStack(alignment: .bottom) { + MapView(coordinates: $coordinates) + createCopyrigtButton() + .padding(.all, 5) + } + } + + VStack { + Spacer() + Text(model.getMyLocationSharingRemainedTimeText()) + .fontWeight(.semibold) + .font(.caption) + .padding([.leading, .trailing], 15) + .padding([.top, .bottom], 5) + .background(Color.black) + .foregroundColor(.white) + .cornerRadius(8) + + if model.isAlreadySharingMyLocation() { + Button { + model.stopSendingLocation() + model.isMapOpened = false + } label: { + HStack { + Image(systemName: "paperplane.fill") + .foregroundColor(.black) + Text(L10n.Actions.stopLocationSharing) + .font(.callout) + } + .padding([.leading, .trailing], 15) + .padding([.top, .bottom], 15) + .background(Color.red) + .foregroundColor(.black) + .cornerRadius(20) + } + .padding(.bottom, 10) + } + } + } + .frame(width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height - navigationBarHeight) + } + } else { + ZStack(alignment: .center) { + MapView(coordinates: $coordinates) + .frame(width: 250, height: 150) + .cornerRadius(viewCornerRadius) + .onTapGesture { + model.isMapOpened = true + } + if model.isAlreadySharingMyLocation() { + VStack { + Spacer() + HStack { + Spacer() + Button { + model.stopSendingLocation() + model.isMapOpened = false + } label: { + Text(L10n.Actions.stopLocationSharing) + .fontWeight(.semibold) + .font(.caption) + .padding([.leading, .trailing], 15) + .padding([.top, .bottom], 5) + .background(Color.red) + .foregroundColor(.white) + .cornerRadius(8) + } + Spacer() + } + .padding(.all, 10) + } + } + createCopyrigtButton() + .padding(.all, 5) + } + .frame(width: 200, height: 150) + } + } + .alert(isPresented: $showCopyrightAlert) { + Alert(title: Text("OpenStreetMap"), message: Text("Map data © OpenStreetMap contributors"), primaryButton: .default(Text("Open in Safari")) { + showCopyrightAlert = false + if let url = URL(string: "https://www.openstreetmap.org/copyright"), + UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url) + } + }, secondaryButton: .cancel()) + } + } + + func createCopyrigtButton() -> some View { + return VStack { + Spacer() + HStack { + Spacer() + Button { + showCopyrightAlert = true + } label: { + Image(systemName: "info") + .renderingMode(.template) + .resizable() + .aspectRatio(contentMode: .fit) + .foregroundColor(.white) + .padding(4) + .frame(width: 15, height: 15) + .background(Color(UIColor.darkGray)) + .clipShape(Circle()) + .frame(width: 30, height: 30) + } + } + } + } +} diff --git a/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/MapView.swift b/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/MapView.swift new file mode 100644 index 0000000000000000000000000000000000000000..c7c8e5767210c7d3243271091c430f1ff0eb3ea1 --- /dev/null +++ b/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/MapView.swift @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022-2023 Savoir-faire Linux Inc. + * + * Author: Alireza Toghiani Khorasgani alireza.toghiani@savoirfairelinux.com * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import SwiftUI +import MapKit + +class LocationSharingAnnotation: NSObject, MKAnnotation { + var avatar: UIImage! + var coordinate: CLLocationCoordinate2D + + init(coordinate: CLLocationCoordinate2D) { + self.coordinate = coordinate + } +} + +class MapViewWrapper: NSObject { + let mapView: MapView + + init(mapView: MapView) { + self.mapView = mapView + } +} + +struct MapView: UIViewRepresentable { + @Binding var coordinates: [(CLLocationCoordinate2D, UIImage)] + + func makeUIView(context: Context) -> MKMapView { + let mapView = MKMapView() + + mapView.delegate = context.coordinator + // Add an OpenStreetMap tile overlay to the map view. + let urlTemplate = "https://tile.openstreetmap.org/{z}/{x}/{y}.png" + let overlay = MKTileOverlay(urlTemplate: urlTemplate) + overlay.canReplaceMapContent = true + mapView.addOverlay(overlay, level: .aboveLabels) + + addPinsAndZoom(mapView: mapView) + return mapView + } + + func updateUIView(_ view: MKMapView, context: Context) { + addPinsAndZoom(mapView: view) + } + + func addPinsAndZoom(mapView: MKMapView) { + mapView.removeAnnotations(mapView.annotations) + for coordinate in coordinates { + let annotation = LocationSharingAnnotation(coordinate: coordinate.0) + annotation.avatar = coordinate.1 + mapView.addAnnotation(annotation) + } + if let firstCoordinate = coordinates.first { + let span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01) + let region = MKCoordinateRegion(center: firstCoordinate.0, span: span) + mapView.setRegion(region, animated: true) + } + } +} + +extension MapView { + class Coordinator: NSObject, MKMapViewDelegate { + + func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { + if overlay is MKTileOverlay { + let renderer = MKTileOverlayRenderer(overlay: overlay) + return renderer + } else { + return MKTileOverlayRenderer() + } + } + + func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { + guard let customAnnotation = annotation as? LocationSharingAnnotation else { + return nil + } + + let reuseId = customAnnotation.avatar?.description ?? customAnnotation.description + + var anView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) + if anView == nil { + anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId) + } else { + anView?.annotation = customAnnotation + } + + let imageWidth = 35 + let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: imageWidth, height: imageWidth)) + imageView.image = customAnnotation.avatar + imageView.layer.cornerRadius = imageView.layer.frame.size.width / 2 + imageView.layer.masksToBounds = true + anView?.addSubview(imageView) + + return anView + } + } + + func makeCoordinator() -> Coordinator { + Coordinator() + } +} + +struct MapView_Previews: PreviewProvider { + static var previews: some View { + MapView(coordinates: .init(projectedValue: .constant([(CLLocationCoordinate2D(latitude: 37.785834, longitude: -122.406417), UIImage())]))) + } +} diff --git a/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/MessagesListView.swift b/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/MessagesListView.swift index aa61d82e3c45479289d7a119d8a094e743a9b70c..6e47a4a092e6651fa1b91c8a09d0416eed3101ee 100644 --- a/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/MessagesListView.swift +++ b/Ring/Ring/Features/Conversations/Conversation/MessageSwiftUI/Views/MessagesListView.swift @@ -2,6 +2,7 @@ * Copyright (C) 2022 Savoir-faire Linux Inc. * * Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> + * Author: Alireza Toghiani Khorasgani alireza.toghiani@savoirfairelinux.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,6 +44,7 @@ struct ScrollViewOffsetPreferenceKey: PreferenceKey { } } +// swiftlint:disable closure_body_length struct MessagesListView: View { @StateObject var model: MessagesListVM @SwiftUI.State var showScrollToLatestButton = false @@ -57,82 +59,87 @@ struct MessagesListView: View { @SwiftUI.State private var screenHeight: CGFloat = 0 var body: some View { - ZStack(alignment: .bottomTrailing) { - ScrollViewReader { scrollView in - ScrollView(showsIndicators: false) { - // update scroll offset - GeometryReader { proxy in - let offset = proxy.frame(in: .named("scroll")).minY - Color.clear.preference(key: ScrollViewOffsetPreferenceKey.self, value: offset) - } - LazyVStack(spacing: 0) { - // scroll to the bottom - Text("") - .id("lastMessage") - // messages - ForEach(model.messagesModels) { message in - MessageRowView(messageModel: message, onLongPress: {(frame, message) in - if showContextMenu == true { - return - } - model.hideNavigationBar.accept(true) - contextMenuModel.presentingMessage = message - contextMenuModel.messageFrame = frame - if let topController = topVC() { - contextMenuModel.currentSnapshot = UIImage.makeSnapshot(from: topController.view) - } - showContextMenu = true - }, model: message.messageRow) + ZStack(alignment: .top) { + ZStack(alignment: .bottomTrailing) { + ScrollViewReader { scrollView in + ScrollView(showsIndicators: false) { + // update scroll offset + GeometryReader { proxy in + let offset = proxy.frame(in: .named("scroll")).minY + Color.clear.preference(key: ScrollViewOffsetPreferenceKey.self, value: offset) } - .flipped() - // load more - Text("") - .onAppear(perform: { - DispatchQueue.global(qos: .background) - .asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 10)) { - self.model.loadMore() + LazyVStack(spacing: 0) { + // scroll to the bottom + Text("") + .id("lastMessage") + // messages + ForEach(model.messagesModels) { message in + MessageRowView(messageModel: message, onLongPress: {(frame, message) in + if showContextMenu == true { + return } - }) - } - .listRowBackground(Color.clear) - .onReceive(model.$scrollToId, perform: { (scrollToId) in - guard scrollToId != nil else { return } - scrollView.scrollTo("lastMessage") - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - model.scrollToId = nil + model.hideNavigationBar.accept(true) + contextMenuModel.presentingMessage = message + contextMenuModel.messageFrame = frame + if let topController = topVC() { + contextMenuModel.currentSnapshot = UIImage.makeSnapshot(from: topController.view) + } + showContextMenu = true + }, model: message.messageRow) + } + .flipped() + // load more + Text("") + .onAppear(perform: { + DispatchQueue.global(qos: .background) + .asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 10)) { + self.model.loadMore() + } + }) } - }) - } - .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in - DispatchQueue.main.async { - let scrollOffset = value ?? 0 - let atTheBottom = scrollOffset < scrollReserved - if atTheBottom != model.atTheBottom { - model.atTheBottom = atTheBottom + .listRowBackground(Color.clear) + .onReceive(model.$scrollToId, perform: { (scrollToId) in + guard scrollToId != nil else { return } + scrollView.scrollTo("lastMessage") + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + model.scrollToId = nil + } + }) + } + .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in + DispatchQueue.main.async { + let scrollOffset = value ?? 0 + let atTheBottom = scrollOffset < scrollReserved + if atTheBottom != model.atTheBottom { + model.atTheBottom = atTheBottom + } } } } + .flipped() + if !model.atTheBottom { + createScrollToBottmView() + } } - .flipped() - if !model.atTheBottom { - createScrollToBottmView() + .overlay(showContextMenu && contextMenuModel.presentingMessage != nil ? makeOverlay() : nil) + // hide navigation bar when presenting context menu + .onChange(of: showContextMenu) { newValue in + model.hideNavigationBar.accept(newValue) } - } - .overlay(showContextMenu && contextMenuModel.presentingMessage != nil ? makeOverlay() : nil) - // hide navigation bar when presenting context menu - .onChange(of: showContextMenu) { newValue in - model.hideNavigationBar.accept(newValue) - } - // hide context menu overly when device is rotated - .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in - if screenHeight != UIScreen.main.bounds.size.height && screenHeight != 0 { + // hide context menu overly when device is rotated + .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in + if screenHeight != UIScreen.main.bounds.size.height && screenHeight != 0 { + screenHeight = UIScreen.main.bounds.size.height + showContextMenu = false + } + } + .onAppear(perform: { screenHeight = UIScreen.main.bounds.size.height - showContextMenu = false + }) + if model.shouldShowMap { + LocationSharingView(model: model, coordinates: $model.coordinates) } } - .onAppear(perform: { - screenHeight = UIScreen.main.bounds.size.height - }) } func makeOverlay() -> some View { diff --git a/Ring/Ring/Services/ConversationsManager.swift b/Ring/Ring/Services/ConversationsManager.swift index ec277819790149ec15cb22e043f43bd2c70b1193..b52368c1d8b741ea99ead2207ad0b2cf5d7812c3 100644 --- a/Ring/Ring/Services/ConversationsManager.swift +++ b/Ring/Ring/Services/ConversationsManager.swift @@ -3,6 +3,7 @@ * * Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> * Author: Raphaël Brulé <raphael.brule@savoirfairelinux.com> + * Author: Alireza Toghiani Khorasgani alireza.toghiani@savoirfairelinux.com * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,10 +73,7 @@ class ConversationsManager { self.subscribeFileTransferEvents() self.subscribeCallsEvents() self.subscribeContactsEvents() - // TODO: fix location sharing with a new API - if false { - self.subscribeLocationSharingEvent() - } + self.subscribeLocationSharingEvent() self.subscribeCallsProviderEvents() self.controlAccountsState() } diff --git a/Ring/Ring/Services/LocationSharingService.swift b/Ring/Ring/Services/LocationSharingService.swift index 833a53ddb090dede794cbaf7cfb5397d8a9b44d3..d4fde66433bf3634bb8ce5ac6324ca6732a1ece5 100644 --- a/Ring/Ring/Services/LocationSharingService.swift +++ b/Ring/Ring/Services/LocationSharingService.swift @@ -22,10 +22,9 @@ import RxSwift import RxCocoa import SwiftyBeaver -// swiftlint:disable redundant_string_enum_value enum SerializableLocationTypes: String { - case position = "position" - case stop = "stop" + case position = "Position" + case stop = "Stop" } // swiftlint:enable redundant_string_enum_value @@ -45,19 +44,19 @@ private class LocationSharingInstanceDictionary<T: LocationSharingInstance> { var isEmpty: Bool { return self.instances.isEmpty } private func key(_ accountId: String, _ contactUri: String) -> String { - return accountId + contactUri + return accountId + contactUri.replacingOccurrences(of: "ring:", with: "") } func get(_ accountId: String, _ contactUri: String) -> T? { - return self.instances[key(accountId, contactUri)] + return self.instances[key(accountId, contactUri.replacingOccurrences(of: "ring:", with: ""))] } func insertOrUpdate(_ instance: T) { - self.instances[key(instance.accountId, instance.contactUri)] = instance + self.instances[key(instance.accountId, instance.contactUri.replacingOccurrences(of: "ring:", with: ""))] = instance } func remove(_ accountId: String, _ contactUri: String) -> T? { - return self.instances.removeValue(forKey: key(accountId, contactUri)) + return self.instances.removeValue(forKey: key(accountId, contactUri.replacingOccurrences(of: "ring:", with: ""))) } func asArray() -> [T] { @@ -79,6 +78,11 @@ private class OutgoingLocationSharingInstance: LocationSharingInstance { private let locationSharingService: LocationSharingService let duration: TimeInterval + var remainingTime: Int { + let nowTime = Date() + guard let sharingTime = endSharingTimer?.fireDate else { return 0 } + return Int(round(sharingTime.timeIntervalSince(nowTime) / 60)) + } private var endSharingTimer: Timer? @@ -136,7 +140,7 @@ class LocationSharingService: NSObject { private let outgoingInstances = LocationSharingInstanceDictionary<OutgoingLocationSharingInstance>() // Receiving my contact's location - let peerUriAndLocationReceived = BehaviorRelay<(String?, CLLocationCoordinate2D?)>(value: (nil, nil)) + let peerUriAndLocationReceived = BehaviorRelay<(String?, CLLocationCoordinate2D?, Int?)>(value: (nil, nil, nil)) private let incomingInstances = LocationSharingInstanceDictionary<IncomingLocationSharingInstance>() var receivingService: Disposable? @@ -213,9 +217,17 @@ class LocationSharingService: NSObject { extension LocationSharingService { func isAlreadySharing(accountId: String, contactUri: String) -> Bool { + return self.incomingInstances.get(accountId, contactUri) != nil + } + + func isAlreadySharingMyLocation(accountId: String, contactUri: String) -> Bool { return self.outgoingInstances.get(accountId, contactUri) != nil } + func getMyLocationSharingRemainedTime(accountId: String, contactUri: String) -> Int { + return Int(self.outgoingInstances.get(accountId, contactUri)?.remainingTime ?? 0) + } + func startSharingLocation(from accountId: String, to recipientUri: String, duration: TimeInterval) { guard !self.isAlreadySharing(accountId: accountId, contactUri: recipientUri) else { return } @@ -298,7 +310,7 @@ extension LocationSharingService { if incomingData.type == nil || incomingData.type == SerializableLocationTypes.position.rawValue { // TODO: altitude? - let peerUriAndData = (peerUri, CLLocationCoordinate2D(latitude: incomingData.lat!, longitude: incomingData.long!)) + let peerUriAndData = (peerUri, CLLocationCoordinate2D(latitude: incomingData.lat!, longitude: incomingData.long!), Int(incomingData.time) / 60000) self.peerUriAndLocationReceived.accept(peerUriAndData) } else if incomingData.type == SerializableLocationTypes.stop.rawValue { @@ -307,7 +319,7 @@ extension LocationSharingService { } func stopReceivingLocation(accountId: String, contactUri: String) { - self.peerUriAndLocationReceived.accept((contactUri, nil)) + self.peerUriAndLocationReceived.accept((contactUri, nil, nil)) self.triggerDeleteLocation(accountId: accountId, peerUri: contactUri, incoming: true, shouldRefreshConversations: true) diff --git a/Ring/WhirlyGlobeMaply b/Ring/WhirlyGlobeMaply deleted file mode 160000 index 07a710bf6deffda8222a7a7864eb855e95aa73b3..0000000000000000000000000000000000000000 --- a/Ring/WhirlyGlobeMaply +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 07a710bf6deffda8222a7a7864eb855e95aa73b3 diff --git a/Ring/fetch-dependencies.sh b/Ring/fetch-dependencies.sh index 3e2c2ecbb6d8dd89a93eb7c28b0e978aa343fc45..6dc4250305743c9bd274bf176f9e4d1e0057e61b 100755 --- a/Ring/fetch-dependencies.sh +++ b/Ring/fetch-dependencies.sh @@ -6,11 +6,3 @@ # Bootstrap Carthage carthage bootstrap --use-xcframeworks --platform iOS --no-use-binaries --cache-builds - -############################################ -## DOWNLOAD WHIRLYGLOBEMAPLY DEPENDENCIES ## -############################################ - -cd WhirlyGlobeMaply -git submodule init -git submodule update