Compare commits
1 commit
master
...
fix/fra_de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05c4300e10 |
|
|
@ -22,7 +22,6 @@
|
||||||
667E5D812B8E444E00EBCFC4 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 667E5D802B8E444D00EBCFC4 /* GoogleService-Info.plist */; };
|
667E5D812B8E444E00EBCFC4 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 667E5D802B8E444D00EBCFC4 /* GoogleService-Info.plist */; };
|
||||||
6691CA7E2B839C2D00B2A7B8 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6691CA7D2B839C2D00B2A7B8 /* NotificationService.swift */; };
|
6691CA7E2B839C2D00B2A7B8 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6691CA7D2B839C2D00B2A7B8 /* NotificationService.swift */; };
|
||||||
66A3E5B72CAE8E5C00FCB7FA /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */; };
|
66A3E5B72CAE8E5C00FCB7FA /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */; };
|
||||||
66C468FB2D2BE54800A836F7 /* PIPViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C468FA2D2BE54300A836F7 /* PIPViewModel.swift */; };
|
|
||||||
66C491F92B24D25B00CEA16D /* ConfigExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491F82B24D25A00CEA16D /* ConfigExtension.swift */; };
|
66C491F92B24D25B00CEA16D /* ConfigExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491F82B24D25A00CEA16D /* ConfigExtension.swift */; };
|
||||||
66C491FB2B24D32600CEA16D /* CoreExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FA2B24D32600CEA16D /* CoreExtension.swift */; };
|
66C491FB2B24D32600CEA16D /* CoreExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FA2B24D32600CEA16D /* CoreExtension.swift */; };
|
||||||
66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FC2B24D36500CEA16D /* AudioRouteUtils.swift */; };
|
66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FC2B24D36500CEA16D /* AudioRouteUtils.swift */; };
|
||||||
|
|
@ -43,8 +42,6 @@
|
||||||
66FBFC4B2B83BD7B00BC6AB1 /* CoreExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FA2B24D32600CEA16D /* CoreExtension.swift */; };
|
66FBFC4B2B83BD7B00BC6AB1 /* CoreExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FA2B24D32600CEA16D /* CoreExtension.swift */; };
|
||||||
66FDB7812C7C689A00561566 /* EventEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FDB7802C7C689A00561566 /* EventEditViewController.swift */; };
|
66FDB7812C7C689A00561566 /* EventEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FDB7802C7C689A00561566 /* EventEditViewController.swift */; };
|
||||||
C60E8F192C0F649200A06DB8 /* UIApplicationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60E8F182C0F649200A06DB8 /* UIApplicationExtension.swift */; };
|
C60E8F192C0F649200A06DB8 /* UIApplicationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60E8F182C0F649200A06DB8 /* UIApplicationExtension.swift */; };
|
||||||
C618BF562D75CA03005A00E0 /* linphonesw in Frameworks */ = {isa = PBXBuildFile; productRef = C618BF552D75CA03005A00E0 /* linphonesw */; };
|
|
||||||
C618BF582D75CA0D005A00E0 /* linphonesw in Frameworks */ = {isa = PBXBuildFile; productRef = C618BF572D75CA0D005A00E0 /* linphonesw */; };
|
|
||||||
C62817282C1B389700DBA646 /* SideMenuAccountRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62817272C1B389700DBA646 /* SideMenuAccountRow.swift */; };
|
C62817282C1B389700DBA646 /* SideMenuAccountRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62817272C1B389700DBA646 /* SideMenuAccountRow.swift */; };
|
||||||
C628172E2C1C3A3600DBA646 /* AccountExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C628172D2C1C3A3600DBA646 /* AccountExtension.swift */; };
|
C628172E2C1C3A3600DBA646 /* AccountExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C628172D2C1C3A3600DBA646 /* AccountExtension.swift */; };
|
||||||
C62817302C1C3DCC00DBA646 /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C628172F2C1C3DCC00DBA646 /* AccountModel.swift */; };
|
C62817302C1C3DCC00DBA646 /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C628172F2C1C3DCC00DBA646 /* AccountModel.swift */; };
|
||||||
|
|
@ -106,10 +103,6 @@
|
||||||
D732A9132B04C7A300DB42BA /* HistoryListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9122B04C7A300DB42BA /* HistoryListFragment.swift */; };
|
D732A9132B04C7A300DB42BA /* HistoryListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9122B04C7A300DB42BA /* HistoryListFragment.swift */; };
|
||||||
D732A9152B04C7FE00DB42BA /* HistoryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */; };
|
D732A9152B04C7FE00DB42BA /* HistoryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */; };
|
||||||
D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A91A2B061BD900DB42BA /* HistoryListBottomSheet.swift */; };
|
D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732A91A2B061BD900DB42BA /* HistoryListBottomSheet.swift */; };
|
||||||
D732C38C2D311D2500F78100 /* SettingsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D732C38B2D311D2100F78100 /* SettingsFragment.swift */; };
|
|
||||||
D7343FE82D3FA2000059D784 /* CodecModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7343FE72D3FA1F40059D784 /* CodecModel.swift */; };
|
|
||||||
D7343FED2D3FE1560059D784 /* HelpFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7343FEC2D3FE1550059D784 /* HelpFragment.swift */; };
|
|
||||||
D7343FEF2D3FE16C0059D784 /* HelpViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7343FEE2D3FE16B0059D784 /* HelpViewModel.swift */; };
|
|
||||||
D73449992BC6932A00778C56 /* MeetingWaitingRoomFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73449982BC6932A00778C56 /* MeetingWaitingRoomFragment.swift */; };
|
D73449992BC6932A00778C56 /* MeetingWaitingRoomFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73449982BC6932A00778C56 /* MeetingWaitingRoomFragment.swift */; };
|
||||||
D734499B2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D734499A2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift */; };
|
D734499B2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D734499A2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift */; };
|
||||||
D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */; };
|
D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */; };
|
||||||
|
|
@ -121,9 +114,6 @@
|
||||||
D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9D002ACB098C0021626A /* PermissionManager.swift */; };
|
D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9D002ACB098C0021626A /* PermissionManager.swift */; };
|
||||||
D74DA0122C047F0700A8561D /* HistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74DA0112C047F0700A8561D /* HistoryModel.swift */; };
|
D74DA0122C047F0700A8561D /* HistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74DA0112C047F0700A8561D /* HistoryModel.swift */; };
|
||||||
D750D3392AD3E6EE00EC99C5 /* PopupLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */; };
|
D750D3392AD3E6EE00EC99C5 /* PopupLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */; };
|
||||||
D756C8152D34FF9200A58F2F /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D756C8142D34FF8900A58F2F /* SettingsViewModel.swift */; };
|
|
||||||
D756C8172D352C5F00A58F2F /* CorePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D756C8162D352C5600A58F2F /* CorePreferences.swift */; };
|
|
||||||
D756C8182D352C5F00A58F2F /* CorePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D756C8162D352C5600A58F2F /* CorePreferences.swift */; };
|
|
||||||
D75759322B56D40900E7AC10 /* ZRTPPopup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75759312B56D40900E7AC10 /* ZRTPPopup.swift */; };
|
D75759322B56D40900E7AC10 /* ZRTPPopup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D75759312B56D40900E7AC10 /* ZRTPPopup.swift */; };
|
||||||
D759CB642C3FBD4200AC35E8 /* StartConversationFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D759CB632C3FBD4200AC35E8 /* StartConversationFragment.swift */; };
|
D759CB642C3FBD4200AC35E8 /* StartConversationFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D759CB632C3FBD4200AC35E8 /* StartConversationFragment.swift */; };
|
||||||
D759CB662C3FBE1D00AC35E8 /* StartConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D759CB652C3FBE1D00AC35E8 /* StartConversationViewModel.swift */; };
|
D759CB662C3FBE1D00AC35E8 /* StartConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D759CB652C3FBE1D00AC35E8 /* StartConversationViewModel.swift */; };
|
||||||
|
|
@ -133,10 +123,8 @@
|
||||||
D77A080E2CB6BCAF0095D589 /* MessageConferenceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77A080D2CB6BCA10095D589 /* MessageConferenceInfo.swift */; };
|
D77A080E2CB6BCAF0095D589 /* MessageConferenceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77A080D2CB6BCA10095D589 /* MessageConferenceInfo.swift */; };
|
||||||
D78290B82ADD3910004AA85C /* ContactsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290B72ADD3910004AA85C /* ContactsFragment.swift */; };
|
D78290B82ADD3910004AA85C /* ContactsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290B72ADD3910004AA85C /* ContactsFragment.swift */; };
|
||||||
D78290BB2ADD40B2004AA85C /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */; };
|
D78290BB2ADD40B2004AA85C /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */; };
|
||||||
D783028F2D414847009CCB60 /* DebugFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D783028E2D414845009CCB60 /* DebugFragment.swift */; };
|
|
||||||
D783C77C2B1089B200622CC2 /* assistant_linphone_default_values in Resources */ = {isa = PBXBuildFile; fileRef = D783C77A2B1089B200622CC2 /* assistant_linphone_default_values */; };
|
D783C77C2B1089B200622CC2 /* assistant_linphone_default_values in Resources */ = {isa = PBXBuildFile; fileRef = D783C77A2B1089B200622CC2 /* assistant_linphone_default_values */; };
|
||||||
D783C77D2B1089B200622CC2 /* assistant_third_party_default_values in Resources */ = {isa = PBXBuildFile; fileRef = D783C77B2B1089B200622CC2 /* assistant_third_party_default_values */; };
|
D783C77D2B1089B200622CC2 /* assistant_third_party_default_values in Resources */ = {isa = PBXBuildFile; fileRef = D783C77B2B1089B200622CC2 /* assistant_third_party_default_values */; };
|
||||||
D78607712D36CB8A009E6A7E /* SettingsAdvancedFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78607702D36CB87009E6A7E /* SettingsAdvancedFragment.swift */; };
|
|
||||||
D78E06282BE3811D00CE3783 /* CallStatsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E06272BE3811D00CE3783 /* CallStatsModel.swift */; };
|
D78E06282BE3811D00CE3783 /* CallStatsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E06272BE3811D00CE3783 /* CallStatsModel.swift */; };
|
||||||
D78E062A2BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E06292BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift */; };
|
D78E062A2BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E06292BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift */; };
|
||||||
D78E062C2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E062B2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift */; };
|
D78E062C2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78E062B2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift */; };
|
||||||
|
|
@ -163,8 +151,6 @@
|
||||||
D7C3650E2AF15BF200FE6142 /* PhotoPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C3650D2AF15BF200FE6142 /* PhotoPicker.swift */; };
|
D7C3650E2AF15BF200FE6142 /* PhotoPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C3650D2AF15BF200FE6142 /* PhotoPicker.swift */; };
|
||||||
D7C48DF42AFA66F900D938CB /* EditContactController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C48DF32AFA66F900D938CB /* EditContactController.swift */; };
|
D7C48DF42AFA66F900D938CB /* EditContactController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C48DF32AFA66F900D938CB /* EditContactController.swift */; };
|
||||||
D7C48DF62AFCDF4700D938CB /* ContactInnerActionsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C48DF52AFCDF4700D938CB /* ContactInnerActionsFragment.swift */; };
|
D7C48DF62AFCDF4700D938CB /* ContactInnerActionsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C48DF52AFCDF4700D938CB /* ContactInnerActionsFragment.swift */; };
|
||||||
D7C500402D27F16C00DD53EC /* AccountSettingsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C5003F2D27F16900DD53EC /* AccountSettingsFragment.swift */; };
|
|
||||||
D7C500422D2BE98100DD53EC /* AccountSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C500412D2BE96E00DD53EC /* AccountSettingsViewModel.swift */; };
|
|
||||||
D7CEE0352B7A210300FD79B7 /* ConversationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CEE0342B7A210300FD79B7 /* ConversationsView.swift */; };
|
D7CEE0352B7A210300FD79B7 /* ConversationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CEE0342B7A210300FD79B7 /* ConversationsView.swift */; };
|
||||||
D7CEE0382B7A214F00FD79B7 /* ConversationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CEE0372B7A214F00FD79B7 /* ConversationsListViewModel.swift */; };
|
D7CEE0382B7A214F00FD79B7 /* ConversationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CEE0372B7A214F00FD79B7 /* ConversationsListViewModel.swift */; };
|
||||||
D7CEE03B2B7A234200FD79B7 /* ConversationsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CEE03A2B7A234200FD79B7 /* ConversationsFragment.swift */; };
|
D7CEE03B2B7A234200FD79B7 /* ConversationsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7CEE03A2B7A234200FD79B7 /* ConversationsFragment.swift */; };
|
||||||
|
|
@ -178,8 +164,6 @@
|
||||||
D7D24D182AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */; };
|
D7D24D182AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */; };
|
||||||
D7DA67622ACCB2FA00E95002 /* LoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */; };
|
D7DA67622ACCB2FA00E95002 /* LoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */; };
|
||||||
D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */; };
|
D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */; };
|
||||||
D7DC096F2CFA1D7600A6D47C /* AccountProfileFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */; };
|
|
||||||
D7DC09712CFDBF9A00A6D47C /* AccountProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DC09702CFDBF8300A6D47C /* AccountProfileViewModel.swift */; };
|
|
||||||
D7E2E69F2CE356C90080DA0D /* PopupViewWithTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E2E69E2CE356C90080DA0D /* PopupViewWithTextField.swift */; };
|
D7E2E69F2CE356C90080DA0D /* PopupViewWithTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E2E69E2CE356C90080DA0D /* PopupViewWithTextField.swift */; };
|
||||||
D7E6ADF32B9875C20009A2BC /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6ADF22B9875C20009A2BC /* Message.swift */; };
|
D7E6ADF32B9875C20009A2BC /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6ADF22B9875C20009A2BC /* Message.swift */; };
|
||||||
D7E6ADF52B9876ED0009A2BC /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6ADF42B9876ED0009A2BC /* Attachment.swift */; };
|
D7E6ADF52B9876ED0009A2BC /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6ADF42B9876ED0009A2BC /* Attachment.swift */; };
|
||||||
|
|
@ -233,7 +217,6 @@
|
||||||
6646A7A22BB2E224006B842A /* ScheduleMeetingFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleMeetingFragment.swift; sourceTree = "<group>"; };
|
6646A7A22BB2E224006B842A /* ScheduleMeetingFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleMeetingFragment.swift; sourceTree = "<group>"; };
|
||||||
667E5D802B8E444D00EBCFC4 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
667E5D802B8E444D00EBCFC4 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||||
6691CA7D2B839C2D00B2A7B8 /* NotificationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
6691CA7D2B839C2D00B2A7B8 /* NotificationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||||
66C468FA2D2BE54300A836F7 /* PIPViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIPViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
66C491F82B24D25A00CEA16D /* ConfigExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigExtension.swift; sourceTree = "<group>"; };
|
66C491F82B24D25A00CEA16D /* ConfigExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigExtension.swift; sourceTree = "<group>"; };
|
||||||
66C491FA2B24D32600CEA16D /* CoreExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreExtension.swift; sourceTree = "<group>"; };
|
66C491FA2B24D32600CEA16D /* CoreExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreExtension.swift; sourceTree = "<group>"; };
|
||||||
66C491FC2B24D36500CEA16D /* AudioRouteUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRouteUtils.swift; sourceTree = "<group>"; };
|
66C491FC2B24D36500CEA16D /* AudioRouteUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRouteUtils.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -313,10 +296,6 @@
|
||||||
D732A9122B04C7A300DB42BA /* HistoryListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListFragment.swift; sourceTree = "<group>"; };
|
D732A9122B04C7A300DB42BA /* HistoryListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListFragment.swift; sourceTree = "<group>"; };
|
||||||
D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListViewModel.swift; sourceTree = "<group>"; };
|
D732A9142B04C7FE00DB42BA /* HistoryListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListViewModel.swift; sourceTree = "<group>"; };
|
||||||
D732A91A2B061BD900DB42BA /* HistoryListBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListBottomSheet.swift; sourceTree = "<group>"; };
|
D732A91A2B061BD900DB42BA /* HistoryListBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryListBottomSheet.swift; sourceTree = "<group>"; };
|
||||||
D732C38B2D311D2100F78100 /* SettingsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFragment.swift; sourceTree = "<group>"; };
|
|
||||||
D7343FE72D3FA1F40059D784 /* CodecModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodecModel.swift; sourceTree = "<group>"; };
|
|
||||||
D7343FEC2D3FE1550059D784 /* HelpFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpFragment.swift; sourceTree = "<group>"; };
|
|
||||||
D7343FEE2D3FE16B0059D784 /* HelpViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
D73449982BC6932A00778C56 /* MeetingWaitingRoomFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingWaitingRoomFragment.swift; sourceTree = "<group>"; };
|
D73449982BC6932A00778C56 /* MeetingWaitingRoomFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingWaitingRoomFragment.swift; sourceTree = "<group>"; };
|
||||||
D734499A2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingWaitingRoomViewModel.swift; sourceTree = "<group>"; };
|
D734499A2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingWaitingRoomViewModel.swift; sourceTree = "<group>"; };
|
||||||
D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartySipAccountLoginFragment.swift; sourceTree = "<group>"; };
|
D748BF2B2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartySipAccountLoginFragment.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -328,8 +307,6 @@
|
||||||
D74C9D002ACB098C0021626A /* PermissionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionManager.swift; sourceTree = "<group>"; };
|
D74C9D002ACB098C0021626A /* PermissionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionManager.swift; sourceTree = "<group>"; };
|
||||||
D74DA0112C047F0700A8561D /* HistoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryModel.swift; sourceTree = "<group>"; };
|
D74DA0112C047F0700A8561D /* HistoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryModel.swift; sourceTree = "<group>"; };
|
||||||
D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupLoadingView.swift; sourceTree = "<group>"; };
|
D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupLoadingView.swift; sourceTree = "<group>"; };
|
||||||
D756C8142D34FF8900A58F2F /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
D756C8162D352C5600A58F2F /* CorePreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CorePreferences.swift; sourceTree = "<group>"; };
|
|
||||||
D75759312B56D40900E7AC10 /* ZRTPPopup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZRTPPopup.swift; sourceTree = "<group>"; };
|
D75759312B56D40900E7AC10 /* ZRTPPopup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZRTPPopup.swift; sourceTree = "<group>"; };
|
||||||
D759CB632C3FBD4200AC35E8 /* StartConversationFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartConversationFragment.swift; sourceTree = "<group>"; };
|
D759CB632C3FBD4200AC35E8 /* StartConversationFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartConversationFragment.swift; sourceTree = "<group>"; };
|
||||||
D759CB652C3FBE1D00AC35E8 /* StartConversationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartConversationViewModel.swift; sourceTree = "<group>"; };
|
D759CB652C3FBE1D00AC35E8 /* StartConversationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartConversationViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -339,10 +316,8 @@
|
||||||
D77A080D2CB6BCA10095D589 /* MessageConferenceInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageConferenceInfo.swift; sourceTree = "<group>"; };
|
D77A080D2CB6BCA10095D589 /* MessageConferenceInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageConferenceInfo.swift; sourceTree = "<group>"; };
|
||||||
D78290B72ADD3910004AA85C /* ContactsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsFragment.swift; sourceTree = "<group>"; };
|
D78290B72ADD3910004AA85C /* ContactsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsFragment.swift; sourceTree = "<group>"; };
|
||||||
D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = "<group>"; };
|
D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = "<group>"; };
|
||||||
D783028E2D414845009CCB60 /* DebugFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugFragment.swift; sourceTree = "<group>"; };
|
|
||||||
D783C77A2B1089B200622CC2 /* assistant_linphone_default_values */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = assistant_linphone_default_values; sourceTree = "<group>"; };
|
D783C77A2B1089B200622CC2 /* assistant_linphone_default_values */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = assistant_linphone_default_values; sourceTree = "<group>"; };
|
||||||
D783C77B2B1089B200622CC2 /* assistant_third_party_default_values */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = assistant_third_party_default_values; sourceTree = "<group>"; };
|
D783C77B2B1089B200622CC2 /* assistant_third_party_default_values */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = assistant_third_party_default_values; sourceTree = "<group>"; };
|
||||||
D78607702D36CB87009E6A7E /* SettingsAdvancedFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAdvancedFragment.swift; sourceTree = "<group>"; };
|
|
||||||
D78E06272BE3811D00CE3783 /* CallStatsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallStatsModel.swift; sourceTree = "<group>"; };
|
D78E06272BE3811D00CE3783 /* CallStatsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallStatsModel.swift; sourceTree = "<group>"; };
|
||||||
D78E06292BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaEncryptedSheetBottomSheet.swift; sourceTree = "<group>"; };
|
D78E06292BEA698E00CE3783 /* MediaEncryptedSheetBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaEncryptedSheetBottomSheet.swift; sourceTree = "<group>"; };
|
||||||
D78E062B2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallStatisticsSheetBottomSheet.swift; sourceTree = "<group>"; };
|
D78E062B2BEA69BC00CE3783 /* CallStatisticsSheetBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallStatisticsSheetBottomSheet.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -370,8 +345,6 @@
|
||||||
D7C3650D2AF15BF200FE6142 /* PhotoPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoPicker.swift; sourceTree = "<group>"; };
|
D7C3650D2AF15BF200FE6142 /* PhotoPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoPicker.swift; sourceTree = "<group>"; };
|
||||||
D7C48DF32AFA66F900D938CB /* EditContactController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactController.swift; sourceTree = "<group>"; };
|
D7C48DF32AFA66F900D938CB /* EditContactController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactController.swift; sourceTree = "<group>"; };
|
||||||
D7C48DF52AFCDF4700D938CB /* ContactInnerActionsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactInnerActionsFragment.swift; sourceTree = "<group>"; };
|
D7C48DF52AFCDF4700D938CB /* ContactInnerActionsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactInnerActionsFragment.swift; sourceTree = "<group>"; };
|
||||||
D7C5003F2D27F16900DD53EC /* AccountSettingsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSettingsFragment.swift; sourceTree = "<group>"; };
|
|
||||||
D7C500412D2BE96E00DD53EC /* AccountSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSettingsViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
D7CEE0342B7A210300FD79B7 /* ConversationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationsView.swift; sourceTree = "<group>"; };
|
D7CEE0342B7A210300FD79B7 /* ConversationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationsView.swift; sourceTree = "<group>"; };
|
||||||
D7CEE0372B7A214F00FD79B7 /* ConversationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationsListViewModel.swift; sourceTree = "<group>"; };
|
D7CEE0372B7A214F00FD79B7 /* ConversationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationsListViewModel.swift; sourceTree = "<group>"; };
|
||||||
D7CEE03A2B7A234200FD79B7 /* ConversationsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationsFragment.swift; sourceTree = "<group>"; };
|
D7CEE03A2B7A234200FD79B7 /* ConversationsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationsFragment.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -385,8 +358,6 @@
|
||||||
D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-ExtraBold.ttf"; sourceTree = "<group>"; };
|
D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-ExtraBold.ttf"; sourceTree = "<group>"; };
|
||||||
D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFragment.swift; sourceTree = "<group>"; };
|
D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginFragment.swift; sourceTree = "<group>"; };
|
||||||
D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModeFragment.swift; sourceTree = "<group>"; };
|
D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModeFragment.swift; sourceTree = "<group>"; };
|
||||||
D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountProfileFragment.swift; sourceTree = "<group>"; };
|
|
||||||
D7DC09702CFDBF8300A6D47C /* AccountProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountProfileViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
D7E2E69E2CE356C90080DA0D /* PopupViewWithTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupViewWithTextField.swift; sourceTree = "<group>"; };
|
D7E2E69E2CE356C90080DA0D /* PopupViewWithTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupViewWithTextField.swift; sourceTree = "<group>"; };
|
||||||
D7E6ADF22B9875C20009A2BC /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
|
D7E6ADF22B9875C20009A2BC /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
|
||||||
D7E6ADF42B9876ED0009A2BC /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
|
D7E6ADF42B9876ED0009A2BC /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -407,7 +378,6 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
C618BF582D75CA0D005A00E0 /* linphonesw in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -415,7 +385,6 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
C618BF562D75CA03005A00E0 /* linphonesw in Frameworks */,
|
|
||||||
4ED1F0A881A9ACB5977A8987 /* BuildFile in Frameworks */,
|
4ED1F0A881A9ACB5977A8987 /* BuildFile in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
@ -630,11 +599,9 @@
|
||||||
D7CEE0332B7A20A400FD79B7 /* Conversations */,
|
D7CEE0332B7A20A400FD79B7 /* Conversations */,
|
||||||
D7A03FBB2ACC2D850081A588 /* Contacts */,
|
D7A03FBB2ACC2D850081A588 /* Contacts */,
|
||||||
D74C9CFD2ACAEC150021626A /* Fragments */,
|
D74C9CFD2ACAEC150021626A /* Fragments */,
|
||||||
D7343FE92D3FE0830059D784 /* Help */,
|
|
||||||
D7A03FBE2ACC2E010081A588 /* History */,
|
D7A03FBE2ACC2E010081A588 /* History */,
|
||||||
66E56BC52BA45E49006CE56F /* Meetings */,
|
66E56BC52BA45E49006CE56F /* Meetings */,
|
||||||
66D382032CEB7DB80063E1C5 /* Models */,
|
66D382032CEB7DB80063E1C5 /* Models */,
|
||||||
D7DC096A2CFA192200A6D47C /* Settings */,
|
|
||||||
D7A2EDD42AC180FE005D90FC /* Viewmodel */,
|
D7A2EDD42AC180FE005D90FC /* Viewmodel */,
|
||||||
D719ABB82ABC67BF00B41C10 /* ContentView.swift */,
|
D719ABB82ABC67BF00B41C10 /* ContentView.swift */,
|
||||||
);
|
);
|
||||||
|
|
@ -644,7 +611,6 @@
|
||||||
D719ABC72ABC6FB200B41C10 /* Core */ = {
|
D719ABC72ABC6FB200B41C10 /* Core */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D756C8162D352C5600A58F2F /* CorePreferences.swift */,
|
|
||||||
D719ABC82ABC6FD700B41C10 /* CoreContext.swift */,
|
D719ABC82ABC6FD700B41C10 /* CoreContext.swift */,
|
||||||
66C491FA2B24D32600CEA16D /* CoreExtension.swift */,
|
66C491FA2B24D32600CEA16D /* CoreExtension.swift */,
|
||||||
);
|
);
|
||||||
|
|
@ -714,32 +680,6 @@
|
||||||
path = Fragments;
|
path = Fragments;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
D7343FE92D3FE0830059D784 /* Help */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D7343FEA2D3FE1080059D784 /* Fragments */,
|
|
||||||
D7343FEB2D3FE1150059D784 /* ViewModel */,
|
|
||||||
);
|
|
||||||
path = Help;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D7343FEA2D3FE1080059D784 /* Fragments */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D783028E2D414845009CCB60 /* DebugFragment.swift */,
|
|
||||||
D7343FEC2D3FE1550059D784 /* HelpFragment.swift */,
|
|
||||||
);
|
|
||||||
path = Fragments;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D7343FEB2D3FE1150059D784 /* ViewModel */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D7343FEE2D3FE16B0059D784 /* HelpViewModel.swift */,
|
|
||||||
);
|
|
||||||
path = ViewModel;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D74C9CF62ACACEB70021626A /* Fragments */ = {
|
D74C9CF62ACACEB70021626A /* Fragments */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
@ -897,7 +837,6 @@
|
||||||
children = (
|
children = (
|
||||||
D7B99E982B29B39000BE7BF2 /* CallViewModel.swift */,
|
D7B99E982B29B39000BE7BF2 /* CallViewModel.swift */,
|
||||||
D734499A2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift */,
|
D734499A2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift */,
|
||||||
66C468FA2D2BE54300A836F7 /* PIPViewModel.swift */,
|
|
||||||
);
|
);
|
||||||
path = ViewModel;
|
path = ViewModel;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -970,45 +909,6 @@
|
||||||
path = Fragments;
|
path = Fragments;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
D7DC096A2CFA192200A6D47C /* Settings */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D7DC096B2CFA192F00A6D47C /* Fragments */,
|
|
||||||
D7DC096C2CFA193B00A6D47C /* Models */,
|
|
||||||
D7DC096D2CFA194600A6D47C /* ViewModel */,
|
|
||||||
);
|
|
||||||
path = Settings;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D7DC096B2CFA192F00A6D47C /* Fragments */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D78607702D36CB87009E6A7E /* SettingsAdvancedFragment.swift */,
|
|
||||||
D732C38B2D311D2100F78100 /* SettingsFragment.swift */,
|
|
||||||
D7C5003F2D27F16900DD53EC /* AccountSettingsFragment.swift */,
|
|
||||||
D7DC096E2CFA1D7400A6D47C /* AccountProfileFragment.swift */,
|
|
||||||
);
|
|
||||||
path = Fragments;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D7DC096C2CFA193B00A6D47C /* Models */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D7343FE72D3FA1F40059D784 /* CodecModel.swift */,
|
|
||||||
);
|
|
||||||
path = Models;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D7DC096D2CFA194600A6D47C /* ViewModel */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D756C8142D34FF8900A58F2F /* SettingsViewModel.swift */,
|
|
||||||
D7C500412D2BE96E00DD53EC /* AccountSettingsViewModel.swift */,
|
|
||||||
D7DC09702CFDBF8300A6D47C /* AccountProfileViewModel.swift */,
|
|
||||||
);
|
|
||||||
path = ViewModel;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
|
@ -1070,7 +970,7 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = D719ABAE2ABC67BF00B41C10 /* Build configuration list for PBXProject "linphone" */;
|
buildConfigurationList = D719ABAE2ABC67BF00B41C10 /* Build configuration list for PBXProject "Linphone" */;
|
||||||
compatibilityVersion = "Xcode 14.0";
|
compatibilityVersion = "Xcode 14.0";
|
||||||
developmentRegion = en;
|
developmentRegion = en;
|
||||||
hasScannedForEncodings = 0;
|
hasScannedForEncodings = 0;
|
||||||
|
|
@ -1079,9 +979,6 @@
|
||||||
Base,
|
Base,
|
||||||
);
|
);
|
||||||
mainGroup = D719ABAA2ABC67BF00B41C10;
|
mainGroup = D719ABAA2ABC67BF00B41C10;
|
||||||
packageReferences = (
|
|
||||||
C618BF542D75CA03005A00E0 /* XCRemoteSwiftPackageReference "linphone-sdk-swift-ios" */,
|
|
||||||
);
|
|
||||||
productRefGroup = D719ABB42ABC67BF00B41C10 /* Products */;
|
productRefGroup = D719ABB42ABC67BF00B41C10 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
|
|
@ -1206,7 +1103,6 @@
|
||||||
66FBFC492B83BD2400BC6AB1 /* ConfigExtension.swift in Sources */,
|
66FBFC492B83BD2400BC6AB1 /* ConfigExtension.swift in Sources */,
|
||||||
66FBFC4A2B83BD3300BC6AB1 /* FileUtils.swift in Sources */,
|
66FBFC4A2B83BD3300BC6AB1 /* FileUtils.swift in Sources */,
|
||||||
66FBFC4B2B83BD7B00BC6AB1 /* CoreExtension.swift in Sources */,
|
66FBFC4B2B83BD7B00BC6AB1 /* CoreExtension.swift in Sources */,
|
||||||
D756C8172D352C5F00A58F2F /* CorePreferences.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -1236,12 +1132,10 @@
|
||||||
D7C2DA1D2CA44DE400A2441B /* EventModel.swift in Sources */,
|
D7C2DA1D2CA44DE400A2441B /* EventModel.swift in Sources */,
|
||||||
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */,
|
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */,
|
||||||
D70A26F22B7F5D95006CC8FC /* ConversationFragment.swift in Sources */,
|
D70A26F22B7F5D95006CC8FC /* ConversationFragment.swift in Sources */,
|
||||||
D7DC096F2CFA1D7600A6D47C /* AccountProfileFragment.swift in Sources */,
|
|
||||||
D717A10E2CEB772300849D92 /* ShareSheetController.swift in Sources */,
|
D717A10E2CEB772300849D92 /* ShareSheetController.swift in Sources */,
|
||||||
66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */,
|
66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */,
|
||||||
D70959F12B8DF3EC0014AC0B /* ConversationModel.swift in Sources */,
|
D70959F12B8DF3EC0014AC0B /* ConversationModel.swift in Sources */,
|
||||||
C6DC4E3D2C199C4E009096FD /* BundleExtenion.swift in Sources */,
|
C6DC4E3D2C199C4E009096FD /* BundleExtenion.swift in Sources */,
|
||||||
D7343FEF2D3FE16C0059D784 /* HelpViewModel.swift in Sources */,
|
|
||||||
D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */,
|
D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */,
|
||||||
D759CB662C3FBE1D00AC35E8 /* StartConversationViewModel.swift in Sources */,
|
D759CB662C3FBE1D00AC35E8 /* StartConversationViewModel.swift in Sources */,
|
||||||
D777DBB32AE12C5900565A99 /* ContactsManager.swift in Sources */,
|
D777DBB32AE12C5900565A99 /* ContactsManager.swift in Sources */,
|
||||||
|
|
@ -1249,7 +1143,6 @@
|
||||||
D796F2002B0BB61A0041115F /* ToastViewModel.swift in Sources */,
|
D796F2002B0BB61A0041115F /* ToastViewModel.swift in Sources */,
|
||||||
D7C3650A2AF001C300FE6142 /* EditContactFragment.swift in Sources */,
|
D7C3650A2AF001C300FE6142 /* EditContactFragment.swift in Sources */,
|
||||||
D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */,
|
D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */,
|
||||||
D78607712D36CB8A009E6A7E /* SettingsAdvancedFragment.swift in Sources */,
|
|
||||||
66E50A492BD12B2300AD61CA /* MeetingsView.swift in Sources */,
|
66E50A492BD12B2300AD61CA /* MeetingsView.swift in Sources */,
|
||||||
D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */,
|
D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */,
|
||||||
D717630D2BD7BD0E00464097 /* ParticipantsListFragment.swift in Sources */,
|
D717630D2BD7BD0E00464097 /* ParticipantsListFragment.swift in Sources */,
|
||||||
|
|
@ -1257,7 +1150,6 @@
|
||||||
C6A5A9452C10B6270070FEA4 /* OIDAuthStateExtension.swift in Sources */,
|
C6A5A9452C10B6270070FEA4 /* OIDAuthStateExtension.swift in Sources */,
|
||||||
D732A90F2B04C3B400DB42BA /* HistoryFragment.swift in Sources */,
|
D732A90F2B04C3B400DB42BA /* HistoryFragment.swift in Sources */,
|
||||||
D79F1C162CD3D6AD00FF0A05 /* ConversationInfoFragment.swift in Sources */,
|
D79F1C162CD3D6AD00FF0A05 /* ConversationInfoFragment.swift in Sources */,
|
||||||
D7C500422D2BE98100DD53EC /* AccountSettingsViewModel.swift in Sources */,
|
|
||||||
D79622342B1DFE600037EACD /* DialerBottomSheet.swift in Sources */,
|
D79622342B1DFE600037EACD /* DialerBottomSheet.swift in Sources */,
|
||||||
C67586B02C09F247002E77BF /* URIHandler.swift in Sources */,
|
C67586B02C09F247002E77BF /* URIHandler.swift in Sources */,
|
||||||
C62817282C1B389700DBA646 /* SideMenuAccountRow.swift in Sources */,
|
C62817282C1B389700DBA646 /* SideMenuAccountRow.swift in Sources */,
|
||||||
|
|
@ -1275,7 +1167,6 @@
|
||||||
C62817302C1C3DCC00DBA646 /* AccountModel.swift in Sources */,
|
C62817302C1C3DCC00DBA646 /* AccountModel.swift in Sources */,
|
||||||
D73449992BC6932A00778C56 /* MeetingWaitingRoomFragment.swift in Sources */,
|
D73449992BC6932A00778C56 /* MeetingWaitingRoomFragment.swift in Sources */,
|
||||||
D7C48DF42AFA66F900D938CB /* EditContactController.swift in Sources */,
|
D7C48DF42AFA66F900D938CB /* EditContactController.swift in Sources */,
|
||||||
D7343FED2D3FE1560059D784 /* HelpFragment.swift in Sources */,
|
|
||||||
C628172E2C1C3A3600DBA646 /* AccountExtension.swift in Sources */,
|
C628172E2C1C3A3600DBA646 /* AccountExtension.swift in Sources */,
|
||||||
66C491FF2B24D4AC00CEA16D /* FileUtils.swift in Sources */,
|
66C491FF2B24D4AC00CEA16D /* FileUtils.swift in Sources */,
|
||||||
C62817322C1C400A00DBA646 /* StringExtension.swift in Sources */,
|
C62817322C1C400A00DBA646 /* StringExtension.swift in Sources */,
|
||||||
|
|
@ -1284,13 +1175,11 @@
|
||||||
D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */,
|
D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */,
|
||||||
D732A9152B04C7FE00DB42BA /* HistoryListViewModel.swift in Sources */,
|
D732A9152B04C7FE00DB42BA /* HistoryListViewModel.swift in Sources */,
|
||||||
6646A7A32BB2E224006B842A /* ScheduleMeetingFragment.swift in Sources */,
|
6646A7A32BB2E224006B842A /* ScheduleMeetingFragment.swift in Sources */,
|
||||||
D7C500402D27F16C00DD53EC /* AccountSettingsFragment.swift in Sources */,
|
|
||||||
D71FCA7F2AE1397200D2E43E /* ContactsListViewModel.swift in Sources */,
|
D71FCA7F2AE1397200D2E43E /* ContactsListViewModel.swift in Sources */,
|
||||||
D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */,
|
D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */,
|
||||||
D734499B2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift in Sources */,
|
D734499B2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift in Sources */,
|
||||||
D719ABB72ABC67BF00B41C10 /* LinphoneApp.swift in Sources */,
|
D719ABB72ABC67BF00B41C10 /* LinphoneApp.swift in Sources */,
|
||||||
66E50A4B2BD12B7800AD61CA /* MeetingsFragment.swift in Sources */,
|
66E50A4B2BD12B7800AD61CA /* MeetingsFragment.swift in Sources */,
|
||||||
D783028F2D414847009CCB60 /* DebugFragment.swift in Sources */,
|
|
||||||
66162A202BDFC2F900DCE913 /* AddParticipantsViewModel.swift in Sources */,
|
66162A202BDFC2F900DCE913 /* AddParticipantsViewModel.swift in Sources */,
|
||||||
D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */,
|
D732A91B2B061BD900DB42BA /* HistoryListBottomSheet.swift in Sources */,
|
||||||
D72250632ADE9615008FB426 /* HistoryViewModel.swift in Sources */,
|
D72250632ADE9615008FB426 /* HistoryViewModel.swift in Sources */,
|
||||||
|
|
@ -1316,14 +1205,12 @@
|
||||||
6613A0B42BAEBE3F008923A4 /* MeetingViewModel.swift in Sources */,
|
6613A0B42BAEBE3F008923A4 /* MeetingViewModel.swift in Sources */,
|
||||||
D7173EBE2B7A5C0A00BCC481 /* LinphoneUtils.swift in Sources */,
|
D7173EBE2B7A5C0A00BCC481 /* LinphoneUtils.swift in Sources */,
|
||||||
66C492012B24DB6900CEA16D /* Log.swift in Sources */,
|
66C492012B24DB6900CEA16D /* Log.swift in Sources */,
|
||||||
D756C8182D352C5F00A58F2F /* CorePreferences.swift in Sources */,
|
|
||||||
C6A5A9432C10B5ED0070FEA4 /* DecodableExtension.swift in Sources */,
|
C6A5A9432C10B5ED0070FEA4 /* DecodableExtension.swift in Sources */,
|
||||||
D714035B2BE11E00004BD8CA /* CallMediaEncryptionModel.swift in Sources */,
|
D714035B2BE11E00004BD8CA /* CallMediaEncryptionModel.swift in Sources */,
|
||||||
D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */,
|
D748BF2C2ACD82D2004844EB /* ThirdPartySipAccountLoginFragment.swift in Sources */,
|
||||||
D7CEE0382B7A214F00FD79B7 /* ConversationsListViewModel.swift in Sources */,
|
D7CEE0382B7A214F00FD79B7 /* ConversationsListViewModel.swift in Sources */,
|
||||||
D74C9CF82ACACECE0021626A /* WelcomePage1Fragment.swift in Sources */,
|
D74C9CF82ACACECE0021626A /* WelcomePage1Fragment.swift in Sources */,
|
||||||
66E56BCE2BA9A1F8006CE56F /* MeetingModel.swift in Sources */,
|
66E56BCE2BA9A1F8006CE56F /* MeetingModel.swift in Sources */,
|
||||||
D7DC09712CFDBF9A00A6D47C /* AccountProfileViewModel.swift in Sources */,
|
|
||||||
D7E6D0552AEBFCCE00A57AAF /* ContactsInnerFragment.swift in Sources */,
|
D7E6D0552AEBFCCE00A57AAF /* ContactsInnerFragment.swift in Sources */,
|
||||||
D72343362AD037AF009AA24E /* ToastView.swift in Sources */,
|
D72343362AD037AF009AA24E /* ToastView.swift in Sources */,
|
||||||
D7FB55112AD447FD00A5AB15 /* RegisterFragment.swift in Sources */,
|
D7FB55112AD447FD00A5AB15 /* RegisterFragment.swift in Sources */,
|
||||||
|
|
@ -1338,7 +1225,6 @@
|
||||||
D72A9A052B9750A1000DC093 /* UIList.swift in Sources */,
|
D72A9A052B9750A1000DC093 /* UIList.swift in Sources */,
|
||||||
D726E43D2B19E4FE0083C415 /* StartCallFragment.swift in Sources */,
|
D726E43D2B19E4FE0083C415 /* StartCallFragment.swift in Sources */,
|
||||||
66E56BCC2BA9A1E0006CE56F /* MeetingsListItemModel.swift in Sources */,
|
66E56BCC2BA9A1E0006CE56F /* MeetingsListItemModel.swift in Sources */,
|
||||||
D756C8152D34FF9200A58F2F /* SettingsViewModel.swift in Sources */,
|
|
||||||
D7B99E9B2B29F7C300BE7BF2 /* ActivityIndicator.swift in Sources */,
|
D7B99E9B2B29F7C300BE7BF2 /* ActivityIndicator.swift in Sources */,
|
||||||
66F626B22BCEBB86003E2DEC /* AddParticipantsFragment.swift in Sources */,
|
66F626B22BCEBB86003E2DEC /* AddParticipantsFragment.swift in Sources */,
|
||||||
D72343302ACEFEF8009AA24E /* QrCodeScannerFragment.swift in Sources */,
|
D72343302ACEFEF8009AA24E /* QrCodeScannerFragment.swift in Sources */,
|
||||||
|
|
@ -1353,16 +1239,13 @@
|
||||||
D72250692ADFBF2D008FB426 /* SideMenu.swift in Sources */,
|
D72250692ADFBF2D008FB426 /* SideMenu.swift in Sources */,
|
||||||
C6DC4E3F2C19C289009096FD /* SideMenuEntry.swift in Sources */,
|
C6DC4E3F2C19C289009096FD /* SideMenuEntry.swift in Sources */,
|
||||||
D714DE622C1C4636006C1F1D /* RegisterCodeConfirmationFragment.swift in Sources */,
|
D714DE622C1C4636006C1F1D /* RegisterCodeConfirmationFragment.swift in Sources */,
|
||||||
66C468FB2D2BE54800A836F7 /* PIPViewModel.swift in Sources */,
|
|
||||||
D7CEE0352B7A210300FD79B7 /* ConversationsView.swift in Sources */,
|
D7CEE0352B7A210300FD79B7 /* ConversationsView.swift in Sources */,
|
||||||
D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */,
|
D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */,
|
||||||
D71968922B86369D00DF4459 /* ChatBubbleView.swift in Sources */,
|
D71968922B86369D00DF4459 /* ChatBubbleView.swift in Sources */,
|
||||||
D732C38C2D311D2500F78100 /* SettingsFragment.swift in Sources */,
|
|
||||||
D78290B82ADD3910004AA85C /* ContactsFragment.swift in Sources */,
|
D78290B82ADD3910004AA85C /* ContactsFragment.swift in Sources */,
|
||||||
D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */,
|
D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */,
|
||||||
D7CEE03D2B7A23B200FD79B7 /* ConversationsListFragment.swift in Sources */,
|
D7CEE03D2B7A23B200FD79B7 /* ConversationsListFragment.swift in Sources */,
|
||||||
D74C9CFC2ACACF370021626A /* WelcomePage3Fragment.swift in Sources */,
|
D74C9CFC2ACACF370021626A /* WelcomePage3Fragment.swift in Sources */,
|
||||||
D7343FE82D3FA2000059D784 /* CodecModel.swift in Sources */,
|
|
||||||
D77A080E2CB6BCAF0095D589 /* MessageConferenceInfo.swift in Sources */,
|
D77A080E2CB6BCAF0095D589 /* MessageConferenceInfo.swift in Sources */,
|
||||||
C6A5A9412C10B5D50070FEA4 /* EncodableExtension.swift in Sources */,
|
C6A5A9412C10B5D50070FEA4 /* EncodableExtension.swift in Sources */,
|
||||||
D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */,
|
D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */,
|
||||||
|
|
@ -1395,14 +1278,13 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = msgNotificationService/msgNotificationService.entitlements;
|
CODE_SIGN_ENTITLEMENTS = msgNotificationService/msgNotificationService.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 73;
|
CURRENT_PROJECT_VERSION = 62;
|
||||||
DEVELOPMENT_TEAM = Z2V957B3D6;
|
DEVELOPMENT_TEAM = Z2V957B3D6;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
"USE_CRASHLYTICS=1",
|
|
||||||
);
|
);
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = msgNotificationService/Info.plist;
|
INFOPLIST_FILE = msgNotificationService/Info.plist;
|
||||||
|
|
@ -1416,7 +1298,7 @@
|
||||||
);
|
);
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 6.0.0;
|
MARKETING_VERSION = 6.0.0;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -DUSE_CRASHLYTICS";
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.msgNotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.msgNotificationService;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|
@ -1438,14 +1320,11 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = msgNotificationService/msgNotificationService.entitlements;
|
CODE_SIGN_ENTITLEMENTS = msgNotificationService/msgNotificationService.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 73;
|
CURRENT_PROJECT_VERSION = 62;
|
||||||
DEVELOPMENT_TEAM = Z2V957B3D6;
|
DEVELOPMENT_TEAM = Z2V957B3D6;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
|
||||||
"$(inherited)",
|
|
||||||
"USE_CRASHLYTICS=1",
|
|
||||||
);
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = msgNotificationService/Info.plist;
|
INFOPLIST_FILE = msgNotificationService/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = msgNotificationService;
|
INFOPLIST_KEY_CFBundleDisplayName = msgNotificationService;
|
||||||
|
|
@ -1458,7 +1337,7 @@
|
||||||
);
|
);
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 6.0.0;
|
MARKETING_VERSION = 6.0.0;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -DUSE_CRASHLYTICS";
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.msgNotificationService;
|
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.msgNotificationService;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|
@ -1595,7 +1474,7 @@
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
CODE_SIGN_ENTITLEMENTS = Linphone/Linphone.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Linphone/Linphone.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 73;
|
CURRENT_PROJECT_VERSION = 62;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Linphone/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Linphone/Preview Content\"";
|
||||||
|
|
@ -1605,15 +1484,11 @@
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
"USE_CRASHLYTICS=1",
|
|
||||||
);
|
);
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = Linphone/Info.plist;
|
INFOPLIST_FILE = Linphone/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Linphone;
|
|
||||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "Deprecated - Prior to iOS 17 full calendar access is required";
|
|
||||||
INFOPLIST_KEY_NSCameraUsageDescription = "Camera usage is required for video VOIP calls";
|
INFOPLIST_KEY_NSCameraUsageDescription = "Camera usage is required for video VOIP calls";
|
||||||
INFOPLIST_KEY_NSContactsUsageDescription = "Make calls with your friends";
|
INFOPLIST_KEY_NSContactsUsageDescription = "Make calls with your friends";
|
||||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "App requires access to the local network to establish VoIP connections";
|
|
||||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Microphone usage is required for VOIP calls";
|
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Microphone usage is required for VOIP calls";
|
||||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Share photos with your friends and customize avatars";
|
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Share photos with your friends and customize avatars";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
|
@ -1635,7 +1510,7 @@
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.3;
|
MACOSX_DEPLOYMENT_TARGET = 13.3;
|
||||||
MARKETING_VERSION = 6.0.0;
|
MARKETING_VERSION = 6.0.0;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -DUSE_CRASHLYTICS";
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone;
|
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|
@ -1655,23 +1530,18 @@
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||||
CODE_SIGN_ENTITLEMENTS = Linphone/Linphone.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Linphone/Linphone.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 73;
|
CURRENT_PROJECT_VERSION = 62;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Linphone/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Linphone/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = Z2V957B3D6;
|
DEVELOPMENT_TEAM = Z2V957B3D6;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
|
||||||
"$(inherited)",
|
|
||||||
"USE_CRASHLYTICS=1",
|
|
||||||
);
|
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = Linphone/Info.plist;
|
INFOPLIST_FILE = Linphone/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Linphone;
|
INFOPLIST_KEY_CFBundleDisplayName = Linphone;
|
||||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "Deprecated - Prior to iOS 17 full calendar access is required";
|
|
||||||
INFOPLIST_KEY_NSCameraUsageDescription = "Camera usage is required for video VOIP calls";
|
INFOPLIST_KEY_NSCameraUsageDescription = "Camera usage is required for video VOIP calls";
|
||||||
INFOPLIST_KEY_NSContactsUsageDescription = "Make calls with your friends";
|
INFOPLIST_KEY_NSContactsUsageDescription = "Make calls with your friends";
|
||||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "App requires access to the local network to establish VoIP connections";
|
|
||||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Microphone usage is required for VOIP calls";
|
INFOPLIST_KEY_NSMicrophoneUsageDescription = "Microphone usage is required for VOIP calls";
|
||||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Share photos with your friends and customize avatars";
|
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Share photos with your friends and customize avatars";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
|
@ -1693,7 +1563,7 @@
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.3;
|
MACOSX_DEPLOYMENT_TARGET = 13.3;
|
||||||
MARKETING_VERSION = 6.0.0;
|
MARKETING_VERSION = 6.0.0;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) -DUSE_CRASHLYTICS";
|
OTHER_SWIFT_FLAGS = "$(inherited)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone;
|
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|
@ -1716,7 +1586,7 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
D719ABAE2ABC67BF00B41C10 /* Build configuration list for PBXProject "linphone" */ = {
|
D719ABAE2ABC67BF00B41C10 /* Build configuration list for PBXProject "Linphone" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
D719ABC02ABC67BF00B41C10 /* Debug */,
|
D719ABC02ABC67BF00B41C10 /* Debug */,
|
||||||
|
|
@ -1735,30 +1605,6 @@
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
|
||||||
C618BF542D75CA03005A00E0 /* XCRemoteSwiftPackageReference "linphone-sdk-swift-ios" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://gitlab.linphone.org/BC/public/linphone-sdk-swift-ios.git";
|
|
||||||
requirement = {
|
|
||||||
branch = stable;
|
|
||||||
kind = branch;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
|
||||||
C618BF552D75CA03005A00E0 /* linphonesw */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = C618BF542D75CA03005A00E0 /* XCRemoteSwiftPackageReference "linphone-sdk-swift-ios" */;
|
|
||||||
productName = linphonesw;
|
|
||||||
};
|
|
||||||
C618BF572D75CA0D005A00E0 /* linphonesw */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = C618BF542D75CA03005A00E0 /* XCRemoteSwiftPackageReference "linphone-sdk-swift-ios" */;
|
|
||||||
productName = linphonesw;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
|
||||||
};
|
};
|
||||||
rootObject = D719ABAB2ABC67BF00B41C10 /* Project object */;
|
rootObject = D719ABAB2ABC67BF00B41C10 /* Project object */;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
BlueprintIdentifier = "D719ABB22ABC67BF00B41C10"
|
BlueprintIdentifier = "D719ABB22ABC67BF00B41C10"
|
||||||
BuildableName = "Linphone.app"
|
BuildableName = "Linphone.app"
|
||||||
BlueprintName = "Linphone"
|
BlueprintName = "Linphone"
|
||||||
ReferencedContainer = "container:linphone.xcodeproj">
|
ReferencedContainer = "container:Linphone.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
</BuildActionEntries>
|
</BuildActionEntries>
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
BlueprintIdentifier = "D719ABB22ABC67BF00B41C10"
|
BlueprintIdentifier = "D719ABB22ABC67BF00B41C10"
|
||||||
BuildableName = "Linphone.app"
|
BuildableName = "Linphone.app"
|
||||||
BlueprintName = "Linphone"
|
BlueprintName = "Linphone"
|
||||||
ReferencedContainer = "container:linphone.xcodeproj">
|
ReferencedContainer = "container:Linphone.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
BlueprintIdentifier = "D719ABB22ABC67BF00B41C10"
|
BlueprintIdentifier = "D719ABB22ABC67BF00B41C10"
|
||||||
BuildableName = "Linphone.app"
|
BuildableName = "Linphone.app"
|
||||||
BlueprintName = "Linphone"
|
BlueprintName = "Linphone"
|
||||||
ReferencedContainer = "container:linphone.xcodeproj">
|
ReferencedContainer = "container:Linphone.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
</ProfileAction>
|
</ProfileAction>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "app-store-logo.svg",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M64.34,196.07l-9.45,16a8,8,0,1,1-13.78-8.14l9.46-16a8,8,0,1,1,13.77,8.14ZM232,152H184.2l-30.73-52a8,8,0,1,0-13.77,8.14l61.41,103.93a8,8,0,0,0,13.78-8.14L193.66,168H232a8,8,0,0,0,0-16Zm-89.53,0H90.38L158.89,36.07a8,8,0,0,0-13.78-8.14L128,56.89l-17.11-29a8,8,0,1,0-13.78,8.14l21.6,36.55L71.8,152H24a8,8,0,0,0,0,16H142.47a8,8,0,1,0,0-16Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 458 B |
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#4e6074" viewBox="0 0 256 256"><path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Z"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 351 B |
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm64-88a8,8,0,0,1-8,8H128a8,8,0,0,1-8-8V72a8,8,0,0,1,16,0v48h48A8,8,0,0,1,192,128Z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#4e6074" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm64-88a8,8,0,0,1-8,8H128a8,8,0,0,1-8-8V72a8,8,0,0,1,16,0v48h48A8,8,0,0,1,192,128Z"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 311 B |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "desktop.svg",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M208,40H48A24,24,0,0,0,24,64V176a24,24,0,0,0,24,24h72v16H96a8,8,0,0,0,0,16h64a8,8,0,0,0,0-16H136V200h72a24,24,0,0,0,24-24V64A24,24,0,0,0,208,40ZM48,56H208a8,8,0,0,1,8,8v80H40V64A8,8,0,0,1,48,56ZM208,184H48a8,8,0,0,1-8-8V160H216v16A8,8,0,0,1,208,184Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 373 B |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "device-mobile-camera.svg",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M176,16H80A24,24,0,0,0,56,40V216a24,24,0,0,0,24,24h96a24,24,0,0,0,24-24V40A24,24,0,0,0,176,16Zm8,200a8,8,0,0,1-8,8H80a8,8,0,0,1-8-8V40a8,8,0,0,1,8-8h96a8,8,0,0,1,8,8ZM140,60a12,12,0,1,1-12-12A12,12,0,0,1,140,60Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 335 B |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "fire.svg",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M183.89,153.34a57.6,57.6,0,0,1-46.56,46.55A8.75,8.75,0,0,1,136,200a8,8,0,0,1-1.32-15.89c16.57-2.79,30.63-16.85,33.44-33.45a8,8,0,0,1,15.78,2.68ZM216,144a88,88,0,0,1-176,0c0-27.92,11-56.47,32.66-84.85a8,8,0,0,1,11.93-.89l24.12,23.41,22-60.41a8,8,0,0,1,12.63-3.41C165.21,36,216,84.55,216,144Zm-16,0c0-46.09-35.79-85.92-58.21-106.33L119.52,98.74a8,8,0,0,1-13.09,3L80.06,76.16C64.09,99.21,56,122,56,144a72,72,0,0,0,144,0Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 541 B |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "mountain2.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 6.8 KiB |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "package.svg",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M223.68,66.15,135.68,18a15.88,15.88,0,0,0-15.36,0l-88,48.17a16,16,0,0,0-8.32,14v95.64a16,16,0,0,0,8.32,14l88,48.17a15.88,15.88,0,0,0,15.36,0l88-48.17a16,16,0,0,0,8.32-14V80.18A16,16,0,0,0,223.68,66.15ZM128,32l80.34,44-29.77,16.3-80.35-44ZM128,120,47.66,76l33.9-18.56,80.34,44ZM40,90l80,43.78v85.79L40,175.82Zm176,85.78h0l-80,43.79V133.82l32-17.51V152a8,8,0,0,0,16,0V107.55L216,90v85.77Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 510 B |
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M140,180a12,12,0,1,1-12-12A12,12,0,0,1,140,180ZM128,72c-22.06,0-40,16.15-40,36v4a8,8,0,0,0,16,0v-4c0-11,10.77-20,24-20s24,9,24,20-10.77,20-24,20a8,8,0,0,0-8,8v8a8,8,0,0,0,16,0v-.72c18.24-3.35,32-17.9,32-35.28C168,88.15,150.06,72,128,72Zm104,56A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#4e6074" viewBox="0 0 256 256"><path d="M140,180a12,12,0,1,1-12-12A12,12,0,0,1,140,180ZM128,72c-22.06,0-40,16.15-40,36v4a8,8,0,0,0,16,0v-4c0-11,10.77-20,24-20s24,9,24,20-10.77,20-24,20a8,8,0,0,0-8,8v8a8,8,0,0,0,16,0v-.72c18.24-3.35,32-17.9,32-35.28C168,88.15,150.06,72,128,72Zm104,56A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 466 B |
|
|
@ -88,9 +88,8 @@ final class ContactsManager: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MagicSearchSingleton.shared.searchForContactsWithoutCoreThread(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
|
||||||
|
|
||||||
let store = CNContactStore()
|
let store = CNContactStore()
|
||||||
|
|
||||||
store.requestAccess(for: .contacts) { (granted, error) in
|
store.requestAccess(for: .contacts) { (granted, error) in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
print("\(#function) - failed to request access", error)
|
print("\(#function) - failed to request access", error)
|
||||||
|
|
@ -106,127 +105,71 @@ final class ContactsManager: ObservableObject {
|
||||||
do {
|
do {
|
||||||
var contactCounter = 0
|
var contactCounter = 0
|
||||||
try store.enumerateContacts(with: request, usingBlock: { (contact, _) in
|
try store.enumerateContacts(with: request, usingBlock: { (contact, _) in
|
||||||
let newContact = Contact(
|
DispatchQueue.main.async {
|
||||||
identifier: contact.identifier,
|
let newContact = Contact(
|
||||||
firstName: contact.givenName,
|
identifier: contact.identifier,
|
||||||
lastName: contact.familyName,
|
firstName: contact.givenName,
|
||||||
organizationName: contact.organizationName,
|
lastName: contact.familyName,
|
||||||
jobTitle: "",
|
organizationName: contact.organizationName,
|
||||||
displayName: contact.nickname,
|
jobTitle: "",
|
||||||
sipAddresses: contact.instantMessageAddresses.map { $0.value.service.lowercased() == "SIP".lowercased() ? $0.value.username : "" },
|
displayName: contact.nickname,
|
||||||
phoneNumbers: contact.phoneNumbers.map { PhoneNumber(numLabel: $0.label ?? "", num: $0.value.stringValue)},
|
sipAddresses: contact.instantMessageAddresses.map { $0.value.service.lowercased() == "SIP".lowercased() ? $0.value.username : "" },
|
||||||
imageData: ""
|
phoneNumbers: contact.phoneNumbers.map { PhoneNumber(numLabel: $0.label ?? "", num: $0.value.stringValue)},
|
||||||
)
|
imageData: ""
|
||||||
|
)
|
||||||
let imageThumbnail = UIImage(data: contact.thumbnailImageData ?? Data())
|
|
||||||
if let image = imageThumbnail {
|
let imageThumbnail = UIImage(data: contact.thumbnailImageData ?? Data())
|
||||||
DispatchQueue.main.async {
|
self.saveImage(
|
||||||
self.saveImage(
|
image: imageThumbnail
|
||||||
image: image,
|
?? self.textToImage(
|
||||||
name: contact.givenName + contact.familyName,
|
firstName: contact.givenName.isEmpty
|
||||||
prefix: "",
|
&& contact.familyName.isEmpty
|
||||||
contact: newContact, linphoneFriend: false, existingFriend: nil) {
|
&& contact.phoneNumbers.first?.value.stringValue != nil
|
||||||
if (self.friendList?.friends.count ?? 0) == contactCounter {
|
? contact.phoneNumbers.first!.value.stringValue
|
||||||
// Every contact properly added, proceed
|
: contact.givenName, lastName: contact.familyName),
|
||||||
self.linphoneFriendList?.updateSubscriptions()
|
name: contact.givenName + contact.familyName,
|
||||||
self.friendList?.updateSubscriptions()
|
prefix: ((imageThumbnail == nil) ? "-default" : ""),
|
||||||
|
contact: newContact, linphoneFriend: false, existingFriend: nil) {
|
||||||
|
if (self.friendList?.friends.count ?? 0) + (self.linphoneFriendList?.friends.count ?? 0) == contactCounter {
|
||||||
|
// Every contact properly added, proceed
|
||||||
|
self.linphoneFriendList?.updateSubscriptions()
|
||||||
|
self.friendList?.updateSubscriptions()
|
||||||
|
|
||||||
|
if let friendListDelegate = self.friendListDelegate {
|
||||||
|
self.friendList?.removeDelegate(delegate: friendListDelegate)
|
||||||
|
}
|
||||||
|
self.friendListDelegate = FriendListDelegateStub(onNewSipAddressDiscovered: { (_: FriendList, linphoneFriend: Friend, sipUri: String) in
|
||||||
|
|
||||||
if let friendListDelegate = self.friendListDelegate {
|
var addedAvatarListModel: [ContactAvatarModel] = []
|
||||||
self.friendList?.removeDelegate(delegate: friendListDelegate)
|
linphoneFriend.phoneNumbers.forEach { phone in
|
||||||
|
let address = core.interpretUrl(url: phone, applyInternationalPrefix: true)
|
||||||
|
|
||||||
|
let presence = linphoneFriend.getPresenceModelForUriOrTel(uriOrTel: address?.asStringUriOnly() ?? "")
|
||||||
|
if address != nil && presence != nil {
|
||||||
|
linphoneFriend.edit()
|
||||||
|
linphoneFriend.addAddress(address: address!)
|
||||||
|
linphoneFriend.done()
|
||||||
|
|
||||||
|
addedAvatarListModel.append(
|
||||||
|
ContactAvatarModel(
|
||||||
|
friend: linphoneFriend,
|
||||||
|
name: linphoneFriend.name ?? "",
|
||||||
|
address: linphoneFriend.address?.clone()?.asStringUriOnly() ?? "",
|
||||||
|
withPresence: true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.friendListDelegate = FriendListDelegateStub(onNewSipAddressDiscovered: { (_: FriendList, linphoneFriend: Friend, sipUri: String) in
|
DispatchQueue.main.async {
|
||||||
var addedAvatarListModel: [ContactAvatarModel] = []
|
self.avatarListModel += addedAvatarListModel
|
||||||
linphoneFriend.phoneNumbers.forEach { _ in
|
|
||||||
let address = try? Factory.Instance.createAddress(addr: sipUri)
|
|
||||||
if address != nil {
|
|
||||||
linphoneFriend.edit()
|
|
||||||
linphoneFriend.addAddress(address: address!)
|
|
||||||
linphoneFriend.done()
|
|
||||||
|
|
||||||
let addressTmp = linphoneFriend.address?.clone()?.asStringUriOnly() ?? ""
|
|
||||||
addedAvatarListModel.append(
|
|
||||||
ContactAvatarModel(
|
|
||||||
friend: linphoneFriend,
|
|
||||||
name: linphoneFriend.name ?? "",
|
|
||||||
address: addressTmp,
|
|
||||||
withPresence: true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
NotificationCenter.default.post(
|
|
||||||
name: NSNotification.Name("ContactAdded"),
|
|
||||||
object: nil,
|
|
||||||
userInfo: ["address": addressTmp]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.avatarListModel += addedAvatarListModel
|
|
||||||
self.avatarListModel = self.avatarListModel.sorted { $0.name < $1.name }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
self.friendList?.addDelegate(delegate: self.friendListDelegate!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.textToImageInMainThread(firstName: contact.givenName, lastName: contact.familyName) { image in
|
|
||||||
self.saveImage(
|
|
||||||
image: image,
|
|
||||||
name: contact.givenName + contact.familyName,
|
|
||||||
prefix: "-default",
|
|
||||||
contact: newContact, linphoneFriend: false, existingFriend: nil) {
|
|
||||||
if (self.friendList?.friends.count ?? 0) == contactCounter {
|
|
||||||
// Every contact properly added, proceed
|
|
||||||
self.linphoneFriendList?.updateSubscriptions()
|
|
||||||
self.friendList?.updateSubscriptions()
|
|
||||||
|
|
||||||
if let friendListDelegate = self.friendListDelegate {
|
|
||||||
self.friendList?.removeDelegate(delegate: friendListDelegate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.friendListDelegate = FriendListDelegateStub(onNewSipAddressDiscovered: { (_: FriendList, linphoneFriend: Friend, sipUri: String) in
|
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||||
var addedAvatarListModel: [ContactAvatarModel] = []
|
})
|
||||||
linphoneFriend.phoneNumbers.forEach { _ in
|
self.friendList?.addDelegate(delegate: self.friendListDelegate!)
|
||||||
let address = try? Factory.Instance.createAddress(addr: sipUri)
|
|
||||||
if address != nil {
|
|
||||||
linphoneFriend.edit()
|
|
||||||
linphoneFriend.addAddress(address: address!)
|
|
||||||
linphoneFriend.done()
|
|
||||||
|
|
||||||
let addressTmp = linphoneFriend.address?.clone()?.asStringUriOnly() ?? ""
|
|
||||||
addedAvatarListModel.append(
|
|
||||||
ContactAvatarModel(
|
|
||||||
friend: linphoneFriend,
|
|
||||||
name: linphoneFriend.name ?? "",
|
|
||||||
address: addressTmp,
|
|
||||||
withPresence: true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
NotificationCenter.default.post(
|
|
||||||
name: NSNotification.Name("ContactAdded"),
|
|
||||||
object: nil,
|
|
||||||
userInfo: ["address": addressTmp]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.avatarListModel += addedAvatarListModel
|
|
||||||
self.avatarListModel = self.avatarListModel.sorted { $0.name < $1.name }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
self.friendList?.addDelegate(delegate: self.friendListDelegate!)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(contact.givenName.isEmpty && contact.familyName.isEmpty) {
|
if !(contact.givenName.isEmpty && contact.familyName.isEmpty) {
|
||||||
|
|
@ -245,35 +188,6 @@ final class ContactsManager: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func textToImageInMainThread(firstName: String, lastName: String, completion: @escaping (UIImage) -> Void) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let lblNameInitialize = UILabel()
|
|
||||||
lblNameInitialize.frame.size = CGSize(width: 200.0, height: 200.0)
|
|
||||||
lblNameInitialize.font = UIFont(name: "NotoSans-ExtraBold", size: 80)
|
|
||||||
lblNameInitialize.textColor = UIColor(Color.grayMain2c600)
|
|
||||||
|
|
||||||
let textToDisplay = (firstName.first.map { String($0) } ?? "") + (lastName.first.map { String($0) } ?? "")
|
|
||||||
|
|
||||||
lblNameInitialize.text = textToDisplay.uppercased()
|
|
||||||
lblNameInitialize.textAlignment = .center
|
|
||||||
lblNameInitialize.backgroundColor = UIColor(Color.grayMain2c200)
|
|
||||||
lblNameInitialize.layer.cornerRadius = 10.0
|
|
||||||
lblNameInitialize.clipsToBounds = true
|
|
||||||
|
|
||||||
UIGraphicsBeginImageContext(lblNameInitialize.frame.size)
|
|
||||||
defer { UIGraphicsEndImageContext() }
|
|
||||||
|
|
||||||
guard let context = UIGraphicsGetCurrentContext() else {
|
|
||||||
completion(UIImage())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
lblNameInitialize.layer.render(in: context)
|
|
||||||
let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
|
|
||||||
completion(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func textToImage(firstName: String, lastName: String) -> UIImage {
|
func textToImage(firstName: String, lastName: String) -> UIImage {
|
||||||
let lblNameInitialize = UILabel()
|
let lblNameInitialize = UILabel()
|
||||||
lblNameInitialize.frame.size = CGSize(width: 200.0, height: 200.0)
|
lblNameInitialize.frame.size = CGSize(width: 200.0, height: 200.0)
|
||||||
|
|
@ -402,15 +316,12 @@ final class ContactsManager: ObservableObject {
|
||||||
if directory != nil {
|
if directory != nil {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
do {
|
do {
|
||||||
if let urlName = URL(string: name + prefix) {
|
let urlName = URL(string: name + prefix)
|
||||||
let imagePath = urlName.absoluteString.replacingOccurrences(of: "%", with: "")
|
let imagePath = urlName != nil ? urlName!.absoluteString.replacingOccurrences(of: "%", with: "") : "ImageError"
|
||||||
|
|
||||||
let decodedData: () = try data.write(to: directory!.appendingPathComponent(imagePath + ".png"))
|
let decodedData: () = try data.write(to: directory!.appendingPathComponent(imagePath + ".png"))
|
||||||
|
|
||||||
completion(decodedData, imagePath + ".png")
|
completion(decodedData, imagePath + ".png")
|
||||||
} else {
|
|
||||||
completion((), "")
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
print("Error: ", error)
|
print("Error: ", error)
|
||||||
completion((), "")
|
completion((), "")
|
||||||
|
|
@ -432,20 +343,25 @@ final class ContactsManager: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFriendWithAddress(address: Address?) -> Friend? {
|
func getFriendWithAddress(address: Address?) -> Friend? {
|
||||||
guard let address = address, let clonedAddress = address.clone() else {
|
if address != nil {
|
||||||
|
let clonedAddress = address!.clone()
|
||||||
|
clonedAddress!.clean()
|
||||||
|
let sipUri = clonedAddress!.asStringUriOnly()
|
||||||
|
|
||||||
|
if self.friendList != nil && !self.friendList!.friends.isEmpty {
|
||||||
|
var friend: Friend?
|
||||||
|
friend = self.friendList!.friends.first(where: {$0.addresses.contains(where: {$0.asStringUriOnly() == sipUri})})
|
||||||
|
if friend == nil && self.linphoneFriendList != nil && !self.linphoneFriendList!.friends.isEmpty {
|
||||||
|
friend = self.linphoneFriendList!.friends.first(where: {$0.addresses.contains(where: {$0.asStringUriOnly() == sipUri})})
|
||||||
|
}
|
||||||
|
|
||||||
|
return friend
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
clonedAddress.clean()
|
|
||||||
let sipUri = clonedAddress.asStringUriOnly()
|
|
||||||
|
|
||||||
var friend: Friend?
|
|
||||||
if let friendList = self.friendList {
|
|
||||||
friend = friendList.friends.first(where: { $0.addresses.contains(where: { $0.asStringUriOnly() == sipUri }) })
|
|
||||||
}
|
|
||||||
if friend == nil, let linphoneFriendList = self.linphoneFriendList {
|
|
||||||
friend = linphoneFriendList.friends.first(where: { $0.addresses.contains(where: { $0.asStringUriOnly() == sipUri }) })
|
|
||||||
}
|
|
||||||
return friend
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFriendWithAddressInCoreQueue(address: Address?, completion: @escaping (Friend?) -> Void) {
|
func getFriendWithAddressInCoreQueue(address: Address?, completion: @escaping (Friend?) -> Void) {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import linphone // needed for unwrapped function linphone_core_set_push_and_app_
|
||||||
import Combine
|
import Combine
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
import Network
|
import Network
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
#if USE_CRASHLYTICS
|
#if USE_CRASHLYTICS
|
||||||
import Firebase
|
import Firebase
|
||||||
|
|
@ -36,15 +35,15 @@ final class CoreContext: ObservableObject {
|
||||||
|
|
||||||
static let shared = CoreContext()
|
static let shared = CoreContext()
|
||||||
private var sharedMainViewModel = SharedMainViewModel.shared
|
private var sharedMainViewModel = SharedMainViewModel.shared
|
||||||
var pipViewModel = PIPViewModel()
|
|
||||||
|
|
||||||
var coreVersion: String = Core.getVersion
|
var coreVersion: String = Core.getVersion
|
||||||
@Published var loggedIn: Bool = false
|
@Published var loggedIn: Bool = false
|
||||||
@Published var loggingInProgress: Bool = false
|
@Published var loggingInProgress: Bool = false
|
||||||
@Published var coreIsStarted: Bool = false
|
@Published var coreIsStarted: Bool = false
|
||||||
@Published var accounts: [AccountModel] = []
|
@Published var accounts: [AccountModel] = []
|
||||||
|
@Published var enteredForeground = false
|
||||||
@Published var shortcuts: [ShortcutModel] = []
|
@Published var shortcuts: [ShortcutModel] = []
|
||||||
var mCore: Core!
|
private var mCore: Core!
|
||||||
private var mIterateSuscription: AnyCancellable?
|
private var mIterateSuscription: AnyCancellable?
|
||||||
|
|
||||||
var bearerAuthInfoPendingPasswordUpdate: AuthInfo?
|
var bearerAuthInfoPendingPasswordUpdate: AuthInfo?
|
||||||
|
|
@ -146,15 +145,9 @@ final class CoreContext: ObservableObject {
|
||||||
self.mCore.videoPreviewEnabled = false
|
self.mCore.videoPreviewEnabled = false
|
||||||
self.mCore.fecEnabled = true
|
self.mCore.fecEnabled = true
|
||||||
self.mCore.friendListSubscriptionEnabled = true
|
self.mCore.friendListSubscriptionEnabled = true
|
||||||
|
self.mCore.maxSizeForAutoDownloadIncomingFiles = 0
|
||||||
// Migration
|
|
||||||
self.mCore.config!.setBool(section: "sip", key: "auto_answer_replacing_calls", value: false)
|
self.mCore.config!.setBool(section: "sip", key: "auto_answer_replacing_calls", value: false)
|
||||||
self.mCore.config!.setBool(section: "sip", key: "deliver_imdn", value: false)
|
self.mCore.config!.setBool(section: "sip", key: "deliver_imdn", value: false)
|
||||||
self.mCore.config!.setString(section: "misc", key: "log_collection_upload_server_url", value: "https://files.linphone.org:443/http-file-transfer-server/hft.php")
|
|
||||||
self.mCore.config!.setString(section: "misc", key: "file_transfer_server_url", value: "https://files.linphone.org:443/http-file-transfer-server/hft.php")
|
|
||||||
self.mCore.config!.setString(section: "misc", key: "version_check_url_root", value: "https://download.linphone.org/releases")
|
|
||||||
|
|
||||||
self.mCore.imdnToEverybodyThreshold = 1
|
|
||||||
|
|
||||||
let shortcutsCount = self.mCore.config!.getInt(section: "ui", key: "shortcut_count", defaultValue: 0)
|
let shortcutsCount = self.mCore.config!.getInt(section: "ui", key: "shortcut_count", defaultValue: 0)
|
||||||
if shortcutsCount > 0 {
|
if shortcutsCount > 0 {
|
||||||
|
|
@ -221,15 +214,6 @@ final class CoreContext: ObservableObject {
|
||||||
}
|
}
|
||||||
}, onCallStateChanged: { (core: Core, call: Call, cstate: Call.State, message: String) in
|
}, onCallStateChanged: { (core: Core, call: Call, cstate: Call.State, message: String) in
|
||||||
TelecomManager.shared.onCallStateChanged(core: core, call: call, state: cstate, message: message)
|
TelecomManager.shared.onCallStateChanged(core: core, call: call, state: cstate, message: message)
|
||||||
|
|
||||||
if core.calls.isEmpty {
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
|
||||||
if UIApplication.shared.applicationState == .background {
|
|
||||||
Log.info("[CoreContext] core is currently in background with no calls, triggering onEnterBackground procedure")
|
|
||||||
self.onEnterBackground()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, onAuthenticationRequested: { (_: Core, authInfo: AuthInfo, method: AuthMethod) in
|
}, onAuthenticationRequested: { (_: Core, authInfo: AuthInfo, method: AuthMethod) in
|
||||||
guard let username = authInfo.username, let server = authInfo.authorizationServer, !server.isEmpty else {
|
guard let username = authInfo.username, let server = authInfo.authorizationServer, !server.isEmpty else {
|
||||||
Log.error("Authentication requested but either username [\(String(describing: authInfo.username))], domain [\(String(describing: authInfo.domain))] or server [\(String(describing: authInfo.authorizationServer))] is nil or empty!")
|
Log.error("Authentication requested but either username [\(String(describing: authInfo.username))], domain [\(String(describing: authInfo.domain))] or server [\(String(describing: authInfo.authorizationServer))] is nil or empty!")
|
||||||
|
|
@ -262,7 +246,7 @@ final class CoreContext: ObservableObject {
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if status == ConfiguringState.Successful {
|
if status == ConfiguringState.Successful {
|
||||||
ToastViewModel.shared.toastMessage = "Success_qr_code_validated"
|
ToastViewModel.shared.toastMessage = "Successful"
|
||||||
ToastViewModel.shared.displayToast = true
|
ToastViewModel.shared.displayToast = true
|
||||||
self.accounts = accountModels
|
self.accounts = accountModels
|
||||||
}
|
}
|
||||||
|
|
@ -289,15 +273,12 @@ final class CoreContext: ObservableObject {
|
||||||
}
|
}
|
||||||
case .Cleared:
|
case .Cleared:
|
||||||
Log.info("[onAccountRegistrationStateChanged] Account \(account.displayName()) registration was cleared. Looking for auth info")
|
Log.info("[onAccountRegistrationStateChanged] Account \(account.displayName()) registration was cleared. Looking for auth info")
|
||||||
// Moved removeAuthInfo to "failed" state to prevent removing auth info when deactivating an account
|
|
||||||
/*
|
|
||||||
if let authInfo = account.findAuthInfo() {
|
if let authInfo = account.findAuthInfo() {
|
||||||
Log.info("[onAccountRegistrationStateChanged] Found auth info for account, removing it")
|
Log.info("[onAccountRegistrationStateChanged] Found auth info for account, removing it")
|
||||||
core.removeAuthInfo(info: authInfo)
|
core.removeAuthInfo(info: authInfo)
|
||||||
} else {
|
} else {
|
||||||
Log.warn("[onAccountRegistrationStateChanged] Failed to find matching auth info for account")
|
Log.warn("[onAccountRegistrationStateChanged] Failed to find matching auth info for account")
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
case .Failed: // If registration failed, remove account from core
|
case .Failed: // If registration failed, remove account from core
|
||||||
if self.networkStatusIsConnected {
|
if self.networkStatusIsConnected {
|
||||||
let params = account.params
|
let params = account.params
|
||||||
|
|
@ -305,10 +286,6 @@ final class CoreContext: ObservableObject {
|
||||||
clonedParams?.registerEnabled = false
|
clonedParams?.registerEnabled = false
|
||||||
account.params = clonedParams
|
account.params = clonedParams
|
||||||
|
|
||||||
if let authInfo = account.findAuthInfo() {
|
|
||||||
core.removeAuthInfo(info: authInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.warn("Registration failed for account \(account.displayName()), deleting it from core")
|
Log.warn("Registration failed for account \(account.displayName()), deleting it from core")
|
||||||
core.removeAccount(account: account)
|
core.removeAccount(account: account)
|
||||||
}
|
}
|
||||||
|
|
@ -327,6 +304,8 @@ final class CoreContext: ObservableObject {
|
||||||
} else if state == .Cleared {
|
} else if state == .Cleared {
|
||||||
self.loggingInProgress = false
|
self.loggingInProgress = false
|
||||||
self.loggedIn = false
|
self.loggedIn = false
|
||||||
|
ToastViewModel.shared.toastMessage = "Success_account_logged_out"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
} else {
|
} else {
|
||||||
self.loggingInProgress = false
|
self.loggingInProgress = false
|
||||||
self.loggedIn = false
|
self.loggedIn = false
|
||||||
|
|
|
||||||
|
|
@ -1,268 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
|
||||||
*
|
|
||||||
* This file is part of linphone-iphone
|
|
||||||
*
|
|
||||||
* 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import linphonesw
|
|
||||||
|
|
||||||
class CorePreferences {
|
|
||||||
static var printLogsInLogcat: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "debug", defaultValue: true)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "debug", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var firstLaunch: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "first_6.0_launch", defaultValue: true)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "first_6.0_launch", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var linphoneConfigurationVersion: Int {
|
|
||||||
get {
|
|
||||||
return Config.get().getInt(section: "app", key: "config_version", defaultValue: 52005)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setInt(section: "app", key: "config_version", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var checkForUpdateServerUrl: String {
|
|
||||||
get {
|
|
||||||
return Config.get().getString(section: "misc", key: "version_check_url_root", defaultString: "")
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setString(section: "misc", key: "version_check_url_root", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var conditionsAndPrivacyPolicyAccepted: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "read_and_agree_terms_and_privacy", defaultValue: false)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "read_and_agree_terms_and_privacy", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var publishPresence: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "publish_presence", defaultValue: true)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "publish_presence", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var keepServiceAlive: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "keep_service_alive", defaultValue: false)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "keep_service_alive", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var deviceName: String {
|
|
||||||
get {
|
|
||||||
return Config.get().getString(section: "app", key: "device", defaultString: "").trimmingCharacters(in: .whitespaces)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setString(section: "app", key: "device", value: newValue.trimmingCharacters(in: .whitespaces))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var routeAudioToSpeakerWhenVideoIsEnabled: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "route_audio_to_speaker_when_video_enabled", defaultValue: true)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "route_audio_to_speaker_when_video_enabled", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var automaticallyStartCallRecording: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "auto_start_call_record", defaultValue: false)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "auto_start_call_record", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var showDialogWhenCallingDeviceUuidDirectly: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "show_confirmation_dialog_zrtp_trust_call", defaultValue: true)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "show_confirmation_dialog_zrtp_trust_call", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var markConversationAsReadWhenDismissingMessageNotification: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "mark_as_read_notif_dismissal", defaultValue: false)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "mark_as_read_notif_dismissal", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var contactsFilter: String {
|
|
||||||
get {
|
|
||||||
return Config.get().getString(section: "ui", key: "contacts_filter", defaultString: "")
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setString(section: "ui", key: "contacts_filter", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var showFavoriteContacts: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "ui", key: "show_favorites_contacts", defaultValue: true)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "ui", key: "show_favorites_contacts", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var voiceRecordingMaxDuration: Int {
|
|
||||||
get {
|
|
||||||
return Config.get().getInt(section: "app", key: "voice_recording_max_duration", defaultValue: 600000)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setInt(section: "app", key: "voice_recording_max_duration", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var darkMode: Int {
|
|
||||||
get {
|
|
||||||
if !darkModeAllowed { return 0 }
|
|
||||||
return Config.get().getInt(section: "app", key: "dark_mode", defaultValue: -1)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setInt(section: "app", key: "dark_mode", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var enableSecureMode: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "ui", key: "enable_secure_mode", defaultValue: true)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "ui", key: "enable_secure_mode", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var themeMainColor: String {
|
|
||||||
get {
|
|
||||||
return Config.get().getString(section: "ui", key: "theme_main_color", defaultString: "orange")
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setString(section: "ui", key: "theme_main_color", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var darkModeAllowed: Bool {
|
|
||||||
return Config.get().getBool(section: "ui", key: "dark_mode_allowed", defaultValue: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
static var changeMainColorAllowed: Bool {
|
|
||||||
return Config.get().getBool(section: "ui", key: "change_main_color_allowed", defaultValue: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
static var hideSettings: Bool {
|
|
||||||
return Config.get().getBool(section: "ui", key: "hide_settings", defaultValue: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
static var maxAccountsCount: Int {
|
|
||||||
return Config.get().getInt(section: "ui", key: "max_account", defaultValue: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
static var configPath: String {
|
|
||||||
return context.view.window?.rootViewController?.view.frame.origin.x ?? "" + "/.linphonerc"
|
|
||||||
}
|
|
||||||
|
|
||||||
static var factoryConfigPath: String {
|
|
||||||
return context.view.window?.rootViewController?.view.frame.origin.x ?? "" + "/linphonerc"
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyAssetsFromPackage() {
|
|
||||||
copy(from: "linphonerc_default", to: configPath)
|
|
||||||
copy(from: "linphonerc_factory", to: factoryConfigPath, overrideIfExists: true)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
static var vfsEnabled: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "app", key: "vfs_enabled", defaultValue: false)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "app", key: "vfs_enabled", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var acceptEarlyMedia: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "sip", key: "incoming_calls_early_media", defaultValue: false)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "sip", key: "incoming_calls_early_media", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var allowOutgoingEarlyMedia: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "sip", key: "real_early_media", defaultValue: false)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "sip", key: "real_early_media", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var defaultDomain: String {
|
|
||||||
get {
|
|
||||||
return Config.get().getString(section: "app", key: "default_domain", defaultString: "sip.linphone.org")
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setString(section: "app", key: "default_domain", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func copy(from: String, to: String, overrideIfExists: Bool = false) {
|
|
||||||
let fileManager = FileManager.default
|
|
||||||
if fileManager.fileExists(atPath: to), !overrideIfExists {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if let assetPath = Bundle.main.path(forResource: from, ofType: "") {
|
|
||||||
do {
|
|
||||||
try fileManager.copyItem(atPath: assetPath, toPath: to)
|
|
||||||
} catch {
|
|
||||||
print("Error copying file: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
|
<string>App requires access to the local network to establish VoIP connections</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
|
@ -109,8 +111,6 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>ITSEncryptionExportComplianceCode</key>
|
<key>ITSEncryptionExportComplianceCode</key>
|
||||||
<string>b5cb085f-772a-4a4f-8c77-5d1332b1f93f</string>
|
<string>b5cb085f-772a-4a4f-8c77-5d1332b1f93f</string>
|
||||||
<key>NSCalendarsWriteOnlyAccessUsageDescription</key>
|
|
||||||
<string></string>
|
|
||||||
<key>NSSupportsSuddenTermination</key>
|
<key>NSSupportsSuddenTermination</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>UIAppFonts</key>
|
<key>UIAppFonts</key>
|
||||||
|
|
@ -126,12 +126,15 @@
|
||||||
<array>
|
<array>
|
||||||
<string>remote-notification</string>
|
<string>remote-notification</string>
|
||||||
<string>voip</string>
|
<string>voip</string>
|
||||||
<string>audio</string>
|
|
||||||
</array>
|
</array>
|
||||||
<key>UILaunchScreen</key>
|
<key>UILaunchScreen</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIImageName</key>
|
<key>UIImageName</key>
|
||||||
<string>linphone</string>
|
<string>linphone</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>NSCalendarsUsageDescription</key>
|
||||||
|
<string>Deprecated - Prior to iOS 17 full calendar access is required</string>
|
||||||
|
<key>NSCalendarsWriteOnlyAccessUsageDescription</key>
|
||||||
|
<string></string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,6 @@ struct LinphoneApp: App {
|
||||||
@State private var meetingsListViewModel: MeetingsListViewModel?
|
@State private var meetingsListViewModel: MeetingsListViewModel?
|
||||||
@State private var meetingViewModel: MeetingViewModel?
|
@State private var meetingViewModel: MeetingViewModel?
|
||||||
@State private var conversationForwardMessageViewModel: ConversationForwardMessageViewModel?
|
@State private var conversationForwardMessageViewModel: ConversationForwardMessageViewModel?
|
||||||
@State private var accountProfileViewModel: AccountProfileViewModel?
|
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
|
|
@ -176,8 +175,7 @@ struct LinphoneApp: App {
|
||||||
&& conversationViewModel != nil
|
&& conversationViewModel != nil
|
||||||
&& meetingsListViewModel != nil
|
&& meetingsListViewModel != nil
|
||||||
&& meetingViewModel != nil
|
&& meetingViewModel != nil
|
||||||
&& conversationForwardMessageViewModel != nil
|
&& conversationForwardMessageViewModel != nil {
|
||||||
&& accountProfileViewModel != nil {
|
|
||||||
ContentView(
|
ContentView(
|
||||||
contactViewModel: contactViewModel!,
|
contactViewModel: contactViewModel!,
|
||||||
editContactViewModel: editContactViewModel!,
|
editContactViewModel: editContactViewModel!,
|
||||||
|
|
@ -191,8 +189,7 @@ struct LinphoneApp: App {
|
||||||
conversationViewModel: conversationViewModel!,
|
conversationViewModel: conversationViewModel!,
|
||||||
meetingsListViewModel: meetingsListViewModel!,
|
meetingsListViewModel: meetingsListViewModel!,
|
||||||
meetingViewModel: meetingViewModel!,
|
meetingViewModel: meetingViewModel!,
|
||||||
conversationForwardMessageViewModel: conversationForwardMessageViewModel!,
|
conversationForwardMessageViewModel: conversationForwardMessageViewModel!
|
||||||
accountProfileViewModel: accountProfileViewModel!
|
|
||||||
)
|
)
|
||||||
.environmentObject(navigationManager)
|
.environmentObject(navigationManager)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
|
@ -204,8 +201,6 @@ struct LinphoneApp: App {
|
||||||
// Notify the app to navigate to the chat room
|
// Notify the app to navigate to the chat room
|
||||||
navigationManager.openChatRoom(callId: callId, peerAddr: peerAddr, localAddr: localAddr)
|
navigationManager.openChatRoom(callId: callId, peerAddr: peerAddr, localAddr: localAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountProfileViewModel!.setAvatarModel()
|
|
||||||
}
|
}
|
||||||
.onOpenURL { url in
|
.onOpenURL { url in
|
||||||
URIHandler.handleURL(url: url)
|
URIHandler.handleURL(url: url)
|
||||||
|
|
@ -231,21 +226,18 @@ struct LinphoneApp: App {
|
||||||
meetingsListViewModel = MeetingsListViewModel()
|
meetingsListViewModel = MeetingsListViewModel()
|
||||||
meetingViewModel = MeetingViewModel()
|
meetingViewModel = MeetingViewModel()
|
||||||
conversationForwardMessageViewModel = ConversationForwardMessageViewModel()
|
conversationForwardMessageViewModel = ConversationForwardMessageViewModel()
|
||||||
accountProfileViewModel = AccountProfileViewModel()
|
|
||||||
}.onOpenURL { url in
|
}.onOpenURL { url in
|
||||||
URIHandler.handleURL(url: url)
|
URIHandler.handleURL(url: url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onChange(of: scenePhase) { newPhase in
|
}.onChange(of: scenePhase) { newPhase in
|
||||||
if !TelecomManager.shared.callInProgress {
|
if newPhase == .active {
|
||||||
if newPhase == .active {
|
Log.info("Entering foreground")
|
||||||
Log.info("Entering foreground")
|
coreContext.onEnterForeground()
|
||||||
coreContext.onEnterForeground()
|
} else if newPhase == .inactive {
|
||||||
} else if newPhase == .inactive {
|
} else if newPhase == .background {
|
||||||
} else if newPhase == .background {
|
Log.info("Entering background")
|
||||||
Log.info("Entering background")
|
coreContext.onEnterBackground()
|
||||||
coreContext.onEnterBackground()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,37 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd">
|
<config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd">
|
||||||
<section name="proxy_default_values">
|
<section name="proxy_default_values">
|
||||||
<entry name="avpf" overwrite="true">1</entry>
|
<entry name="avpf" overwrite="true">1</entry>
|
||||||
<entry name="dial_escape_plus" overwrite="true">0</entry>
|
<entry name="dial_escape_plus" overwrite="true">0</entry>
|
||||||
<entry name="publish" overwrite="true">1</entry>
|
<entry name="publish" overwrite="true">1</entry>
|
||||||
<entry name="publish_expires" overwrite="true">120</entry>
|
<entry name="publish_expires" overwrite="true">120</entry>
|
||||||
<entry name="quality_reporting_collector" overwrite="true">sip:voip-metrics@sip.linphone.org;transport=tls</entry>
|
<entry name="quality_reporting_collector" overwrite="true">sip:voip-metrics@sip.linphone.org;transport=tls</entry>
|
||||||
<entry name="quality_reporting_enabled" overwrite="true">1</entry>
|
<entry name="quality_reporting_enabled" overwrite="true">1</entry>
|
||||||
<entry name="quality_reporting_interval" overwrite="true">180</entry>
|
<entry name="quality_reporting_interval" overwrite="true">180</entry>
|
||||||
<entry name="reg_expires" overwrite="true">31536000</entry>
|
<entry name="reg_expires" overwrite="true">31536000</entry>
|
||||||
<entry name="reg_identity" overwrite="true">sip:?@sip.linphone.org</entry>
|
<entry name="reg_identity" overwrite="true">sip:?@sip.linphone.org</entry>
|
||||||
<entry name="reg_proxy" overwrite="true"><sip:sip.linphone.org;transport=tls></entry>
|
<entry name="reg_proxy" overwrite="true"><sip:sip.linphone.org;transport=tls></entry>
|
||||||
<entry name="reg_route" overwrite="true"><sip:sip.linphone.org;transport=tls></entry>
|
<entry name="reg_route" overwrite="true"><sip:sip.linphone.org;transport=tls></entry>
|
||||||
<entry name="reg_sendregister" overwrite="true">1</entry>
|
<entry name="reg_sendregister" overwrite="true">1</entry>
|
||||||
<entry name="nat_policy_ref" overwrite="true">nat_policy_default_values</entry>
|
<entry name="nat_policy_ref" overwrite="true">nat_policy_default_values</entry>
|
||||||
<entry name="realm" overwrite="true">sip.linphone.org</entry>
|
<entry name="realm" overwrite="true">sip.linphone.org</entry>
|
||||||
<entry name="conference_factory_uri" overwrite="true">sip:conference-factory@sip.linphone.org</entry>
|
<entry name="conference_factory_uri" overwrite="true">sip:conference-factory@sip.linphone.org</entry>
|
||||||
<entry name="audio_video_conference_factory_uri" overwrite="true">sip:videoconference-factory@sip.linphone.org</entry>
|
<entry name="audio_video_conference_factory_uri" overwrite="true">sip:videoconference-factory@sip.linphone.org</entry>
|
||||||
<entry name="push_notification_allowed" overwrite="true">1</entry>
|
<entry name="push_notification_allowed" overwrite="true">1</entry>
|
||||||
<entry name="remote_push_notification_allowed" overwrite="true">1</entry>
|
<entry name="remote_push_notification_allowed" overwrite="true">1</entry>
|
||||||
<entry name="cpim_in_basic_chat_rooms_enabled" overwrite="true">1</entry>
|
<entry name="cpim_in_basic_chat_rooms_enabled" overwrite="true">1</entry>
|
||||||
<entry name="rtp_bundle" overwrite="true">1</entry>
|
<entry name="rtp_bundle" overwrite="true">1</entry>
|
||||||
<entry name="lime_server_url" overwrite="true">https://lime.linphone.org/lime-server/lime-server.php</entry>
|
<entry name="lime_server_url" overwrite="true">https://lime.linphone.org/lime-server/lime-server.php</entry>
|
||||||
<entry name="lime_algo" overwrite="true">c25519</entry>
|
|
||||||
<entry name="supported" overwrite="true"></entry>
|
|
||||||
</section>
|
</section>
|
||||||
<section name="nat_policy_default_values">
|
<section name="nat_policy_default_values">
|
||||||
<entry name="stun_server" overwrite="true">stun.linphone.org</entry>
|
<entry name="stun_server" overwrite="true">stun.linphone.org</entry>
|
||||||
<entry name="protocols" overwrite="true">stun,ice</entry>
|
<entry name="protocols" overwrite="true">stun,ice</entry>
|
||||||
</section>
|
</section>
|
||||||
<section name="sip">
|
<section name="sip">
|
||||||
<entry name="media_encryption" overwrite="true">zrtp</entry>
|
<entry name="media_encryption" overwrite="true">zrtp</entry>
|
||||||
<entry name="media_encryption_mandatory">1</entry>
|
<entry name="media_encryption_mandatory">1</entry>
|
||||||
</section>
|
</section>
|
||||||
<section name="net">
|
<section name="net">
|
||||||
<entry name="friendlist_subscription_enabled" overwrite="true">1</entry>
|
<entry name="friendlist_subscription_enabled" overwrite="true">1</entry>
|
||||||
</section>
|
</section>
|
||||||
</config>
|
</config>
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,33 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd">
|
<config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd">
|
||||||
<section name="proxy_default_values">
|
<section name="proxy_default_values">
|
||||||
<entry name="avpf" overwrite="true">0</entry>
|
<entry name="avpf" overwrite="true">0</entry>
|
||||||
<entry name="dial_escape_plus" overwrite="true">0</entry>
|
<entry name="dial_escape_plus" overwrite="true">0</entry>
|
||||||
<entry name="publish" overwrite="true">0</entry>
|
<entry name="publish" overwrite="true">0</entry>
|
||||||
<entry name="publish_expires" overwrite="true">-1</entry>
|
<entry name="publish_expires" overwrite="true">-1</entry>
|
||||||
<entry name="quality_reporting_collector" overwrite="true"></entry>
|
<entry name="quality_reporting_collector" overwrite="true"></entry>
|
||||||
<entry name="quality_reporting_enabled" overwrite="true">0</entry>
|
<entry name="quality_reporting_enabled" overwrite="true">0</entry>
|
||||||
<entry name="quality_reporting_interval" overwrite="true">0</entry>
|
<entry name="quality_reporting_interval" overwrite="true">0</entry>
|
||||||
<entry name="reg_expires" overwrite="true">3600</entry>
|
<entry name="reg_expires" overwrite="true">3600</entry>
|
||||||
<entry name="reg_identity" overwrite="true"></entry>
|
<entry name="reg_identity" overwrite="true"></entry>
|
||||||
<entry name="reg_proxy" overwrite="true"></entry>
|
<entry name="reg_proxy" overwrite="true"></entry>
|
||||||
<entry name="reg_route" overwrite="true"></entry>
|
<entry name="reg_route" overwrite="true"></entry>
|
||||||
<entry name="reg_sendregister" overwrite="true">1</entry>
|
<entry name="reg_sendregister" overwrite="true">1</entry>
|
||||||
<entry name="nat_policy_ref" overwrite="true"></entry>
|
<entry name="nat_policy_ref" overwrite="true"></entry>
|
||||||
<entry name="realm" overwrite="true"></entry>
|
<entry name="realm" overwrite="true"></entry>
|
||||||
<entry name="conference_factory_uri" overwrite="true"></entry>
|
<entry name="conference_factory_uri" overwrite="true"></entry>
|
||||||
<entry name="audio_video_conference_factory_uri" overwrite="true"></entry>
|
<entry name="audio_video_conference_factory_uri" overwrite="true"></entry>
|
||||||
<entry name="push_notification_allowed" overwrite="true">0</entry>
|
<entry name="push_notification_allowed" overwrite="true">0</entry>
|
||||||
<entry name="cpim_in_basic_chat_rooms_enabled" overwrite="true">0</entry>
|
<entry name="cpim_in_basic_chat_rooms_enabled" overwrite="true">0</entry>
|
||||||
<entry name="rtp_bundle" overwrite="true">0</entry>
|
<entry name="rtp_bundle" overwrite="true">0</entry>
|
||||||
<entry name="lime_server_url" overwrite="true"></entry>
|
<entry name="lime_server_url" overwrite="true"></entry>
|
||||||
<entry name="lime_algo" overwrite="true"></entry>
|
|
||||||
<entry name="supported" overwrite="true">outbound</entry>
|
|
||||||
</section>
|
</section>
|
||||||
<section name="nat_policy_default_values">
|
<section name="nat_policy_default_values">
|
||||||
<entry name="stun_server" overwrite="true">stun.linphone.org</entry>
|
<entry name="stun_server" overwrite="true">stun.linphone.org</entry>
|
||||||
<entry name="protocols" overwrite="true">stun,ice</entry>
|
<entry name="protocols" overwrite="true">stun,ice</entry>
|
||||||
</section>
|
</section>
|
||||||
<section name="sip">
|
<section name="sip">
|
||||||
<entry name="media_encryption">srtp</entry>
|
<entry name="media_encryption">srtp</entry>
|
||||||
<entry name="media_encryption_mandatory" overwrite="true">0</entry>
|
<entry name="media_encryption_mandatory" overwrite="true">0</entry>
|
||||||
</section>
|
</section>
|
||||||
</config>
|
</config>
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@ sip_tcp_port=-1
|
||||||
sip_tls_port=-1
|
sip_tls_port=-1
|
||||||
media_encryption=none
|
media_encryption=none
|
||||||
update_presence_model_timestamp_before_publish_expires_refresh=1
|
update_presence_model_timestamp_before_publish_expires_refresh=1
|
||||||
use_rfc2833=1
|
|
||||||
use_info=1
|
|
||||||
|
|
||||||
[net]
|
[net]
|
||||||
#Because dynamic bitrate adaption can increase bitrate, we must allow "no limit"
|
#Because dynamic bitrate adaption can increase bitrate, we must allow "no limit"
|
||||||
|
|
@ -30,29 +28,19 @@ tunnel=disabled
|
||||||
auto_download_incoming_voice_recordings=1
|
auto_download_incoming_voice_recordings=1
|
||||||
auto_download_incoming_icalendars=1
|
auto_download_incoming_icalendars=1
|
||||||
|
|
||||||
|
|
||||||
[tunnel]
|
[tunnel]
|
||||||
host=
|
host=
|
||||||
port=443
|
port=443
|
||||||
|
|
||||||
[misc]
|
[misc]
|
||||||
log_collection_upload_server_url=https://files.linphone.org/http-file-transfer-server/hft.php
|
log_collection_upload_server_url=https://www.linphone.org:444/lft.php
|
||||||
file_transfer_server_url=https://files.linphone.org/http-file-transfer-server/hft.php
|
file_transfer_server_url=https://www.linphone.org:444/lft.php
|
||||||
version_check_url_root=https://download.linphone.org/releases
|
version_check_url_root=https://www.linphone.org/releases
|
||||||
max_calls=10
|
max_calls=10
|
||||||
history_max_size=100
|
|
||||||
conference_layout=1
|
conference_layout=1
|
||||||
hide_empty_chat_rooms=1
|
|
||||||
|
|
||||||
[fec]
|
[fec]
|
||||||
fec_enabled=1
|
fec_enabled=1
|
||||||
|
|
||||||
[magic_search]
|
|
||||||
return_empty_friends=1
|
|
||||||
|
|
||||||
[chat]
|
|
||||||
imdn_to_everybody_threshold=1
|
|
||||||
|
|
||||||
[ui]
|
|
||||||
contacts_filter=sip.linphone.org
|
|
||||||
|
|
||||||
## End of default rc
|
## End of default rc
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ accept_any_encryption=1
|
||||||
guess_hostname=1
|
guess_hostname=1
|
||||||
register_only_when_network_is_up=1
|
register_only_when_network_is_up=1
|
||||||
auto_net_state_mon=1
|
auto_net_state_mon=1
|
||||||
auto_answer_replacing_calls=1
|
auto_answer_replacing_calls=0
|
||||||
ping_with_options=0
|
ping_with_options=0
|
||||||
use_cpim=1
|
use_cpim=1
|
||||||
zrtp_key_agreements_suites=MS_ZRTP_KEY_AGREEMENT_K255_MLK512,MS_ZRTP_KEY_AGREEMENT_K255_KYB512
|
zrtp_key_agreements_suites=MS_ZRTP_KEY_AGREEMENT_K255_KYB512
|
||||||
chat_messages_aggregation_delay=1000
|
chat_messages_aggregation_delay=1000
|
||||||
chat_messages_aggregation=1
|
chat_messages_aggregation=1
|
||||||
update_presence_model_timestamp_before_publish_expires_refresh=1
|
update_presence_model_timestamp_before_publish_expires_refresh=1
|
||||||
|
|
@ -27,9 +27,6 @@ rls_uri=sips:rls@sip.linphone.org
|
||||||
[sound]
|
[sound]
|
||||||
#remove this property for any application that is not Linphone public version itself
|
#remove this property for any application that is not Linphone public version itself
|
||||||
ec_calibrator_cool_tones=1
|
ec_calibrator_cool_tones=1
|
||||||
disable_ringing=1
|
|
||||||
|
|
||||||
[audio]
|
|
||||||
|
|
||||||
[video]
|
[video]
|
||||||
auto_resize_preview_to_keep_ratio=1
|
auto_resize_preview_to_keep_ratio=1
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ class TelecomManager: ObservableObject {
|
||||||
@Published var meetingWaitingRoomDisplayed: Bool = false
|
@Published var meetingWaitingRoomDisplayed: Bool = false
|
||||||
@Published var meetingWaitingRoomSelected: Address?
|
@Published var meetingWaitingRoomSelected: Address?
|
||||||
@Published var meetingWaitingRoomName: String = ""
|
@Published var meetingWaitingRoomName: String = ""
|
||||||
@Published var participantsInvited: Bool = false
|
|
||||||
|
|
||||||
var actionToFulFill: CXCallAction?
|
var actionToFulFill: CXCallAction?
|
||||||
var callkitAudioSessionActivated: Bool?
|
var callkitAudioSessionActivated: Bool?
|
||||||
|
|
@ -369,8 +368,6 @@ class TelecomManager: ObservableObject {
|
||||||
completion(call.remoteAddress!.displayName!)
|
completion(call.remoteAddress!.displayName!)
|
||||||
} else if call.remoteAddress!.username != nil {
|
} else if call.remoteAddress!.username != nil {
|
||||||
completion(call.remoteAddress!.username!)
|
completion(call.remoteAddress!.username!)
|
||||||
} else {
|
|
||||||
completion(String(call.remoteAddress!.asStringUriOnly().dropFirst(4)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,29 +428,6 @@ class TelecomManager: ObservableObject {
|
||||||
func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) {
|
func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) {
|
||||||
let callLog = call.callLog
|
let callLog = call.callLog
|
||||||
let callId = callLog?.callId ?? ""
|
let callId = callLog?.callId ?? ""
|
||||||
if !callInProgress && participantsInvited {
|
|
||||||
if let remoteAddress = call.remoteAddress {
|
|
||||||
let uuid = UUID()
|
|
||||||
let name = remoteAddress.asStringUriOnly()
|
|
||||||
let handle = CXHandle(type: .generic, value: remoteAddress.asStringUriOnly())
|
|
||||||
let startCallAction = CXStartCallAction(call: uuid, handle: handle)
|
|
||||||
let transaction = CXTransaction(action: startCallAction)
|
|
||||||
|
|
||||||
let callInfo = CallInfo.newOutgoingCallInfo(addr: remoteAddress, isSas: false, displayName: name, isVideo: true, isConference: true)
|
|
||||||
providerDelegate.callInfos.updateValue(callInfo, forKey: uuid)
|
|
||||||
providerDelegate.uuids.updateValue(uuid, forKey: callId)
|
|
||||||
|
|
||||||
setHeldOtherCalls(core: core, exceptCallid: callId)
|
|
||||||
requestTransaction(transaction, action: "startCall")
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.participantsInvited = false
|
|
||||||
withAnimation {
|
|
||||||
self.callDisplayed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cstate == .PushIncomingReceived {
|
if cstate == .PushIncomingReceived {
|
||||||
Log.info("PushIncomingReceived in core delegate, display callkit call")
|
Log.info("PushIncomingReceived in core delegate, display callkit call")
|
||||||
TelecomManager.shared.displayIncomingCall(call: call, handle: "Calling", hasVideo: false, callId: callId, displayName: "Calling")
|
TelecomManager.shared.displayIncomingCall(call: call, handle: "Calling", hasVideo: false, callId: callId, displayName: "Calling")
|
||||||
|
|
@ -501,8 +475,6 @@ class TelecomManager: ObservableObject {
|
||||||
displayName = call.remoteAddress!.displayName!
|
displayName = call.remoteAddress!.displayName!
|
||||||
} else if call.remoteAddress!.username != nil {
|
} else if call.remoteAddress!.username != nil {
|
||||||
displayName = call.remoteAddress!.username!
|
displayName = call.remoteAddress!.username!
|
||||||
} else {
|
|
||||||
displayName = String(call.remoteAddress!.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -669,7 +641,7 @@ class TelecomManager: ObservableObject {
|
||||||
if UIApplication.shared.applicationState != .active && (callLog == nil || callLog?.status == .Missed || callLog?.status == .Aborted || callLog?.status == .EarlyAborted) {
|
if UIApplication.shared.applicationState != .active && (callLog == nil || callLog?.status == .Missed || callLog?.status == .Aborted || callLog?.status == .EarlyAborted) {
|
||||||
// Configure the notification's payload.
|
// Configure the notification's payload.
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = NSString.localizedUserNotificationString(forKey: NSLocalizedString("notification_missed_call_title", comment: ""), arguments: nil)
|
content.title = NSString.localizedUserNotificationString(forKey: NSLocalizedString("Missed call", comment: ""), arguments: nil)
|
||||||
content.body = NSString.localizedUserNotificationString(forKey: displayName, arguments: nil)
|
content.body = NSString.localizedUserNotificationString(forKey: displayName, arguments: nil)
|
||||||
|
|
||||||
// Deliver the notification.
|
// Deliver the notification.
|
||||||
|
|
|
||||||
|
|
@ -45,50 +45,269 @@ struct LoginFragment: View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
ZStack {
|
ZStack {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
if #available(iOS 16.4, *) {
|
ScrollView(.vertical) {
|
||||||
ScrollView(.vertical) {
|
VStack {
|
||||||
innerScrollView(geometry: geometry)
|
ZStack {
|
||||||
}
|
Image("mountain")
|
||||||
.scrollBounceBehavior(.basedOnSize)
|
.resizable()
|
||||||
} else {
|
.scaledToFill()
|
||||||
ScrollView(.vertical) {
|
.frame(width: geometry.size.width, height: 100)
|
||||||
innerScrollView(geometry: geometry)
|
.clipped()
|
||||||
|
|
||||||
|
if isShowBack {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
HStack {
|
||||||
|
Image("caret-left")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
.padding(.top, -75)
|
||||||
|
.padding(.leading, -10)
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation {
|
||||||
|
onBackPressed?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.leading)
|
||||||
|
}
|
||||||
|
.frame(width: geometry.size.width)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text("assistant_account_login")
|
||||||
|
.default_text_style_white_800(styleSize: 20)
|
||||||
|
.padding(.top, 20)
|
||||||
|
}
|
||||||
|
.padding(.top, 35)
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(String(localized: "username")+"*")
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
TextField("username", text: $accountLoginViewModel.username)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.disabled(coreContext.loggedIn)
|
||||||
|
.frame(height: 25)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(isNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
.focused($isNameFocused)
|
||||||
|
|
||||||
|
Text(String(localized: "password")+"*")
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
ZStack(alignment: .trailing) {
|
||||||
|
Group {
|
||||||
|
if isSecured {
|
||||||
|
SecureField("password", text: $accountLoginViewModel.passwd)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.frame(height: 25)
|
||||||
|
.focused($isPasswordFocused)
|
||||||
|
} else {
|
||||||
|
TextField("password", text: $accountLoginViewModel.passwd)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.frame(height: 25)
|
||||||
|
.focused($isPasswordFocused)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
isSecured.toggle()
|
||||||
|
}, label: {
|
||||||
|
Image(self.isSecured ? "eye-slash" : "eye")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.disabled(coreContext.loggedIn)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(isPasswordFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
sharedMainViewModel.changeDisplayProfileMode()
|
||||||
|
self.accountLoginViewModel.login()
|
||||||
|
coreContext.loggingInProgress = true
|
||||||
|
}, label: {
|
||||||
|
Text(coreContext.loggedIn ? "Log out" : "assistant_account_login")
|
||||||
|
.default_text_style_white_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background((accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty) ? Color.orangeMain100 : Color.orangeMain500)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.disabled(accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty)
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Text("[Forgotten password?](https://subscribe.linphone.org/)")
|
||||||
|
.underline()
|
||||||
|
.tint(Color.grayMain2c600)
|
||||||
|
.default_text_style_600(styleSize: 15)
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding(.bottom, 30)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
VStack {
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
Text(" or ")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
VStack {
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
|
NavigationLink(destination: {
|
||||||
|
QrCodeScannerFragment()
|
||||||
|
}, label: {
|
||||||
|
HStack {
|
||||||
|
Image("qr-code")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.orangeMain500)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
|
||||||
|
Text("Scan QR code")
|
||||||
|
.default_text_style_orange_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.orangeMain500, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
NavigationLink(isActive: $isLinkSIPActive, destination: {
|
||||||
|
ThirdPartySipAccountWarningFragment(accountLoginViewModel: accountLoginViewModel)
|
||||||
|
}, label: {
|
||||||
|
Text("Use SIP Account")
|
||||||
|
.default_text_style_orange_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
})
|
||||||
|
.disabled(!sharedMainViewModel.generalTermsAccepted)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.orangeMain500, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
.simultaneousGesture(
|
||||||
|
TapGesture().onEnded {
|
||||||
|
self.linkActive = "SIP"
|
||||||
|
if !sharedMainViewModel.generalTermsAccepted {
|
||||||
|
withAnimation {
|
||||||
|
self.isShowPopup.toggle()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.isLinkSIPActive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("Not account yet?")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
|
||||||
|
NavigationLink(destination: RegisterFragment(registerViewModel: RegisterViewModel()), isActive: $isLinkREGActive, label: {Text("Register")
|
||||||
|
.default_text_style_white_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
})
|
||||||
|
.disabled(!sharedMainViewModel.generalTermsAccepted)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(Color.orangeMain500)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
.simultaneousGesture(
|
||||||
|
TapGesture().onEnded {
|
||||||
|
self.linkActive = "REG"
|
||||||
|
if !sharedMainViewModel.generalTermsAccepted {
|
||||||
|
withAnimation {
|
||||||
|
self.isShowPopup.toggle()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.isLinkREGActive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
}
|
}
|
||||||
|
.frame(minHeight: geometry.size.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.isShowPopup {
|
if self.isShowPopup {
|
||||||
let generalTerms = String(format: "[%@](%@)", String(localized: "assistant_dialog_general_terms_label"), "https://www.linphone.org/en/terms-of-use/")
|
let contentPopup1 = Text("En continuant, vous acceptez ces conditions, ")
|
||||||
let privacyPolicy = String(format: "[%@](%@)", String(localized: "assistant_dialog_privacy_policy_label"), "https://linphone.org/en/privacy-policy")
|
let contentPopup2 = Text("[notre politique de confidentialité](https://linphone.org/privacy-policy)").underline()
|
||||||
let splitMsg = String(localized: "assistant_dialog_general_terms_and_privacy_policy_message").components(separatedBy: "%@")
|
let contentPopup3 = Text(" et ")
|
||||||
if splitMsg.count == 3 { // We expect form of STRING %A STRING %@ STRING
|
let contentPopup4 = Text("[nos conditions d’utilisation](https://linphone.org/general-terms)").underline()
|
||||||
let contentPopup1 = Text(.init(splitMsg[0]))
|
let contentPopup5 = Text(".")
|
||||||
let contentPopup2 = Text(.init(generalTerms)).underline()
|
PopupView(isShowPopup: $isShowPopup,
|
||||||
let contentPopup3 = Text(.init(splitMsg[1]))
|
title: Text("Conditions de service"),
|
||||||
let contentPopup4 = Text(.init(privacyPolicy)).underline()
|
content: contentPopup1 + contentPopup2 + contentPopup3 + contentPopup4 + contentPopup5,
|
||||||
let contentPopup5 = Text(.init(splitMsg[2]))
|
titleFirstButton: Text("Deny all"),
|
||||||
PopupView(isShowPopup: $isShowPopup,
|
actionFirstButton: {self.isShowPopup.toggle()},
|
||||||
title: Text("assistant_dialog_general_terms_and_privacy_policy_title"),
|
titleSecondButton: Text("Accept all"),
|
||||||
content: contentPopup1 + contentPopup2 + contentPopup3 + contentPopup4 + contentPopup5,
|
actionSecondButton: {acceptGeneralTerms()})
|
||||||
titleFirstButton: Text("dialog_deny"),
|
.background(.black.opacity(0.65))
|
||||||
actionFirstButton: {self.isShowPopup.toggle()},
|
.onTapGesture {
|
||||||
titleSecondButton: Text("dialog_accept"),
|
self.isShowPopup.toggle()
|
||||||
actionSecondButton: {acceptGeneralTerms()})
|
|
||||||
.background(.black.opacity(0.65))
|
|
||||||
.onTapGesture {
|
|
||||||
self.isShowPopup.toggle()
|
|
||||||
}
|
|
||||||
} else { // backup just in case
|
|
||||||
PopupView(isShowPopup: $isShowPopup,
|
|
||||||
title: Text("assistant_dialog_general_terms_and_privacy_policy_title"),
|
|
||||||
content: Text(.init(String(format: String(localized: "assistant_dialog_general_terms_and_privacy_policy_message"), generalTerms, privacyPolicy))),
|
|
||||||
titleFirstButton: Text("dialog_deny"),
|
|
||||||
actionFirstButton: {self.isShowPopup.toggle()},
|
|
||||||
titleSecondButton: Text("dialog_accept"),
|
|
||||||
actionSecondButton: {acceptGeneralTerms()})
|
|
||||||
.background(.black.opacity(0.65))
|
|
||||||
.onTapGesture {
|
|
||||||
self.isShowPopup.toggle()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,257 +319,10 @@ struct LoginFragment: View {
|
||||||
}
|
}
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
.edgesIgnoringSafeArea(.bottom)
|
|
||||||
.edgesIgnoringSafeArea(.horizontal)
|
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
}
|
}
|
||||||
|
|
||||||
func innerScrollView(geometry: GeometryProxy) -> some View {
|
|
||||||
VStack {
|
|
||||||
ZStack {
|
|
||||||
HStack {
|
|
||||||
if isShowBack {
|
|
||||||
Image("caret-left")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
onBackPressed?()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Color.clear
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("assistant_account_login")
|
|
||||||
.default_text_style_800(styleSize: 20)
|
|
||||||
}
|
|
||||||
.frame(width: geometry.size.width)
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.bottom, 20)
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text(String(localized: "username")+"*")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
TextField("username", text: $accountLoginViewModel.username)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.disabled(coreContext.loggedIn)
|
|
||||||
.frame(height: 25)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(isNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
.focused($isNameFocused)
|
|
||||||
|
|
||||||
Text(String(localized: "password")+"*")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
ZStack(alignment: .trailing) {
|
|
||||||
Group {
|
|
||||||
if isSecured {
|
|
||||||
SecureField("password", text: $accountLoginViewModel.passwd)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.frame(height: 25)
|
|
||||||
.focused($isPasswordFocused)
|
|
||||||
} else {
|
|
||||||
TextField("password", text: $accountLoginViewModel.passwd)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.frame(height: 25)
|
|
||||||
.focused($isPasswordFocused)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
isSecured.toggle()
|
|
||||||
}, label: {
|
|
||||||
Image(self.isSecured ? "eye-slash" : "eye")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.disabled(coreContext.loggedIn)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(isPasswordFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
sharedMainViewModel.changeDisplayProfileMode()
|
|
||||||
self.accountLoginViewModel.login()
|
|
||||||
coreContext.loggingInProgress = true
|
|
||||||
}, label: {
|
|
||||||
Text(coreContext.loggedIn ? "manage_account_delete" : "assistant_account_login")
|
|
||||||
.default_text_style_white_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background((accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty) ? Color.orangeMain100 : Color.orangeMain500)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.disabled(accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Text(.init(String(format: ("[%@](%@)"), String(localized: "assistant_forgotten_password"), "https://subscribe.linphone.org/")))
|
|
||||||
.underline()
|
|
||||||
.tint(Color.grayMain2c600)
|
|
||||||
.default_text_style_600(styleSize: 15)
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.padding(.bottom, 30)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
VStack {
|
|
||||||
Divider()
|
|
||||||
}
|
|
||||||
Text("or")
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
VStack {
|
|
||||||
Divider()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.bottom, 10)
|
|
||||||
|
|
||||||
NavigationLink(destination: {
|
|
||||||
QrCodeScannerFragment()
|
|
||||||
}, label: {
|
|
||||||
HStack {
|
|
||||||
Image("qr-code")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
|
|
||||||
Text("assistant_scan_qr_code")
|
|
||||||
.default_text_style_orange_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.orangeMain500, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
NavigationLink(isActive: $isLinkSIPActive, destination: {
|
|
||||||
ThirdPartySipAccountWarningFragment(accountLoginViewModel: accountLoginViewModel)
|
|
||||||
}, label: {
|
|
||||||
Text("assistant_login_third_party_sip_account")
|
|
||||||
.default_text_style_orange_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
|
|
||||||
})
|
|
||||||
.disabled(!sharedMainViewModel.generalTermsAccepted)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.orangeMain500, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
.simultaneousGesture(
|
|
||||||
TapGesture().onEnded {
|
|
||||||
self.linkActive = "SIP"
|
|
||||||
if !sharedMainViewModel.generalTermsAccepted {
|
|
||||||
withAnimation {
|
|
||||||
self.isShowPopup.toggle()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.isLinkSIPActive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Text("assistant_no_account_yet")
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
|
|
||||||
NavigationLink(destination: RegisterFragment(registerViewModel: RegisterViewModel()), isActive: $isLinkREGActive, label: { Text("assistant_account_register")
|
|
||||||
.default_text_style_white_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
})
|
|
||||||
.disabled(!sharedMainViewModel.generalTermsAccepted)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background(Color.orangeMain500)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.simultaneousGesture(
|
|
||||||
TapGesture().onEnded {
|
|
||||||
self.linkActive = "REG"
|
|
||||||
if !sharedMainViewModel.generalTermsAccepted {
|
|
||||||
withAnimation {
|
|
||||||
self.isShowPopup.toggle()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.isLinkREGActive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
Image("mountain2")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.frame(width: geometry.size.width, height: 60)
|
|
||||||
.clipped()
|
|
||||||
}
|
|
||||||
.frame(minHeight: geometry.size.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
func acceptGeneralTerms() {
|
func acceptGeneralTerms() {
|
||||||
sharedMainViewModel.changeGeneralTerms()
|
sharedMainViewModel.changeGeneralTerms()
|
||||||
self.isShowPopup.toggle()
|
self.isShowPopup.toggle()
|
||||||
|
|
|
||||||
|
|
@ -29,21 +29,171 @@ struct PermissionsFragment: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
if #available(iOS 16.4, *) {
|
ScrollView(.vertical) {
|
||||||
ScrollView(.vertical) {
|
VStack {
|
||||||
innerScrollView(geometry: geometry)
|
ZStack {
|
||||||
}
|
Image("mountain")
|
||||||
.scrollBounceBehavior(.basedOnSize)
|
.resizable()
|
||||||
} else {
|
.scaledToFill()
|
||||||
ScrollView(.vertical) {
|
.frame(width: geometry.size.width, height: 100)
|
||||||
innerScrollView(geometry: geometry)
|
.clipped()
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
HStack {
|
||||||
|
Image("caret-left")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
.padding(.top, -75)
|
||||||
|
.padding(.leading, -10)
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.leading)
|
||||||
|
}
|
||||||
|
.frame(width: geometry.size.width)
|
||||||
|
|
||||||
|
Text("assistant_permissions_title")
|
||||||
|
.default_text_style_white_800(styleSize: 20)
|
||||||
|
.padding(.top, 20)
|
||||||
|
}
|
||||||
|
.padding(.top, 35)
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
|
Text(String(format: String(localized: "assistant_permissions_subtitle"), Bundle.main.displayName))
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
HStack {
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Image("bell-ringing")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20, alignment: .leading)
|
||||||
|
}
|
||||||
|
.padding(16)
|
||||||
|
.background(Color.grayMain2c200)
|
||||||
|
.cornerRadius(40)
|
||||||
|
|
||||||
|
Text("assistant_permissions_post_notifications_title")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.padding(.leading, 10)
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Image("address-book")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20, alignment: .leading)
|
||||||
|
}
|
||||||
|
.padding(16)
|
||||||
|
.background(Color.grayMain2c200)
|
||||||
|
.cornerRadius(40)
|
||||||
|
|
||||||
|
Text("assistant_permissions_read_contacts_title")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.padding(.leading, 10)
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Image("microphone")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20, alignment: .leading)
|
||||||
|
}
|
||||||
|
.padding(16)
|
||||||
|
.background(Color.grayMain2c200)
|
||||||
|
.cornerRadius(40)
|
||||||
|
|
||||||
|
Text("assistant_permissions_record_audio_title")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.padding(.leading, 10)
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Image("video-camera")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20, alignment: .leading)
|
||||||
|
}
|
||||||
|
.padding(16)
|
||||||
|
.background(Color.grayMain2c200)
|
||||||
|
.cornerRadius(40)
|
||||||
|
|
||||||
|
Text("assistant_permissions_access_camera_title")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.padding(.leading, 10)
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
withAnimation {
|
||||||
|
sharedMainViewModel.changeWelcomeView()
|
||||||
|
}
|
||||||
|
}, label: {
|
||||||
|
Text("assistant_permissions_skip_permissions")
|
||||||
|
.default_text_style_orange_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.orangeMain500, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
permissionManager.getPermissions()
|
||||||
|
} label: {
|
||||||
|
Text("assistant_permissions_grant_all_of_them")
|
||||||
|
.default_text_style_white_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(Color.orangeMain500)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
|
.padding(.horizontal)
|
||||||
|
.padding(.bottom, geometry.safeAreaInsets.bottom.isEqual(to: 0.0) ? 20 : 0)
|
||||||
}
|
}
|
||||||
|
.frame(minHeight: geometry.size.height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
.edgesIgnoringSafeArea(.bottom)
|
|
||||||
.edgesIgnoringSafeArea(.horizontal)
|
|
||||||
.onReceive(permissionManager.$allPermissionsHaveBeenDisplayed, perform: { (granted) in
|
.onReceive(permissionManager.$allPermissionsHaveBeenDisplayed, perform: { (granted) in
|
||||||
if granted {
|
if granted {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
|
|
@ -52,161 +202,6 @@ struct PermissionsFragment: View {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func innerScrollView(geometry: GeometryProxy) -> some View {
|
|
||||||
VStack {
|
|
||||||
ZStack {
|
|
||||||
HStack {
|
|
||||||
Image("caret-left")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("assistant_permissions_title")
|
|
||||||
.default_text_style_800(styleSize: 20)
|
|
||||||
}
|
|
||||||
.frame(width: geometry.size.width)
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.bottom, 20)
|
|
||||||
|
|
||||||
Text(String(format: String(localized: "assistant_permissions_subtitle"), Bundle.main.displayName))
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
HStack {
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Image("bell-ringing")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 20, height: 20, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(16)
|
|
||||||
.background(Color.grayMain2c200)
|
|
||||||
.cornerRadius(40)
|
|
||||||
|
|
||||||
Text("assistant_permissions_post_notifications_title")
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(.leading, 10)
|
|
||||||
}
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Image("address-book")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 20, height: 20, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(16)
|
|
||||||
.background(Color.grayMain2c200)
|
|
||||||
.cornerRadius(40)
|
|
||||||
Text(.init(String(format: String(localized: "assistant_permissions_read_contacts_title"), Bundle.main.displayName)))
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(.leading, 10)
|
|
||||||
}
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Image("microphone")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 20, height: 20, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(16)
|
|
||||||
.background(Color.grayMain2c200)
|
|
||||||
.cornerRadius(40)
|
|
||||||
|
|
||||||
Text("assistant_permissions_record_audio_title")
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(.leading, 10)
|
|
||||||
}
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Image("video-camera")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 20, height: 20, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(16)
|
|
||||||
.background(Color.grayMain2c200)
|
|
||||||
.cornerRadius(40)
|
|
||||||
|
|
||||||
Text("assistant_permissions_access_camera_title")
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(.leading, 10)
|
|
||||||
}
|
|
||||||
.padding(.bottom)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.frame(maxHeight: .infinity)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
withAnimation {
|
|
||||||
sharedMainViewModel.changeWelcomeView()
|
|
||||||
}
|
|
||||||
}, label: {
|
|
||||||
Text("assistant_permissions_skip_permissions")
|
|
||||||
.default_text_style_orange_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.orangeMain500, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
Button {
|
|
||||||
permissionManager.getPermissions()
|
|
||||||
} label: {
|
|
||||||
Text("assistant_permissions_grant_all_of_them")
|
|
||||||
.default_text_style_white_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background(Color.orangeMain500)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
Image("mountain2")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.frame(width: geometry.size.width, height: 60)
|
|
||||||
.clipped()
|
|
||||||
}
|
|
||||||
.frame(minHeight: geometry.size.height)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ struct ProfileModeFragment: View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
sharedMainViewModel.changeHideProfileMode()
|
sharedMainViewModel.changeHideProfileMode()
|
||||||
}, label: {
|
}, label: {
|
||||||
Text("dialog_continue")
|
Text("Continue")
|
||||||
.default_text_style_white_600(styleSize: 20)
|
.default_text_style_white_600(styleSize: 20)
|
||||||
.frame(height: 35)
|
.frame(height: 35)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
@ -156,7 +156,7 @@ struct ProfileModeFragment: View {
|
||||||
+ " Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula."),
|
+ " Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula."),
|
||||||
titleFirstButton: nil,
|
titleFirstButton: nil,
|
||||||
actionFirstButton: {},
|
actionFirstButton: {},
|
||||||
titleSecondButton: Text("dialog_close"),
|
titleSecondButton: Text("Close"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
self.isShowPopup.toggle()
|
self.isShowPopup.toggle()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,14 +41,121 @@ struct RegisterCodeConfirmationFragment: View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
ZStack {
|
ZStack {
|
||||||
if #available(iOS 16.4, *) {
|
ScrollView(.vertical) {
|
||||||
ScrollView(.vertical) {
|
VStack {
|
||||||
innerScrollView(geometry: geometry)
|
ZStack {
|
||||||
|
Image("mountain")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFill()
|
||||||
|
.frame(width: geometry.size.width, height: 100)
|
||||||
|
.clipped()
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
HStack {
|
||||||
|
Image("caret-left")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
.padding(.top, -75)
|
||||||
|
.padding(.leading, -10)
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.leading)
|
||||||
|
}
|
||||||
|
.frame(width: geometry.size.width)
|
||||||
|
|
||||||
|
Text("assistant_account_register")
|
||||||
|
.default_text_style_white_800(styleSize: 20)
|
||||||
|
.padding(.top, 20)
|
||||||
|
}
|
||||||
|
.padding(.top, 35)
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
|
ZStack {
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Image("confirm_sms_code_illu")
|
||||||
|
.padding(.bottom, -geometry.safeAreaInsets.bottom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VStack(alignment: .center) {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text(String(format: NSLocalizedString("assistant_account_creation_sms_confirmation_explanation", comment: ""), registerViewModel.phoneNumber))
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
ZStack {
|
||||||
|
|
||||||
|
HStack(spacing: spaceBetweenBoxes) {
|
||||||
|
otpText(text: registerViewModel.otp1, focused: registerViewModel.otpField.isEmpty)
|
||||||
|
otpText(text: registerViewModel.otp2, focused: registerViewModel.otpField.count == 1)
|
||||||
|
otpText(text: registerViewModel.otp3, focused: registerViewModel.otpField.count == 2)
|
||||||
|
otpText(text: registerViewModel.otp4, focused: registerViewModel.otpField.count == 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField("", text: $registerViewModel.otpField)
|
||||||
|
.default_text_style_600(styleSize: 80)
|
||||||
|
.frame(width: isFocused ? 0 : textFieldOriginalWidth, height: textBoxHeight)
|
||||||
|
.textContentType(.oneTimeCode)
|
||||||
|
.foregroundColor(.clear)
|
||||||
|
.accentColor(.clear)
|
||||||
|
.background(.clear)
|
||||||
|
.keyboardType(.numberPad)
|
||||||
|
.focused($isFocused)
|
||||||
|
.onChange(of: registerViewModel.otpField) { _ in
|
||||||
|
limitText(textLimit)
|
||||||
|
if registerViewModel.otpField.count > 3 {
|
||||||
|
registerViewModel.validateCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
.padding(.vertical, 20)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
dismiss()
|
||||||
|
}, label: {
|
||||||
|
Text("assistant_account_creation_wrong_phone_number")
|
||||||
|
.default_text_style_orange_600(styleSize: 15)
|
||||||
|
.frame(height: 35)
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 15)
|
||||||
|
.padding(.vertical, 5)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.orangeMain500, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.scrollBounceBehavior(.basedOnSize)
|
.frame(minHeight: geometry.size.height)
|
||||||
} else {
|
.onAppear {
|
||||||
ScrollView(.vertical) {
|
registerViewModel.otpField = ""
|
||||||
innerScrollView(geometry: geometry)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,127 +167,12 @@ struct RegisterCodeConfirmationFragment: View {
|
||||||
}
|
}
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
.edgesIgnoringSafeArea(.bottom)
|
|
||||||
.edgesIgnoringSafeArea(.horizontal)
|
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func innerScrollView(geometry: GeometryProxy) -> some View {
|
|
||||||
VStack {
|
|
||||||
ZStack {
|
|
||||||
HStack {
|
|
||||||
Image("caret-left")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("assistant_account_register")
|
|
||||||
.default_text_style_800(styleSize: 20)
|
|
||||||
}
|
|
||||||
.frame(width: geometry.size.width)
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.bottom, 20)
|
|
||||||
|
|
||||||
ZStack {
|
|
||||||
VStack {
|
|
||||||
Spacer()
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
Image("confirm_sms_code_illu")
|
|
||||||
.padding(.bottom, -geometry.safeAreaInsets.bottom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VStack(alignment: .center) {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Text(String(format: NSLocalizedString("assistant_account_creation_sms_confirmation_explanation", comment: ""), registerViewModel.phoneNumber))
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
ZStack {
|
|
||||||
|
|
||||||
HStack(spacing: spaceBetweenBoxes) {
|
|
||||||
otpText(text: registerViewModel.otp1, focused: registerViewModel.otpField.isEmpty)
|
|
||||||
otpText(text: registerViewModel.otp2, focused: registerViewModel.otpField.count == 1)
|
|
||||||
otpText(text: registerViewModel.otp3, focused: registerViewModel.otpField.count == 2)
|
|
||||||
otpText(text: registerViewModel.otp4, focused: registerViewModel.otpField.count == 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField("", text: $registerViewModel.otpField)
|
|
||||||
.default_text_style_600(styleSize: 80)
|
|
||||||
.frame(width: isFocused ? 0 : textFieldOriginalWidth, height: textBoxHeight)
|
|
||||||
.textContentType(.oneTimeCode)
|
|
||||||
.foregroundColor(.clear)
|
|
||||||
.accentColor(.clear)
|
|
||||||
.background(.clear)
|
|
||||||
.keyboardType(.numberPad)
|
|
||||||
.focused($isFocused)
|
|
||||||
.onChange(of: registerViewModel.otpField) { _ in
|
|
||||||
limitText(textLimit)
|
|
||||||
if registerViewModel.otpField.count > 3 {
|
|
||||||
registerViewModel.validateCode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
.padding(.vertical, 20)
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
dismiss()
|
|
||||||
}, label: {
|
|
||||||
Text("assistant_account_creation_wrong_phone_number")
|
|
||||||
.default_text_style_orange_600(styleSize: 15)
|
|
||||||
.frame(height: 35)
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 15)
|
|
||||||
.padding(.vertical, 5)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.orangeMain500, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Image("mountain2")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.frame(width: geometry.size.width, height: 60)
|
|
||||||
.clipped()
|
|
||||||
}
|
|
||||||
.frame(minHeight: geometry.size.height)
|
|
||||||
.onAppear {
|
|
||||||
registerViewModel.otpField = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func otpText(text: String, focused: Bool) -> some View {
|
private func otpText(text: String, focused: Bool) -> some View {
|
||||||
|
|
||||||
return Text(text)
|
return Text(text)
|
||||||
|
|
|
||||||
|
|
@ -39,30 +39,281 @@ struct RegisterFragment: View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
ZStack {
|
ZStack {
|
||||||
if #available(iOS 16.4, *) {
|
ScrollView(.vertical) {
|
||||||
ScrollView(.vertical) {
|
VStack {
|
||||||
innerScrollView(geometry: geometry)
|
ZStack {
|
||||||
}
|
Image("mountain")
|
||||||
.scrollBounceBehavior(.basedOnSize)
|
.resizable()
|
||||||
} else {
|
.scaledToFill()
|
||||||
ScrollView(.vertical) {
|
.frame(width: geometry.size.width, height: 100)
|
||||||
innerScrollView(geometry: geometry)
|
.clipped()
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
HStack {
|
||||||
|
Image("caret-left")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
.padding(.top, -75)
|
||||||
|
.padding(.leading, -10)
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.leading)
|
||||||
|
}
|
||||||
|
.frame(width: geometry.size.width)
|
||||||
|
|
||||||
|
Text("assistant_account_register")
|
||||||
|
.default_text_style_white_800(styleSize: 20)
|
||||||
|
.padding(.top, 20)
|
||||||
|
}
|
||||||
|
.padding(.top, 35)
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(String(localized: "username")+"*")
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
TextField("username", text: $registerViewModel.username)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.frame(height: 25)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(isNameFocused ? Color.orangeMain500 : (!registerViewModel.usernameError.isEmpty ? Color.redDanger500 : Color.gray200), lineWidth: 1)
|
||||||
|
)
|
||||||
|
.focused($isNameFocused)
|
||||||
|
.onChange(of: registerViewModel.username) { _ in
|
||||||
|
if !registerViewModel.usernameError.isEmpty {
|
||||||
|
registerViewModel.usernameError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(registerViewModel.usernameError)
|
||||||
|
.foregroundStyle(Color.redDanger500)
|
||||||
|
.default_text_style_600(styleSize: 15)
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
Text(String(localized: "Phone number")+"*")
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Menu {
|
||||||
|
Picker("", selection: $registerViewModel.dialPlanValueSelected) {
|
||||||
|
ForEach(Array(registerViewModel.dialPlansLabelList.enumerated()), id: \.offset) { index, dialPlan in
|
||||||
|
Text(dialPlan).tag(registerViewModel.dialPlansShortLabelList[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text(registerViewModel.dialPlanValueSelected)
|
||||||
|
|
||||||
|
Image("caret-down")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.blue)
|
||||||
|
.frame(width: 15, height: 15)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.trailing, 5)
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
TextField("Phone number", text: $registerViewModel.phoneNumber)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.padding(.leading, 5)
|
||||||
|
.keyboardType(.numberPad)
|
||||||
|
.onChange(of: registerViewModel.phoneNumber) { _ in
|
||||||
|
if !registerViewModel.phoneNumberError.isEmpty {
|
||||||
|
registerViewModel.phoneNumberError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(height: 25)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(isPhoneNumberFocused ? Color.orangeMain500 : (!registerViewModel.phoneNumberError.isEmpty ? Color.redDanger500 : Color.gray200), lineWidth: 1)
|
||||||
|
)
|
||||||
|
.focused($isPhoneNumberFocused)
|
||||||
|
|
||||||
|
Text(registerViewModel.phoneNumberError)
|
||||||
|
.foregroundStyle(Color.redDanger500)
|
||||||
|
.default_text_style_600(styleSize: 15)
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
Text(String(localized: "password")+"*")
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
ZStack(alignment: .trailing) {
|
||||||
|
Group {
|
||||||
|
if isSecured {
|
||||||
|
SecureField("password", text: $registerViewModel.passwd)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.frame(height: 25)
|
||||||
|
.focused($isPasswordFocused)
|
||||||
|
.onChange(of: registerViewModel.passwd) { _ in
|
||||||
|
if !registerViewModel.passwordError.isEmpty {
|
||||||
|
registerViewModel.passwordError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TextField("password", text: $registerViewModel.passwd)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.frame(height: 25)
|
||||||
|
.focused($isPasswordFocused)
|
||||||
|
.onChange(of: registerViewModel.passwd) { _ in
|
||||||
|
if !registerViewModel.passwordError.isEmpty {
|
||||||
|
registerViewModel.passwordError = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
isSecured.toggle()
|
||||||
|
}, label: {
|
||||||
|
Image(self.isSecured ? "eye-slash" : "eye")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(isPasswordFocused ? Color.orangeMain500 : (!registerViewModel.passwordError.isEmpty ? Color.redDanger500 : Color.gray200), lineWidth: 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(registerViewModel.passwordError)
|
||||||
|
.foregroundStyle(Color.redDanger500)
|
||||||
|
.default_text_style_600(styleSize: 15)
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
NavigationLink(isActive: $registerViewModel.isLinkActive, destination: {
|
||||||
|
RegisterCodeConfirmationFragment(registerViewModel: registerViewModel)
|
||||||
|
}, label: {
|
||||||
|
Text("assistant_account_create")
|
||||||
|
.default_text_style_white_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background((registerViewModel.username.isEmpty || registerViewModel.phoneNumber.isEmpty || registerViewModel.passwd.isEmpty) ? Color.orangeMain100 : Color.orangeMain500)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.disabled(!registerViewModel.isLinkActive)
|
||||||
|
.padding(.bottom)
|
||||||
|
.simultaneousGesture(
|
||||||
|
TapGesture().onEnded {
|
||||||
|
if !(registerViewModel.username.isEmpty || registerViewModel.phoneNumber.isEmpty || registerViewModel.passwd.isEmpty) {
|
||||||
|
withAnimation {
|
||||||
|
self.isShowPopup = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("assistant_create_account_using_email_on_our_web_platform")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
UIApplication.shared.open(URL(string: "https://subscribe.linphone.org/register/email")!)
|
||||||
|
}, label: {
|
||||||
|
Text("assistant_web_platform_link")
|
||||||
|
.default_text_style_orange_600(styleSize: 15)
|
||||||
|
.frame(height: 35)
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 15)
|
||||||
|
.padding(.vertical, 5)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.orangeMain500, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text("assistant_already_have_an_account")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
dismiss()
|
||||||
|
}, label: {
|
||||||
|
Text("assistant_account_login")
|
||||||
|
.default_text_style_white_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(Color.orangeMain500)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
}
|
}
|
||||||
|
.frame(minHeight: geometry.size.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.isShowPopup {
|
if self.isShowPopup {
|
||||||
let titlePopup = Text("assistant_dialog_confirm_phone_number_title")
|
let titlePopup = Text("assistant_dialog_confirm_phone_number_title")
|
||||||
let contentPopup = Text("assistant_dialog_confirm_phone_number_message")
|
let contentPopup = Text(String(format: NSLocalizedString("assistant_dialog_confirm_phone_number_message", comment: ""), registerViewModel.phoneNumber))
|
||||||
|
|
||||||
PopupView(
|
PopupView(
|
||||||
isShowPopup: $isShowPopup,
|
isShowPopup: $isShowPopup,
|
||||||
title: titlePopup,
|
title: titlePopup,
|
||||||
content: contentPopup,
|
content: contentPopup,
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
titleFirstButton: Text("Cancel"),
|
||||||
actionFirstButton: {
|
actionFirstButton: {
|
||||||
self.isShowPopup = false
|
self.isShowPopup = false
|
||||||
},
|
},
|
||||||
titleSecondButton: Text("dialog_continue"),
|
titleSecondButton: Text("Continue"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
self.isShowPopup = false
|
self.isShowPopup = false
|
||||||
registerViewModel.createInProgress = true
|
registerViewModel.createInProgress = true
|
||||||
|
|
@ -84,268 +335,11 @@ struct RegisterFragment: View {
|
||||||
}
|
}
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
.edgesIgnoringSafeArea(.bottom)
|
|
||||||
.edgesIgnoringSafeArea(.horizontal)
|
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func innerScrollView(geometry: GeometryProxy) -> some View {
|
|
||||||
VStack {
|
|
||||||
ZStack {
|
|
||||||
HStack {
|
|
||||||
Image("caret-left")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("assistant_account_register")
|
|
||||||
.default_text_style_800(styleSize: 20)
|
|
||||||
}
|
|
||||||
.frame(width: geometry.size.width)
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.bottom, 20)
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text(String(localized: "username")+"*")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
TextField("username", text: $registerViewModel.username)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.frame(height: 25)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(isNameFocused ? Color.orangeMain500 : (!registerViewModel.usernameError.isEmpty ? Color.redDanger500 : Color.gray200), lineWidth: 1)
|
|
||||||
)
|
|
||||||
.focused($isNameFocused)
|
|
||||||
.onChange(of: registerViewModel.username) { _ in
|
|
||||||
if !registerViewModel.usernameError.isEmpty {
|
|
||||||
registerViewModel.usernameError = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(registerViewModel.usernameError)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style_600(styleSize: 15)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
Text(String(localized: "phone_number")+"*")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Menu {
|
|
||||||
Picker("", selection: $registerViewModel.dialPlanValueSelected) {
|
|
||||||
ForEach(Array(registerViewModel.dialPlansLabelList.enumerated()), id: \.offset) { index, dialPlan in
|
|
||||||
Text(dialPlan).tag(registerViewModel.dialPlansShortLabelList[index])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Text(registerViewModel.dialPlanValueSelected)
|
|
||||||
|
|
||||||
Image("caret-down")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.blue)
|
|
||||||
.frame(width: 15, height: 15)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.trailing, 5)
|
|
||||||
|
|
||||||
Divider()
|
|
||||||
|
|
||||||
TextField("phone_number", text: $registerViewModel.phoneNumber)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.padding(.leading, 5)
|
|
||||||
.keyboardType(.numberPad)
|
|
||||||
.onChange(of: registerViewModel.phoneNumber) { _ in
|
|
||||||
if !registerViewModel.phoneNumberError.isEmpty {
|
|
||||||
registerViewModel.phoneNumberError = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(height: 25)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(isPhoneNumberFocused ? Color.orangeMain500 : (!registerViewModel.phoneNumberError.isEmpty ? Color.redDanger500 : Color.gray200), lineWidth: 1)
|
|
||||||
)
|
|
||||||
.focused($isPhoneNumberFocused)
|
|
||||||
|
|
||||||
Text(registerViewModel.phoneNumberError)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style_600(styleSize: 15)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
Text(String(localized: "password")+"*")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
ZStack(alignment: .trailing) {
|
|
||||||
Group {
|
|
||||||
if isSecured {
|
|
||||||
SecureField("password", text: $registerViewModel.passwd)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.frame(height: 25)
|
|
||||||
.focused($isPasswordFocused)
|
|
||||||
.onChange(of: registerViewModel.passwd) { _ in
|
|
||||||
if !registerViewModel.passwordError.isEmpty {
|
|
||||||
registerViewModel.passwordError = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TextField("password", text: $registerViewModel.passwd)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.frame(height: 25)
|
|
||||||
.focused($isPasswordFocused)
|
|
||||||
.onChange(of: registerViewModel.passwd) { _ in
|
|
||||||
if !registerViewModel.passwordError.isEmpty {
|
|
||||||
registerViewModel.passwordError = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
isSecured.toggle()
|
|
||||||
}, label: {
|
|
||||||
Image(self.isSecured ? "eye-slash" : "eye")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(isPasswordFocused ? Color.orangeMain500 : (!registerViewModel.passwordError.isEmpty ? Color.redDanger500 : Color.gray200), lineWidth: 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
Text(registerViewModel.passwordError)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style_600(styleSize: 15)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
NavigationLink(isActive: $registerViewModel.isLinkActive, destination: {
|
|
||||||
RegisterCodeConfirmationFragment(registerViewModel: registerViewModel)
|
|
||||||
}, label: {
|
|
||||||
Text("assistant_account_create")
|
|
||||||
.default_text_style_white_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background((registerViewModel.username.isEmpty || registerViewModel.phoneNumber.isEmpty || registerViewModel.passwd.isEmpty) ? Color.orangeMain100 : Color.orangeMain500)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.disabled(!registerViewModel.isLinkActive)
|
|
||||||
.padding(.bottom)
|
|
||||||
.simultaneousGesture(
|
|
||||||
TapGesture().onEnded {
|
|
||||||
if !(registerViewModel.username.isEmpty || registerViewModel.phoneNumber.isEmpty || registerViewModel.passwd.isEmpty) {
|
|
||||||
withAnimation {
|
|
||||||
self.isShowPopup = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Text("assistant_create_account_using_email_on_our_web_platform")
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
UIApplication.shared.open(URL(string: "https://subscribe.linphone.org/register/email")!)
|
|
||||||
}, label: {
|
|
||||||
Text("assistant_web_platform_link")
|
|
||||||
.default_text_style_orange_600(styleSize: 15)
|
|
||||||
.frame(height: 35)
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 15)
|
|
||||||
.padding(.vertical, 5)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.orangeMain500, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Text("assistant_already_have_an_account")
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
dismiss()
|
|
||||||
}, label: {
|
|
||||||
Text("assistant_account_login")
|
|
||||||
.default_text_style_white_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background(Color.orangeMain500)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
Image("mountain2")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.frame(width: geometry.size.width, height: 60)
|
|
||||||
.clipped()
|
|
||||||
}
|
|
||||||
.frame(minHeight: geometry.size.height)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
|
|
|
||||||
|
|
@ -36,216 +36,207 @@ struct ThirdPartySipAccountLoginFragment: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
if #available(iOS 16.4, *) {
|
ScrollView(.vertical) {
|
||||||
ScrollView(.vertical) {
|
VStack {
|
||||||
innerScrollView(geometry: geometry)
|
ZStack {
|
||||||
}
|
Image("mountain")
|
||||||
.scrollBounceBehavior(.basedOnSize)
|
|
||||||
} else {
|
|
||||||
ScrollView(.vertical) {
|
|
||||||
innerScrollView(geometry: geometry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.navigationTitle("")
|
|
||||||
.navigationBarHidden(true)
|
|
||||||
.edgesIgnoringSafeArea(.bottom)
|
|
||||||
.edgesIgnoringSafeArea(.horizontal)
|
|
||||||
}
|
|
||||||
|
|
||||||
func innerScrollView(geometry: GeometryProxy) -> some View {
|
|
||||||
VStack {
|
|
||||||
ZStack {
|
|
||||||
HStack {
|
|
||||||
Image("caret-left")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
accountLoginViewModel.domain = "sip.linphone.org"
|
|
||||||
accountLoginViewModel.transportType = "TLS"
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("assistant_login_third_party_sip_account")
|
|
||||||
.default_text_style_800(styleSize: 20)
|
|
||||||
}
|
|
||||||
.frame(width: geometry.size.width)
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.bottom, 20)
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text(String(localized: "username")+"*")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
TextField("username", text: $accountLoginViewModel.username)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.disabled(coreContext.loggedIn)
|
|
||||||
.frame(height: 25)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(isNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
.focused($isNameFocused)
|
|
||||||
|
|
||||||
Text(String(localized: "password")+"*")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
ZStack(alignment: .trailing) {
|
|
||||||
Group {
|
|
||||||
if isSecured {
|
|
||||||
SecureField("password", text: $accountLoginViewModel.passwd)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.frame(height: 25)
|
|
||||||
.focused($isPasswordFocused)
|
|
||||||
} else {
|
|
||||||
TextField("password", text: $accountLoginViewModel.passwd)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.frame(height: 25)
|
|
||||||
.focused($isPasswordFocused)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button(action: {
|
|
||||||
isSecured.toggle()
|
|
||||||
}, label: {
|
|
||||||
Image(self.isSecured ? "eye-slash" : "eye")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
.scaledToFill()
|
||||||
.frame(width: 20, height: 20)
|
.frame(width: geometry.size.width, height: 100)
|
||||||
})
|
.clipped()
|
||||||
}
|
|
||||||
.disabled(coreContext.loggedIn)
|
VStack(alignment: .leading) {
|
||||||
.padding(.horizontal, 20)
|
HStack {
|
||||||
.padding(.vertical, 15)
|
Image("caret-left")
|
||||||
.cornerRadius(60)
|
.renderingMode(.template)
|
||||||
.overlay(
|
.resizable()
|
||||||
RoundedRectangle(cornerRadius: 60)
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
.inset(by: 0.5)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.stroke(isPasswordFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
.padding(.all, 10)
|
||||||
)
|
.padding(.top, -75)
|
||||||
.padding(.bottom)
|
.padding(.leading, -10)
|
||||||
|
.onTapGesture {
|
||||||
Text(String(localized: "sip_address_domain")+"*")
|
withAnimation {
|
||||||
.default_text_style_700(styleSize: 15)
|
accountLoginViewModel.domain = "sip.linphone.org"
|
||||||
.padding(.bottom, -5)
|
accountLoginViewModel.transportType = "TLS"
|
||||||
|
dismiss()
|
||||||
TextField("sip.linphone.org", text: $accountLoginViewModel.domain)
|
}
|
||||||
.default_text_style(styleSize: 15)
|
}
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(.none)
|
Spacer()
|
||||||
.disabled(coreContext.loggedIn)
|
}
|
||||||
.frame(height: 25)
|
.padding(.leading)
|
||||||
|
}
|
||||||
|
.frame(width: geometry.size.width)
|
||||||
|
|
||||||
|
Text("Use a SIP account")
|
||||||
|
.default_text_style_white_800(styleSize: 20)
|
||||||
|
.padding(.top, 20)
|
||||||
|
}
|
||||||
|
.padding(.top, 35)
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(String(localized: "username")+"*")
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
TextField("username", text: $accountLoginViewModel.username)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.disabled(coreContext.loggedIn)
|
||||||
|
.frame(height: 25)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(isNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
.focused($isNameFocused)
|
||||||
|
|
||||||
|
Text(String(localized: "password")+"*")
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
ZStack(alignment: .trailing) {
|
||||||
|
Group {
|
||||||
|
if isSecured {
|
||||||
|
SecureField("password", text: $accountLoginViewModel.passwd)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.frame(height: 25)
|
||||||
|
.focused($isPasswordFocused)
|
||||||
|
} else {
|
||||||
|
TextField("password", text: $accountLoginViewModel.passwd)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.frame(height: 25)
|
||||||
|
.focused($isPasswordFocused)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(action: {
|
||||||
|
isSecured.toggle()
|
||||||
|
}, label: {
|
||||||
|
Image(self.isSecured ? "eye-slash" : "eye")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.disabled(coreContext.loggedIn)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(isPasswordFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
Text(String(localized: "Domain")+"*")
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
TextField("sip.linphone.org", text: $accountLoginViewModel.domain)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.disabled(coreContext.loggedIn)
|
||||||
|
.frame(height: 25)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(isDomainFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
.focused($isDomainFocused)
|
||||||
|
|
||||||
|
Text(String(localized: "Display Name"))
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
TextField("Display Name", text: $accountLoginViewModel.displayName)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.disabled(coreContext.loggedIn)
|
||||||
|
.frame(height: 25)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(isDisplayNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
.focused($isDisplayNameFocused)
|
||||||
|
|
||||||
|
Text(String(localized: "Transport"))
|
||||||
|
.default_text_style_700(styleSize: 15)
|
||||||
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
Button("TLS") {accountLoginViewModel.transportType = "TLS"}
|
||||||
|
Button("TCP") {accountLoginViewModel.transportType = "TCP"}
|
||||||
|
Button("UDP") {accountLoginViewModel.transportType = "UDP"}
|
||||||
|
} label: {
|
||||||
|
Text(accountLoginViewModel.transportType)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
Image("caret-down")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
}
|
||||||
|
.frame(height: 25)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.gray200, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
self.accountLoginViewModel.login()
|
||||||
|
}, label: {
|
||||||
|
Text(coreContext.loggedIn ? "Log out" : "assistant_account_login")
|
||||||
|
.default_text_style_white_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(
|
||||||
|
(accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty || accountLoginViewModel.domain.isEmpty)
|
||||||
|
? Color.orangeMain100
|
||||||
|
: Color.orangeMain500)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.disabled(accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty || accountLoginViewModel.domain.isEmpty)
|
||||||
|
.padding(.bottom, geometry.safeAreaInsets.bottom.isEqual(to: 0.0) ? 20 : 0)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
.padding(.vertical, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(isDomainFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
.focused($isDomainFocused)
|
|
||||||
|
|
||||||
Text(String(localized: "sip_address_display_name"))
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
TextField("sip_address_display_name", text: $accountLoginViewModel.displayName)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.disableAutocorrection(true)
|
|
||||||
.autocapitalization(.none)
|
|
||||||
.disabled(coreContext.loggedIn)
|
|
||||||
.frame(height: 25)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(isDisplayNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
.focused($isDisplayNameFocused)
|
|
||||||
|
|
||||||
Text(String(localized: "assistant_sip_account_transport_protocol"))
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
Button("TLS") {accountLoginViewModel.transportType = "TLS"}
|
|
||||||
Button("TCP") {accountLoginViewModel.transportType = "TCP"}
|
|
||||||
Button("UDP") {accountLoginViewModel.transportType = "UDP"}
|
|
||||||
} label: {
|
|
||||||
Text(accountLoginViewModel.transportType)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
Image("caret-down")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
}
|
}
|
||||||
.frame(height: 25)
|
.frame(minHeight: geometry.size.height)
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.bottom)
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
self.accountLoginViewModel.login()
|
|
||||||
}, label: {
|
|
||||||
Text(coreContext.loggedIn ? "manage_account_delete" : "assistant_account_login")
|
|
||||||
.default_text_style_white_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background(
|
|
||||||
(accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty || accountLoginViewModel.domain.isEmpty)
|
|
||||||
? Color.orangeMain100
|
|
||||||
: Color.orangeMain500)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.disabled(accountLoginViewModel.username.isEmpty || accountLoginViewModel.passwd.isEmpty || accountLoginViewModel.domain.isEmpty)
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
Image("mountain2")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.frame(width: geometry.size.width, height: 60)
|
|
||||||
.clipped()
|
|
||||||
}
|
}
|
||||||
.frame(minHeight: geometry.size.height)
|
.navigationBarHidden(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,160 +30,156 @@ struct ThirdPartySipAccountWarningFragment: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
if #available(iOS 16.4, *) {
|
ScrollView(.vertical) {
|
||||||
ScrollView(.vertical) {
|
VStack {
|
||||||
innerScrollView(geometry: geometry)
|
ZStack {
|
||||||
}
|
Image("mountain")
|
||||||
.scrollBounceBehavior(.basedOnSize)
|
.resizable()
|
||||||
} else {
|
.scaledToFill()
|
||||||
ScrollView(.vertical) {
|
.frame(width: geometry.size.width, height: 100)
|
||||||
innerScrollView(geometry: geometry)
|
.clipped()
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
HStack {
|
||||||
|
Image("caret-left")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
.padding(.top, -75)
|
||||||
|
.padding(.leading, -10)
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.leading)
|
||||||
|
}
|
||||||
|
.frame(width: geometry.size.width)
|
||||||
|
|
||||||
|
Text("Use a SIP account")
|
||||||
|
.default_text_style_white_800(styleSize: 20)
|
||||||
|
.padding(.top, 20)
|
||||||
|
}
|
||||||
|
.padding(.top, 35)
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Image("chat-teardrop-text-slash")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20, alignment: .leading)
|
||||||
|
}
|
||||||
|
.padding(16)
|
||||||
|
.background(Color.grayMain2c200)
|
||||||
|
.cornerRadius(40)
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Image("video-camera-slash")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 20, height: 20, alignment: .leading)
|
||||||
|
}
|
||||||
|
.padding(16)
|
||||||
|
.background(Color.grayMain2c200)
|
||||||
|
.cornerRadius(40)
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.bottom, 40)
|
||||||
|
|
||||||
|
Text("Some features require a Linphone account, such as group messaging, video conferences...\n\n"
|
||||||
|
+ "These features are hidden when you register with a third party SIP account.\n\n"
|
||||||
|
+ "To enable it in a commercial projet, please contact us. ")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.padding(.bottom)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Text("[linphone.org/contact](https://linphone.org/contact)")
|
||||||
|
.tint(Color.orangeMain500)
|
||||||
|
.default_text_style_orange_600(styleSize: 15)
|
||||||
|
.frame(height: 35)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 15)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.orangeMain500, lineWidth: 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
dismiss()
|
||||||
|
}, label: {
|
||||||
|
Text("I prefere create an account")
|
||||||
|
.default_text_style_orange_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 60)
|
||||||
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.orangeMain500, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
NavigationLink(destination: {
|
||||||
|
ThirdPartySipAccountLoginFragment(accountLoginViewModel: accountLoginViewModel)
|
||||||
|
}, label: {
|
||||||
|
Text("I understand")
|
||||||
|
.default_text_style_white_600(styleSize: 20)
|
||||||
|
.frame(height: 35)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(Color.orangeMain500)
|
||||||
|
.cornerRadius(60)
|
||||||
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
|
.padding(.horizontal)
|
||||||
|
.padding(.bottom, geometry.safeAreaInsets.bottom.isEqual(to: 0.0) ? 20 : 0)
|
||||||
}
|
}
|
||||||
|
.frame(minHeight: geometry.size.height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
.edgesIgnoringSafeArea(.bottom)
|
|
||||||
.edgesIgnoringSafeArea(.horizontal)
|
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func innerScrollView(geometry: GeometryProxy) -> some View {
|
|
||||||
VStack {
|
|
||||||
ZStack {
|
|
||||||
HStack {
|
|
||||||
Image("caret-left")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("assistant_login_third_party_sip_account")
|
|
||||||
.default_text_style_800(styleSize: 20)
|
|
||||||
}
|
|
||||||
.frame(width: geometry.size.width)
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.bottom, 20)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Image("chat-teardrop-text-slash")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(20)
|
|
||||||
.background(Color.grayMain2c200)
|
|
||||||
.cornerRadius(40)
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Image("video-camera-slash")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(20)
|
|
||||||
.background(Color.grayMain2c200)
|
|
||||||
.cornerRadius(40)
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.bottom, 40)
|
|
||||||
|
|
||||||
Text(.init(String(format: String(localized: "assistant_third_party_sip_account_warning_explanation"), Bundle.main.displayName)))
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Text("[linphone.org/contact](https://linphone.org/contact)")
|
|
||||||
.tint(Color.orangeMain500)
|
|
||||||
.default_text_style_orange_600(styleSize: 15)
|
|
||||||
.frame(height: 35)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 15)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.orangeMain500, lineWidth: 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
dismiss()
|
|
||||||
}, label: {
|
|
||||||
Text("assistant_third_party_sip_account_create_linphone_account")
|
|
||||||
.default_text_style_orange_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.orangeMain500, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
NavigationLink(destination: {
|
|
||||||
ThirdPartySipAccountLoginFragment(accountLoginViewModel: accountLoginViewModel)
|
|
||||||
}, label: {
|
|
||||||
Text("assistant_third_party_sip_account_warning_ok")
|
|
||||||
.default_text_style_white_600(styleSize: 20)
|
|
||||||
.frame(height: 35)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background(Color.orangeMain500)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.bottom)
|
|
||||||
|
|
||||||
Image("mountain2")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.frame(width: geometry.size.width, height: 60)
|
|
||||||
.clipped()
|
|
||||||
}
|
|
||||||
.frame(minHeight: geometry.size.height)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
|
|
|
||||||
|
|
@ -128,10 +128,8 @@ class AccountLoginViewModel: ObservableObject {
|
||||||
// Also set the newly added account as default
|
// Also set the newly added account as default
|
||||||
core.defaultAccount = account
|
core.defaultAccount = account
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
self.domain = "sip.linphone.org"
|
||||||
self.domain = "sip.linphone.org"
|
self.transportType = "TLS"
|
||||||
self.transportType = "TLS"
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch { NSLog(error.localizedDescription) }
|
} catch { NSLog(error.localizedDescription) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
// swiftlint:disable line_length
|
// swiftlint:disable line_length
|
||||||
// swiftlint:disable type_body_length
|
// swiftlint:disable type_body_length
|
||||||
|
|
@ -208,25 +207,15 @@ class RegisterViewModel: ObservableObject {
|
||||||
func getDialPlansList() {
|
func getDialPlansList() {
|
||||||
coreContext.doOnCoreQueue { _ in
|
coreContext.doOnCoreQueue { _ in
|
||||||
let dialPlans = Factory.Instance.dialPlans
|
let dialPlans = Factory.Instance.dialPlans
|
||||||
var dialPlansListTmp: [DialPlan] = []
|
|
||||||
var dialPlansLabelListTmp: [String] = []
|
|
||||||
var dialPlansShortLabelListTmp: [String] = []
|
|
||||||
|
|
||||||
dialPlans.forEach { dialPlan in
|
dialPlans.forEach { dialPlan in
|
||||||
dialPlansListTmp.append(dialPlan)
|
self.dialPlansList.append(dialPlan)
|
||||||
dialPlansLabelListTmp.append(
|
self.dialPlansLabelList.append(
|
||||||
"\(dialPlan.flag) \(dialPlan.country) | +\(dialPlan.countryCallingCode)"
|
"\(dialPlan.flag) \(dialPlan.country) | +\(dialPlan.countryCallingCode)"
|
||||||
)
|
)
|
||||||
dialPlansShortLabelListTmp.append(
|
self.dialPlansShortLabelList.append(
|
||||||
"\(dialPlan.flag) +\(dialPlan.countryCallingCode)"
|
"\(dialPlan.flag) +\(dialPlan.countryCallingCode)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.dialPlansList = dialPlansListTmp
|
|
||||||
self.dialPlansLabelList = dialPlansLabelListTmp
|
|
||||||
self.dialPlansShortLabelList = dialPlansShortLabelListTmp
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ struct CallView: View {
|
||||||
@ObservedObject var contactViewModel: ContactViewModel
|
@ObservedObject var contactViewModel: ContactViewModel
|
||||||
@ObservedObject var editContactViewModel: EditContactViewModel
|
@ObservedObject var editContactViewModel: EditContactViewModel
|
||||||
@ObservedObject var meetingViewModel: MeetingViewModel
|
@ObservedObject var meetingViewModel: MeetingViewModel
|
||||||
@ObservedObject var accountProfileViewModel: AccountProfileViewModel
|
|
||||||
|
|
||||||
@State private var addParticipantsViewModel: AddParticipantsViewModel?
|
@State private var addParticipantsViewModel: AddParticipantsViewModel?
|
||||||
|
|
||||||
|
|
@ -212,7 +211,6 @@ struct CallView: View {
|
||||||
contactViewModel: contactViewModel,
|
contactViewModel: contactViewModel,
|
||||||
editContactViewModel: editContactViewModel,
|
editContactViewModel: editContactViewModel,
|
||||||
meetingViewModel: meetingViewModel,
|
meetingViewModel: meetingViewModel,
|
||||||
accountProfileViewModel: accountProfileViewModel,
|
|
||||||
isShowConversationFragment: $isShowConversationFragment,
|
isShowConversationFragment: $isShowConversationFragment,
|
||||||
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
|
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
|
||||||
isShowEditContactFragment: $isShowEditContactFragment,
|
isShowEditContactFragment: $isShowEditContactFragment,
|
||||||
|
|
@ -302,10 +300,10 @@ struct CallView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if callViewModel.isPaused {
|
if callViewModel.isPaused {
|
||||||
Text("call_state_paused")
|
Text("Paused")
|
||||||
.default_text_style_white_800(styleSize: 16)
|
.default_text_style_white_800(styleSize: 16)
|
||||||
} else if telecomManager.isPausedByRemote {
|
} else if telecomManager.isPausedByRemote {
|
||||||
Text("call_state_paused_by_remote")
|
Text("Paused by remote")
|
||||||
.default_text_style_white_800(styleSize: 16)
|
.default_text_style_white_800(styleSize: 16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -534,9 +532,6 @@ struct CallView: View {
|
||||||
LinphoneVideoViewHolder { view in
|
LinphoneVideoViewHolder { view in
|
||||||
coreContext.doOnCoreQueue { core in
|
coreContext.doOnCoreQueue { core in
|
||||||
core.nativeVideoWindow = view
|
core.nativeVideoWindow = view
|
||||||
DispatchQueue.main.async {
|
|
||||||
CoreContext.shared.pipViewModel.setupPiPViewController(remoteView: view)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(
|
.frame(
|
||||||
|
|
@ -552,9 +547,6 @@ struct CallView: View {
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if callViewModel.videoDisplayed {
|
if callViewModel.videoDisplayed {
|
||||||
if coreContext.pipViewModel.pipController?.isPictureInPictureActive ?? false {
|
|
||||||
coreContext.pipViewModel.pipController?.stopPictureInPicture()
|
|
||||||
}
|
|
||||||
callViewModel.videoDisplayed = false
|
callViewModel.videoDisplayed = false
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
callViewModel.videoDisplayed = true
|
callViewModel.videoDisplayed = true
|
||||||
|
|
@ -563,10 +555,6 @@ struct CallView: View {
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
if callViewModel.videoDisplayed {
|
if callViewModel.videoDisplayed {
|
||||||
if !callViewModel.isPaused && TelecomManager.shared.callInProgress
|
|
||||||
&& !(coreContext.pipViewModel.pipController?.isPictureInPictureActive ?? false) {
|
|
||||||
coreContext.pipViewModel.pipController?.startPictureInPicture()
|
|
||||||
}
|
|
||||||
callViewModel.videoDisplayed = false
|
callViewModel.videoDisplayed = false
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
callViewModel.videoDisplayed = true
|
callViewModel.videoDisplayed = true
|
||||||
|
|
@ -657,7 +645,7 @@ struct CallView: View {
|
||||||
VStack {
|
VStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("conference_call_empty")
|
Text("En attente d'autres participants...")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_300(styleSize: 25)
|
.default_text_style_300(styleSize: 25)
|
||||||
|
|
@ -682,7 +670,7 @@ struct CallView: View {
|
||||||
.foregroundStyle(Color.grayMain2c400)
|
.foregroundStyle(Color.grayMain2c400)
|
||||||
.frame(width: 30, height: 30)
|
.frame(width: 30, height: 30)
|
||||||
|
|
||||||
Text("conference_share_link_title")
|
Text("Partager le lien")
|
||||||
.foregroundStyle(Color.grayMain2c400)
|
.foregroundStyle(Color.grayMain2c400)
|
||||||
.default_text_style(styleSize: 25)
|
.default_text_style(styleSize: 25)
|
||||||
.frame(height: 40)
|
.frame(height: 40)
|
||||||
|
|
@ -841,7 +829,7 @@ struct CallView: View {
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: 40, height: 40)
|
.frame(width: 40, height: 40)
|
||||||
|
|
||||||
Text("conference_participant_paused_text")
|
Text("En pause")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -903,20 +891,6 @@ struct CallView: View {
|
||||||
LinphoneVideoViewHolder { view in
|
LinphoneVideoViewHolder { view in
|
||||||
coreContext.doOnCoreQueue { core in
|
coreContext.doOnCoreQueue { core in
|
||||||
core.nativeVideoWindow = view
|
core.nativeVideoWindow = view
|
||||||
DispatchQueue.main.async {
|
|
||||||
CoreContext.shared.pipViewModel.setupPiPViewController(remoteView: view)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
if coreContext.pipViewModel.pipController?.isPictureInPictureActive ?? false {
|
|
||||||
coreContext.pipViewModel.pipController?.stopPictureInPicture()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onDisappear {
|
|
||||||
if !callViewModel.isPaused && TelecomManager.shared.callInProgress
|
|
||||||
&& !(coreContext.pipViewModel.pipController?.isPictureInPictureActive ?? false) {
|
|
||||||
coreContext.pipViewModel.pipController?.startPictureInPicture()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1041,7 +1015,7 @@ struct CallView: View {
|
||||||
.frame(width: 40, height: 40)
|
.frame(width: 40, height: 40)
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, 5)
|
||||||
|
|
||||||
Text("conference_participant_joining_text")
|
Text("Joining...")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1060,7 +1034,7 @@ struct CallView: View {
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: 40, height: 40)
|
.frame(width: 40, height: 40)
|
||||||
|
|
||||||
Text("conference_participant_paused_text")
|
Text("En pause")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1208,7 +1182,7 @@ struct CallView: View {
|
||||||
.frame(width: 40, height: 40)
|
.frame(width: 40, height: 40)
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, 5)
|
||||||
|
|
||||||
Text("conference_participant_joining_text")
|
Text("Joining...")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1227,7 +1201,7 @@ struct CallView: View {
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: 40, height: 40)
|
.frame(width: 40, height: 40)
|
||||||
|
|
||||||
Text("conference_participant_paused_text")
|
Text("En pause")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1351,7 +1325,7 @@ struct CallView: View {
|
||||||
.frame(width: maxValue/4, height: maxValue/4)
|
.frame(width: maxValue/4, height: maxValue/4)
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, 5)
|
||||||
|
|
||||||
Text("conference_participant_joining_text")
|
Text("Joining...")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1370,7 +1344,7 @@ struct CallView: View {
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: maxValue/4, height: maxValue/4)
|
.frame(width: maxValue/4, height: maxValue/4)
|
||||||
|
|
||||||
Text("conference_participant_paused_text")
|
Text("En pause")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1467,7 +1441,7 @@ struct CallView: View {
|
||||||
.frame(width: maxValue/4, height: maxValue/4)
|
.frame(width: maxValue/4, height: maxValue/4)
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, 5)
|
||||||
|
|
||||||
Text("conference_participant_joining_text")
|
Text("Joining...")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1486,7 +1460,7 @@ struct CallView: View {
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: maxValue/4, height: maxValue/4)
|
.frame(width: maxValue/4, height: maxValue/4)
|
||||||
|
|
||||||
Text("conference_participant_paused_text")
|
Text("En pause")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1587,7 +1561,7 @@ struct CallView: View {
|
||||||
.frame(width: maxValue/4, height: maxValue/4)
|
.frame(width: maxValue/4, height: maxValue/4)
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, 5)
|
||||||
|
|
||||||
Text("conference_participant_joining_text")
|
Text("Joining...")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1606,7 +1580,7 @@ struct CallView: View {
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: maxValue/4, height: maxValue/4)
|
.frame(width: maxValue/4, height: maxValue/4)
|
||||||
|
|
||||||
Text("conference_participant_paused_text")
|
Text("En pause")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1703,7 +1677,7 @@ struct CallView: View {
|
||||||
.frame(width: maxValue/4, height: maxValue/4)
|
.frame(width: maxValue/4, height: maxValue/4)
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, 5)
|
||||||
|
|
||||||
Text("conference_participant_joining_text")
|
Text("Joining...")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -1722,7 +1696,7 @@ struct CallView: View {
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: maxValue/4, height: maxValue/4)
|
.frame(width: maxValue/4, height: maxValue/4)
|
||||||
|
|
||||||
Text("conference_participant_paused_text")
|
Text("En pause")
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
.foregroundStyle(Color.white)
|
.foregroundStyle(Color.white)
|
||||||
.default_text_style_500(styleSize: 14)
|
.default_text_style_500(styleSize: 14)
|
||||||
|
|
@ -2061,7 +2035,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text(callViewModel.callsCounter < 2 ? "call_action_blind_transfer" : "call_action_attended_transfer")
|
Text(callViewModel.callsCounter < 2 ? "Transfer" : "Attended transfer")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2094,7 +2068,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("call_action_start_new_call")
|
Text("New call")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2117,7 +2091,7 @@ struct CallView: View {
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
.disabled(true)
|
.disabled(true)
|
||||||
|
|
||||||
Text("conference_action_screen_sharing")
|
Text("Partage d'écran")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2142,7 +2116,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("conference_action_show_participants")
|
Text("Participants")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2197,7 +2171,7 @@ struct CallView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("call_action_go_to_calls_list")
|
Text("Call list")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2227,7 +2201,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("call_action_show_dialer")
|
Text("Dialer")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2250,7 +2224,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("call_action_change_layout")
|
Text("Disposition")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2262,14 +2236,16 @@ struct CallView: View {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
VStack {
|
VStack {
|
||||||
Button {
|
Button {
|
||||||
callViewModel.createConversation()
|
if callViewModel.isOneOneCall && callViewModel.remoteAddress != nil {
|
||||||
|
callViewModel.createOneToOneChatRoomWith(remote: callViewModel.remoteAddress!)
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
if !callViewModel.operationInProgress {
|
if !callViewModel.operationInProgress {
|
||||||
Image("chat-teardrop-text")
|
Image("chat-teardrop-text")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(callViewModel.isOneOneCall ? .white : Color.gray500)
|
||||||
.frame(width: 32, height: 32)
|
.frame(width: 32, height: 32)
|
||||||
} else {
|
} else {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
|
|
@ -2277,13 +2253,8 @@ struct CallView: View {
|
||||||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||||||
.frame(width: 32, height: 32, alignment: .center)
|
.frame(width: 32, height: 32, alignment: .center)
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
if callViewModel.displayedConversation != nil {
|
if callViewModel.isOneOneCall && callViewModel.displayedConversation != nil {
|
||||||
indexPage = 2
|
conversationViewModel.changeDisplayedChatRoom(conversationModel: callViewModel.displayedConversation!)
|
||||||
self.conversationViewModel.changeDisplayedChatRoom(conversationModel: callViewModel.displayedConversation!)
|
|
||||||
callViewModel.displayedConversation = nil
|
|
||||||
withAnimation {
|
|
||||||
telecomManager.callDisplayed = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2291,10 +2262,11 @@ struct CallView: View {
|
||||||
}
|
}
|
||||||
.buttonStyle(PressedButtonStyle(buttonSize: buttonSize))
|
.buttonStyle(PressedButtonStyle(buttonSize: buttonSize))
|
||||||
.frame(width: buttonSize, height: buttonSize)
|
.frame(width: buttonSize, height: buttonSize)
|
||||||
.background(Color.gray500)
|
.background(callViewModel.isOneOneCall ? Color.gray500 : .white)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
.disabled(!callViewModel.isOneOneCall)
|
||||||
|
|
||||||
Text("call_action_show_messages")
|
Text("Messages")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2318,7 +2290,7 @@ struct CallView: View {
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
.disabled(telecomManager.isPausedByRemote)
|
.disabled(telecomManager.isPausedByRemote)
|
||||||
|
|
||||||
Text("call_action_pause_call")
|
Text("Pause")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2343,7 +2315,7 @@ struct CallView: View {
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
|
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
|
||||||
|
|
||||||
Text("call_action_record_call")
|
Text("Record")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2366,7 +2338,7 @@ struct CallView: View {
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
.disabled(true)
|
.disabled(true)
|
||||||
|
|
||||||
Text("call_action_record_call")
|
Text("Record")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2389,7 +2361,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("call_action_change_layout")
|
Text("Disposition")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2432,7 +2404,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text(callViewModel.callsCounter < 2 ? "call_action_blind_transfer" : "call_action_attended_transfer")
|
Text(callViewModel.callsCounter < 2 ? "Transfer" : "Attended transfer")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2465,7 +2437,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("call_action_start_new_call")
|
Text("New call")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2489,7 +2461,7 @@ struct CallView: View {
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
.disabled(true)
|
.disabled(true)
|
||||||
|
|
||||||
Text("conference_action_screen_sharing")
|
Text("Partage d'écran")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2515,7 +2487,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("conference_action_show_participants")
|
Text("Participants")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2571,7 +2543,7 @@ struct CallView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("call_action_go_to_calls_list")
|
Text("Call list")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2601,7 +2573,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("call_action_show_dialer")
|
Text("Dialer")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2624,7 +2596,7 @@ struct CallView: View {
|
||||||
.background(Color.gray500)
|
.background(Color.gray500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("call_action_change_layout")
|
Text("Disposition")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2633,14 +2605,16 @@ struct CallView: View {
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
Button {
|
Button {
|
||||||
callViewModel.createConversation()
|
if callViewModel.isOneOneCall && callViewModel.remoteAddress != nil {
|
||||||
|
callViewModel.createOneToOneChatRoomWith(remote: callViewModel.remoteAddress!)
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
if !callViewModel.operationInProgress {
|
if !callViewModel.operationInProgress {
|
||||||
Image("chat-teardrop-text")
|
Image("chat-teardrop-text")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(callViewModel.isOneOneCall ? .white : Color.gray500)
|
||||||
.frame(width: 32, height: 32)
|
.frame(width: 32, height: 32)
|
||||||
} else {
|
} else {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
|
|
@ -2648,7 +2622,7 @@ struct CallView: View {
|
||||||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||||||
.frame(width: 32, height: 32, alignment: .center)
|
.frame(width: 32, height: 32, alignment: .center)
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
if callViewModel.displayedConversation != nil {
|
if callViewModel.isOneOneCall && callViewModel.displayedConversation != nil {
|
||||||
conversationViewModel.changeDisplayedChatRoom(conversationModel: callViewModel.displayedConversation!)
|
conversationViewModel.changeDisplayedChatRoom(conversationModel: callViewModel.displayedConversation!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2657,10 +2631,11 @@ struct CallView: View {
|
||||||
}
|
}
|
||||||
.buttonStyle(PressedButtonStyle(buttonSize: buttonSize))
|
.buttonStyle(PressedButtonStyle(buttonSize: buttonSize))
|
||||||
.frame(width: buttonSize, height: buttonSize)
|
.frame(width: buttonSize, height: buttonSize)
|
||||||
.background(Color.gray500)
|
.background(callViewModel.isOneOneCall ? Color.gray500 : .white)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
.disabled(!callViewModel.isOneOneCall)
|
||||||
|
|
||||||
Text("call_action_show_messages")
|
Text("Messages")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2684,7 +2659,7 @@ struct CallView: View {
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
.disabled(telecomManager.isPausedByRemote)
|
.disabled(telecomManager.isPausedByRemote)
|
||||||
|
|
||||||
Text("call_action_pause_call")
|
Text("Pause")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2709,7 +2684,7 @@ struct CallView: View {
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
|
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
|
||||||
|
|
||||||
Text("call_action_record_call")
|
Text("Record")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2732,7 +2707,7 @@ struct CallView: View {
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
.disabled(true)
|
.disabled(true)
|
||||||
|
|
||||||
Text("call_action_record_call")
|
Text("Record")
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
}
|
}
|
||||||
|
|
@ -2867,7 +2842,6 @@ struct PressedButtonStyle: ButtonStyle {
|
||||||
contactViewModel: ContactViewModel(),
|
contactViewModel: ContactViewModel(),
|
||||||
editContactViewModel: EditContactViewModel(),
|
editContactViewModel: EditContactViewModel(),
|
||||||
meetingViewModel: MeetingViewModel(),
|
meetingViewModel: MeetingViewModel(),
|
||||||
accountProfileViewModel: AccountProfileViewModel(),
|
|
||||||
fullscreenVideo: .constant(false),
|
fullscreenVideo: .constant(false),
|
||||||
isShowStartCallFragment: .constant(false),
|
isShowStartCallFragment: .constant(false),
|
||||||
isShowConversationFragment: .constant(false),
|
isShowConversationFragment: .constant(false),
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ struct AudioRouteBottomSheet: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
|
|
||||||
Text(!callViewModel.isHeadPhoneAvailable() ? "call_audio_device_type_earpiece" : "call_audio_device_type_headphones")
|
Text(!callViewModel.isHeadPhoneAvailable() ? "Earpiece" : "Headphones")
|
||||||
.default_text_style_white(styleSize: 15)
|
.default_text_style_white(styleSize: 15)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
@ -87,7 +87,7 @@ struct AudioRouteBottomSheet: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
|
|
||||||
Text("call_audio_device_type_speaker")
|
Text("Speaker")
|
||||||
.default_text_style_white(styleSize: 15)
|
.default_text_style_white(styleSize: 15)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
@ -121,8 +121,7 @@ struct AudioRouteBottomSheet: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
|
|
||||||
Text(String(format: String(localized: "call_audio_device_type_bluetooth"),
|
Text("Bluetooth")
|
||||||
AVAudioSession.sharedInstance().currentRoute.outputs.first?.portName ?? ""))
|
|
||||||
.default_text_style_white(styleSize: 15)
|
.default_text_style_white(styleSize: 15)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ struct CallStatisticsSheetBottomSheet: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("dialog_close") {
|
Button("Close") {
|
||||||
callStatisticsSheet = false
|
callStatisticsSheet = false
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ struct CallStatisticsSheetBottomSheet: View {
|
||||||
.padding(15)
|
.padding(15)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("call_stats_audio_title")
|
Text("Audio")
|
||||||
.default_text_style_white_600(styleSize: 15)
|
.default_text_style_white_600(styleSize: 15)
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
|
|
@ -77,7 +77,7 @@ struct CallStatisticsSheetBottomSheet: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if callViewModel.callStatsModel.isVideoEnabled {
|
if callViewModel.callStatsModel.isVideoEnabled {
|
||||||
Text("call_stats_video_title")
|
Text("Vidéo")
|
||||||
.default_text_style_white_600(styleSize: 15)
|
.default_text_style_white_600(styleSize: 15)
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ struct CallsListFragment: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("calls_list_title")
|
Text("Call list")
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.default_text_style_orange_800(styleSize: 16)
|
.default_text_style_orange_800(styleSize: 16)
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ struct CallsListFragment: View {
|
||||||
PopupView(isShowPopup: $isShowPopup,
|
PopupView(isShowPopup: $isShowPopup,
|
||||||
title: Text("calls_list_dialog_merge_into_conference_title"),
|
title: Text("calls_list_dialog_merge_into_conference_title"),
|
||||||
content: nil,
|
content: nil,
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
titleFirstButton: Text("Cancel"),
|
||||||
actionFirstButton: {self.isShowPopup.toggle()},
|
actionFirstButton: {self.isShowPopup.toggle()},
|
||||||
titleSecondButton: Text("calls_list_dialog_merge_into_conference_label"),
|
titleSecondButton: Text("calls_list_dialog_merge_into_conference_label"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
|
|
@ -171,7 +171,7 @@ struct CallsListFragment: View {
|
||||||
HStack {
|
HStack {
|
||||||
Image((callViewModel.selectedCall!.state == .PausedByRemote
|
Image((callViewModel.selectedCall!.state == .PausedByRemote
|
||||||
|| callViewModel.selectedCall!.state == .Pausing
|
|| callViewModel.selectedCall!.state == .Pausing
|
||||||
|| callViewModel.selectedCall!.state == .Paused) ? String(localized: "call_action_resume_call") : String(localized: "call_action_pause_call"))
|
|| callViewModel.selectedCall!.state == .Paused) ? "play" : "pause")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 30, height: 30)
|
.frame(width: 30, height: 30)
|
||||||
|
|
||||||
|
|
@ -182,7 +182,7 @@ struct CallsListFragment: View {
|
||||||
|
|
||||||
Text((callViewModel.selectedCall!.state == .PausedByRemote
|
Text((callViewModel.selectedCall!.state == .PausedByRemote
|
||||||
|| callViewModel.selectedCall!.state == .Pausing
|
|| callViewModel.selectedCall!.state == .Pausing
|
||||||
|| callViewModel.selectedCall!.state == .Paused) ? "call_action_resume_call" : "call_action_pause_call")
|
|| callViewModel.selectedCall!.state == .Paused) ? "Resume" : "Pause")
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
@ -217,7 +217,7 @@ struct CallsListFragment: View {
|
||||||
.background(Color.redDanger500)
|
.background(Color.redDanger500)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("call_action_hang_up")
|
Text("Hang up call")
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style_white(styleSize: 15)
|
.default_text_style_white(styleSize: 15)
|
||||||
|
|
||||||
|
|
@ -271,7 +271,7 @@ struct CallsListFragment: View {
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
} else {
|
} else {
|
||||||
Text(callViewModel.calls[index].callLog!.conferenceInfo!.subject ?? String(localized: "conference_name_error"))
|
Text(callViewModel.calls[index].callLog!.conferenceInfo!.subject ?? "Conference Name error")
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
@ -284,7 +284,7 @@ struct CallsListFragment: View {
|
||||||
|| callViewModel.calls[index].state == .Pausing
|
|| callViewModel.calls[index].state == .Pausing
|
||||||
|| callViewModel.calls[index].state == .Paused
|
|| callViewModel.calls[index].state == .Paused
|
||||||
|| callViewModel.calls[index].state == .Resuming {
|
|| callViewModel.calls[index].state == .Resuming {
|
||||||
Text(callViewModel.calls[index].state == .Resuming ? String(localized: "call_state_resuming") : String(localized: "call_state_paused"))
|
Text(callViewModel.calls[index].state == .Resuming ? "Resuming" : "Paused")
|
||||||
.default_text_style_300(styleSize: 14)
|
.default_text_style_300(styleSize: 14)
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
@ -294,7 +294,7 @@ struct CallsListFragment: View {
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
} else {
|
} else {
|
||||||
Text("call_state_connected")
|
Text("Active")
|
||||||
.default_text_style_300(styleSize: 14)
|
.default_text_style_300(styleSize: 14)
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ struct ChangeLayoutBottomSheet: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
|
|
||||||
Text("conference_layout_grid")
|
Text("Mosaïque")
|
||||||
.foregroundStyle(callViewModel.participantList.count > 5 ? Color.gray500 : .white)
|
.foregroundStyle(callViewModel.participantList.count > 5 ? Color.gray500 : .white)
|
||||||
.default_text_style_white(styleSize: 15)
|
.default_text_style_white(styleSize: 15)
|
||||||
|
|
||||||
|
|
@ -77,7 +77,7 @@ struct ChangeLayoutBottomSheet: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
|
|
||||||
Text("conference_layout_active_speaker")
|
Text("Participant actif")
|
||||||
.default_text_style_white(styleSize: 15)
|
.default_text_style_white(styleSize: 15)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
@ -109,7 +109,7 @@ struct ChangeLayoutBottomSheet: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
|
|
||||||
Text("conference_layout_audio_only")
|
Text("Audio seulement")
|
||||||
.default_text_style_white(styleSize: 15)
|
.default_text_style_white(styleSize: 15)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ struct MediaEncryptedSheetBottomSheet: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("dialog_close") {
|
Button("Close") {
|
||||||
mediaEncryptedSheet = false
|
mediaEncryptedSheet = false
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ struct MediaEncryptedSheetBottomSheet: View {
|
||||||
.padding(15)
|
.padding(15)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("call_stats_media_encryption_title")
|
Text("Chiffrement du média")
|
||||||
.default_text_style_white_600(styleSize: 15)
|
.default_text_style_white_600(styleSize: 15)
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ struct MediaEncryptedSheetBottomSheet: View {
|
||||||
mediaEncryptedSheet = false
|
mediaEncryptedSheet = false
|
||||||
dismiss()
|
dismiss()
|
||||||
}, label: {
|
}, label: {
|
||||||
Text("call_do_zrtp_sas_validation_again")
|
Text("Faire la validation à nouveau")
|
||||||
.default_text_style_white_600(styleSize: 20)
|
.default_text_style_white_600(styleSize: 20)
|
||||||
.frame(height: 35)
|
.frame(height: 35)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
|
||||||
|
|
@ -107,13 +107,13 @@ struct ParticipantsListFragment: View {
|
||||||
.background(.white)
|
.background(.white)
|
||||||
|
|
||||||
if self.isShowPopup {
|
if self.isShowPopup {
|
||||||
let contentPopup = Text(String(format: String(localized: "meeting_call_remove_participant_confirmation_message"), callViewModel.participantList[indexToRemove].name))
|
let contentPopup = Text("Etes-vous sûr de vouloir supprimer \(callViewModel.participantList[indexToRemove].name) ?")
|
||||||
PopupView(isShowPopup: $isShowPopup,
|
PopupView(isShowPopup: $isShowPopup,
|
||||||
title: Text("meeting_call_remove_participant_confirmation_title"),
|
title: Text("Supprimer un participant"),
|
||||||
content: contentPopup,
|
content: contentPopup,
|
||||||
titleFirstButton: Text("dialog_no"),
|
titleFirstButton: Text("Non"),
|
||||||
actionFirstButton: {self.isShowPopup.toggle()},
|
actionFirstButton: {self.isShowPopup.toggle()},
|
||||||
titleSecondButton: Text("dialog_yes"),
|
titleSecondButton: Text("Oui"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
callViewModel.removeParticipant(index: indexToRemove)
|
callViewModel.removeParticipant(index: indexToRemove)
|
||||||
self.isShowPopup.toggle()
|
self.isShowPopup.toggle()
|
||||||
|
|
@ -158,7 +158,7 @@ struct ParticipantsListFragment: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if callViewModel.myParticipantModel!.isAdmin {
|
if callViewModel.myParticipantModel!.isAdmin {
|
||||||
Text("conversation_info_participant_is_admin_label")
|
Text("Administrateur")
|
||||||
.foregroundStyle(Color.grayMain2c300)
|
.foregroundStyle(Color.grayMain2c300)
|
||||||
.default_text_style(styleSize: 12)
|
.default_text_style(styleSize: 12)
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
|
|
@ -202,7 +202,7 @@ struct ParticipantsListFragment: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if callViewModel.participantList[index].isAdmin {
|
if callViewModel.participantList[index].isAdmin {
|
||||||
Text("conversation_info_participant_is_admin_label")
|
Text("Administrateur")
|
||||||
.foregroundStyle(Color.grayMain2c300)
|
.foregroundStyle(Color.grayMain2c300)
|
||||||
.default_text_style(styleSize: 12)
|
.default_text_style(styleSize: 12)
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
|
|
@ -250,7 +250,7 @@ struct ParticipantsListFragment: View {
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.clipped()
|
.clipped()
|
||||||
.padding(.all)
|
.padding(.all)
|
||||||
Text("meeting_call_remove_no_participants")
|
Text("No participant for the moment...")
|
||||||
.default_text_style_800(styleSize: 16)
|
.default_text_style_800(styleSize: 16)
|
||||||
Spacer()
|
Spacer()
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
|
||||||
|
|
@ -470,7 +470,7 @@ struct MeetingWaitingRoomFragment: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
|
|
||||||
Text(!meetingWaitingRoomViewModel.isHeadPhoneAvailable() ? "call_audio_device_type_earpiece" : "call_audio_device_type_headphones")
|
Text(!meetingWaitingRoomViewModel.isHeadPhoneAvailable() ? "Earpiece" : "Headphones")
|
||||||
.default_text_style_white(styleSize: 15)
|
.default_text_style_white(styleSize: 15)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
@ -536,8 +536,7 @@ struct MeetingWaitingRoomFragment: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
|
|
||||||
Text(String(format: String(localized: "call_audio_device_type_bluetooth"),
|
Text("Bluetooth")
|
||||||
AVAudioSession.sharedInstance().currentRoute.outputs.first?.portName ?? ""))
|
|
||||||
.default_text_style_white(styleSize: 15)
|
.default_text_style_white(styleSize: 15)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class CallMediaEncryptionModel: ObservableObject {
|
class CallMediaEncryptionModel: ObservableObject {
|
||||||
var coreContext = CoreContext.shared
|
var coreContext = CoreContext.shared
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class SentVideoWindow: ObservableObject {
|
class SentVideoWindow: ObservableObject {
|
||||||
var widthFactor: CGFloat = 1
|
var widthFactor: CGFloat = 1
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class ParticipantModel: ObservableObject {
|
class ParticipantModel: ObservableObject {
|
||||||
|
|
||||||
|
|
@ -54,13 +53,7 @@ class ParticipantModel: ObservableObject {
|
||||||
if let addressFriend = friendResult {
|
if let addressFriend = friendResult {
|
||||||
self.name = addressFriend.name!
|
self.name = addressFriend.name!
|
||||||
} else {
|
} else {
|
||||||
if address.displayName != nil {
|
self.name = address.displayName != nil ? address.displayName! : address.username!
|
||||||
self.name = address.displayName!
|
|
||||||
} else if address.username != nil {
|
|
||||||
self.name = address.username!
|
|
||||||
} else {
|
|
||||||
self.name = String(address.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import SwiftUI
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import AVFAudio
|
import AVFAudio
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
// swiftlint:disable line_length
|
// swiftlint:disable line_length
|
||||||
// swiftlint:disable type_body_length
|
// swiftlint:disable type_body_length
|
||||||
|
|
@ -96,9 +95,6 @@ class CallViewModel: ObservableObject {
|
||||||
} catch _ {
|
} catch _ {
|
||||||
|
|
||||||
}
|
}
|
||||||
NotificationCenter.default.addObserver(forName: Notification.Name("CallViewModelReset"), object: nil, queue: nil) { notification in
|
|
||||||
self.resetCallView()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetCallView() {
|
func resetCallView() {
|
||||||
|
|
@ -174,8 +170,6 @@ class CallViewModel: ObservableObject {
|
||||||
displayNameTmp = self.currentCall!.remoteAddress!.displayName!
|
displayNameTmp = self.currentCall!.remoteAddress!.displayName!
|
||||||
} else if self.currentCall!.remoteAddress!.username != nil && displayNameTmp.isEmpty {
|
} else if self.currentCall!.remoteAddress!.username != nil && displayNameTmp.isEmpty {
|
||||||
displayNameTmp = self.currentCall!.remoteAddress!.username!
|
displayNameTmp = self.currentCall!.remoteAddress!.username!
|
||||||
} else if displayNameTmp.isEmpty {
|
|
||||||
displayNameTmp = String(self.currentCall!.remoteAddress!.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -357,8 +351,6 @@ class CallViewModel: ObservableObject {
|
||||||
activeSpeakerNameTmp = activeSpeakerParticipantTmp!.address.displayName!
|
activeSpeakerNameTmp = activeSpeakerParticipantTmp!.address.displayName!
|
||||||
} else if activeSpeakerParticipantTmp!.address.username != nil {
|
} else if activeSpeakerParticipantTmp!.address.username != nil {
|
||||||
activeSpeakerNameTmp = activeSpeakerParticipantTmp!.address.username!
|
activeSpeakerNameTmp = activeSpeakerParticipantTmp!.address.username!
|
||||||
} else {
|
|
||||||
activeSpeakerNameTmp = String(activeSpeakerParticipantTmp!.address.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -475,9 +467,7 @@ class CallViewModel: ObservableObject {
|
||||||
activeSpeakerNameTmp = activeSpeakerParticipantTmp!.address.displayName!
|
activeSpeakerNameTmp = activeSpeakerParticipantTmp!.address.displayName!
|
||||||
} else if activeSpeakerParticipantTmp!.address.username != nil {
|
} else if activeSpeakerParticipantTmp!.address.username != nil {
|
||||||
activeSpeakerNameTmp = activeSpeakerParticipantTmp!.address.username!
|
activeSpeakerNameTmp = activeSpeakerParticipantTmp!.address.username!
|
||||||
} else {
|
}
|
||||||
activeSpeakerNameTmp = String(activeSpeakerParticipantTmp!.address.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if self.activeSpeakerParticipant == nil {
|
if self.activeSpeakerParticipant == nil {
|
||||||
|
|
@ -584,15 +574,15 @@ class CallViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, onActiveSpeakerParticipantDevice: { (conference: Conference, participantDevice: ParticipantDevice?) in
|
}, onActiveSpeakerParticipantDevice: { (conference: Conference, participantDevice: ParticipantDevice) in
|
||||||
if participantDevice != nil && participantDevice!.address != nil {
|
if participantDevice.address != nil {
|
||||||
let activeSpeakerParticipantBis = self.activeSpeakerParticipant
|
let activeSpeakerParticipantBis = self.activeSpeakerParticipant
|
||||||
|
|
||||||
let activeSpeakerParticipantTmp = ParticipantModel(
|
let activeSpeakerParticipantTmp = ParticipantModel(
|
||||||
address: participantDevice!.address!,
|
address: participantDevice.address!,
|
||||||
isJoining: false,
|
isJoining: false,
|
||||||
onPause: participantDevice!.state == .OnHold,
|
onPause: participantDevice.state == .OnHold,
|
||||||
isMuted: participantDevice!.isMuted
|
isMuted: participantDevice.isMuted
|
||||||
)
|
)
|
||||||
|
|
||||||
var activeSpeakerNameTmp = ""
|
var activeSpeakerNameTmp = ""
|
||||||
|
|
@ -604,8 +594,6 @@ class CallViewModel: ObservableObject {
|
||||||
activeSpeakerNameTmp = activeSpeakerParticipantTmp.address.displayName!
|
activeSpeakerNameTmp = activeSpeakerParticipantTmp.address.displayName!
|
||||||
} else if activeSpeakerParticipantTmp.address.username != nil {
|
} else if activeSpeakerParticipantTmp.address.username != nil {
|
||||||
activeSpeakerNameTmp = activeSpeakerParticipantTmp.address.username!
|
activeSpeakerNameTmp = activeSpeakerParticipantTmp.address.username!
|
||||||
} else {
|
|
||||||
activeSpeakerNameTmp = String(activeSpeakerParticipantTmp.address.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1166,179 +1154,141 @@ class CallViewModel: ObservableObject {
|
||||||
Log.info("\(CallViewModel.TAG) \(list.count) participants added to conference")
|
Log.info("\(CallViewModel.TAG) \(list.count) participants added to conference")
|
||||||
}
|
}
|
||||||
|
|
||||||
func createConversation() {
|
func createOneToOneChatRoomWith(remote: Address) {
|
||||||
if currentCall != nil {
|
CoreContext.shared.doOnCoreQueue { core in
|
||||||
self.operationInProgress = true
|
let account = core.defaultAccount
|
||||||
CoreContext.shared.doOnCoreQueue { _ in
|
if account == nil {
|
||||||
let existingConversation = self.lookupCurrentCallConversation(call: self.currentCall!)
|
Log.error(
|
||||||
if existingConversation != nil {
|
"\(StartConversationViewModel.TAG) No default account found, can't create conversation with \(remote.asStringUriOnly())!"
|
||||||
Log.info("\(CallViewModel.TAG) Found existing conversation \(LinphoneUtils.getConversationId(chatRoom: existingConversation!)), going to it")
|
)
|
||||||
|
return
|
||||||
let model = ConversationModel(chatRoom: existingConversation!)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.displayedConversation = model
|
|
||||||
self.operationInProgress = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.info("\(CallViewModel.TAG) No existing conversation was found, let's create it")
|
|
||||||
self.createCurrentCallConversation(call: self.currentCall!)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupCurrentCallConversation(call: Call) -> ChatRoom? {
|
|
||||||
let localAddress = call.callLog?.localAddress
|
|
||||||
let remoteAddress = call.remoteAddress
|
|
||||||
|
|
||||||
let params: ConferenceParams? = nil
|
|
||||||
let existingConversation: ChatRoom?
|
|
||||||
if call.conference != nil {
|
|
||||||
existingConversation = call.core?.searchChatRoom(
|
|
||||||
params: params,
|
|
||||||
localAddr: localAddress,
|
|
||||||
remoteAddr: remoteAddress,
|
|
||||||
participants: []
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let participants = [remoteAddress!]
|
|
||||||
existingConversation = call.core?.searchChatRoom(
|
|
||||||
params: params,
|
|
||||||
localAddr: localAddress,
|
|
||||||
remoteAddr: nil,
|
|
||||||
participants: participants
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if existingConversation != nil {
|
|
||||||
Log.info("\(CallViewModel.TAG) Found existing conversation \(existingConversation!.peerAddress?.asStringUriOnly() ?? "") found for current call with local address \(localAddress?.asStringUriOnly() ?? "") and remote address \(remoteAddress?.asStringUriOnly() ?? "")")
|
|
||||||
} else {
|
|
||||||
Log.warn("\(CallViewModel.TAG) No existing conversation found for current call with local address \(localAddress?.asStringUriOnly() ?? "") and remote address \(remoteAddress?.asStringUriOnly() ?? "")")
|
|
||||||
}
|
|
||||||
|
|
||||||
return existingConversation
|
|
||||||
}
|
|
||||||
|
|
||||||
func createCurrentCallConversation(call: Call) {
|
|
||||||
if let remoteAddress = call.remoteAddress {
|
|
||||||
let participants = [remoteAddress]
|
|
||||||
let core = call.core
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = true
|
self.operationInProgress = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if let params = getChatRoomParams(call: call) {
|
do {
|
||||||
do {
|
let params: ChatRoomParams = try core.createDefaultChatRoomParams()
|
||||||
if core != nil {
|
params.groupEnabled = false
|
||||||
let chatRoom = try core!.createChatRoom(params: params, participants: participants)
|
params.subject = "Dummy subject"
|
||||||
if params.chatParams?.backend == ChatRoom.Backend.FlexisipChat {
|
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
|
||||||
if chatRoom.state == ChatRoom.State.Created {
|
|
||||||
let chatRoomId = LinphoneUtils.getConversationId(chatRoom: chatRoom)
|
let sameDomain = remote.domain == account?.params?.domain ?? ""
|
||||||
Log.info("\(CallViewModel.TAG) 1-1 conversation \(chatRoomId) has been created")
|
if StartConversationViewModel.isEndToEndEncryptionMandatory() && sameDomain {
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
Log.info("\(StartConversationViewModel.TAG) Account is in secure mode & domain matches, creating a E2E conversation")
|
||||||
DispatchQueue.main.async {
|
params.backend = ChatRoom.Backend.FlexisipChat
|
||||||
self.displayedConversation = model
|
params.encryptionEnabled = true
|
||||||
self.operationInProgress = false
|
} else if !StartConversationViewModel.isEndToEndEncryptionMandatory() {
|
||||||
}
|
if LinphoneUtils.isEndToEndEncryptedChatAvailable(core: core) {
|
||||||
} else {
|
Log.info(
|
||||||
Log.info("\(CallViewModel.TAG) Conversation isn't in Created state yet, wait for it")
|
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME is available, creating a E2E conversation"
|
||||||
self.chatRoomAddDelegate(core: core!, chatRoom: chatRoom)
|
)
|
||||||
}
|
params.backend = ChatRoom.Backend.FlexisipChat
|
||||||
} else {
|
params.encryptionEnabled = true
|
||||||
let id = LinphoneUtils.getConversationId(chatRoom: chatRoom)
|
} else {
|
||||||
Log.info("\(CallViewModel.TAG) Conversation successfully created \(id)")
|
Log.info(
|
||||||
|
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
)
|
||||||
DispatchQueue.main.async {
|
params.backend = ChatRoom.Backend.Basic
|
||||||
self.displayedConversation = model
|
params.encryptionEnabled = false
|
||||||
self.operationInProgress = false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch {
|
} else {
|
||||||
Log.error(
|
Log.error(
|
||||||
"\(CallViewModel.TAG) Failed to create 1-1 conversation with \(remoteAddress.asStringUriOnly())"
|
"\(StartConversationViewModel.TAG) Account is in secure mode, can't chat with SIP address of different domain \(remote.asStringUriOnly())"
|
||||||
)
|
)
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_invalid_participant_error"
|
||||||
ToastViewModel.shared.displayToast = true
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getChatRoomParams(call: Call) -> ConferenceParams? {
|
|
||||||
let localAddress = call.callLog?.localAddress
|
|
||||||
let remoteAddress = call.remoteAddress
|
|
||||||
let core = call.core
|
|
||||||
if let account = LinphoneUtils.getAccountForAddress(address: localAddress!) ?? LinphoneUtils.getDefaultAccount() {
|
|
||||||
do {
|
|
||||||
let params = try coreContext.mCore.createConferenceParams(conference: call.conference)
|
|
||||||
params.chatEnabled = true
|
|
||||||
params.groupEnabled = false
|
|
||||||
params.subject = NSLocalizedString("conversation_one_to_one_hidden_subject", comment: "")
|
|
||||||
params.account = account
|
|
||||||
|
|
||||||
if let chatParams = params.chatParams {
|
let participants = [remote]
|
||||||
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
|
let localAddress = account?.params?.identityAddress
|
||||||
|
let existingChatRoom = core.searchChatRoom(params: params, localAddr: localAddress, remoteAddr: nil, participants: participants)
|
||||||
let sameDomain = remoteAddress?.domain == CorePreferences.defaultDomain && remoteAddress?.domain == account.params?.domain
|
if existingChatRoom == nil {
|
||||||
if account.params != nil && (account.params!.instantMessagingEncryptionMandatory && sameDomain) {
|
Log.info(
|
||||||
Log.info(
|
"\(StartConversationViewModel.TAG) No existing 1-1 conversation between local account "
|
||||||
"\(CallViewModel.TAG) Account is in secure mode & domain matches, requesting E2E encryption"
|
+ "\(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) was found for given parameters, let's create it"
|
||||||
)
|
)
|
||||||
chatParams.backend = ChatRoom.Backend.FlexisipChat
|
let chatRoom = try core.createChatRoom(params: params, localAddr: localAddress, participants: participants)
|
||||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
if params.backend == ChatRoom.Backend.FlexisipChat {
|
||||||
} else if account.params != nil && !account.params!.instantMessagingEncryptionMandatory {
|
if chatRoom.state == ChatRoom.State.Created {
|
||||||
if core != nil && LinphoneUtils.isEndToEndEncryptedChatAvailable(core: core!) {
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
Log.info(
|
Log.info("\(StartConversationViewModel.TAG) 1-1 conversation \(id) has been created")
|
||||||
"\(CallViewModel.TAG) Account is in interop mode but LIME is available, requesting E2E encryption"
|
|
||||||
)
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
chatParams.backend = ChatRoom.Backend.FlexisipChat
|
if self.operationInProgress == false {
|
||||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.info(
|
Log.info("\(StartConversationViewModel.TAG) Conversation isn't in Created state yet, wait for it")
|
||||||
"\(CallViewModel.TAG) Account is in interop mode but LIME isn't available, disabling E2E encryption"
|
self.chatRoomAddDelegate(core: core, chatRoom: chatRoom)
|
||||||
)
|
|
||||||
chatParams.backend = ChatRoom.Backend.Basic
|
|
||||||
params.securityLevel = Conference.SecurityLevel.None
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.error(
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
"\(CallViewModel.TAG) Account is in secure mode, can't chat with SIP address of different domain \(remoteAddress?.asStringUriOnly() ?? "")"
|
Log.info("\(StartConversationViewModel.TAG) Conversation successfully created \(id)")
|
||||||
)
|
|
||||||
return nil
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
|
if self.operationInProgress == false {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return params
|
|
||||||
} else {
|
} else {
|
||||||
return nil
|
Log.warn(
|
||||||
|
"\(StartConversationViewModel.TAG) A 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) for given parameters already exists!"
|
||||||
|
)
|
||||||
|
|
||||||
|
let model = ConversationModel(chatRoom: existingChatRoom!)
|
||||||
|
if self.operationInProgress == false {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Log.error("\(CallViewModel.TAG) Can't create ConferenceParams \(remoteAddress?.asStringUriOnly() ?? "")")
|
DispatchQueue.main.async {
|
||||||
return nil
|
self.operationInProgress = false
|
||||||
}
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
} else {
|
ToastViewModel.shared.displayToast = true
|
||||||
return nil
|
}
|
||||||
}
|
Log.error("\(StartConversationViewModel.TAG) Failed to create 1-1 conversation with \(remote.asStringUriOnly())!")
|
||||||
}
|
|
||||||
|
|
||||||
func goToConversation(model: ConversationModel) {
|
|
||||||
if self.operationInProgress == false {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.operationInProgress = true
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
||||||
self.displayedConversation = model
|
|
||||||
self.operationInProgress = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.displayedConversation = model
|
|
||||||
self.operationInProgress = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1373,13 +1323,13 @@ class CallViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
self.displayedConversation = model
|
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.displayedConversation = model
|
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if state == ChatRoom.State.CreationFailed {
|
} else if state == ChatRoom.State.CreationFailed {
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,6 @@ class MeetingWaitingRoomViewModel: ObservableObject {
|
||||||
userNameTmp = core.defaultAccount!.contactAddress!.displayName!
|
userNameTmp = core.defaultAccount!.contactAddress!.displayName!
|
||||||
} else if core.defaultAccount!.contactAddress!.username != nil {
|
} else if core.defaultAccount!.contactAddress!.username != nil {
|
||||||
userNameTmp = core.defaultAccount!.contactAddress!.username!
|
userNameTmp = core.defaultAccount!.contactAddress!.username!
|
||||||
} else {
|
|
||||||
userNameTmp = String(core.defaultAccount!.contactAddress!.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
|
||||||
*
|
|
||||||
* This file is part of linphone-iphone
|
|
||||||
*
|
|
||||||
* 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import linphonesw
|
|
||||||
import SwiftUI
|
|
||||||
import AVKit
|
|
||||||
|
|
||||||
class PIPViewModel: NSObject, AVPictureInPictureControllerDelegate {
|
|
||||||
static let TAG = "[PIPViewModel]"
|
|
||||||
|
|
||||||
var pipController: AVPictureInPictureController?
|
|
||||||
var pipVideoCallViewController: PictureInPictureVideoCallViewController?
|
|
||||||
var pipRemoteVideoView = SampleBufferVideoCallView()
|
|
||||||
var videoCallView = UIView()
|
|
||||||
|
|
||||||
var callStateChangedDelegate: CallDelegate?
|
|
||||||
|
|
||||||
func setupPiPViewController(remoteView: UIView) {
|
|
||||||
Log.info("\(PIPViewModel.TAG) Setup PiPViewController")
|
|
||||||
videoCallView = remoteView
|
|
||||||
self.pipVideoCallViewController = PictureInPictureVideoCallViewController()
|
|
||||||
pipRemoteVideoView = pipVideoCallViewController!.pipRemoteVideoView
|
|
||||||
let pipContentSource = AVPictureInPictureController.ContentSource(
|
|
||||||
activeVideoCallSourceView: videoCallView,
|
|
||||||
contentViewController: pipVideoCallViewController!)
|
|
||||||
pipController = AVPictureInPictureController(contentSource: pipContentSource)
|
|
||||||
pipController?.delegate = self
|
|
||||||
pipController?.canStartPictureInPictureAutomaticallyFromInline = true
|
|
||||||
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
if let call = core.currentCall {
|
|
||||||
self.callStateChangedDelegate = CallDelegateStub(onStateChanged: { (call: Call, cstate: Call.State, _: String) in
|
|
||||||
if CoreContext.shared.pipViewModel.pipController?.isPictureInPictureActive ?? false {
|
|
||||||
if cstate == .End || cstate == .Error {
|
|
||||||
Log.info("\(PIPViewModel.TAG) call state 'End' or 'Error' detected, stopping picture in picture")
|
|
||||||
CoreContext.shared.pipViewModel.pipController?.stopPictureInPicture()
|
|
||||||
self.callStateChangedDelegate = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
call.addDelegate(delegate: self.callStateChangedDelegate!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
|
||||||
Log.info("\(PIPViewModel.TAG) pictureInPictureControllerWillStartPictureInPicture")
|
|
||||||
self.pipVideoCallViewController?.matchVideoDimension()
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
core.nativeVideoWindow = self.pipRemoteVideoView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
|
|
||||||
Log.info("\(PIPViewModel.TAG) pictureInPictureControllerDidStopPictureInPicture")
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
core.nativeVideoWindow = self.videoCallView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
core.nativeVideoWindow = self.videoCallView
|
|
||||||
}
|
|
||||||
Log.error("\(PIPViewModel.TAG) failedToStartPictureInPictureWithError : \(error)")
|
|
||||||
}
|
|
||||||
|
|
||||||
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
|
|
||||||
Log.info("\(PIPViewModel.TAG) restoreUserInterfaceForPictureInPictureStopWithCompletionHandler")
|
|
||||||
TelecomManager.shared.callDisplayed = true
|
|
||||||
completionHandler(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PictureInPictureVideoCallViewController: AVPictureInPictureVideoCallViewController {
|
|
||||||
|
|
||||||
var pipRemoteVideoView = SampleBufferVideoCallView()
|
|
||||||
var pipWidth: Double = 720
|
|
||||||
var pipHeight: Double = 480
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
view.backgroundColor = .black
|
|
||||||
view.clipsToBounds = true
|
|
||||||
view.addSubview(pipRemoteVideoView)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
view.layer.cornerRadius = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLayoutSubviews() {
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
self.matchVideoDimension()
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchVideoDimension() {
|
|
||||||
Log.info("\(PIPViewModel.TAG) - PIPViewController - matchVideoDimension to \(pipWidth)x\(pipHeight)")
|
|
||||||
self.preferredContentSize = CGSize(width: pipWidth, height: pipHeight)
|
|
||||||
pipRemoteVideoView.frame = view.bounds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// swiftlint:disable force_cast
|
|
||||||
class SampleBufferVideoCallView: UIView {
|
|
||||||
override class var layerClass: AnyClass {
|
|
||||||
AVSampleBufferDisplayLayer.self
|
|
||||||
}
|
|
||||||
|
|
||||||
var sampleBufferDisplayLayer: AVSampleBufferDisplayLayer {
|
|
||||||
layer as! AVSampleBufferDisplayLayer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// swiftlint:enable force_cast
|
|
||||||
|
|
@ -121,11 +121,11 @@ struct ContactInnerActionsFragment: View {
|
||||||
VStack {
|
VStack {
|
||||||
if contactAvatarModel.friend!.phoneNumbersWithLabel[index].label != nil
|
if contactAvatarModel.friend!.phoneNumbersWithLabel[index].label != nil
|
||||||
&& !contactAvatarModel.friend!.phoneNumbersWithLabel[index].label!.isEmpty {
|
&& !contactAvatarModel.friend!.phoneNumbersWithLabel[index].label!.isEmpty {
|
||||||
Text(String(localized: "phone_number") + " \(contactAvatarModel.friend!.phoneNumbersWithLabel[index].label!)) :")
|
Text("Phone (\(contactAvatarModel.friend!.phoneNumbersWithLabel[index].label!)) :")
|
||||||
.default_text_style_700(styleSize: 14)
|
.default_text_style_700(styleSize: 14)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
} else {
|
} else {
|
||||||
Text(String(localized: "phone_number") + " :")
|
Text("Phone :")
|
||||||
.default_text_style_700(styleSize: 14)
|
.default_text_style_700(styleSize: 14)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +170,7 @@ struct ContactInnerActionsFragment: View {
|
||||||
VStack {
|
VStack {
|
||||||
if contactAvatarModel.friend!.organization != nil
|
if contactAvatarModel.friend!.organization != nil
|
||||||
&& !contactAvatarModel.friend!.organization!.isEmpty {
|
&& !contactAvatarModel.friend!.organization!.isEmpty {
|
||||||
Text(.init(String(format:"**%@ :** %@", String(localized: "contact_editor_company"), contactAvatarModel.friend!.organization!)))
|
Text("**Company :** \(contactAvatarModel.friend!.organization!)")
|
||||||
.default_text_style(styleSize: 14)
|
.default_text_style(styleSize: 14)
|
||||||
.padding(.vertical, 15)
|
.padding(.vertical, 15)
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
|
|
@ -179,7 +179,7 @@ struct ContactInnerActionsFragment: View {
|
||||||
|
|
||||||
if contactAvatarModel.friend!.jobTitle != nil
|
if contactAvatarModel.friend!.jobTitle != nil
|
||||||
&& !contactAvatarModel.friend!.jobTitle!.isEmpty {
|
&& !contactAvatarModel.friend!.jobTitle!.isEmpty {
|
||||||
Text(.init(String(format:"**%@ :** %@", String(localized: "contact_editor_job_title"), contactAvatarModel.friend!.jobTitle!)))
|
Text("**Job :** \(contactAvatarModel.friend!.jobTitle!)")
|
||||||
.default_text_style(styleSize: 14)
|
.default_text_style(styleSize: 14)
|
||||||
.padding(.top,
|
.padding(.top,
|
||||||
contactAvatarModel.friend!.organization != nil
|
contactAvatarModel.friend!.organization != nil
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ struct ContactInnerFragment: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
if contactViewModel.indexDisplayedFriend != nil && contactViewModel.indexDisplayedFriend! < contactsManager.lastSearch.count
|
||||||
if !contactAvatarModel.nativeUri.isEmpty {
|
&& !contactAvatarModel.nativeUri.isEmpty {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
editNativeContact()
|
editNativeContact()
|
||||||
}, label: {
|
}, label: {
|
||||||
|
|
@ -118,9 +118,17 @@ struct ContactInnerFragment: View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
if contactViewModel.indexDisplayedFriend != nil {
|
if contactViewModel.indexDisplayedFriend != nil && contactViewModel.indexDisplayedFriend! < contactsManager.lastSearch.count {
|
||||||
Avatar(contactAvatarModel: contactAvatarModel, avatarSize: 100)
|
Avatar(contactAvatarModel: contactAvatarModel, avatarSize: 100)
|
||||||
|
} else if contactViewModel.indexDisplayedFriend != nil
|
||||||
|
&& contactViewModel.indexDisplayedFriend! < contactsManager.lastSearch.count {
|
||||||
|
Image("profil-picture-default")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 100, height: 100)
|
||||||
|
.clipShape(Circle())
|
||||||
|
}
|
||||||
|
if contactViewModel.indexDisplayedFriend != nil
|
||||||
|
&& contactViewModel.indexDisplayedFriend! < contactsManager.lastSearch.count {
|
||||||
Text(contactAvatarModel.name)
|
Text(contactAvatarModel.name)
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|
@ -136,6 +144,7 @@ struct ContactInnerFragment: View {
|
||||||
.default_text_style_300(styleSize: 12)
|
.default_text_style_300(styleSize: 12)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.frame(minHeight: 150)
|
.frame(minHeight: 150)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
@ -163,18 +172,17 @@ struct ContactInnerFragment: View {
|
||||||
Image("phone")
|
Image("phone")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(contactAvatarModel.address.isEmpty ? Color.grayMain2c400 : Color.grayMain2c600)
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
}
|
}
|
||||||
.padding(16)
|
.padding(16)
|
||||||
.background(contactAvatarModel.address.isEmpty ? Color.grayMain2c100 : Color.grayMain2c200)
|
.background(Color.grayMain2c200)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("contact_call_action")
|
Text("contact_call_action")
|
||||||
.default_text_style(styleSize: 14)
|
.default_text_style(styleSize: 14)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.disabled(contactAvatarModel.address.isEmpty)
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
|
@ -196,18 +204,17 @@ struct ContactInnerFragment: View {
|
||||||
Image("chat-teardrop-text")
|
Image("chat-teardrop-text")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(contactAvatarModel.address.isEmpty ? Color.grayMain2c400 : Color.grayMain2c600)
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
}
|
}
|
||||||
.padding(16)
|
.padding(16)
|
||||||
.background(contactAvatarModel.address.isEmpty ? Color.grayMain2c100 : Color.grayMain2c200)
|
.background(Color.grayMain2c200)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("contact_message_action")
|
Text("contact_message_action")
|
||||||
.default_text_style(styleSize: 14)
|
.default_text_style(styleSize: 14)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.disabled(contactAvatarModel.address.isEmpty)
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
|
@ -229,18 +236,17 @@ struct ContactInnerFragment: View {
|
||||||
Image("video-camera")
|
Image("video-camera")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(contactAvatarModel.address.isEmpty ? Color.grayMain2c400 : Color.grayMain2c600)
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
}
|
}
|
||||||
.padding(16)
|
.padding(16)
|
||||||
.background(contactAvatarModel.address.isEmpty ? Color.grayMain2c100 : Color.grayMain2c200)
|
.background(Color.grayMain2c200)
|
||||||
.cornerRadius(40)
|
.cornerRadius(40)
|
||||||
|
|
||||||
Text("contact_video_call_action")
|
Text("contact_video_call_action")
|
||||||
.default_text_style(styleSize: 14)
|
.default_text_style(styleSize: 14)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.disabled(contactAvatarModel.address.isEmpty)
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +278,7 @@ struct ContactInnerFragment: View {
|
||||||
.fullScreenCover(isPresented: $presentingEditContact) {
|
.fullScreenCover(isPresented: $presentingEditContact) {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
EditContactView(contact: $cnContact)
|
EditContactView(contact: $cnContact)
|
||||||
.navigationBarTitle("contact_edit_title")
|
.navigationBarTitle("Edit Contact")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.edgesIgnoringSafeArea(.vertical)
|
.edgesIgnoringSafeArea(.vertical)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ struct ContactListBottomSheet: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("dialog_close") {
|
Button("Close") {
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
showingSheet.toggle()
|
showingSheet.toggle()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -147,7 +147,7 @@ struct ContactListBottomSheet: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
Text(contactViewModel.stringToCopy.prefix(4) == "sip:"
|
Text(contactViewModel.stringToCopy.prefix(4) == "sip:"
|
||||||
? "menu_block_address" : "menu_block_number")
|
? "Block the address" : "Block the number")
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ struct ContactsListBottomSheet: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("dialog_close") {
|
Button("Close") {
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
showingSheet.toggle()
|
showingSheet.toggle()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,9 @@ struct EditContactFragment: View {
|
||||||
.padding(.top, 2)
|
.padding(.top, 2)
|
||||||
.disabled(editContactViewModel.firstName.isEmpty)
|
.disabled(editContactViewModel.firstName.isEmpty)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
addOrEditFriend()
|
withAnimation {
|
||||||
|
addOrEditFriend()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
@ -530,28 +532,25 @@ struct EditContactFragment: View {
|
||||||
contact: newContact, linphoneFriend: true, existingFriend: editContactViewModel.selectedEditFriend) {
|
contact: newContact, linphoneFriend: true, existingFriend: editContactViewModel.selectedEditFriend) {
|
||||||
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
if editContactViewModel.selectedEditFriend != nil
|
||||||
if editContactViewModel.selectedEditFriend != nil {
|
&& editContactViewModel.selectedEditFriend!.name != editContactViewModel.firstName + " " + editContactViewModel.lastName {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
let result = ContactsManager.shared.lastSearch.firstIndex(where: {
|
let result = ContactsManager.shared.lastSearch.firstIndex(where: {
|
||||||
$0.friend!.name == newContact.firstName + " " + newContact.lastName
|
$0.friend!.name == newContact.firstName + " " + newContact.lastName
|
||||||
})
|
})
|
||||||
contactViewModel.indexDisplayedFriend = result
|
contactViewModel.indexDisplayedFriend = result
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delayColorDismiss()
|
|
||||||
if editContactViewModel.selectedEditFriend == nil {
|
|
||||||
withAnimation {
|
|
||||||
isShowEditContactFragment.toggle()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
withAnimation {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
editContactViewModel.resetValues()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delayColorDismiss()
|
||||||
|
if editContactViewModel.selectedEditFriend == nil {
|
||||||
|
withAnimation {
|
||||||
|
isShowEditContactFragment.toggle()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
editContactViewModel.resetValues()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import linphonesw
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
class ContactAvatarModel: ObservableObject, Identifiable {
|
class ContactAvatarModel: ObservableObject, Identifiable {
|
||||||
let id = UUID()
|
|
||||||
|
|
||||||
let friend: Friend?
|
let friend: Friend?
|
||||||
|
|
||||||
|
|
@ -47,23 +46,24 @@ class ContactAvatarModel: ObservableObject, Identifiable {
|
||||||
self.name = name
|
self.name = name
|
||||||
self.address = address
|
self.address = address
|
||||||
var addressesTmp: [String] = []
|
var addressesTmp: [String] = []
|
||||||
if let friend = friend {
|
if friend != nil {
|
||||||
friend.addresses.forEach { address in
|
friend!.addresses.forEach { address in
|
||||||
addressesTmp.append(address.asStringUriOnly())
|
addressesTmp.append(address.asStringUriOnly())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.addresses = addressesTmp
|
self.addresses = addressesTmp
|
||||||
self.nativeUri = friend?.nativeUri ?? ""
|
self.nativeUri = friend?.nativeUri ?? ""
|
||||||
self.withPresence = withPresence
|
self.withPresence = withPresence
|
||||||
if let friend = friend, withPresence == true {
|
if friend != nil &&
|
||||||
|
withPresence == true {
|
||||||
self.lastPresenceInfo = ""
|
self.lastPresenceInfo = ""
|
||||||
|
|
||||||
self.presenceStatus = friend.consolidatedPresence
|
self.presenceStatus = friend!.consolidatedPresence
|
||||||
|
|
||||||
if friend.consolidatedPresence == .Online || friend.consolidatedPresence == .Busy {
|
if friend!.consolidatedPresence == .Online || friend!.consolidatedPresence == .Busy {
|
||||||
if friend.consolidatedPresence == .Online || friend.presenceModel?.latestActivityTimestamp != -1 {
|
if friend!.consolidatedPresence == .Online || friend!.presenceModel!.latestActivityTimestamp != -1 {
|
||||||
self.lastPresenceInfo = (friend.consolidatedPresence == .Online) ?
|
self.lastPresenceInfo = (friend!.consolidatedPresence == .Online) ?
|
||||||
"Online" : getCallTime(startDate: friend.presenceModel!.latestActivityTimestamp)
|
"Online" : getCallTime(startDate: friend!.presenceModel!.latestActivityTimestamp)
|
||||||
} else {
|
} else {
|
||||||
self.lastPresenceInfo = "Away"
|
self.lastPresenceInfo = "Away"
|
||||||
}
|
}
|
||||||
|
|
@ -86,12 +86,11 @@ class ContactAvatarModel: ObservableObject, Identifiable {
|
||||||
func addFriendDelegate() {
|
func addFriendDelegate() {
|
||||||
friendDelegate = FriendDelegateStub(onPresenceReceived: { (friend: Friend) in
|
friendDelegate = FriendDelegateStub(onPresenceReceived: { (friend: Friend) in
|
||||||
let latestActivityTimestamp = friend.presenceModel?.latestActivityTimestamp ?? -1
|
let latestActivityTimestamp = friend.presenceModel?.latestActivityTimestamp ?? -1
|
||||||
let consolidatedPresenceTmp = friend.consolidatedPresence
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.presenceStatus = consolidatedPresenceTmp
|
self.presenceStatus = friend.consolidatedPresence
|
||||||
if consolidatedPresenceTmp == .Online || consolidatedPresenceTmp == .Busy {
|
if friend.consolidatedPresence == .Online || friend.consolidatedPresence == .Busy {
|
||||||
if consolidatedPresenceTmp == .Online || latestActivityTimestamp != -1 {
|
if friend.consolidatedPresence == .Online || latestActivityTimestamp != -1 {
|
||||||
self.lastPresenceInfo = consolidatedPresenceTmp == .Online ?
|
self.lastPresenceInfo = friend.consolidatedPresence == .Online ?
|
||||||
"Online" : self.getCallTime(startDate: latestActivityTimestamp)
|
"Online" : self.getCallTime(startDate: latestActivityTimestamp)
|
||||||
} else {
|
} else {
|
||||||
self.lastPresenceInfo = "Away"
|
self.lastPresenceInfo = "Away"
|
||||||
|
|
@ -101,10 +100,7 @@ class ContactAvatarModel: ObservableObject, Identifiable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
friend?.addDelegate(delegate: friendDelegate!)
|
||||||
if friend != nil && friendDelegate != nil {
|
|
||||||
friend!.addDelegate(delegate: friendDelegate!)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeFriendDelegate() {
|
func removeFriendDelegate() {
|
||||||
|
|
@ -153,25 +149,11 @@ class ContactAvatarModel: ObservableObject, Identifiable {
|
||||||
}
|
}
|
||||||
completion(avatarModel!)
|
completion(avatarModel!)
|
||||||
} else {
|
} else {
|
||||||
var name = ""
|
let name = address.displayName != nil ? address.displayName! : address.username!
|
||||||
if address.displayName != nil {
|
|
||||||
name = address.displayName!
|
|
||||||
} else if address.username != nil {
|
|
||||||
name = address.username!
|
|
||||||
} else {
|
|
||||||
name = String(address.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
|
||||||
completion(ContactAvatarModel(friend: nil, name: name, address: address.asStringUriOnly(), withPresence: false))
|
completion(ContactAvatarModel(friend: nil, name: name, address: address.asStringUriOnly(), withPresence: false))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var name = ""
|
let name = address.displayName != nil ? address.displayName! : address.username!
|
||||||
if address.displayName != nil {
|
|
||||||
name = address.displayName!
|
|
||||||
} else if address.username != nil {
|
|
||||||
name = address.username!
|
|
||||||
} else {
|
|
||||||
name = String(address.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
|
||||||
completion(ContactAvatarModel(friend: nil, name: name, address: address.asStringUriOnly(), withPresence: false))
|
completion(ContactAvatarModel(friend: nil, name: name, address: address.asStringUriOnly(), withPresence: false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
// swiftlint:disable line_length
|
// swiftlint:disable line_length
|
||||||
class ContactViewModel: ObservableObject {
|
class ContactViewModel: ObservableObject {
|
||||||
|
|
@ -44,7 +43,7 @@ class ContactViewModel: ObservableObject {
|
||||||
let account = core.defaultAccount
|
let account = core.defaultAccount
|
||||||
if account == nil {
|
if account == nil {
|
||||||
Log.error(
|
Log.error(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) No default account found, can't create conversation with \(remote.asStringUriOnly())"
|
"\(StartConversationViewModel.TAG) No default account found, can't create conversation with \(remote.asStringUriOnly())!"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -54,42 +53,37 @@ class ContactViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let params = try core.createConferenceParams(conference: nil)
|
let params: ChatRoomParams = try core.createDefaultChatRoomParams()
|
||||||
params.chatEnabled = true
|
|
||||||
params.groupEnabled = false
|
params.groupEnabled = false
|
||||||
params.subject = NSLocalizedString("conversation_one_to_one_hidden_subject", comment: "")
|
params.subject = "Dummy subject"
|
||||||
params.account = account
|
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
|
||||||
|
|
||||||
guard let chatParams = params.chatParams else { return }
|
let sameDomain = remote.domain == account?.params?.domain ?? ""
|
||||||
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
|
if StartConversationViewModel.isEndToEndEncryptionMandatory() && sameDomain {
|
||||||
|
Log.info("\(StartConversationViewModel.TAG) Account is in secure mode & domain matches, creating a E2E conversation")
|
||||||
let sameDomain = remote.domain == CorePreferences.defaultDomain && remote.domain == account!.params?.domain
|
params.backend = ChatRoom.Backend.FlexisipChat
|
||||||
if account!.params != nil && (account!.params!.instantMessagingEncryptionMandatory && sameDomain) {
|
params.encryptionEnabled = true
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) Account is in secure mode & domain matches, creating an E2E encrypted conversation")
|
} else if !StartConversationViewModel.isEndToEndEncryptionMandatory() {
|
||||||
chatParams.backend = ChatRoom.Backend.FlexisipChat
|
|
||||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
|
||||||
} else if account!.params != nil && (!account!.params!.instantMessagingEncryptionMandatory) {
|
|
||||||
if LinphoneUtils.isEndToEndEncryptedChatAvailable(core: core) {
|
if LinphoneUtils.isEndToEndEncryptedChatAvailable(core: core) {
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) Account is in interop mode but LIME is available, creating an E2E encrypted conversation"
|
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME is available, creating a E2E conversation"
|
||||||
)
|
)
|
||||||
chatParams.backend = ChatRoom.Backend.FlexisipChat
|
params.backend = ChatRoom.Backend.FlexisipChat
|
||||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
params.encryptionEnabled = true
|
||||||
} else {
|
} else {
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
|
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
|
||||||
)
|
)
|
||||||
chatParams.backend = ChatRoom.Backend.Basic
|
params.backend = ChatRoom.Backend.Basic
|
||||||
params.securityLevel = Conference.SecurityLevel.None
|
params.encryptionEnabled = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.error(
|
Log.error(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) Account is in secure mode, can't chat with SIP address of different domain \(remote.asStringUriOnly())"
|
"\(StartConversationViewModel.TAG) Account is in secure mode, can't chat with SIP address of different domain \(remote.asStringUriOnly())"
|
||||||
)
|
)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_invalid_participant_error"
|
||||||
ToastViewModel.shared.displayToast = true
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -100,56 +94,85 @@ class ContactViewModel: ObservableObject {
|
||||||
let existingChatRoom = core.searchChatRoom(params: params, localAddr: localAddress, remoteAddr: nil, participants: participants)
|
let existingChatRoom = core.searchChatRoom(params: params, localAddr: localAddress, remoteAddr: nil, participants: participants)
|
||||||
if existingChatRoom == nil {
|
if existingChatRoom == nil {
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) No existing 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) was found for given parameters, let's create it"
|
"\(StartConversationViewModel.TAG) No existing 1-1 conversation between local account "
|
||||||
|
+ "\(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) was found for given parameters, let's create it"
|
||||||
)
|
)
|
||||||
|
let chatRoom = try core.createChatRoom(params: params, localAddr: localAddress, participants: participants)
|
||||||
do {
|
if params.backend == ChatRoom.Backend.FlexisipChat {
|
||||||
let chatRoom = try core.createChatRoom(params: params, participants: participants)
|
if chatRoom.state == ChatRoom.State.Created {
|
||||||
if chatParams.backend == ChatRoom.Backend.FlexisipChat {
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
let state = chatRoom.state
|
Log.info("\(StartConversationViewModel.TAG) 1-1 conversation \(id) has been created")
|
||||||
if state == ChatRoom.State.Created {
|
|
||||||
let chatRoomId = LinphoneUtils.getConversationId(chatRoom: chatRoom)
|
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) 1-1 conversation \(chatRoomId) has been created")
|
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.displayedConversation = model
|
|
||||||
self.operationInProgress = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) Conversation isn't in Created state yet (state is \(state)), wait for it")
|
|
||||||
self.chatRoomAddDelegate(core: core, chatRoom: chatRoom)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let chatRoomId = LinphoneUtils.getConversationId(chatRoom: chatRoom)
|
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) Conversation successfully created \(chatRoomId)")
|
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
DispatchQueue.main.async {
|
if self.operationInProgress == false {
|
||||||
self.displayedConversation = model
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.info("\(StartConversationViewModel.TAG) Conversation isn't in Created state yet, wait for it")
|
||||||
|
self.chatRoomAddDelegate(core: core, chatRoom: chatRoom)
|
||||||
}
|
}
|
||||||
} catch {
|
} else {
|
||||||
Log.error("\(ConversationForwardMessageViewModel.TAG) Failed to create 1-1 conversation with \(remote.asStringUriOnly())")
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
|
Log.info("\(StartConversationViewModel.TAG) Conversation successfully created \(id)")
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
self.operationInProgress = false
|
if self.operationInProgress == false {
|
||||||
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.displayToast = true
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) A 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) for given parameters already exists!"
|
"\(StartConversationViewModel.TAG) A 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) for given parameters already exists!"
|
||||||
)
|
)
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: existingChatRoom!)
|
let model = ConversationModel(chatRoom: existingChatRoom!)
|
||||||
DispatchQueue.main.async {
|
if self.operationInProgress == false {
|
||||||
self.displayedConversation = model
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
|
}
|
||||||
|
Log.error("\(StartConversationViewModel.TAG) Failed to create 1-1 conversation with \(remote.asStringUriOnly())!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class ContactsListViewModel: ObservableObject {
|
class ContactsListViewModel: ObservableObject {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class EditContactViewModel: ObservableObject {
|
class EditContactViewModel: ObservableObject {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class FavoriteContactsListViewModel: ObservableObject {
|
class FavoriteContactsListViewModel: ObservableObject {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ struct ContentView: View {
|
||||||
@ObservedObject var meetingsListViewModel: MeetingsListViewModel
|
@ObservedObject var meetingsListViewModel: MeetingsListViewModel
|
||||||
@ObservedObject var meetingViewModel: MeetingViewModel
|
@ObservedObject var meetingViewModel: MeetingViewModel
|
||||||
@ObservedObject var conversationForwardMessageViewModel: ConversationForwardMessageViewModel
|
@ObservedObject var conversationForwardMessageViewModel: ConversationForwardMessageViewModel
|
||||||
@ObservedObject var accountProfileViewModel: AccountProfileViewModel
|
|
||||||
|
|
||||||
@State var index = 0
|
@State var index = 0
|
||||||
@State private var orientation = UIDevice.current.orientation
|
@State private var orientation = UIDevice.current.orientation
|
||||||
|
|
@ -71,26 +70,16 @@ struct ContentView: View {
|
||||||
@State var isShowSipAddressesPopup = false
|
@State var isShowSipAddressesPopup = false
|
||||||
@State var isShowSipAddressesPopupType = 0 // 0 to call, 1 to message, 2 to video call
|
@State var isShowSipAddressesPopupType = 0 // 0 to call, 1 to message, 2 to video call
|
||||||
@State var isShowConversationFragment = false
|
@State var isShowConversationFragment = false
|
||||||
@State var isShowAccountProfileFragment = false
|
|
||||||
@State var isShowSettingsFragment = false
|
|
||||||
@State var isShowHelpFragment = false
|
|
||||||
|
|
||||||
@State var fullscreenVideo = false
|
@State var fullscreenVideo = false
|
||||||
|
|
||||||
@State var isShowScheduleMeetingFragment = false
|
@State var isShowScheduleMeetingFragment = false
|
||||||
@State private var isShowLoginFragment: Bool = false
|
@State private var isShowLoginFragment: Bool = false
|
||||||
|
|
||||||
private let avatarSize = 45.0
|
|
||||||
@State private var imagePath: URL?
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let pub = NotificationCenter.default
|
let pub = NotificationCenter.default
|
||||||
.publisher(for: NSNotification.Name("ContactLoaded"))
|
.publisher(for: NSNotification.Name("ContactLoaded"))
|
||||||
let pub2 = NotificationCenter.default
|
|
||||||
.publisher(for: NSNotification.Name("ContactAdded"))
|
|
||||||
.compactMap { $0.userInfo?["address"] as? String }
|
|
||||||
let imageChanged = NotificationCenter.default
|
|
||||||
.publisher(for: NSNotification.Name("ImageChanged"))
|
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
if (telecomManager.callInProgress && !fullscreenVideo && ((!telecomManager.callDisplayed && callViewModel.callsCounter == 1) || callViewModel.callsCounter > 1)) || isShowConversationFragment {
|
if (telecomManager.callInProgress && !fullscreenVideo && ((!telecomManager.callDisplayed && callViewModel.callsCounter == 1) || callViewModel.callsCounter > 1)) || isShowConversationFragment {
|
||||||
|
|
@ -103,7 +92,7 @@ struct ContentView: View {
|
||||||
.padding(.leading, 10)
|
.padding(.leading, 10)
|
||||||
|
|
||||||
if callViewModel.callsCounter > 1 {
|
if callViewModel.callsCounter > 1 {
|
||||||
Text(String(format: String(localized: "calls_count_label"), callViewModel.callsCounter.description))
|
Text("\(callViewModel.callsCounter) appels")
|
||||||
.default_text_style_white(styleSize: 16)
|
.default_text_style_white(styleSize: 16)
|
||||||
} else {
|
} else {
|
||||||
Text("\(callViewModel.displayName)")
|
Text("\(callViewModel.displayName)")
|
||||||
|
|
@ -113,7 +102,7 @@ struct ContentView: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if callViewModel.callsCounter == 1 {
|
if callViewModel.callsCounter == 1 {
|
||||||
Text(callViewModel.isPaused || telecomManager.isPausedByRemote ? String(localized: "call_state_paused") : String(localized: "call_state_connected"))
|
Text("\(callViewModel.isPaused || telecomManager.isPausedByRemote ? "En pause" : "Actif")")
|
||||||
.default_text_style_white(styleSize: 16)
|
.default_text_style_white(styleSize: 16)
|
||||||
.padding(.trailing, 10)
|
.padding(.trailing, 10)
|
||||||
}
|
}
|
||||||
|
|
@ -151,10 +140,10 @@ struct ContentView: View {
|
||||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
if self.index == 0 {
|
if self.index == 0 {
|
||||||
Text("bottom_navigation_contacts_label")
|
Text("Contacts")
|
||||||
.default_text_style_700(styleSize: 10)
|
.default_text_style_700(styleSize: 10)
|
||||||
} else {
|
} else {
|
||||||
Text("bottom_navigation_contacts_label")
|
Text("Contacts")
|
||||||
.default_text_style(styleSize: 10)
|
.default_text_style(styleSize: 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -199,10 +188,10 @@ struct ContentView: View {
|
||||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
if self.index == 1 {
|
if self.index == 1 {
|
||||||
Text("bottom_navigation_calls_label")
|
Text("Calls")
|
||||||
.default_text_style_700(styleSize: 10)
|
.default_text_style_700(styleSize: 10)
|
||||||
} else {
|
} else {
|
||||||
Text("bottom_navigation_calls_label")
|
Text("Calls")
|
||||||
.default_text_style(styleSize: 10)
|
.default_text_style(styleSize: 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -294,9 +283,9 @@ struct ContentView: View {
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(Color.orangeMain500)
|
.foregroundColor(Color.orangeMain500)
|
||||||
.edgesIgnoringSafeArea(.top)
|
.edgesIgnoringSafeArea(.top)
|
||||||
.frame(height: 1)
|
.frame(height: 1)
|
||||||
|
|
||||||
ZStack {
|
ZStack {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
@ -316,62 +305,15 @@ struct ContentView: View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
if searchIsActive == false {
|
if searchIsActive == false {
|
||||||
HStack {
|
HStack {
|
||||||
if (accountProfileViewModel.accountModelIndex ?? 0) < CoreContext.shared.accounts.count {
|
Image("profile-image-example")
|
||||||
AsyncImage(url: imagePath) { image in
|
.resizable()
|
||||||
switch image {
|
.frame(width: 45, height: 45)
|
||||||
case .empty:
|
.clipShape(Circle())
|
||||||
ProgressView()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
case .success(let image):
|
|
||||||
image
|
|
||||||
.resizable()
|
|
||||||
.aspectRatio(contentMode: .fill)
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
case .failure:
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex ?? 0].avatarModel?.name ?? "",
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
@unknown default:
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
openMenu()
|
openMenu()
|
||||||
}
|
}
|
||||||
.onAppear {
|
|
||||||
if let accountModelIndex = accountProfileViewModel.accountModelIndex,
|
|
||||||
accountModelIndex < CoreContext.shared.accounts.count {
|
|
||||||
let imagePathTmp = CoreContext.shared.accounts[accountModelIndex].getImagePath()
|
|
||||||
if !(imagePathTmp.lastPathComponent.isEmpty || imagePathTmp.lastPathComponent == "Error" || imagePathTmp.lastPathComponent == "ImageError.png") {
|
|
||||||
imagePath = imagePathTmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onChange(of: CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex ?? 0].usernaneAvatar) { _ in
|
|
||||||
if let accountModelIndex = accountProfileViewModel.accountModelIndex,
|
|
||||||
accountModelIndex < CoreContext.shared.accounts.count {
|
|
||||||
let imagePathTmp = CoreContext.shared.accounts[accountModelIndex].getImagePath()
|
|
||||||
if !(imagePathTmp.lastPathComponent.isEmpty || imagePathTmp.lastPathComponent == "Error" || imagePathTmp.lastPathComponent == "ImageError.png") {
|
|
||||||
sharedMainViewModel.changeDefaultAvatar(defaultAvatarURL: imagePathTmp)
|
|
||||||
imagePath = imagePathTmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onReceive(imageChanged) { _ in
|
|
||||||
if let accountModelIndex = accountProfileViewModel.accountModelIndex,
|
|
||||||
accountModelIndex < CoreContext.shared.accounts.count {
|
|
||||||
let imagePathTmp = CoreContext.shared.accounts[accountModelIndex].getImagePath()
|
|
||||||
sharedMainViewModel.changeDefaultAvatar(defaultAvatarURL: imagePathTmp)
|
|
||||||
imagePath = imagePathTmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(String(localized: index == 0 ? "bottom_navigation_contacts_label" : (index == 1 ? "bottom_navigation_calls_label" : (index == 2 ? "bottom_navigation_conversations_label" : "bottom_navigation_meetings_label"))))
|
Text(index == 0 ? "bottom_navigation_contacts_label" : (index == 1 ? "bottom_navigation_calls_label" : (index == 2 ? "bottom_navigation_conversations_label" : "bottom_navigation_meetings_label")))
|
||||||
.default_text_style_white_800(styleSize: 20)
|
.default_text_style_white_800(styleSize: 20)
|
||||||
.padding(.leading, 10)
|
.padding(.leading, 10)
|
||||||
|
|
||||||
|
|
@ -449,7 +391,7 @@ struct ContentView: View {
|
||||||
isShowDeleteAllHistoryPopup.toggle()
|
isShowDeleteAllHistoryPopup.toggle()
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Text("menu_delete_history")
|
Text("Delete all history")
|
||||||
Spacer()
|
Spacer()
|
||||||
Image("trash-simple-red")
|
Image("trash-simple-red")
|
||||||
.resizable()
|
.resizable()
|
||||||
|
|
@ -727,10 +669,10 @@ struct ContentView: View {
|
||||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
if self.index == 0 {
|
if self.index == 0 {
|
||||||
Text("bottom_navigation_contacts_label")
|
Text("Contacts")
|
||||||
.default_text_style_700(styleSize: 10)
|
.default_text_style_700(styleSize: 10)
|
||||||
} else {
|
} else {
|
||||||
Text("bottom_navigation_contacts_label")
|
Text("Contacts")
|
||||||
.default_text_style(styleSize: 10)
|
.default_text_style(styleSize: 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -777,10 +719,10 @@ struct ContentView: View {
|
||||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
if self.index == 1 {
|
if self.index == 1 {
|
||||||
Text("bottom_navigation_calls_label")
|
Text("Calls")
|
||||||
.default_text_style_700(styleSize: 9)
|
.default_text_style_700(styleSize: 9)
|
||||||
} else {
|
} else {
|
||||||
Text("bottom_navigation_calls_label")
|
Text("Calls")
|
||||||
.default_text_style(styleSize: 9)
|
.default_text_style(styleSize: 9)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -921,18 +863,17 @@ struct ContentView: View {
|
||||||
conversationsListViewModel: conversationsListViewModel,
|
conversationsListViewModel: conversationsListViewModel,
|
||||||
conversationForwardMessageViewModel: conversationForwardMessageViewModel,
|
conversationForwardMessageViewModel: conversationForwardMessageViewModel,
|
||||||
contactViewModel: contactViewModel,
|
contactViewModel: contactViewModel,
|
||||||
editContactViewModel: editContactViewModel,
|
editContactViewModel: editContactViewModel,
|
||||||
meetingViewModel: meetingViewModel,
|
meetingViewModel: meetingViewModel,
|
||||||
accountProfileViewModel: accountProfileViewModel,
|
|
||||||
isShowConversationFragment: $isShowConversationFragment,
|
isShowConversationFragment: $isShowConversationFragment,
|
||||||
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
|
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
|
||||||
isShowEditContactFragment: $isShowEditContactFragment,
|
isShowEditContactFragment: $isShowEditContactFragment,
|
||||||
indexPage: $index,
|
indexPage: $index,
|
||||||
isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment
|
isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment
|
||||||
)
|
)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.background(Color.gray100)
|
.background(Color.gray100)
|
||||||
.ignoresSafeArea(.keyboard)
|
.ignoresSafeArea(.keyboard)
|
||||||
} else if self.index == 3 {
|
} else if self.index == 3 {
|
||||||
MeetingFragment(meetingViewModel: meetingViewModel, meetingsListViewModel: meetingsListViewModel, isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment, isShowSendCancelMeetingNotificationPopup: $isShowSendCancelMeetingNotificationPopup)
|
MeetingFragment(meetingViewModel: meetingViewModel, meetingsListViewModel: meetingsListViewModel, isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment, isShowSendCancelMeetingNotificationPopup: $isShowSendCancelMeetingNotificationPopup)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
@ -966,15 +907,11 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
SideMenu(
|
SideMenu(
|
||||||
accountProfileViewModel: accountProfileViewModel,
|
|
||||||
width: geometry.size.width / 5 * 4,
|
width: geometry.size.width / 5 * 4,
|
||||||
isOpen: $sideMenuIsOpen,
|
isOpen: self.sideMenuIsOpen,
|
||||||
menuClose: self.openMenu,
|
menuClose: self.openMenu,
|
||||||
safeAreaInsets: geometry.safeAreaInsets,
|
safeAreaInsets: geometry.safeAreaInsets,
|
||||||
isShowLoginFragment: $isShowLoginFragment,
|
isShowLoginFragment: $isShowLoginFragment
|
||||||
isShowAccountProfileFragment: $isShowAccountProfileFragment,
|
|
||||||
isShowSettingsFragment: $isShowSettingsFragment,
|
|
||||||
isShowHelpFragment: $isShowHelpFragment
|
|
||||||
)
|
)
|
||||||
.ignoresSafeArea(.all)
|
.ignoresSafeArea(.all)
|
||||||
.zIndex(2)
|
.zIndex(2)
|
||||||
|
|
@ -1064,16 +1001,17 @@ struct ContentView: View {
|
||||||
|
|
||||||
if isShowDeleteContactPopup {
|
if isShowDeleteContactPopup {
|
||||||
PopupView(isShowPopup: $isShowDeleteContactPopup,
|
PopupView(isShowPopup: $isShowDeleteContactPopup,
|
||||||
title: Text(String(format: String(localized: "contact_dialog_delete_title"),contactViewModel.selectedFriend != nil
|
title: Text(
|
||||||
? contactViewModel.selectedFriend!.name!
|
contactViewModel.selectedFriend != nil
|
||||||
: (contactViewModel.indexDisplayedFriend != nil
|
? "Delete \(contactViewModel.selectedFriend!.name!)?"
|
||||||
? contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.name!
|
: (contactViewModel.indexDisplayedFriend != nil
|
||||||
: "Error Name"))),
|
? "Delete \(contactsManager.lastSearch[contactViewModel.indexDisplayedFriend!].friend!.name!)?"
|
||||||
content: Text("contact_dialog_delete_message"),
|
: "Error Name")),
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
content: Text("This contact will be deleted definitively."),
|
||||||
|
titleFirstButton: Text("Cancel"),
|
||||||
actionFirstButton: {
|
actionFirstButton: {
|
||||||
self.isShowDeleteContactPopup.toggle()},
|
self.isShowDeleteContactPopup.toggle()},
|
||||||
titleSecondButton: Text("dialog_ok"),
|
titleSecondButton: Text("Ok"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
if contactViewModel.selectedFriendToDelete != nil {
|
if contactViewModel.selectedFriendToDelete != nil {
|
||||||
if contactViewModel.indexDisplayedFriend != nil {
|
if contactViewModel.indexDisplayedFriend != nil {
|
||||||
|
|
@ -1105,14 +1043,14 @@ struct ContentView: View {
|
||||||
|
|
||||||
if isShowDeleteAllHistoryPopup {
|
if isShowDeleteAllHistoryPopup {
|
||||||
PopupView(isShowPopup: $isShowDeleteContactPopup,
|
PopupView(isShowPopup: $isShowDeleteContactPopup,
|
||||||
title: Text("history_dialog_delete_all_call_logs_title"),
|
title: Text("Do you really want to delete all calls history?"),
|
||||||
content: Text("history_dialog_delete_all_call_logs_message"),
|
content: Text("All calls will be removed from the history."),
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
titleFirstButton: Text("Cancel"),
|
||||||
actionFirstButton: {
|
actionFirstButton: {
|
||||||
self.isShowDeleteAllHistoryPopup.toggle()
|
self.isShowDeleteAllHistoryPopup.toggle()
|
||||||
historyListViewModel.callLogsAddressToDelete = ""
|
historyListViewModel.callLogsAddressToDelete = ""
|
||||||
},
|
},
|
||||||
titleSecondButton: Text("dialog_ok"),
|
titleSecondButton: Text("Ok"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
historyListViewModel.removeCallLogs()
|
historyListViewModel.removeCallLogs()
|
||||||
self.isShowDeleteAllHistoryPopup.toggle()
|
self.isShowDeleteAllHistoryPopup.toggle()
|
||||||
|
|
@ -1130,11 +1068,11 @@ struct ContentView: View {
|
||||||
|
|
||||||
if isShowDismissPopup {
|
if isShowDismissPopup {
|
||||||
PopupView(isShowPopup: $isShowDismissPopup,
|
PopupView(isShowPopup: $isShowDismissPopup,
|
||||||
title: Text("contact_editor_dialog_abort_confirmation_title"),
|
title: Text("Don’t save modifications?"),
|
||||||
content: Text("contact_editor_dialog_abort_confirmation_message"),
|
content: Text("All modifications will be canceled."),
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
titleFirstButton: Text("Cancel"),
|
||||||
actionFirstButton: {self.isShowDismissPopup.toggle()},
|
actionFirstButton: {self.isShowDismissPopup.toggle()},
|
||||||
titleSecondButton: Text("dialog_ok"),
|
titleSecondButton: Text("Ok"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
if editContactViewModel.selectedEditFriend == nil {
|
if editContactViewModel.selectedEditFriend == nil {
|
||||||
self.isShowDismissPopup.toggle()
|
self.isShowDismissPopup.toggle()
|
||||||
|
|
@ -1203,45 +1141,17 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isShowAccountProfileFragment {
|
|
||||||
AccountProfileFragment(
|
|
||||||
accountProfileViewModel: accountProfileViewModel,
|
|
||||||
registerViewModel: RegisterViewModel(),
|
|
||||||
isShowAccountProfileFragment: $isShowAccountProfileFragment
|
|
||||||
)
|
|
||||||
.zIndex(3)
|
|
||||||
.transition(.move(edge: .trailing))
|
|
||||||
}
|
|
||||||
|
|
||||||
if isShowSettingsFragment {
|
|
||||||
SettingsFragment(
|
|
||||||
settingsViewModel: SettingsViewModel(),
|
|
||||||
isShowSettingsFragment: $isShowSettingsFragment
|
|
||||||
)
|
|
||||||
.zIndex(3)
|
|
||||||
.transition(.move(edge: .trailing))
|
|
||||||
}
|
|
||||||
|
|
||||||
if isShowHelpFragment {
|
|
||||||
HelpFragment(
|
|
||||||
helpViewModel: HelpViewModel(),
|
|
||||||
isShowHelpFragment: $isShowHelpFragment
|
|
||||||
)
|
|
||||||
.zIndex(3)
|
|
||||||
.transition(.move(edge: .trailing))
|
|
||||||
}
|
|
||||||
|
|
||||||
if isShowSendCancelMeetingNotificationPopup {
|
if isShowSendCancelMeetingNotificationPopup {
|
||||||
PopupView(isShowPopup: $isShowSendCancelMeetingNotificationPopup,
|
PopupView(isShowPopup: $isShowSendCancelMeetingNotificationPopup,
|
||||||
title: Text("meeting_schedule_cancel_dialog_title"),
|
title: Text("The meeting will be cancelled"),
|
||||||
content: Text("meeting_schedule_cancel_dialog_message"),
|
content: Text("Send notification to participants ?"),
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
titleFirstButton: Text("Cancel for me only"),
|
||||||
actionFirstButton: {
|
actionFirstButton: {
|
||||||
meetingViewModel.displayedMeeting = nil
|
meetingViewModel.displayedMeeting = nil
|
||||||
meetingsListViewModel.deleteSelectedMeeting()
|
meetingsListViewModel.deleteSelectedMeeting()
|
||||||
self.isShowSendCancelMeetingNotificationPopup.toggle(
|
self.isShowSendCancelMeetingNotificationPopup.toggle(
|
||||||
) },
|
) },
|
||||||
titleSecondButton: Text("dialog_ok"),
|
titleSecondButton: Text("Send cancellation notifications"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
meetingViewModel.displayedMeeting = nil
|
meetingViewModel.displayedMeeting = nil
|
||||||
if let meetingToDelete = self.meetingsListViewModel.selectedMeetingToDelete {
|
if let meetingToDelete = self.meetingsListViewModel.selectedMeetingToDelete {
|
||||||
|
|
@ -1262,11 +1172,11 @@ struct ContentView: View {
|
||||||
isShowPopup: $isShowStartCallGroupPopup,
|
isShowPopup: $isShowStartCallGroupPopup,
|
||||||
title: Text("conversation_info_confirm_start_group_call_dialog_title"),
|
title: Text("conversation_info_confirm_start_group_call_dialog_title"),
|
||||||
content: Text("conversation_info_confirm_start_group_call_dialog_message"),
|
content: Text("conversation_info_confirm_start_group_call_dialog_message"),
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
titleFirstButton: Text("Cancel"),
|
||||||
actionFirstButton: {
|
actionFirstButton: {
|
||||||
self.isShowStartCallGroupPopup.toggle()
|
self.isShowStartCallGroupPopup.toggle()
|
||||||
},
|
},
|
||||||
titleSecondButton: Text("dialog_ok"),
|
titleSecondButton: Text("Confirm"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
if conversationViewModel.displayedConversation != nil {
|
if conversationViewModel.displayedConversation != nil {
|
||||||
conversationViewModel.displayedConversation!.createGroupCall()
|
conversationViewModel.displayedConversation!.createGroupCall()
|
||||||
|
|
@ -1286,11 +1196,11 @@ struct ContentView: View {
|
||||||
isShowPopup: $isShowStartCallGroupPopup,
|
isShowPopup: $isShowStartCallGroupPopup,
|
||||||
title: Text("conversation_info_confirm_start_group_call_dialog_title"),
|
title: Text("conversation_info_confirm_start_group_call_dialog_title"),
|
||||||
content: Text("conversation_info_confirm_start_group_call_dialog_message"),
|
content: Text("conversation_info_confirm_start_group_call_dialog_message"),
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
titleFirstButton: Text("Cancel"),
|
||||||
actionFirstButton: {
|
actionFirstButton: {
|
||||||
self.isShowStartCallGroupPopup.toggle()
|
self.isShowStartCallGroupPopup.toggle()
|
||||||
},
|
},
|
||||||
titleSecondButton: Text("dialog_ok"),
|
titleSecondButton: Text("Confirm"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
if conversationViewModel.displayedConversation != nil {
|
if conversationViewModel.displayedConversation != nil {
|
||||||
conversationViewModel.displayedConversation!.createGroupCall()
|
conversationViewModel.displayedConversation!.createGroupCall()
|
||||||
|
|
@ -1330,9 +1240,8 @@ struct ContentView: View {
|
||||||
conversationsListViewModel: conversationsListViewModel,
|
conversationsListViewModel: conversationsListViewModel,
|
||||||
conversationForwardMessageViewModel: conversationForwardMessageViewModel,
|
conversationForwardMessageViewModel: conversationForwardMessageViewModel,
|
||||||
contactViewModel: contactViewModel,
|
contactViewModel: contactViewModel,
|
||||||
editContactViewModel: editContactViewModel,
|
editContactViewModel: editContactViewModel,
|
||||||
meetingViewModel: meetingViewModel,
|
meetingViewModel: meetingViewModel,
|
||||||
accountProfileViewModel: accountProfileViewModel,
|
|
||||||
fullscreenVideo: $fullscreenVideo,
|
fullscreenVideo: $fullscreenVideo,
|
||||||
isShowStartCallFragment: $isShowStartCallFragment,
|
isShowStartCallFragment: $isShowStartCallFragment,
|
||||||
isShowConversationFragment: $isShowConversationFragment,
|
isShowConversationFragment: $isShowConversationFragment,
|
||||||
|
|
@ -1361,18 +1270,18 @@ struct ContentView: View {
|
||||||
.zIndex(6)
|
.zIndex(6)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onAppear {
|
||||||
|
MagicSearchSingleton.shared.searchForContacts(sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||||
|
}
|
||||||
.onChange(of: navigationManager.selectedCallId) { newCallId in
|
.onChange(of: navigationManager.selectedCallId) { newCallId in
|
||||||
if newCallId != nil {
|
if newCallId != nil {
|
||||||
self.index = 2
|
self.index = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onReceive(pub) { _ in
|
.onReceive(pub) { _ in
|
||||||
conversationsListViewModel.updateChatRoomsList()
|
conversationsListViewModel.computeChatRoomsList(filter: "")
|
||||||
historyListViewModel.refreshHistoryAvatarModel()
|
historyListViewModel.refreshHistoryAvatarModel()
|
||||||
}
|
}
|
||||||
.onReceive(pub2) { address in
|
|
||||||
conversationsListViewModel.updateChatRoom(address: address)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.overlay {
|
.overlay {
|
||||||
if isMenuOpen {
|
if isMenuOpen {
|
||||||
|
|
@ -1393,11 +1302,8 @@ struct ContentView: View {
|
||||||
orientation = newOrientation
|
orientation = newOrientation
|
||||||
}
|
}
|
||||||
.onChange(of: scenePhase) { newPhase in
|
.onChange(of: scenePhase) { newPhase in
|
||||||
|
CoreContext.shared.enteredForeground = newPhase == .active
|
||||||
orientation = UIDevice.current.orientation
|
orientation = UIDevice.current.orientation
|
||||||
if newPhase == .active {
|
|
||||||
conversationsListViewModel.computeChatRoomsList(filter: "")
|
|
||||||
accountProfileViewModel.setAvatarModel()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1434,8 +1340,7 @@ class NavigationManager: ObservableObject {
|
||||||
conversationViewModel: ConversationViewModel(),
|
conversationViewModel: ConversationViewModel(),
|
||||||
meetingsListViewModel: MeetingsListViewModel(),
|
meetingsListViewModel: MeetingsListViewModel(),
|
||||||
meetingViewModel: MeetingViewModel(),
|
meetingViewModel: MeetingViewModel(),
|
||||||
conversationForwardMessageViewModel: ConversationForwardMessageViewModel(),
|
conversationForwardMessageViewModel: ConversationForwardMessageViewModel()
|
||||||
accountProfileViewModel: AccountProfileViewModel()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// swiftlint:enable type_body_length
|
// swiftlint:enable type_body_length
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,7 @@ struct ChatBubbleView: View {
|
||||||
|
|
||||||
VStack(spacing: 2) {
|
VStack(spacing: 2) {
|
||||||
if !eventLogMessage.message.messageConferenceInfo!.meetingDescription.isEmpty {
|
if !eventLogMessage.message.messageConferenceInfo!.meetingDescription.isEmpty {
|
||||||
Text("meeting_schedule_description_title")
|
Text("Description")
|
||||||
.default_text_style(styleSize: 14)
|
.default_text_style(styleSize: 14)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
|
||||||
|
|
@ -374,8 +374,10 @@ struct ChatBubbleView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
conversationViewModel.selectedMessageToDisplayDetails = eventLogMessage
|
if !CoreContext.shared.enteredForeground {
|
||||||
conversationViewModel.prepareBottomSheetForDeliveryStatus()
|
conversationViewModel.selectedMessageToDisplayDetails = eventLogMessage
|
||||||
|
conversationViewModel.prepareBottomSheetForDeliveryStatus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.disabled(conversationViewModel.selectedMessage != nil)
|
.disabled(conversationViewModel.selectedMessage != nil)
|
||||||
.padding(.top, -4)
|
.padding(.top, -4)
|
||||||
|
|
|
||||||
|
|
@ -223,11 +223,6 @@ struct ConversationForwardMessageFragment: View {
|
||||||
}
|
}
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
.onAppear {
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
||||||
MagicSearchSingleton.shared.searchForSuggestions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||||
magicSearch.searchForContacts(
|
magicSearch.searchForContacts(
|
||||||
|
|
@ -281,51 +276,27 @@ struct ConversationForwardMessageFragment: View {
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
if index < contactsManager.lastSearchSuggestions.count
|
if index < contactsManager.lastSearchSuggestions.count
|
||||||
&& contactsManager.lastSearchSuggestions[index].address != nil {
|
&& contactsManager.lastSearchSuggestions[index].address != nil
|
||||||
if contactsManager.lastSearchSuggestions[index].address!.displayName != nil {
|
&& contactsManager.lastSearchSuggestions[index].address!.username != nil {
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: contactsManager.lastSearchSuggestions[index].address!.displayName!,
|
Image(uiImage: contactsManager.textToImage(
|
||||||
lastName: ""))
|
firstName: contactsManager.lastSearchSuggestions[index].address!.username!,
|
||||||
.resizable()
|
lastName: ""))
|
||||||
.frame(width: 45, height: 45)
|
.resizable()
|
||||||
.clipShape(Circle())
|
.frame(width: 45, height: 45)
|
||||||
|
.clipShape(Circle())
|
||||||
Text(contactsManager.lastSearchSuggestions[index].address?.displayName ?? "")
|
|
||||||
.default_text_style(styleSize: 16)
|
Text(contactsManager.lastSearchSuggestions[index].address?.username ?? "")
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.default_text_style(styleSize: 16)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
} else if contactsManager.lastSearchSuggestions[index].address!.username != nil {
|
.foregroundStyle(Color.orangeMain500)
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: contactsManager.lastSearchSuggestions[index].address!.username!,
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(contactsManager.lastSearchSuggestions[index].address!.username ?? "")
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
} else {
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Image("profil-picture-default")
|
Image("profil-picture-default")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 45, height: 45)
|
.frame(width: 45, height: 45)
|
||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
|
|
||||||
Text("username_error")
|
Text("Username error")
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.foregroundStyle(Color.orangeMain500)
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,9 @@ import UniformTypeIdentifiers
|
||||||
// swiftlint:disable type_body_length
|
// swiftlint:disable type_body_length
|
||||||
struct ConversationFragment: View {
|
struct ConversationFragment: View {
|
||||||
|
|
||||||
@Environment(\.scenePhase) var scenePhase
|
|
||||||
@State private var orientation = UIDevice.current.orientation
|
@State private var orientation = UIDevice.current.orientation
|
||||||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom }
|
||||||
|
|
||||||
@EnvironmentObject var navigationManager: NavigationManager
|
|
||||||
|
|
||||||
@ObservedObject var contactsManager = ContactsManager.shared
|
@ObservedObject var contactsManager = ContactsManager.shared
|
||||||
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
|
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
|
||||||
|
|
||||||
|
|
@ -39,7 +36,6 @@ struct ConversationFragment: View {
|
||||||
@ObservedObject var contactViewModel: ContactViewModel
|
@ObservedObject var contactViewModel: ContactViewModel
|
||||||
@ObservedObject var editContactViewModel: EditContactViewModel
|
@ObservedObject var editContactViewModel: EditContactViewModel
|
||||||
@ObservedObject var meetingViewModel: MeetingViewModel
|
@ObservedObject var meetingViewModel: MeetingViewModel
|
||||||
@ObservedObject var accountProfileViewModel: AccountProfileViewModel
|
|
||||||
|
|
||||||
@State var isMenuOpen = false
|
@State var isMenuOpen = false
|
||||||
@State private var isMuted: Bool = false
|
@State private var isMuted: Bool = false
|
||||||
|
|
@ -85,11 +81,13 @@ struct ConversationFragment: View {
|
||||||
.onRotate { newOrientation in
|
.onRotate { newOrientation in
|
||||||
orientation = newOrientation
|
orientation = newOrientation
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear() {
|
||||||
displayedChatroomPeerAddr = conversationViewModel.displayedConversation?.remoteSipUri
|
displayedChatroomPeerAddr = conversationViewModel.displayedConversation?.remoteSipUri
|
||||||
|
Log.info("debugtrace = onAppear: displayedChatroomPeerAddr = \(displayedChatroomPeerAddr)")
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
displayedChatroomPeerAddr = nil
|
displayedChatroomPeerAddr = nil
|
||||||
|
Log.info("debugtrace = onDisappear: displayedChatroomPeerAddr = nil")
|
||||||
conversationViewModel.removeConversationDelegate()
|
conversationViewModel.removeConversationDelegate()
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $conversationViewModel.isShowSelectedMessageToDisplayDetails, onDismiss: {
|
.sheet(isPresented: $conversationViewModel.isShowSelectedMessageToDisplayDetails, onDismiss: {
|
||||||
|
|
@ -129,11 +127,13 @@ struct ConversationFragment: View {
|
||||||
.onRotate { newOrientation in
|
.onRotate { newOrientation in
|
||||||
orientation = newOrientation
|
orientation = newOrientation
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear() {
|
||||||
displayedChatroomPeerAddr = conversationViewModel.displayedConversation?.remoteSipUri
|
displayedChatroomPeerAddr = conversationViewModel.displayedConversation?.remoteSipUri
|
||||||
|
Log.info("debugtrace = onAppear: displayedChatroomPeerAddr = \(displayedChatroomPeerAddr)")
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
displayedChatroomPeerAddr = nil
|
displayedChatroomPeerAddr = nil
|
||||||
|
Log.info("debugtrace = onDisappear: displayedChatroomPeerAddr = nil")
|
||||||
conversationViewModel.removeConversationDelegate()
|
conversationViewModel.removeConversationDelegate()
|
||||||
}
|
}
|
||||||
.halfSheet(showSheet: $conversationViewModel.isShowSelectedMessageToDisplayDetails) {
|
.halfSheet(showSheet: $conversationViewModel.isShowSelectedMessageToDisplayDetails) {
|
||||||
|
|
@ -165,13 +165,6 @@ struct ConversationFragment: View {
|
||||||
.background(Color.gray100.ignoresSafeArea(.keyboard))
|
.background(Color.gray100.ignoresSafeArea(.keyboard))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: scenePhase) { newPhase in
|
|
||||||
if newPhase == .active {
|
|
||||||
if conversationViewModel.displayedConversation != nil && (navigationManager.peerAddr == nil || navigationManager.peerAddr!.contains(conversationViewModel.displayedConversation!.remoteSipUri)) {
|
|
||||||
conversationViewModel.resetDisplayedChatRoom()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationViewStyle(.stack)
|
.navigationViewStyle(.stack)
|
||||||
}
|
}
|
||||||
|
|
@ -672,7 +665,7 @@ struct ConversationFragment: View {
|
||||||
.focused($isMessageTextFocused)
|
.focused($isMessageTextFocused)
|
||||||
.padding(.vertical, 5)
|
.padding(.vertical, 5)
|
||||||
.onChange(of: conversationViewModel.messageText) { text in
|
.onChange(of: conversationViewModel.messageText) { text in
|
||||||
if !text.isEmpty {
|
if !text.isEmpty && !CoreContext.shared.enteredForeground {
|
||||||
conversationViewModel.compose()
|
conversationViewModel.compose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -685,7 +678,7 @@ struct ConversationFragment: View {
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.focused($isMessageTextFocused)
|
.focused($isMessageTextFocused)
|
||||||
.onChange(of: conversationViewModel.messageText) { text in
|
.onChange(of: conversationViewModel.messageText) { text in
|
||||||
if !text.isEmpty {
|
if !text.isEmpty && !CoreContext.shared.enteredForeground {
|
||||||
conversationViewModel.compose()
|
conversationViewModel.compose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -718,7 +711,7 @@ struct ConversationFragment: View {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Button {
|
Button {
|
||||||
if conversationViewModel.displayedConversationHistorySize > 1 {
|
if conversationViewModel.displayedConversationHistorySize > 0 {
|
||||||
NotificationCenter.default.post(name: .onScrollToBottom, object: nil)
|
NotificationCenter.default.post(name: .onScrollToBottom, object: nil)
|
||||||
}
|
}
|
||||||
conversationViewModel.sendMessage()
|
conversationViewModel.sendMessage()
|
||||||
|
|
@ -1002,7 +995,6 @@ struct ConversationFragment: View {
|
||||||
contactViewModel: contactViewModel,
|
contactViewModel: contactViewModel,
|
||||||
editContactViewModel: editContactViewModel,
|
editContactViewModel: editContactViewModel,
|
||||||
meetingViewModel: meetingViewModel,
|
meetingViewModel: meetingViewModel,
|
||||||
accountProfileViewModel: accountProfileViewModel,
|
|
||||||
isMuted: $isMuted,
|
isMuted: $isMuted,
|
||||||
isShowEphemeralFragment: $isShowEphemeralFragment,
|
isShowEphemeralFragment: $isShowEphemeralFragment,
|
||||||
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
|
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
|
||||||
|
|
@ -1036,7 +1028,7 @@ struct ImdnOrReactionsSheet: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
Picker("picker_categories", selection: $selectedCategoryIndex) {
|
Picker("Categories", selection: $selectedCategoryIndex) {
|
||||||
ForEach(0..<conversationViewModel.sheetCategories.count, id: \.self) { index in
|
ForEach(0..<conversationViewModel.sheetCategories.count, id: \.self) { index in
|
||||||
Text(conversationViewModel.sheetCategories[index].name)
|
Text(conversationViewModel.sheetCategories[index].name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ struct ConversationInfoFragment: View {
|
||||||
@ObservedObject var contactViewModel: ContactViewModel
|
@ObservedObject var contactViewModel: ContactViewModel
|
||||||
@ObservedObject var editContactViewModel: EditContactViewModel
|
@ObservedObject var editContactViewModel: EditContactViewModel
|
||||||
@ObservedObject var meetingViewModel: MeetingViewModel
|
@ObservedObject var meetingViewModel: MeetingViewModel
|
||||||
@ObservedObject var accountProfileViewModel: AccountProfileViewModel
|
|
||||||
|
|
||||||
@State var addParticipantsViewModel = AddParticipantsViewModel()
|
@State var addParticipantsViewModel = AddParticipantsViewModel()
|
||||||
|
|
||||||
|
|
@ -47,7 +46,6 @@ struct ConversationInfoFragment: View {
|
||||||
@State private var participantListIsOpen = true
|
@State private var participantListIsOpen = true
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let accountModel = CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex ?? 0]
|
|
||||||
NavigationView {
|
NavigationView {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
if conversationViewModel.displayedConversation != nil {
|
if conversationViewModel.displayedConversation != nil {
|
||||||
|
|
@ -289,48 +287,14 @@ struct ConversationInfoFragment: View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
ForEach(conversationViewModel.participantConversationModel) { participantConversationModel in
|
ForEach(conversationViewModel.participantConversationModel) { participantConversationModel in
|
||||||
HStack {
|
HStack {
|
||||||
if conversationViewModel.myParticipantConversationModel != nil && conversationViewModel.myParticipantConversationModel!.address != participantConversationModel.address {
|
Avatar(contactAvatarModel: participantConversationModel, avatarSize: 50)
|
||||||
Avatar(contactAvatarModel: participantConversationModel, avatarSize: 50)
|
|
||||||
} else {
|
|
||||||
let avatarSize = 50.0
|
|
||||||
AsyncImage(url: CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex!].imagePathAvatar) { image in
|
|
||||||
switch image {
|
|
||||||
case .empty:
|
|
||||||
ProgressView()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
case .success(let image):
|
|
||||||
image
|
|
||||||
.resizable()
|
|
||||||
.aspectRatio(contentMode: .fill)
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
case .failure:
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: accountModel.avatarModel?.name ?? "",
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
@unknown default:
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
if conversationViewModel.myParticipantConversationModel != nil && conversationViewModel.myParticipantConversationModel!.address != participantConversationModel.address {
|
Text(participantConversationModel.name)
|
||||||
Text(participantConversationModel.name)
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
.default_text_style(styleSize: 14)
|
||||||
.default_text_style(styleSize: 14)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.lineLimit(1)
|
||||||
.lineLimit(1)
|
|
||||||
} else {
|
|
||||||
Text(accountModel.displayName.isEmpty ? participantConversationModel.name : accountModel.displayName)
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
let participantConversationModelIsAdmin = conversationViewModel.participantConversationModelAdmin.first(
|
let participantConversationModelIsAdmin = conversationViewModel.participantConversationModelAdmin.first(
|
||||||
where: {$0.address == participantConversationModel.address})
|
where: {$0.address == participantConversationModel.address})
|
||||||
|
|
@ -703,7 +667,6 @@ struct ConversationInfoFragment: View {
|
||||||
contactViewModel: ContactViewModel(),
|
contactViewModel: ContactViewModel(),
|
||||||
editContactViewModel: EditContactViewModel(),
|
editContactViewModel: EditContactViewModel(),
|
||||||
meetingViewModel: MeetingViewModel(),
|
meetingViewModel: MeetingViewModel(),
|
||||||
accountProfileViewModel: AccountProfileViewModel(),
|
|
||||||
addParticipantsViewModel: AddParticipantsViewModel(),
|
addParticipantsViewModel: AddParticipantsViewModel(),
|
||||||
isMuted: .constant(false),
|
isMuted: .constant(false),
|
||||||
isShowEphemeralFragment: .constant(false),
|
isShowEphemeralFragment: .constant(false),
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ struct ConversationsListBottomSheet: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("dialog_close") {
|
Button("Close") {
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
showingSheet.toggle()
|
showingSheet.toggle()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -80,7 +80,7 @@ struct ConversationsListBottomSheet: View {
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
Text("conversation_action_mark_as_read")
|
Text("Marquer comme non lu")
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +118,7 @@ struct ConversationsListBottomSheet: View {
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
Text(conversationsListViewModel.selectedConversation!.isMuted ? "conversation_action_unmute" : "conversation_action_mute")
|
Text(conversationsListViewModel.selectedConversation!.isMuted ? "Réactiver les notifications" : "Mettre en sourdine")
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ import linphonesw
|
||||||
|
|
||||||
struct ConversationsListFragment: View {
|
struct ConversationsListFragment: View {
|
||||||
|
|
||||||
@Environment(\.scenePhase) var scenePhase
|
|
||||||
|
|
||||||
@EnvironmentObject var navigationManager: NavigationManager
|
@EnvironmentObject var navigationManager: NavigationManager
|
||||||
|
|
||||||
@ObservedObject var conversationViewModel: ConversationViewModel
|
@ObservedObject var conversationViewModel: ConversationViewModel
|
||||||
|
|
@ -71,14 +69,6 @@ struct ConversationsListFragment: View {
|
||||||
}
|
}
|
||||||
.navigationTitle("")
|
.navigationTitle("")
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
.onChange(of: scenePhase) { newPhase in
|
|
||||||
if newPhase == .active {
|
|
||||||
if navigationManager.peerAddr != nil {
|
|
||||||
conversationViewModel.getChatRoomWithStringAddress(conversationsList: conversationsListViewModel.conversationsList, stringAddr: navigationManager.peerAddr!)
|
|
||||||
navigationManager.peerAddr = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,6 +83,8 @@ struct ConversationRow: View {
|
||||||
@Binding var text: String
|
@Binding var text: String
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
let pub = NotificationCenter.default
|
||||||
|
.publisher(for: NSNotification.Name("ChatRoomsComputed"))
|
||||||
HStack {
|
HStack {
|
||||||
Avatar(contactAvatarModel: conversation.avatarModel, avatarSize: 50)
|
Avatar(contactAvatarModel: conversation.avatarModel, avatarSize: 50)
|
||||||
|
|
||||||
|
|
@ -195,6 +187,22 @@ struct ConversationRow: View {
|
||||||
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
|
.listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20))
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
.background(.white)
|
.background(.white)
|
||||||
|
.onReceive(pub) { _ in
|
||||||
|
if CoreContext.shared.enteredForeground && conversationViewModel.displayedConversation != nil
|
||||||
|
&& (navigationManager.peerAddr == nil || navigationManager.peerAddr == conversationViewModel.displayedConversation!.remoteSipUri) {
|
||||||
|
if conversationViewModel.displayedConversation != nil {
|
||||||
|
conversationViewModel.resetDisplayedChatRoom(conversationsList: conversationsListViewModel.conversationsList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreContext.shared.enteredForeground = false
|
||||||
|
|
||||||
|
if navigationManager.peerAddr != nil
|
||||||
|
&& conversation.remoteSipUri.contains(navigationManager.peerAddr!) {
|
||||||
|
conversationViewModel.getChatRoomWithStringAddress(conversationsList: conversationsListViewModel.conversationsList, stringAddr: navigationManager.peerAddr!)
|
||||||
|
navigationManager.peerAddr = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
conversationViewModel.changeDisplayedChatRoom(conversationModel: conversation)
|
conversationViewModel.changeDisplayedChatRoom(conversationModel: conversation)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ struct StartConversationFragment: View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
if !ContactsManager.shared.lastSearch.isEmpty {
|
if !ContactsManager.shared.lastSearch.isEmpty {
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
Text("contacts_list_all_contacts_title")
|
Text("All contacts")
|
||||||
.default_text_style_800(styleSize: 16)
|
.default_text_style_800(styleSize: 16)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
@ -187,7 +187,9 @@ struct StartConversationFragment: View {
|
||||||
|
|
||||||
ContactsListFragment(contactViewModel: ContactViewModel(), contactsListViewModel: ContactsListViewModel(), showingSheet: .constant(false)
|
ContactsListFragment(contactViewModel: ContactViewModel(), contactsListViewModel: ContactsListViewModel(), showingSheet: .constant(false)
|
||||||
, startCallFunc: { addr in
|
, startCallFunc: { addr in
|
||||||
|
withAnimation {
|
||||||
startConversationViewModel.createOneToOneChatRoomWith(remote: addr)
|
startConversationViewModel.createOneToOneChatRoomWith(remote: addr)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
|
|
@ -264,57 +266,35 @@ struct StartConversationFragment: View {
|
||||||
var suggestionsList: some View {
|
var suggestionsList: some View {
|
||||||
ForEach(0..<contactsManager.lastSearchSuggestions.count, id: \.self) { index in
|
ForEach(0..<contactsManager.lastSearchSuggestions.count, id: \.self) { index in
|
||||||
Button {
|
Button {
|
||||||
if let address = contactsManager.lastSearchSuggestions[index].address {
|
withAnimation {
|
||||||
startConversationViewModel.createOneToOneChatRoomWith(remote: address)
|
if contactsManager.lastSearchSuggestions[index].address != nil {
|
||||||
|
startConversationViewModel.createOneToOneChatRoomWith(remote: contactsManager.lastSearchSuggestions[index].address!)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
if index < contactsManager.lastSearchSuggestions.count
|
if index < contactsManager.lastSearchSuggestions.count
|
||||||
&& contactsManager.lastSearchSuggestions[index].address != nil {
|
&& contactsManager.lastSearchSuggestions[index].address != nil
|
||||||
if contactsManager.lastSearchSuggestions[index].address!.displayName != nil {
|
&& contactsManager.lastSearchSuggestions[index].address!.username != nil {
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: contactsManager.lastSearchSuggestions[index].address!.displayName!,
|
Image(uiImage: contactsManager.textToImage(
|
||||||
lastName: ""))
|
firstName: contactsManager.lastSearchSuggestions[index].address!.username!,
|
||||||
.resizable()
|
lastName: ""))
|
||||||
.frame(width: 45, height: 45)
|
.resizable()
|
||||||
.clipShape(Circle())
|
.frame(width: 45, height: 45)
|
||||||
|
.clipShape(Circle())
|
||||||
Text(contactsManager.lastSearchSuggestions[index].address?.displayName ?? "")
|
|
||||||
.default_text_style(styleSize: 16)
|
Text(contactsManager.lastSearchSuggestions[index].address?.username ?? "")
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.default_text_style(styleSize: 16)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
} else if contactsManager.lastSearchSuggestions[index].address!.username != nil {
|
.foregroundStyle(Color.orangeMain500)
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: contactsManager.lastSearchSuggestions[index].address!.username!,
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(contactsManager.lastSearchSuggestions[index].address!.username ?? "")
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
} else {
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Image("profil-picture-default")
|
Image("profil-picture-default")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 45, height: 45)
|
.frame(width: 45, height: 45)
|
||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
|
|
||||||
Text("username_error")
|
Text("Username error")
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.foregroundStyle(Color.orangeMain500)
|
||||||
|
|
@ -390,9 +370,6 @@ struct StartConversationFragment: View {
|
||||||
.shadow(color: Color.orangeMain500, radius: 0, x: 0, y: 2)
|
.shadow(color: Color.orangeMain500, radius: 0, x: 0, y: 2)
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
||||||
.position(x: geometry.size.width / 2, y: geometry.size.height / 2)
|
.position(x: geometry.size.width / 2, y: geometry.size.height / 2)
|
||||||
.onDisappear {
|
|
||||||
startConversationViewModel.messageText = ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,7 @@ struct UIList: UIViewRepresentable {
|
||||||
|
|
||||||
if isScrolledToBottom && conversationViewModel.displayedConversationUnreadMessagesCount > 0 {
|
if isScrolledToBottom && conversationViewModel.displayedConversationUnreadMessagesCount > 0 {
|
||||||
conversationViewModel.markAsRead()
|
conversationViewModel.markAsRead()
|
||||||
|
conversationsListViewModel.computeChatRoomsList(filter: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -362,16 +363,15 @@ struct UIList: UIViewRepresentable {
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(forName: .onScrollToBottom, object: nil, queue: nil) { _ in
|
NotificationCenter.default.addObserver(forName: .onScrollToBottom, object: nil, queue: nil) { _ in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
guard !self.sections.isEmpty,
|
if !self.sections.isEmpty {
|
||||||
let firstSection = self.sections.first,
|
if self.sections.first != nil
|
||||||
let firstConversationSection = parent.conversationViewModel.conversationMessagesSection.first,
|
&& parent.conversationViewModel.conversationMessagesSection.first != nil
|
||||||
let displayedConversation = parent.conversationViewModel.displayedConversation,
|
&& parent.conversationViewModel.displayedConversation != nil
|
||||||
let tableView = self.tableView,
|
&& self.sections.first!.chatRoomID == parent.conversationViewModel.displayedConversation!.id
|
||||||
firstSection.chatRoomID == displayedConversation.id,
|
&& self.sections.first!.rows.count == parent.conversationViewModel.conversationMessagesSection.first!.rows.count {
|
||||||
firstSection.rows.count == firstConversationSection.rows.count else {
|
self.tableView!.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
|
||||||
return
|
}
|
||||||
}
|
}
|
||||||
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -467,6 +467,7 @@ struct UIList: UIViewRepresentable {
|
||||||
|
|
||||||
if self.parent.isScrolledToBottom && self.parent.conversationViewModel.displayedConversationUnreadMessagesCount > 0 {
|
if self.parent.isScrolledToBottom && self.parent.conversationViewModel.displayedConversationUnreadMessagesCount > 0 {
|
||||||
self.parent.conversationViewModel.markAsRead()
|
self.parent.conversationViewModel.markAsRead()
|
||||||
|
self.parent.conversationsListViewModel.computeChatRoomsList(filter: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
private var coreContext = CoreContext.shared
|
private var coreContext = CoreContext.shared
|
||||||
private var contactsManager = ContactsManager.shared
|
private var contactsManager = ContactsManager.shared
|
||||||
|
|
||||||
var chatRoom: ChatRoom
|
let chatRoom: ChatRoom
|
||||||
let isDisabledBecauseNotSecured: Bool = false
|
let isDisabledBecauseNotSecured: Bool = false
|
||||||
|
|
||||||
static let TAG = "[Conversation Model]"
|
static let TAG = "[Conversation Model]"
|
||||||
|
|
@ -50,7 +50,8 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
@Published var unreadMessagesCount: Int
|
@Published var unreadMessagesCount: Int
|
||||||
@Published var avatarModel: ContactAvatarModel
|
@Published var avatarModel: ContactAvatarModel
|
||||||
|
|
||||||
private var conferenceDelegate: ConferenceDelegate?
|
private var conferenceScheduler: ConferenceScheduler?
|
||||||
|
private var conferenceSchedulerDelegate: ConferenceSchedulerDelegate?
|
||||||
|
|
||||||
init(chatRoom: ChatRoom) {
|
init(chatRoom: ChatRoom) {
|
||||||
self.chatRoom = chatRoom
|
self.chatRoom = chatRoom
|
||||||
|
|
@ -133,28 +134,35 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
var participantsList: [Address] = []
|
let conferenceInfo = try Factory.Instance.createConferenceInfo()
|
||||||
|
conferenceInfo.organizer = account!.params?.identityAddress
|
||||||
|
conferenceInfo.subject = self.chatRoom.subject ?? "Conference"
|
||||||
|
|
||||||
|
var participantsList: [ParticipantInfo] = []
|
||||||
self.chatRoom.participants.forEach { participant in
|
self.chatRoom.participants.forEach { participant in
|
||||||
participantsList.append(participant.address!)
|
do {
|
||||||
|
let info = try Factory.Instance.createParticipantInfo(address: participant.address!)
|
||||||
|
// For meetings, all participants must have Speaker role
|
||||||
|
info.role = Participant.Role.Speaker
|
||||||
|
participantsList.append(info)
|
||||||
|
} catch let error {
|
||||||
|
Log.error(
|
||||||
|
"\(ConversationModel.TAG) Can't create ParticipantInfo: \(error)"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conferenceInfo.addParticipantInfos(participantInfos: participantsList)
|
||||||
|
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(ConversationModel.TAG) Creating group call with subject \(self.chatRoom.subject ?? "Conference") and \(participantsList.count) participant(s)"
|
"\(ConversationModel.TAG) Creating group call with subject \(self.chatRoom.subject ?? "Conference") and \(participantsList.count) participant(s)"
|
||||||
)
|
)
|
||||||
if let conference = LinphoneUtils.createGroupCall(core: core, account: account, subject: self.chatRoom.subject ?? "Conference") {
|
|
||||||
let callParams = try? core.createCallParams(call: nil)
|
self.conferenceScheduler = try core.createConferenceScheduler(account: account)
|
||||||
if let callParams = callParams {
|
if self.conferenceScheduler != nil {
|
||||||
callParams.videoEnabled = true
|
self.conferenceAddDelegate(core: core, conferenceScheduler: self.conferenceScheduler!)
|
||||||
callParams.videoDirection = .RecvOnly
|
// Will trigger the conference creation/update automatically
|
||||||
|
self.conferenceScheduler!.info = conferenceInfo
|
||||||
Log.info("\(ConversationModel.TAG) Inviting \(participantsList.count) participant(s) into newly created conference")
|
|
||||||
|
|
||||||
try conference.inviteParticipants(addresses: participantsList, params: callParams)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
TelecomManager.shared.participantsInvited = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch let error {
|
||||||
Log.error(
|
Log.error(
|
||||||
|
|
@ -164,132 +172,145 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func conferenceAddDelegate(core: Core, conference: Conference) {
|
func conferenceAddDelegate(core: Core, conferenceScheduler: ConferenceScheduler) {
|
||||||
self.conferenceDelegate = ConferenceDelegateStub(onStateChanged: { (conference: Conference, state: Conference.State) in
|
self.conferenceSchedulerDelegate = ConferenceSchedulerDelegateStub(onStateChanged: { (conferenceScheduler: ConferenceScheduler, state: ConferenceScheduler.State) in
|
||||||
Log.info("\(ConversationModel.TAG) Conference state is \(state)")
|
Log.info("\(ConversationModel.TAG) Conference scheduler state is \(state)")
|
||||||
if state == .Created {
|
if state == ConferenceScheduler.State.Ready {
|
||||||
NotificationCenter.default.post(name: Notification.Name("CallViewModelReset"), object: self)
|
conferenceScheduler.removeDelegate(delegate: self.conferenceSchedulerDelegate!)
|
||||||
} else if state == .CreationFailed {
|
self.conferenceSchedulerDelegate = nil
|
||||||
Log.error("\(ConversationModel.TAG) Failed to create group call!")
|
|
||||||
DispatchQueue.main.async {
|
let conferenceAddress = conferenceScheduler.info?.uri
|
||||||
|
if conferenceAddress != nil {
|
||||||
|
Log.info(
|
||||||
|
"\(ConversationModel.TAG) Conference info created, address is \(conferenceAddress!.asStringUriOnly())"
|
||||||
|
)
|
||||||
|
|
||||||
|
TelecomManager.shared.doCallWithCore(addr: conferenceAddress!, isVideo: true, isConference: true)
|
||||||
|
} else {
|
||||||
|
Log.error("\(ConversationModel.TAG) Conference info URI is null!")
|
||||||
|
|
||||||
ToastViewModel.shared.toastMessage = "Failed_to_create_group_call_error"
|
ToastViewModel.shared.toastMessage = "Failed_to_create_group_call_error"
|
||||||
ToastViewModel.shared.displayToast = true
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
|
} else if state == ConferenceScheduler.State.Error {
|
||||||
|
conferenceScheduler.removeDelegate(delegate: self.conferenceSchedulerDelegate!)
|
||||||
|
self.conferenceSchedulerDelegate = nil
|
||||||
|
Log.error("\(ConversationModel.TAG) Failed to create group call!")
|
||||||
|
|
||||||
|
ToastViewModel.shared.toastMessage = "Failed_to_create_group_call_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
conferenceScheduler.addDelegate(delegate: self.conferenceSchedulerDelegate!)
|
||||||
if self.conferenceDelegate != nil {
|
|
||||||
conference.addDelegate(delegate: self.conferenceDelegate!)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContentTextMessage() {
|
func getContentTextMessage() {
|
||||||
let lastMessage = self.chatRoom.lastMessageInHistory
|
coreContext.doOnCoreQueue { _ in
|
||||||
if lastMessage != nil {
|
let lastMessage = self.chatRoom.lastMessageInHistory
|
||||||
var fromAddressFriend = lastMessage!.fromAddress != nil
|
if lastMessage != nil {
|
||||||
? self.contactsManager.getFriendWithAddress(address: lastMessage!.fromAddress)?.name ?? nil
|
var fromAddressFriend = lastMessage!.fromAddress != nil
|
||||||
: nil
|
? self.contactsManager.getFriendWithAddress(address: lastMessage!.fromAddress)?.name ?? nil
|
||||||
|
: nil
|
||||||
if !lastMessage!.isOutgoing && lastMessage!.chatRoom != nil && !lastMessage!.chatRoom!.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) {
|
|
||||||
if fromAddressFriend == nil {
|
if !lastMessage!.isOutgoing && lastMessage!.chatRoom != nil && !lastMessage!.chatRoom!.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) {
|
||||||
if lastMessage!.fromAddress!.displayName != nil {
|
if fromAddressFriend == nil {
|
||||||
fromAddressFriend = lastMessage!.fromAddress!.displayName! + ": "
|
if lastMessage!.fromAddress!.displayName != nil {
|
||||||
} else if lastMessage!.fromAddress!.username != nil {
|
fromAddressFriend = lastMessage!.fromAddress!.displayName! + ": "
|
||||||
fromAddressFriend = lastMessage!.fromAddress!.username! + ": "
|
} else if lastMessage!.fromAddress!.username != nil {
|
||||||
|
fromAddressFriend = lastMessage!.fromAddress!.username! + ": "
|
||||||
|
} else {
|
||||||
|
fromAddressFriend = ""
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fromAddressFriend = String(lastMessage!.fromAddress!.asStringUriOnly().dropFirst(4)) + ": "
|
fromAddressFriend! += ": "
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fromAddressFriend! += ": "
|
fromAddressFriend = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
let lastMessageTextTmp = (fromAddressFriend ?? "")
|
||||||
fromAddressFriend = nil
|
+ (lastMessage!.contents.first(where: {$0.isText == true})?.utf8Text ?? (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name ?? ""))
|
||||||
|
|
||||||
|
let lastMessageIsOutgoingTmp = lastMessage?.isOutgoing ?? false
|
||||||
|
|
||||||
|
let lastMessageStateTmp = lastMessage?.state.rawValue ?? 0
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.lastMessageText = lastMessageTextTmp
|
||||||
|
|
||||||
|
self.lastMessageIsOutgoing = lastMessageIsOutgoingTmp
|
||||||
|
|
||||||
|
self.lastMessageState = lastMessageStateTmp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastMessageTextTmp = (fromAddressFriend ?? "")
|
|
||||||
+ (lastMessage!.contents.first(where: {$0.isText == true})?.utf8Text ?? (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name ?? ""))
|
|
||||||
|
|
||||||
let lastMessageIsOutgoingTmp = lastMessage?.isOutgoing ?? false
|
|
||||||
|
|
||||||
let lastMessageStateTmp = lastMessage?.state.rawValue ?? 0
|
|
||||||
|
|
||||||
// DispatchQueue.main.async {
|
|
||||||
self.lastMessageText = lastMessageTextTmp
|
|
||||||
|
|
||||||
self.lastMessageIsOutgoing = lastMessageIsOutgoingTmp
|
|
||||||
|
|
||||||
self.lastMessageState = lastMessageStateTmp
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChatRoomSubject() {
|
func getChatRoomSubject() {
|
||||||
let addressFriend = (self.chatRoom.participants.first != nil && self.chatRoom.participants.first!.address != nil)
|
coreContext.doOnCoreQueue { _ in
|
||||||
? self.contactsManager.getFriendWithAddress(address: self.chatRoom.participants.first?.address)
|
let addressFriend = (self.chatRoom.participants.first != nil && self.chatRoom.participants.first!.address != nil)
|
||||||
: nil
|
? self.contactsManager.getFriendWithAddress(address: self.chatRoom.participants.first?.address)
|
||||||
|
: nil
|
||||||
var subjectTmp = ""
|
|
||||||
|
var subjectTmp = ""
|
||||||
if self.isGroup {
|
|
||||||
subjectTmp = self.chatRoom.subject!
|
if self.isGroup {
|
||||||
} else if addressFriend != nil {
|
subjectTmp = self.chatRoom.subject!
|
||||||
subjectTmp = addressFriend!.name!
|
} else if addressFriend != nil {
|
||||||
} else {
|
subjectTmp = addressFriend!.name!
|
||||||
if self.chatRoom.participants.first != nil
|
|
||||||
&& self.chatRoom.participants.first!.address != nil {
|
|
||||||
|
|
||||||
subjectTmp = self.chatRoom.participants.first!.address!.displayName != nil
|
|
||||||
? self.chatRoom.participants.first!.address!.displayName!
|
|
||||||
: (self.chatRoom.participants.first!.address!.username ?? String(self.chatRoom.participants.first!.address!.asStringUriOnly().dropFirst(4)))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let addressTmp = addressFriend?.address?.asStringUriOnly() ?? ""
|
|
||||||
|
|
||||||
let avatarModelTmp: ContactAvatarModel
|
|
||||||
if let addressFriend = addressFriend, !self.isGroup {
|
|
||||||
if let existingAvatarModel = ContactsManager.shared.avatarListModel.first(where: {
|
|
||||||
$0.friend?.name == addressFriend.name &&
|
|
||||||
$0.friend?.address?.asStringUriOnly() == addressFriend.address?.asStringUriOnly()
|
|
||||||
}) {
|
|
||||||
avatarModelTmp = existingAvatarModel
|
|
||||||
} else {
|
} else {
|
||||||
avatarModelTmp = ContactAvatarModel(
|
if self.chatRoom.participants.first != nil
|
||||||
friend: nil,
|
&& self.chatRoom.participants.first!.address != nil {
|
||||||
name: subjectTmp,
|
|
||||||
address: addressTmp,
|
subjectTmp = self.chatRoom.participants.first!.address!.displayName != nil
|
||||||
withPresence: false
|
? self.chatRoom.participants.first!.address!.displayName!
|
||||||
)
|
: self.chatRoom.participants.first!.address!.username!
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
avatarModelTmp = ContactAvatarModel(
|
let addressTmp = addressFriend?.address?.asStringUriOnly() ?? ""
|
||||||
|
|
||||||
|
let avatarModelTmp = addressFriend != nil && !self.isGroup
|
||||||
|
? ContactsManager.shared.avatarListModel.first(where: {
|
||||||
|
$0.friend!.name == addressFriend!.name
|
||||||
|
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
|
||||||
|
})
|
||||||
|
?? ContactAvatarModel(
|
||||||
|
friend: nil,
|
||||||
|
name: subjectTmp,
|
||||||
|
address: addressTmp,
|
||||||
|
withPresence: false
|
||||||
|
)
|
||||||
|
: ContactAvatarModel(
|
||||||
friend: nil,
|
friend: nil,
|
||||||
name: subjectTmp,
|
name: subjectTmp,
|
||||||
address: self.chatRoom.peerAddress?.asStringUriOnly() ?? addressTmp,
|
address: self.chatRoom.peerAddress?.asStringUriOnly() ?? addressTmp,
|
||||||
withPresence: false
|
withPresence: false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var participantsAddressTmp: [String] = []
|
||||||
|
|
||||||
|
self.chatRoom.participants.forEach { participant in
|
||||||
|
participantsAddressTmp.append(participant.address?.asStringUriOnly() ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.subject = subjectTmp
|
||||||
|
self.avatarModel = avatarModelTmp
|
||||||
|
self.participantsAddress = participantsAddressTmp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var participantsAddressTmp: [String] = []
|
|
||||||
|
|
||||||
self.chatRoom.participants.forEach { participant in
|
|
||||||
participantsAddressTmp.append(participant.address?.asStringUriOnly() ?? "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// DispatchQueue.main.async {
|
|
||||||
self.subject = subjectTmp
|
|
||||||
self.avatarModel = avatarModelTmp
|
|
||||||
self.participantsAddress = participantsAddressTmp
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUnreadMessagesCount() {
|
func getUnreadMessagesCount() {
|
||||||
let unreadMessagesCountTmp = self.chatRoom.unreadMessagesCount
|
coreContext.doOnCoreQueue { _ in
|
||||||
// DispatchQueue.main.async {
|
let unreadMessagesCountTmp = self.chatRoom.unreadMessagesCount
|
||||||
self.unreadMessagesCount = unreadMessagesCountTmp
|
DispatchQueue.main.async {
|
||||||
// }
|
self.unreadMessagesCount = unreadMessagesCountTmp
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshAvatarModel() {
|
func refreshAvatarModel() {
|
||||||
|
|
|
||||||
|
|
@ -45,13 +45,7 @@ class EventModel: ObservableObject {
|
||||||
if let addressFriend = friendResult {
|
if let addressFriend = friendResult {
|
||||||
name = addressFriend.name!
|
name = addressFriend.name!
|
||||||
} else {
|
} else {
|
||||||
if address!.displayName != nil {
|
name = address!.displayName != nil ? address!.displayName! : address!.username!
|
||||||
name = address!.displayName!
|
|
||||||
} else if address!.username != nil {
|
|
||||||
name = address!.username!
|
|
||||||
} else {
|
|
||||||
name = String(address!.asStringUriOnly().dropFirst(4))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let textValue: String
|
let textValue: String
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
// swiftlint:disable line_length
|
// swiftlint:disable line_length
|
||||||
class ConversationForwardMessageViewModel: ObservableObject {
|
class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
|
|
@ -85,7 +84,7 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
let account = core.defaultAccount
|
let account = core.defaultAccount
|
||||||
if account == nil {
|
if account == nil {
|
||||||
Log.error(
|
Log.error(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) No default account found, can't create conversation with \(remote.asStringUriOnly())"
|
"\(StartConversationViewModel.TAG) No default account found, can't create conversation with \(remote.asStringUriOnly())!"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -95,42 +94,37 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let params = try core.createConferenceParams(conference: nil)
|
let params: ChatRoomParams = try core.createDefaultChatRoomParams()
|
||||||
params.chatEnabled = true
|
|
||||||
params.groupEnabled = false
|
params.groupEnabled = false
|
||||||
params.subject = NSLocalizedString("conversation_one_to_one_hidden_subject", comment: "")
|
params.subject = "Dummy subject"
|
||||||
params.account = account
|
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
|
||||||
|
|
||||||
guard let chatParams = params.chatParams else { return }
|
let sameDomain = remote.domain == account?.params?.domain ?? ""
|
||||||
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
|
if StartConversationViewModel.isEndToEndEncryptionMandatory() && sameDomain {
|
||||||
|
Log.info("\(StartConversationViewModel.TAG) Account is in secure mode & domain matches, creating a E2E conversation")
|
||||||
let sameDomain = remote.domain == CorePreferences.defaultDomain && remote.domain == account!.params?.domain
|
params.backend = ChatRoom.Backend.FlexisipChat
|
||||||
if account!.params != nil && (account!.params!.instantMessagingEncryptionMandatory && sameDomain) {
|
params.encryptionEnabled = true
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) Account is in secure mode & domain matches, creating an E2E encrypted conversation")
|
} else if !StartConversationViewModel.isEndToEndEncryptionMandatory() {
|
||||||
chatParams.backend = ChatRoom.Backend.FlexisipChat
|
|
||||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
|
||||||
} else if account!.params != nil && (!account!.params!.instantMessagingEncryptionMandatory) {
|
|
||||||
if LinphoneUtils.isEndToEndEncryptedChatAvailable(core: core) {
|
if LinphoneUtils.isEndToEndEncryptedChatAvailable(core: core) {
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) Account is in interop mode but LIME is available, creating an E2E encrypted conversation"
|
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME is available, creating a E2E conversation"
|
||||||
)
|
)
|
||||||
chatParams.backend = ChatRoom.Backend.FlexisipChat
|
params.backend = ChatRoom.Backend.FlexisipChat
|
||||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
params.encryptionEnabled = true
|
||||||
} else {
|
} else {
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
|
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
|
||||||
)
|
)
|
||||||
chatParams.backend = ChatRoom.Backend.Basic
|
params.backend = ChatRoom.Backend.Basic
|
||||||
params.securityLevel = Conference.SecurityLevel.None
|
params.encryptionEnabled = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.error(
|
Log.error(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) Account is in secure mode, can't chat with SIP address of different domain \(remote.asStringUriOnly())"
|
"\(StartConversationViewModel.TAG) Account is in secure mode, can't chat with SIP address of different domain \(remote.asStringUriOnly())"
|
||||||
)
|
)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_invalid_participant_error"
|
||||||
ToastViewModel.shared.displayToast = true
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -141,56 +135,85 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
let existingChatRoom = core.searchChatRoom(params: params, localAddr: localAddress, remoteAddr: nil, participants: participants)
|
let existingChatRoom = core.searchChatRoom(params: params, localAddr: localAddress, remoteAddr: nil, participants: participants)
|
||||||
if existingChatRoom == nil {
|
if existingChatRoom == nil {
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) No existing 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) was found for given parameters, let's create it"
|
"\(StartConversationViewModel.TAG) No existing 1-1 conversation between local account "
|
||||||
|
+ "\(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) was found for given parameters, let's create it"
|
||||||
)
|
)
|
||||||
|
let chatRoom = try core.createChatRoom(params: params, localAddr: localAddress, participants: participants)
|
||||||
do {
|
if params.backend == ChatRoom.Backend.FlexisipChat {
|
||||||
let chatRoom = try core.createChatRoom(params: params, participants: participants)
|
if chatRoom.state == ChatRoom.State.Created {
|
||||||
if chatParams.backend == ChatRoom.Backend.FlexisipChat {
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
let state = chatRoom.state
|
Log.info("\(StartConversationViewModel.TAG) 1-1 conversation \(id) has been created")
|
||||||
if state == ChatRoom.State.Created {
|
|
||||||
let chatRoomId = LinphoneUtils.getConversationId(chatRoom: chatRoom)
|
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) 1-1 conversation \(chatRoomId) has been created")
|
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.displayedConversation = model
|
|
||||||
self.operationInProgress = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) Conversation isn't in Created state yet (state is \(state)), wait for it")
|
|
||||||
self.chatRoomAddDelegate(core: core, chatRoom: chatRoom)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let chatRoomId = LinphoneUtils.getConversationId(chatRoom: chatRoom)
|
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) Conversation successfully created \(chatRoomId)")
|
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
DispatchQueue.main.async {
|
if self.operationInProgress == false {
|
||||||
self.displayedConversation = model
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.info("\(StartConversationViewModel.TAG) Conversation isn't in Created state yet, wait for it")
|
||||||
|
self.chatRoomAddDelegate(core: core, chatRoom: chatRoom)
|
||||||
}
|
}
|
||||||
} catch {
|
} else {
|
||||||
Log.error("\(ConversationForwardMessageViewModel.TAG) Failed to create 1-1 conversation with \(remote.asStringUriOnly())")
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
|
Log.info("\(StartConversationViewModel.TAG) Conversation successfully created \(id)")
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
self.operationInProgress = false
|
if self.operationInProgress == false {
|
||||||
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.displayToast = true
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
"\(ConversationForwardMessageViewModel.TAG) A 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) for given parameters already exists!"
|
"\(StartConversationViewModel.TAG) A 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) for given parameters already exists!"
|
||||||
)
|
)
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: existingChatRoom!)
|
let model = ConversationModel(chatRoom: existingChatRoom!)
|
||||||
DispatchQueue.main.async {
|
if self.operationInProgress == false {
|
||||||
self.displayedConversation = model
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
|
}
|
||||||
|
Log.error("\(StartConversationViewModel.TAG) Failed to create 1-1 conversation with \(remote.asStringUriOnly())!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +223,7 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
let state = chatRoom.state
|
let state = chatRoom.state
|
||||||
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
if state == ChatRoom.State.CreationFailed {
|
if state == ChatRoom.State.CreationFailed {
|
||||||
Log.error("\(ConversationForwardMessageViewModel.TAG) Conversation \(id) creation has failed!")
|
Log.error("\(StartConversationViewModel.TAG) Conversation \(id) creation has failed!")
|
||||||
if let chatRoomDelegate = self.chatRoomDelegate {
|
if let chatRoomDelegate = self.chatRoomDelegate {
|
||||||
chatRoom.removeDelegate(delegate: chatRoomDelegate)
|
chatRoom.removeDelegate(delegate: chatRoomDelegate)
|
||||||
}
|
}
|
||||||
|
|
@ -214,9 +237,9 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
||||||
let state = chatRoom.state
|
let state = chatRoom.state
|
||||||
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) Conversation \(id) \(chatRoom.subject ?? "") state changed: \(state)")
|
Log.info("\(StartConversationViewModel.TAG) Conversation \(id) \(chatRoom.subject ?? "") state changed: \(state)")
|
||||||
if state == ChatRoom.State.Created {
|
if state == ChatRoom.State.Created {
|
||||||
Log.info("\(ConversationForwardMessageViewModel.TAG) Conversation \(id) successfully created")
|
Log.info("\(StartConversationViewModel.TAG) Conversation \(id) successfully created")
|
||||||
if let chatRoomDelegate = self.chatRoomDelegate {
|
if let chatRoomDelegate = self.chatRoomDelegate {
|
||||||
chatRoom.removeDelegate(delegate: chatRoomDelegate)
|
chatRoom.removeDelegate(delegate: chatRoomDelegate)
|
||||||
}
|
}
|
||||||
|
|
@ -239,7 +262,7 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if state == ChatRoom.State.CreationFailed {
|
} else if state == ChatRoom.State.CreationFailed {
|
||||||
Log.error("\(ConversationForwardMessageViewModel.TAG) Conversation \(id) creation has failed!")
|
Log.error("\(StartConversationViewModel.TAG) Conversation \(id) creation has failed!")
|
||||||
if let chatRoomDelegate = self.chatRoomDelegate {
|
if let chatRoomDelegate = self.chatRoomDelegate {
|
||||||
chatRoom.removeDelegate(delegate: chatRoomDelegate)
|
chatRoom.removeDelegate(delegate: chatRoomDelegate)
|
||||||
}
|
}
|
||||||
|
|
@ -257,7 +280,8 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
func forwardMessage() {
|
func forwardMessage() {
|
||||||
CoreContext.shared.doOnCoreQueue { _ in
|
CoreContext.shared.doOnCoreQueue { _ in
|
||||||
if self.displayedConversation != nil && self.selectedMessage != nil {
|
if self.displayedConversation != nil && self.selectedMessage != nil {
|
||||||
if let messageToForward = self.selectedMessage!.eventModel.eventLog.chatMessage {
|
let chatMessageToDisplay = self.displayedConversation!.chatRoom.findEventLog(messageId: self.selectedMessage!.eventModel.eventLogId)?.chatMessage
|
||||||
|
if let messageToForward = chatMessageToDisplay {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
do {
|
do {
|
||||||
let forwardedMessage = try self.displayedConversation!.chatRoom.createForwardMessage(message: messageToForward)
|
let forwardedMessage = try self.displayedConversation!.chatRoom.createForwardMessage(message: messageToForward)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
// swiftlint:disable line_length
|
// swiftlint:disable line_length
|
||||||
class ConversationsListViewModel: ObservableObject {
|
class ConversationsListViewModel: ObservableObject {
|
||||||
|
|
@ -48,180 +47,35 @@ class ConversationsListViewModel: ObservableObject {
|
||||||
let chatRooms = account != nil ? account!.chatRooms : core.chatRooms
|
let chatRooms = account != nil ? account!.chatRooms : core.chatRooms
|
||||||
|
|
||||||
self.conversationsListTmp = []
|
self.conversationsListTmp = []
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.conversationsList = []
|
|
||||||
}
|
|
||||||
|
|
||||||
chatRooms.forEach { chatRoom in
|
chatRooms.forEach { chatRoom in
|
||||||
if filter.isEmpty {
|
if filter.isEmpty {
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
self.conversationsListTmp.append(model)
|
self.conversationsListTmp.append(model)
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.conversationsList.append(model)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.conversationsList = self.conversationsListTmp
|
||||||
|
NotificationCenter.default.post(name: NSNotification.Name("ChatRoomsComputed"), object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
self.updateUnreadMessagesCount()
|
self.updateUnreadMessagesCount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateChatRoomsList() {
|
|
||||||
CoreContext.shared.doOnCoreQueue { _ in
|
|
||||||
if !self.conversationsListTmp.isEmpty {
|
|
||||||
self.contactsManager.avatarListModel.forEach { contactAvatarModel in
|
|
||||||
self.conversationsListTmp.forEach { conversationModel in
|
|
||||||
if conversationModel.participantsAddress.contains(contactAvatarModel.address) {
|
|
||||||
if conversationModel.isGroup && conversationModel.participantsAddress.count > 1 {
|
|
||||||
if let lastMessage = conversationModel.chatRoom.lastMessageInHistory, let fromAddress = lastMessage.fromAddress, fromAddress.asStringUriOnly().contains(contactAvatarModel.address) {
|
|
||||||
var fromAddressFriend = self.contactsManager.getFriendWithAddress(address: fromAddress)?.name
|
|
||||||
|
|
||||||
if !lastMessage.isOutgoing && lastMessage.chatRoom != nil && !lastMessage.chatRoom!.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) {
|
|
||||||
if fromAddressFriend == nil {
|
|
||||||
if let displayName = fromAddress.displayName {
|
|
||||||
fromAddressFriend = displayName + ": "
|
|
||||||
} else if let username = fromAddress.username {
|
|
||||||
fromAddressFriend = username + ": "
|
|
||||||
} else {
|
|
||||||
fromAddressFriend = String(fromAddress.asStringUriOnly().dropFirst(4)) + ": "
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fromAddressFriend! += ": "
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fromAddressFriend = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastMessageTextTmp = (fromAddressFriend ?? "") + (lastMessage.contents.first(where: { $0.isText })?.utf8Text ?? (lastMessage.contents.first(where: { $0.isFile || $0.isFileTransfer })?.name ?? ""))
|
|
||||||
|
|
||||||
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
conversationModel.lastMessageText = lastMessageTextTmp
|
|
||||||
self.conversationsList[index].lastMessageText = lastMessageTextTmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
conversationModel.lastMessageText = lastMessageTextTmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if !conversationModel.isGroup, let firstParticipantAddress = conversationModel.participantsAddress.first, firstParticipantAddress.contains(contactAvatarModel.address) {
|
|
||||||
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
conversationModel.avatarModel = contactAvatarModel
|
|
||||||
conversationModel.subject = contactAvatarModel.name
|
|
||||||
self.conversationsList[index].avatarModel = contactAvatarModel
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
conversationModel.avatarModel = contactAvatarModel
|
|
||||||
conversationModel.subject = contactAvatarModel.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateChatRoom(address: String) {
|
|
||||||
CoreContext.shared.doOnCoreQueue { _ in
|
|
||||||
if let contactAvatarModel = self.contactsManager.avatarListModel.first(where: { $0.addresses.contains(address) }) {
|
|
||||||
self.conversationsListTmp.forEach { conversationModel in
|
|
||||||
if conversationModel.participantsAddress.contains(contactAvatarModel.address) {
|
|
||||||
if conversationModel.isGroup && conversationModel.participantsAddress.count > 1 {
|
|
||||||
if let lastMessage = conversationModel.chatRoom.lastMessageInHistory, let fromAddress = lastMessage.fromAddress, fromAddress.asStringUriOnly().contains(contactAvatarModel.address) {
|
|
||||||
var fromAddressFriend = self.contactsManager.getFriendWithAddress(address: fromAddress)?.name
|
|
||||||
|
|
||||||
if !lastMessage.isOutgoing && lastMessage.chatRoom != nil && !lastMessage.chatRoom!.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) {
|
|
||||||
if fromAddressFriend == nil {
|
|
||||||
if let displayName = fromAddress.displayName {
|
|
||||||
fromAddressFriend = displayName + ": "
|
|
||||||
} else if let username = fromAddress.username {
|
|
||||||
fromAddressFriend = username + ": "
|
|
||||||
} else {
|
|
||||||
fromAddressFriend = String(fromAddress.asStringUriOnly().dropFirst(4)) + ": "
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fromAddressFriend! += ": "
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fromAddressFriend = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastMessageTextTmp = (fromAddressFriend ?? "") + (lastMessage.contents.first(where: { $0.isText })?.utf8Text ?? (lastMessage.contents.first(where: { $0.isFile || $0.isFileTransfer })?.name ?? ""))
|
|
||||||
|
|
||||||
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
conversationModel.lastMessageText = lastMessageTextTmp
|
|
||||||
self.conversationsList[index].lastMessageText = lastMessageTextTmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
conversationModel.lastMessageText = lastMessageTextTmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if !conversationModel.isGroup, let firstParticipantAddress = conversationModel.participantsAddress.first, firstParticipantAddress.contains(contactAvatarModel.address) {
|
|
||||||
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
conversationModel.avatarModel = contactAvatarModel
|
|
||||||
conversationModel.subject = contactAvatarModel.name
|
|
||||||
self.conversationsList[index].avatarModel = contactAvatarModel
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
conversationModel.avatarModel = contactAvatarModel
|
|
||||||
conversationModel.subject = contactAvatarModel.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addConversationDelegate() {
|
func addConversationDelegate() {
|
||||||
coreContext.doOnCoreQueue { core in
|
coreContext.doOnCoreQueue { core in
|
||||||
self.coreConversationDelegate = CoreDelegateStub(onMessagesReceived: { (_: Core, chatRoom: ChatRoom, _: [ChatMessage]) in
|
let account = core.defaultAccount
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
let chatRoomsCounter = account?.chatRooms != nil ? account!.chatRooms.count : core.chatRooms.count
|
||||||
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
|
||||||
let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
|
self.coreConversationDelegate = CoreDelegateStub(onMessagesReceived: { (_: Core, _: ChatRoom, _: [ChatMessage]) in
|
||||||
DispatchQueue.main.async {
|
self.computeChatRoomsList(filter: "")
|
||||||
if index != nil {
|
}, onMessageSent: { (_: Core, _: ChatRoom, _: ChatMessage) in
|
||||||
self.conversationsList.remove(at: index!)
|
self.computeChatRoomsList(filter: "")
|
||||||
}
|
}, onChatRoomRead: { (_: Core, _: ChatRoom) in
|
||||||
self.conversationsList.insert(model, at: 0)
|
self.computeChatRoomsList(filter: "")
|
||||||
}
|
}, onChatRoomStateChanged: { (core: Core, chatRoom: ChatRoom, state: ChatRoom.State) in
|
||||||
self.updateUnreadMessagesCount()
|
|
||||||
}, onMessageSent: { (_: Core, chatRoom: ChatRoom, _: ChatMessage) in
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
|
||||||
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
|
||||||
let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if index != nil {
|
|
||||||
self.conversationsList.remove(at: index!)
|
|
||||||
}
|
|
||||||
self.conversationsList.insert(model, at: 0)
|
|
||||||
}
|
|
||||||
self.updateUnreadMessagesCount()
|
|
||||||
}, onChatRoomRead: { (_: Core, chatRoom: ChatRoom) in
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
|
||||||
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
|
||||||
let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if index != nil {
|
|
||||||
self.conversationsList.remove(at: index!)
|
|
||||||
self.conversationsList.insert(model, at: index!)
|
|
||||||
} else {
|
|
||||||
self.conversationsList.insert(model, at: 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.updateUnreadMessagesCount()
|
|
||||||
}, onChatRoomStateChanged: { (core: Core, _: ChatRoom, state: ChatRoom.State) in
|
|
||||||
// Log.info("[ConversationsListViewModel] Conversation [${LinphoneUtils.getChatRoomId(chatRoom)}] state changed [$state]")
|
// Log.info("[ConversationsListViewModel] Conversation [${LinphoneUtils.getChatRoomId(chatRoom)}] state changed [$state]")
|
||||||
if core.globalState == .On {
|
if core.globalState == .On {
|
||||||
switch state {
|
switch state {
|
||||||
|
|
@ -280,7 +134,7 @@ class ConversationsListViewModel: ObservableObject {
|
||||||
} else if message.fromAddress!.username != nil {
|
} else if message.fromAddress!.username != nil {
|
||||||
fromAddressFriend = message.fromAddress!.username! + ": "
|
fromAddressFriend = message.fromAddress!.username! + ": "
|
||||||
} else {
|
} else {
|
||||||
fromAddressFriend = String(message.fromAddress!.asStringUriOnly().dropFirst(4)) + ": "
|
fromAddressFriend = ""
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fromAddressFriend! += ": "
|
fromAddressFriend! += ": "
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
// swiftlint:disable line_length
|
// swiftlint:disable line_length
|
||||||
class StartConversationViewModel: ObservableObject {
|
class StartConversationViewModel: ObservableObject {
|
||||||
|
|
@ -79,31 +78,27 @@ class StartConversationViewModel: ObservableObject {
|
||||||
|
|
||||||
let groupChatRoomSubject = self.messageText
|
let groupChatRoomSubject = self.messageText
|
||||||
do {
|
do {
|
||||||
let chatRoomParams = try core.createConferenceParams(conference: nil)
|
let params: ChatRoomParams = try core.createDefaultChatRoomParams()
|
||||||
chatRoomParams.chatEnabled = true
|
params.groupEnabled = true
|
||||||
chatRoomParams.groupEnabled = true
|
params.subject = groupChatRoomSubject
|
||||||
chatRoomParams.subject = groupChatRoomSubject
|
params.backend = ChatRoom.Backend.FlexisipChat
|
||||||
chatRoomParams.securityLevel = Conference.SecurityLevel.EndToEnd
|
params.encryptionEnabled = true
|
||||||
chatRoomParams.account = account
|
|
||||||
|
|
||||||
if let chatParams = chatRoomParams.chatParams {
|
|
||||||
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
|
|
||||||
chatParams.backend = ChatRoom.Backend.FlexisipChat
|
|
||||||
}
|
|
||||||
|
|
||||||
var participantsTmp: [Address] = []
|
var participantsTmp: [Address] = []
|
||||||
self.participants.forEach { participant in
|
self.participants.forEach { participant in
|
||||||
participantsTmp.append(participant.address)
|
participantsTmp.append(participant.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.participants.removeAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
if account!.params != nil {
|
if account!.params != nil {
|
||||||
let chatRoom = try core.createChatRoom(params: chatRoomParams, participants: participantsTmp)
|
let localAddress = account!.params!.identityAddress
|
||||||
|
|
||||||
if chatRoomParams.chatParams != nil && chatRoomParams.chatParams!.backend == ChatRoom.Backend.FlexisipChat {
|
let chatRoom = try core.createChatRoom(
|
||||||
|
params: params,
|
||||||
|
localAddr: localAddress,
|
||||||
|
participants: participantsTmp
|
||||||
|
)
|
||||||
|
|
||||||
|
if params.backend == ChatRoom.Backend.FlexisipChat {
|
||||||
if chatRoom.state == ChatRoom.State.Created {
|
if chatRoom.state == ChatRoom.State.Created {
|
||||||
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
Log.info(
|
Log.info(
|
||||||
|
|
@ -111,9 +106,20 @@ class StartConversationViewModel: ObservableObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
DispatchQueue.main.async {
|
if self.operationInProgress == false {
|
||||||
self.displayedConversation = model
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.info(
|
Log.info(
|
||||||
|
|
@ -126,9 +132,20 @@ class StartConversationViewModel: ObservableObject {
|
||||||
Log.info("\(StartConversationViewModel.TAG) Conversation successfully created \(id) \(groupChatRoomSubject)")
|
Log.info("\(StartConversationViewModel.TAG) Conversation successfully created \(id) \(groupChatRoomSubject)")
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
DispatchQueue.main.async {
|
if self.operationInProgress == false {
|
||||||
self.displayedConversation = model
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -146,11 +163,11 @@ class StartConversationViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createOneToOneChatRoomWith(remote: Address) {
|
func createOneToOneChatRoomWith(remote: Address) {
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
coreContext.doOnCoreQueue { core in
|
||||||
let account = core.defaultAccount
|
let account = core.defaultAccount
|
||||||
if account == nil {
|
if account == nil {
|
||||||
Log.error(
|
Log.error(
|
||||||
"\(StartConversationViewModel.TAG) No default account found, can't create conversation with \(remote.asStringUriOnly())"
|
"\(StartConversationViewModel.TAG) No default account found, can't create conversation with \(remote.asStringUriOnly())!"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -160,42 +177,37 @@ class StartConversationViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let params = try core.createConferenceParams(conference: nil)
|
let params: ChatRoomParams = try core.createDefaultChatRoomParams()
|
||||||
params.chatEnabled = true
|
|
||||||
params.groupEnabled = false
|
params.groupEnabled = false
|
||||||
params.subject = NSLocalizedString("conversation_one_to_one_hidden_subject", comment: "")
|
params.subject = "Dummy subject"
|
||||||
params.account = account
|
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
|
||||||
|
|
||||||
guard let chatParams = params.chatParams else { return }
|
let sameDomain = remote.domain == account?.params?.domain ?? ""
|
||||||
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
|
if StartConversationViewModel.isEndToEndEncryptionMandatory() && sameDomain {
|
||||||
|
Log.info("\(StartConversationViewModel.TAG) Account is in secure mode & domain matches, creating a E2E conversation")
|
||||||
let sameDomain = remote.domain == CorePreferences.defaultDomain && remote.domain == account!.params?.domain
|
params.backend = ChatRoom.Backend.FlexisipChat
|
||||||
if account!.params != nil && (account!.params!.instantMessagingEncryptionMandatory && sameDomain) {
|
params.encryptionEnabled = true
|
||||||
Log.info("\(StartConversationViewModel.TAG) Account is in secure mode & domain matches, creating an E2E encrypted conversation")
|
} else if !StartConversationViewModel.isEndToEndEncryptionMandatory() {
|
||||||
chatParams.backend = ChatRoom.Backend.FlexisipChat
|
|
||||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
|
||||||
} else if account!.params != nil && (!account!.params!.instantMessagingEncryptionMandatory) {
|
|
||||||
if LinphoneUtils.isEndToEndEncryptedChatAvailable(core: core) {
|
if LinphoneUtils.isEndToEndEncryptedChatAvailable(core: core) {
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME is available, creating an E2E encrypted conversation"
|
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME is available, creating a E2E conversation"
|
||||||
)
|
)
|
||||||
chatParams.backend = ChatRoom.Backend.FlexisipChat
|
params.backend = ChatRoom.Backend.FlexisipChat
|
||||||
params.securityLevel = Conference.SecurityLevel.EndToEnd
|
params.encryptionEnabled = true
|
||||||
} else {
|
} else {
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
|
"\(StartConversationViewModel.TAG) Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
|
||||||
)
|
)
|
||||||
chatParams.backend = ChatRoom.Backend.Basic
|
params.backend = ChatRoom.Backend.Basic
|
||||||
params.securityLevel = Conference.SecurityLevel.None
|
params.encryptionEnabled = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.error(
|
Log.error(
|
||||||
"\(StartConversationViewModel.TAG) Account is in secure mode, can't chat with SIP address of different domain \(remote.asStringUriOnly())"
|
"\(StartConversationViewModel.TAG) Account is in secure mode, can't chat with SIP address of different domain \(remote.asStringUriOnly())"
|
||||||
)
|
)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_invalid_participant_error"
|
||||||
ToastViewModel.shared.displayToast = true
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -206,56 +218,85 @@ class StartConversationViewModel: ObservableObject {
|
||||||
let existingChatRoom = core.searchChatRoom(params: params, localAddr: localAddress, remoteAddr: nil, participants: participants)
|
let existingChatRoom = core.searchChatRoom(params: params, localAddr: localAddress, remoteAddr: nil, participants: participants)
|
||||||
if existingChatRoom == nil {
|
if existingChatRoom == nil {
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(StartConversationViewModel.TAG) No existing 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) was found for given parameters, let's create it"
|
"\(StartConversationViewModel.TAG) No existing 1-1 conversation between local account "
|
||||||
|
+ "\(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) was found for given parameters, let's create it"
|
||||||
)
|
)
|
||||||
|
let chatRoom = try core.createChatRoom(params: params, localAddr: localAddress, participants: participants)
|
||||||
do {
|
if params.backend == ChatRoom.Backend.FlexisipChat {
|
||||||
let chatRoom = try core.createChatRoom(params: params, participants: participants)
|
if chatRoom.state == ChatRoom.State.Created {
|
||||||
if chatParams.backend == ChatRoom.Backend.FlexisipChat {
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
let state = chatRoom.state
|
Log.info("\(StartConversationViewModel.TAG) 1-1 conversation \(id) has been created")
|
||||||
if state == ChatRoom.State.Created {
|
|
||||||
let chatRoomId = LinphoneUtils.getConversationId(chatRoom: chatRoom)
|
|
||||||
Log.info("\(StartConversationViewModel.TAG) 1-1 conversation \(chatRoomId) has been created")
|
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.displayedConversation = model
|
|
||||||
self.operationInProgress = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.info("\(StartConversationViewModel.TAG) Conversation isn't in Created state yet (state is \(state)), wait for it")
|
|
||||||
self.chatRoomAddDelegate(core: core, chatRoom: chatRoom)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let chatRoomId = LinphoneUtils.getConversationId(chatRoom: chatRoom)
|
|
||||||
Log.info("\(StartConversationViewModel.TAG) Conversation successfully created \(chatRoomId)")
|
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
DispatchQueue.main.async {
|
if self.operationInProgress == false {
|
||||||
self.displayedConversation = model
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.info("\(StartConversationViewModel.TAG) Conversation isn't in Created state yet, wait for it")
|
||||||
|
self.chatRoomAddDelegate(core: core, chatRoom: chatRoom)
|
||||||
}
|
}
|
||||||
} catch {
|
} else {
|
||||||
Log.error("\(StartConversationViewModel.TAG) Failed to create 1-1 conversation with \(remote.asStringUriOnly())")
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
|
Log.info("\(StartConversationViewModel.TAG) Conversation successfully created \(id)")
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
self.operationInProgress = false
|
if self.operationInProgress == false {
|
||||||
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.displayToast = true
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
"\(StartConversationViewModel.TAG) A 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) for given parameters already exists!"
|
"\(StartConversationViewModel.TAG) A 1-1 conversation between local account \(localAddress?.asStringUriOnly() ?? "") and remote \(remote.asStringUriOnly()) for given parameters already exists!"
|
||||||
)
|
)
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: existingChatRoom!)
|
let model = ConversationModel(chatRoom: existingChatRoom!)
|
||||||
DispatchQueue.main.async {
|
if self.operationInProgress == false {
|
||||||
self.displayedConversation = model
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
|
}
|
||||||
|
Log.error("\(StartConversationViewModel.TAG) Failed to create 1-1 conversation with \(remote.asStringUriOnly())!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -263,9 +304,9 @@ class StartConversationViewModel: ObservableObject {
|
||||||
func chatRoomAddDelegate(core: Core, chatRoom: ChatRoom) {
|
func chatRoomAddDelegate(core: Core, chatRoom: ChatRoom) {
|
||||||
self.chatRoomDelegate = ChatRoomDelegateStub(onStateChanged: { (chatRoom: ChatRoom, state: ChatRoom.State) in
|
self.chatRoomDelegate = ChatRoomDelegateStub(onStateChanged: { (chatRoom: ChatRoom, state: ChatRoom.State) in
|
||||||
let state = chatRoom.state
|
let state = chatRoom.state
|
||||||
let chatRoomId = LinphoneUtils.getChatRoomId(room: chatRoom)
|
let id = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
if state == ChatRoom.State.CreationFailed {
|
if state == ChatRoom.State.CreationFailed {
|
||||||
Log.error("\(StartConversationViewModel.TAG) Conversation \(chatRoomId) creation has failed!")
|
Log.error("\(StartConversationViewModel.TAG) Conversation \(id) creation has failed!")
|
||||||
if let chatRoomDelegate = self.chatRoomDelegate {
|
if let chatRoomDelegate = self.chatRoomDelegate {
|
||||||
chatRoom.removeDelegate(delegate: chatRoomDelegate)
|
chatRoom.removeDelegate(delegate: chatRoomDelegate)
|
||||||
}
|
}
|
||||||
|
|
@ -288,9 +329,20 @@ class StartConversationViewModel: ObservableObject {
|
||||||
self.chatRoomDelegate = nil
|
self.chatRoomDelegate = nil
|
||||||
|
|
||||||
let model = ConversationModel(chatRoom: chatRoom)
|
let model = ConversationModel(chatRoom: chatRoom)
|
||||||
DispatchQueue.main.async {
|
if self.operationInProgress == false {
|
||||||
self.displayedConversation = model
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = true
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.operationInProgress = false
|
||||||
|
self.displayedConversation = model
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if state == ChatRoom.State.CreationFailed {
|
} else if state == ChatRoom.State.CreationFailed {
|
||||||
Log.error("\(StartConversationViewModel.TAG) Conversation \(id) creation has failed!")
|
Log.error("\(StartConversationViewModel.TAG) Conversation \(id) creation has failed!")
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class HelpView { // TODO (basic debug moved here until halp view is implemented)
|
||||||
CoreContext.shared.doOnCoreQueue { _ in
|
CoreContext.shared.doOnCoreQueue { _ in
|
||||||
Core.resetLogCollection()
|
Core.resetLogCollection()
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.toastMessage = "help_troubleshooting_debug_logs_cleaned_toast_message"
|
ToastViewModel.shared.toastMessage = "Success_clear_logs"
|
||||||
ToastViewModel.shared.displayToast = true
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ struct PopupLoadingView: View {
|
||||||
.padding(.top)
|
.padding(.top)
|
||||||
.padding(.bottom)
|
.padding(.bottom)
|
||||||
|
|
||||||
Text("operation_in_progress_overlay")
|
Text("Opération en cours...")
|
||||||
.tint(Color.grayMain2c600)
|
.tint(Color.grayMain2c600)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ struct PopupViewWithTextField: View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
conversationViewModel.isShowConversationInfoPopup = false
|
conversationViewModel.isShowConversationInfoPopup = false
|
||||||
}, label: {
|
}, label: {
|
||||||
Text("dialog_cancel")
|
Text("Cancel")
|
||||||
.default_text_style_orange_600(styleSize: 20)
|
.default_text_style_orange_600(styleSize: 20)
|
||||||
.frame(height: 35)
|
.frame(height: 35)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
@ -58,7 +58,7 @@ struct PopupViewWithTextField: View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
conversationViewModel.setNewChatRoomSubject()
|
conversationViewModel.setNewChatRoomSubject()
|
||||||
}, label: {
|
}, label: {
|
||||||
Text("dialog_ok")
|
Text("Confirm")
|
||||||
.default_text_style_white_600(styleSize: 20)
|
.default_text_style_white_600(styleSize: 20)
|
||||||
.frame(height: 35)
|
.frame(height: 35)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,11 @@ import UniformTypeIdentifiers
|
||||||
|
|
||||||
struct SideMenu: View {
|
struct SideMenu: View {
|
||||||
|
|
||||||
@ObservedObject var accountProfileViewModel: AccountProfileViewModel
|
|
||||||
|
|
||||||
let width: CGFloat
|
let width: CGFloat
|
||||||
@Binding var isOpen: Bool
|
let isOpen: Bool
|
||||||
let menuClose: () -> Void
|
let menuClose: () -> Void
|
||||||
let safeAreaInsets: EdgeInsets
|
let safeAreaInsets: EdgeInsets
|
||||||
@Binding var isShowLoginFragment: Bool
|
@Binding var isShowLoginFragment: Bool
|
||||||
@Binding var isShowAccountProfileFragment: Bool
|
|
||||||
@Binding var isShowSettingsFragment: Bool
|
|
||||||
@Binding var isShowHelpFragment: Bool
|
|
||||||
@State private var showHelp = false
|
@State private var showHelp = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
@ -71,7 +66,7 @@ struct SideMenu: View {
|
||||||
|
|
||||||
List {
|
List {
|
||||||
ForEach(0..<CoreContext.shared.accounts.count, id: \.self) { index in
|
ForEach(0..<CoreContext.shared.accounts.count, id: \.self) { index in
|
||||||
SideMenuAccountRow( model: CoreContext.shared.accounts[index], accountProfileViewModel: accountProfileViewModel, isOpen: $isOpen, isShowAccountProfileFragment: $isShowAccountProfileFragment)
|
SideMenuAccountRow( model: CoreContext.shared.accounts[index])
|
||||||
.background()
|
.background()
|
||||||
.listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
|
.listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
|
||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
|
|
@ -126,33 +121,30 @@ struct SideMenu: View {
|
||||||
ForEach(0..<CoreContext.shared.shortcuts.count, id: \.self) { index in
|
ForEach(0..<CoreContext.shared.shortcuts.count, id: \.self) { index in
|
||||||
SideMenuShortcut(shortcutModel: CoreContext.shared.shortcuts[index])
|
SideMenuShortcut(shortcutModel: CoreContext.shared.shortcuts[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
SideMenuEntry(
|
SideMenuEntry(
|
||||||
iconName: "gear",
|
iconName: "gear",
|
||||||
title: "settings_title"
|
title: "settings_title"
|
||||||
).onTapGesture {
|
)
|
||||||
self.menuClose()
|
|
||||||
withAnimation {
|
|
||||||
isShowSettingsFragment = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
SideMenuEntry(
|
SideMenuEntry(
|
||||||
iconName: "record-fill",
|
iconName: "record-fill",
|
||||||
title: "recordings_title"
|
title: "recordings_title"
|
||||||
)
|
)
|
||||||
*/
|
|
||||||
|
|
||||||
SideMenuEntry(
|
SideMenuEntry(
|
||||||
iconName: "question",
|
iconName: "question",
|
||||||
title: "help_title"
|
title: "help_title"
|
||||||
).onTapGesture {
|
).onTapGesture {
|
||||||
self.menuClose()
|
showHelp = true
|
||||||
withAnimation {
|
}
|
||||||
isShowHelpFragment = true
|
.confirmationDialog("Temp Help", isPresented: $showHelp, titleVisibility: .visible) {
|
||||||
|
Button("Send Logs") {
|
||||||
|
HelpView.sendLogs()
|
||||||
|
}
|
||||||
|
Button("Clear Logs") {
|
||||||
|
HelpView.clearLogs()
|
||||||
|
}
|
||||||
|
Button("Logout") {
|
||||||
|
HelpView.logout()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom, safeAreaInsets.bottom + 13)
|
.padding(.bottom, safeAreaInsets.bottom + 13)
|
||||||
|
|
@ -178,15 +170,11 @@ struct SideMenu: View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
@State var triggerNavigateToLogin: Bool = false
|
@State var triggerNavigateToLogin: Bool = false
|
||||||
SideMenu(
|
SideMenu(
|
||||||
accountProfileViewModel: AccountProfileViewModel(),
|
|
||||||
width: geometry.size.width / 5 * 4,
|
width: geometry.size.width / 5 * 4,
|
||||||
isOpen: .constant(true),
|
isOpen: true,
|
||||||
menuClose: {},
|
menuClose: {},
|
||||||
safeAreaInsets: geometry.safeAreaInsets,
|
safeAreaInsets: geometry.safeAreaInsets,
|
||||||
isShowLoginFragment: $triggerNavigateToLogin,
|
isShowLoginFragment: $triggerNavigateToLogin
|
||||||
isShowAccountProfileFragment: .constant(false),
|
|
||||||
isShowSettingsFragment: .constant(false),
|
|
||||||
isShowHelpFragment: .constant(false)
|
|
||||||
)
|
)
|
||||||
.ignoresSafeArea(.all)
|
.ignoresSafeArea(.all)
|
||||||
.zIndex(2)
|
.zIndex(2)
|
||||||
|
|
|
||||||
|
|
@ -22,103 +22,63 @@ import linphonesw
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
struct SideMenuAccountRow: View {
|
struct SideMenuAccountRow: View {
|
||||||
@ObservedObject var contactsManager = ContactsManager.shared
|
|
||||||
|
|
||||||
@ObservedObject var model: AccountModel
|
@ObservedObject var model: AccountModel
|
||||||
@ObservedObject var accountProfileViewModel: AccountProfileViewModel
|
|
||||||
|
|
||||||
@State private var navigateToOption = false
|
|
||||||
@Binding var isOpen: Bool
|
|
||||||
@Binding var isShowAccountProfileFragment: Bool
|
|
||||||
|
|
||||||
private let avatarSize = 45.0
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
if accountProfileViewModel.accountModelIndex != nil && CoreContext.shared.accounts.count > accountProfileViewModel.accountModelIndex! {
|
|
||||||
AsyncImage(url: CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex!].imagePathAvatar) { image in
|
Avatar(contactAvatarModel:
|
||||||
switch image {
|
ContactAvatarModel(friend: nil,
|
||||||
case .empty:
|
name: model.displayName,
|
||||||
ProgressView()
|
address: model.address,
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
withPresence: true),
|
||||||
case .success(let image):
|
avatarSize: 45)
|
||||||
image
|
.padding(.leading, 6)
|
||||||
.resizable()
|
|
||||||
.aspectRatio(contentMode: .fill)
|
VStack {
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
Text(model.displayName)
|
||||||
.clipShape(Circle())
|
.default_text_style_grey_400(styleSize: 14)
|
||||||
case .failure:
|
.lineLimit(1)
|
||||||
Image(uiImage: contactsManager.textToImage(
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
firstName: model.avatarModel?.name ?? "",
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
@unknown default:
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.leading, 6)
|
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
Text(model.displayName)
|
Text(model.humanReadableRegistrationState)
|
||||||
.default_text_style_grey_400(styleSize: 14)
|
.default_text_style_uncolored(styleSize: 12)
|
||||||
.lineLimit(1)
|
.foregroundStyle(model.registrationStateAssociatedUIColor)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
Text(model.humanReadableRegistrationState)
|
|
||||||
.default_text_style_uncolored(styleSize: 12)
|
|
||||||
.foregroundStyle(model.registrationStateAssociatedUIColor)
|
|
||||||
}
|
|
||||||
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
|
||||||
.background(Color.grayMain2c200)
|
|
||||||
.cornerRadius(12)
|
|
||||||
.frame(height: 20)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.onTapGesture {
|
|
||||||
model.refreshRegiter()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.padding(.leading, 4)
|
.padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing: 8))
|
||||||
|
.background(Color.grayMain2c200)
|
||||||
Spacer()
|
.cornerRadius(12)
|
||||||
|
.frame(height: 20)
|
||||||
HStack {
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
if model.notificationsCount > 0 {
|
.onTapGesture {
|
||||||
Text(String(model.notificationsCount))
|
model.refreshRegiter()
|
||||||
.foregroundStyle(.white)
|
|
||||||
.default_text_style(styleSize: 12)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
.background(Color.redDanger500)
|
|
||||||
.cornerRadius(50)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
Button {
|
|
||||||
withAnimation {
|
|
||||||
|
|
||||||
isOpen = false
|
|
||||||
isShowAccountProfileFragment = true
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Label("drawer_menu_manage_account", systemImage: "arrow.right.circle")
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Image("dots-three-vertical")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundColor(Color.gray)
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(height: 30)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.frame(width: 64, alignment: .trailing)
|
|
||||||
.padding(.top, 12)
|
|
||||||
.padding(.bottom, 12)
|
|
||||||
}
|
}
|
||||||
|
.padding(.leading, 4)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
if model.notificationsCount > 0 {
|
||||||
|
Text(String(model.notificationsCount))
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.default_text_style(styleSize: 12)
|
||||||
|
.lineLimit(1)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
.background(Color.redDanger500)
|
||||||
|
.cornerRadius(50)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
Image("dots-three-vertical")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(height: 30)
|
||||||
|
}
|
||||||
|
.frame(width: 64, alignment: .trailing)
|
||||||
|
.padding(.top, 12)
|
||||||
|
.padding(.bottom, 12)
|
||||||
}
|
}
|
||||||
.frame(height: 61)
|
.frame(height: 61)
|
||||||
.background(model.isDefaultAccount ? Color.grayMain2c100 : .clear)
|
.background(model.isDefaultAccount ? Color.grayMain2c100 : .clear)
|
||||||
|
|
|
||||||
|
|
@ -52,3 +52,8 @@ struct SideMenuShortcut: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
SideMenuShortcut(shortcutModel: ShortcutModel(linkUrl: URL("link")!, name: "name", iconLinkUrl: URL("icon")!)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,57 +52,43 @@ struct ToastView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch toastViewModel.toastMessage {
|
switch toastViewModel.toastMessage {
|
||||||
case "Success_qr_code_validated":
|
case "Successful":
|
||||||
Text("qr_code_validated")
|
Text("QR code validated!")
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "Success_version_up_to_date":
|
|
||||||
Text("help_version_up_to_date_toast_message")
|
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Success_remove_call_logs":
|
case "Success_remove_call_logs":
|
||||||
Text("call_history_deleted_toast")
|
Text("History has been deleted")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Success_clear_logs":
|
case "Success_clear_logs":
|
||||||
Text("help_troubleshooting_debug_logs_cleaned_toast_message")
|
Text("Logs cleared")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Success_send_logs":
|
case "Success_send_logs":
|
||||||
Text("debug_logs_copied_to_clipboard_toast")
|
Text("Logs URL copied into clipboard")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Success_address_copied_into_clipboard":
|
case "Success_address_copied_into_clipboard":
|
||||||
Text("sip_address_copied_to_clipboard_toast")
|
Text("SIP address copied into clipboard")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Success_message_copied_into_clipboard":
|
case "Success_message_copied_into_clipboard":
|
||||||
Text("message_copied_to_clipboard_toast")
|
Text("Message copied into clipboard")
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "Success_text_copied_into_clipboard":
|
|
||||||
Text("text_copied_to_clipboard_toast")
|
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
|
|
@ -123,112 +109,105 @@ struct ToastView: View {
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Failed":
|
case "Failed":
|
||||||
Text("assistant_qr_code_invalid_toast")
|
Text("Invalid QR code!")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Invalide URI":
|
case "Invalide URI":
|
||||||
Text("assistant_invalid_uri_toast")
|
Text("Invalide URI")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Registration_failed":
|
case "Registration_failed":
|
||||||
Text("assistant_account_login_forbidden_error")
|
Text("The user name or password is incorrects")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Unavailable_network":
|
case "Unavailable_network":
|
||||||
Text("network_not_reachable")
|
Text("Network is not reachable")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Success_toast_network_connected":
|
case "Success_toast_network_connected":
|
||||||
Text("network_reachable_again")
|
Text("Network is now reachable again")
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
.padding(8)
|
||||||
|
|
||||||
|
case "Success_account_logged_out":
|
||||||
|
Text("Account successfully logged out")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Success_toast_call_transfer_successful":
|
case "Success_toast_call_transfer_successful":
|
||||||
Text("call_transfer_successful_toast")
|
Text("Call has been successfully transferred")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Success_toast_call_transfer_in_progress":
|
case "Success_toast_call_transfer_in_progress":
|
||||||
Text("call_transfer_in_progress_toast")
|
Text("Call is being transferred")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Success_toast_meeting_deleted":
|
case "Success_toast_meeting_deleted":
|
||||||
Text("meeting_info_deleted_toast")
|
Text("Successfully removed meeting")
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "Success_meeting_info_created_toast":
|
|
||||||
Text("meeting_info_created_toast")
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "Success_meeting_info_updated_toast":
|
|
||||||
Text("meeting_info_updated_toast")
|
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Failed_toast_call_transfer_failed":
|
case "Failed_toast_call_transfer_failed":
|
||||||
Text("call_transfer_failed_toast")
|
Text("Call transfer failed!")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Failed_uri_handler_call_failed":
|
case "Failed_uri_handler_call_failed":
|
||||||
Text("uri_handler_call_failed_toast")
|
Text("Call failed")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Failed_uri_handler_config_failed":
|
case "Failed_uri_handler_config_failed":
|
||||||
Text("uri_handler_config_failed_toast")
|
Text("Configuration failed")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "uri_handler_config_success":
|
case "uri_handler_config_success":
|
||||||
Text("uri_handler_config_success_toast")
|
Text("Configuration successfully applied")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Failed_uri_handler_bad_call_address":
|
case "Failed_uri_handler_bad_call_address":
|
||||||
Text("uri_handler_bad_call_address_failed_toast")
|
Text("Unable to call, invalid address")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Failed_uri_handler_bad_config_address":
|
case "Failed_uri_handler_bad_config_address":
|
||||||
Text("uri_handler_bad_config_address_failed_toast")
|
Text("Unable to retrieve configuration, invalid address")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
|
|
@ -275,42 +254,21 @@ struct ToastView: View {
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
|
|
||||||
case "Meeting_added_to_calendar":
|
case "Meeting_added_to_calendar":
|
||||||
Text("meeting_exported_as_calendar_event")
|
Text("Meeting added to iPhone calendar")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
.foregroundStyle(Color.greenSuccess500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "meeting_failed_to_edit_toast":
|
case "Failed_meeting_invitations_not_sent":
|
||||||
Text("meeting_failed_to_edit_toast")
|
Text("Could not send ICS invitations to meeting to any participant")
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "meeting_failed_to_schedule_toast":
|
|
||||||
Text("meeting_failed_to_schedule_toast")
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "meeting_failed_to_send_invites_toast":
|
|
||||||
Text("meeting_failed_to_send_invites_toast")
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "meeting_failed_to_send_part_of_invites_toast":
|
|
||||||
Text("meeting_failed_to_send_part_of_invites_toast")
|
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Failed_no_subject_or_participant":
|
case "Failed_no_subject_or_participant":
|
||||||
Text("meeting_schedule_failed_no_subject_or_participant_toast")
|
Text("A subject and at least one participant is required to create a meeting")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
|
|
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
|
||||||
*
|
|
||||||
* This file is part of linphone-iphone
|
|
||||||
*
|
|
||||||
* 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import UniformTypeIdentifiers
|
|
||||||
|
|
||||||
struct DebugFragment: View {
|
|
||||||
@ObservedObject var helpViewModel: HelpViewModel
|
|
||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
|
||||||
|
|
||||||
@State private var showShareSheet: Bool = false
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack {
|
|
||||||
VStack(spacing: 1) {
|
|
||||||
Rectangle()
|
|
||||||
.foregroundColor(Color.orangeMain500)
|
|
||||||
.edgesIgnoringSafeArea(.top)
|
|
||||||
.frame(height: 0)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Image("caret-left")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.padding(.top, 4)
|
|
||||||
.padding(.leading, -10)
|
|
||||||
.onTapGesture {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("help_troubleshooting_title")
|
|
||||||
.default_text_style_orange_800(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(.top, 4)
|
|
||||||
.lineLimit(1)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.frame(height: 50)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.bottom, 4)
|
|
||||||
.background(.white)
|
|
||||||
|
|
||||||
ScrollView {
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
VStack(spacing: 20) {
|
|
||||||
/*
|
|
||||||
Toggle("help_troubleshooting_print_logs_in_logcat", isOn: $helpViewModel.logcat)
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
*/
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(
|
|
||||||
action: {
|
|
||||||
helpViewModel.cleanLogs()
|
|
||||||
}, label: {
|
|
||||||
Text("help_troubleshooting_clean_logs")
|
|
||||||
.default_text_style_orange_500(styleSize: 14)
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.padding(.horizontal, 15)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background(Color.orangeMain100)
|
|
||||||
.cornerRadius(60)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(
|
|
||||||
action: {
|
|
||||||
helpViewModel.shareLogs()
|
|
||||||
}, label: {
|
|
||||||
Text("help_troubleshooting_share_logs")
|
|
||||||
.default_text_style_orange_500(styleSize: 14)
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.padding(.horizontal, 15)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background(Color.orangeMain100)
|
|
||||||
.cornerRadius(60)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
UIPasteboard.general.setValue(
|
|
||||||
helpViewModel.version,
|
|
||||||
forPasteboardType: UTType.plainText.identifier
|
|
||||||
)
|
|
||||||
ToastViewModel.shared.toastMessage = "Success_text_copied_into_clipboard"
|
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image("app-store-logo")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
Text("help_troubleshooting_app_version_title")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
|
|
||||||
Text(helpViewModel.version)
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 5)
|
|
||||||
|
|
||||||
Image("copy")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
UIPasteboard.general.setValue(
|
|
||||||
helpViewModel.sdkVersion,
|
|
||||||
forPasteboardType: UTType.plainText.identifier
|
|
||||||
)
|
|
||||||
ToastViewModel.shared.toastMessage = "Success_text_copied_into_clipboard"
|
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image("package")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
Text("help_troubleshooting_sdk_version_title")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
|
|
||||||
Text(helpViewModel.sdkVersion)
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 5)
|
|
||||||
|
|
||||||
Image("copy")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image("fire")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
Text("help_troubleshooting_firebase_project_title")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
|
|
||||||
Text(helpViewModel.firebaseProjectId)
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.vertical, 20)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.background(Color.gray100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.background(Color.gray100)
|
|
||||||
}
|
|
||||||
.background(Color.gray100)
|
|
||||||
|
|
||||||
if helpViewModel.logsUploadInProgress {
|
|
||||||
PopupLoadingView()
|
|
||||||
.background(.black.opacity(0.65))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.navigationTitle("")
|
|
||||||
.navigationBarHidden(true)
|
|
||||||
.onChange(of: helpViewModel.logText) { _ in
|
|
||||||
showShareSheet = true
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $showShareSheet) {
|
|
||||||
ShareAnySheet(items: [helpViewModel.logText])
|
|
||||||
.edgesIgnoringSafeArea(.bottom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ShareAnySheet: UIViewControllerRepresentable {
|
|
||||||
var items: [Any] // The content to share
|
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> UIActivityViewController {
|
|
||||||
UIActivityViewController(activityItems: items, applicationActivities: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
|
|
||||||
// No updates needed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,275 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
|
||||||
*
|
|
||||||
* This file is part of linphone-iphone
|
|
||||||
*
|
|
||||||
* 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct HelpFragment: View {
|
|
||||||
|
|
||||||
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
|
|
||||||
|
|
||||||
@ObservedObject var helpViewModel: HelpViewModel
|
|
||||||
|
|
||||||
@Binding var isShowHelpFragment: Bool
|
|
||||||
|
|
||||||
@State var advancedSettingsIsOpen: Bool = false
|
|
||||||
|
|
||||||
@FocusState var isVoicemailUriFocused: Bool
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationView {
|
|
||||||
ZStack {
|
|
||||||
VStack(spacing: 1) {
|
|
||||||
Rectangle()
|
|
||||||
.foregroundColor(Color.orangeMain500)
|
|
||||||
.edgesIgnoringSafeArea(.top)
|
|
||||||
.frame(height: 0)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Image("caret-left")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.padding(.top, 4)
|
|
||||||
.padding(.leading, -10)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
isShowHelpFragment = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("help_title")
|
|
||||||
.default_text_style_orange_800(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(.top, 4)
|
|
||||||
.lineLimit(1)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.frame(height: 50)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.bottom, 4)
|
|
||||||
.background(.white)
|
|
||||||
|
|
||||||
ScrollView {
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
VStack(spacing: 20) {
|
|
||||||
Text("help_about_title")
|
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(.bottom, 5)
|
|
||||||
|
|
||||||
Button {
|
|
||||||
if let url = URL(string: NSLocalizedString("website_privacy_policy_url", comment: "")) {
|
|
||||||
UIApplication.shared.open(url)
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image("detective")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
Text("help_about_privacy_policy_title")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
|
|
||||||
Text("help_about_privacy_policy_subtitle")
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 5)
|
|
||||||
|
|
||||||
Image("arrow-square-out")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Image("info")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
Text("help_about_version_title")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
|
|
||||||
Text(helpViewModel.version)
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 5)
|
|
||||||
|
|
||||||
Button(
|
|
||||||
action: {
|
|
||||||
helpViewModel.checkForUpdate()
|
|
||||||
}, label: {
|
|
||||||
Text("help_about_check_for_update")
|
|
||||||
.default_text_style_orange_500(styleSize: 14)
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.padding(.horizontal, 15)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.background(Color.orangeMain100)
|
|
||||||
.cornerRadius(60)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
if let url = URL(string: NSLocalizedString("website_open_source_licences_usage_url", comment: "")) {
|
|
||||||
UIApplication.shared.open(url)
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image("check-square-offset")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
Text("help_about_open_source_licenses_title")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
|
|
||||||
Text("help_about_open_source_licenses_subtitle")
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 5)
|
|
||||||
|
|
||||||
Image("arrow-square-out")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
if let url = URL(string: NSLocalizedString("website_translate_weblate_url", comment: "")) {
|
|
||||||
UIApplication.shared.open(url)
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image("earth")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
|
|
||||||
Text("help_about_contribute_translations_title")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
.padding(.horizontal, 5)
|
|
||||||
|
|
||||||
Image("arrow-square-out")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("help_about_advanced_title")
|
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(.top, 20)
|
|
||||||
.padding(.bottom, 5)
|
|
||||||
|
|
||||||
NavigationLink(destination: {
|
|
||||||
DebugFragment(helpViewModel: helpViewModel)
|
|
||||||
}, label: {
|
|
||||||
HStack {
|
|
||||||
Image("wrench")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
|
|
||||||
Text("help_troubleshooting_title")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
.padding(.horizontal, 5)
|
|
||||||
|
|
||||||
Image("caret-right")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.padding(.all, 20)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
}
|
|
||||||
.background(Color.gray100)
|
|
||||||
}
|
|
||||||
.background(Color.gray100)
|
|
||||||
|
|
||||||
if helpViewModel.checkUpdateAvailable {
|
|
||||||
PopupView(
|
|
||||||
isShowPopup: $helpViewModel.checkUpdateAvailable,
|
|
||||||
title: Text("help_dialog_update_available_title"),
|
|
||||||
content: Text(String(format: String(localized: "help_dialog_update_available_message"), helpViewModel.versionAvailable)),
|
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
|
||||||
actionFirstButton: {
|
|
||||||
helpViewModel.checkUpdateAvailable = false
|
|
||||||
},
|
|
||||||
titleSecondButton: Text("dialog_install"),
|
|
||||||
actionSecondButton: {
|
|
||||||
helpViewModel.checkUpdateAvailable = false
|
|
||||||
if let url = URL(string: helpViewModel.urlVersionAvailable) {
|
|
||||||
UIApplication.shared.open(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
|
||||||
.zIndex(3)
|
|
||||||
.onTapGesture {
|
|
||||||
helpViewModel.checkUpdateAvailable = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.navigationTitle("")
|
|
||||||
.navigationBarHidden(true)
|
|
||||||
}
|
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
|
||||||
*
|
|
||||||
* This file is part of linphone-iphone
|
|
||||||
*
|
|
||||||
* 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import linphonesw
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class HelpViewModel: ObservableObject {
|
|
||||||
private let TAG = "[HelpViewModel]"
|
|
||||||
|
|
||||||
@Published var logcat: Bool = false
|
|
||||||
@Published var logText: String = ""
|
|
||||||
@Published var version: String = ""
|
|
||||||
@Published var appVersion: String = ""
|
|
||||||
@Published var sdkVersion: String = ""
|
|
||||||
@Published var firebaseProjectId: String = ""
|
|
||||||
@Published var checkUpdateAvailable: Bool = false
|
|
||||||
@Published var uploadLogsAvailable: Bool = false
|
|
||||||
@Published var logsUploadInProgress: Bool = false
|
|
||||||
@Published var versionAvailable: String = ""
|
|
||||||
@Published var urlVersionAvailable: String = ""
|
|
||||||
|
|
||||||
private var coreDelegate: CoreDelegate?
|
|
||||||
|
|
||||||
init() {
|
|
||||||
let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String
|
|
||||||
let versionTmp = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
|
||||||
let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String
|
|
||||||
|
|
||||||
self.version = (versionTmp ?? "6.0.0") + "-beta-" + (build ?? "0")
|
|
||||||
|
|
||||||
self.sdkVersion = Core.getVersion
|
|
||||||
|
|
||||||
if let path = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist"),
|
|
||||||
let plist = NSDictionary(contentsOfFile: path) as? [String: Any],
|
|
||||||
let projectID = plist["PROJECT_ID"] as? String {
|
|
||||||
firebaseProjectId = projectID
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
self.coreDelegate = CoreDelegateStub(
|
|
||||||
onLogCollectionUploadStateChanged: {(
|
|
||||||
core: Core,
|
|
||||||
state: Core.LogCollectionUploadState,
|
|
||||||
info: String
|
|
||||||
) in
|
|
||||||
if info.starts(with: "https") {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.logsUploadInProgress = false
|
|
||||||
self.logText = info
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onVersionUpdateCheckResultReceived: {(
|
|
||||||
core: Core,
|
|
||||||
result: VersionUpdateCheckResult,
|
|
||||||
version: String?,
|
|
||||||
url: String?
|
|
||||||
) in
|
|
||||||
switch result {
|
|
||||||
case .NewVersionAvailable:
|
|
||||||
if let version = version, let url = url {
|
|
||||||
Log.info("\(self.TAG): Update available, version [\(version)], url [\(url)]")
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.versionAvailable = version
|
|
||||||
self.urlVersionAvailable = url
|
|
||||||
self.checkUpdateAvailable = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .UpToDate:
|
|
||||||
Log.info("\(self.TAG): This version is up-to-date")
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
ToastViewModel.shared.toastMessage = "Success_version_up_to_date"
|
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
Log.info("\(self.TAG): Can't check for update, an error happened [\(result)]")
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
ToastViewModel.shared.toastMessage = "Error"
|
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
core.addDelegate(delegate: self.coreDelegate!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
if let delegate = coreDelegate {
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
core.removeDelegate(delegate: delegate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func toggleLogcat() {
|
|
||||||
let newValue = !self.logcat
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
CorePreferences.printLogsInLogcat = newValue
|
|
||||||
Factory.Instance.enableLogcatLogs(newValue)
|
|
||||||
self.logcat = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func shareLogs() {
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
Log.info("\(self.TAG) Uploading debug logs for sharing")
|
|
||||||
core.uploadLogCollection()
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.logsUploadInProgress = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanLogs() {
|
|
||||||
CoreContext.shared.doOnCoreQueue { _ in
|
|
||||||
Core.resetLogCollection()
|
|
||||||
Log.info("\(self.TAG) Debug logs have been cleaned")
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
ToastViewModel.shared.toastMessage = "Success_clear_logs"
|
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkForUpdate() {
|
|
||||||
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
Log.info("\(self.TAG) Checking for update using current version \(currentVersion ?? "6.0.0")")
|
|
||||||
core.checkForUpdate(currentVersion: currentVersion ?? "6.0.0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func showConfigFile() {
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
Log.i("\(self.TAG) Dumping & displaying Core's config")
|
|
||||||
let config = core.config.dump()
|
|
||||||
let file = FileUtils.getFileStorageCacheDir(
|
|
||||||
"linphonerc.txt",
|
|
||||||
overrideExisting: true
|
|
||||||
)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if FileUtils.dumpStringToFile(config, file: file) {
|
|
||||||
Log.i("\(self.TAG) .linphonerc string saved as file in cache folder")
|
|
||||||
self.showConfigFileEvent = Event(value: file.absolutePath)
|
|
||||||
} else {
|
|
||||||
Log.e("\(self.TAG) Failed to save .linphonerc string as file in cache folder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func clearNativeFriendsDatabase() {
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
if let list = core.getFriendListByName(NATIVE_ADDRESS_BOOK_FRIEND_LIST) {
|
|
||||||
let friends = list.friends
|
|
||||||
Log.i("\(self.TAG) Friend list to remove found with [\(friends.count)] friends")
|
|
||||||
for friend in friends {
|
|
||||||
list.removeFriend(friend)
|
|
||||||
}
|
|
||||||
core.removeFriendList(list)
|
|
||||||
Log.i("\(self.TAG) Friend list [\(NATIVE_ADDRESS_BOOK_FRIEND_LIST)] removed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
@ -54,7 +54,7 @@ struct DialerBottomSheet: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("dialog_close") {
|
Button("Close") {
|
||||||
showingDialer.toggle()
|
showingDialer.toggle()
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ struct HistoryListBottomSheet: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("dialog_close") {
|
Button("Close") {
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
showingSheet.toggle()
|
showingSheet.toggle()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ struct StartCallFragment: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(!callViewModel.isTransferInsteadCall ? "history_call_start_title" : "call_transfer_current_call_title")
|
Text(!callViewModel.isTransferInsteadCall ? "history_call_start_title" : "Transfer call to")
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.default_text_style_orange_800(styleSize: 16)
|
.default_text_style_orange_800(styleSize: 16)
|
||||||
|
|
||||||
|
|
@ -304,10 +304,8 @@ struct StartCallFragment: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("")
|
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Sendable private func delayColor() async {
|
@Sendable private func delayColor() async {
|
||||||
|
|
@ -377,51 +375,27 @@ struct StartCallFragment: View {
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
if index < contactsManager.lastSearchSuggestions.count
|
if index < contactsManager.lastSearchSuggestions.count
|
||||||
&& contactsManager.lastSearchSuggestions[index].address != nil {
|
&& contactsManager.lastSearchSuggestions[index].address != nil
|
||||||
if contactsManager.lastSearchSuggestions[index].address!.displayName != nil {
|
&& contactsManager.lastSearchSuggestions[index].address!.username != nil {
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: contactsManager.lastSearchSuggestions[index].address!.displayName!,
|
Image(uiImage: contactsManager.textToImage(
|
||||||
lastName: ""))
|
firstName: contactsManager.lastSearchSuggestions[index].address!.username!,
|
||||||
.resizable()
|
lastName: ""))
|
||||||
.frame(width: 45, height: 45)
|
.resizable()
|
||||||
.clipShape(Circle())
|
.frame(width: 45, height: 45)
|
||||||
|
.clipShape(Circle())
|
||||||
Text(contactsManager.lastSearchSuggestions[index].address?.displayName ?? "")
|
|
||||||
.default_text_style(styleSize: 16)
|
Text(contactsManager.lastSearchSuggestions[index].address?.username ?? "")
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.default_text_style(styleSize: 16)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
} else if contactsManager.lastSearchSuggestions[index].address!.username != nil {
|
.foregroundStyle(Color.orangeMain500)
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: contactsManager.lastSearchSuggestions[index].address!.username!,
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(contactsManager.lastSearchSuggestions[index].address!.username ?? "")
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
} else {
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Image("profil-picture-default")
|
Image("profil-picture-default")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 45, height: 45)
|
.frame(width: 45, height: 45)
|
||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
|
|
||||||
Text("username_error")
|
Text("Username error")
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.foregroundStyle(Color.orangeMain500)
|
||||||
|
|
|
||||||
|
|
@ -112,27 +112,24 @@ class HistoryModel: ObservableObject {
|
||||||
|
|
||||||
func refreshAvatarModel() {
|
func refreshAvatarModel() {
|
||||||
coreContext.doOnCoreQueue { _ in
|
coreContext.doOnCoreQueue { _ in
|
||||||
guard let address = (self.callLog.dir == .Outgoing ? self.callLog.toAddress : self.callLog.fromAddress) else {
|
let addressFriendTmp = ContactsManager.shared.getFriendWithAddress(
|
||||||
DispatchQueue.main.async {
|
address: self.callLog.dir == .Outgoing ? self.callLog.toAddress! : self.callLog.fromAddress!
|
||||||
self.avatarModel = ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
|
)
|
||||||
}
|
if addressFriendTmp != nil {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let addressFriendTmp = ContactsManager.shared.getFriendWithAddress(address: address)
|
|
||||||
if let addressFriendTmp = addressFriendTmp {
|
|
||||||
self.addressFriend = addressFriendTmp
|
self.addressFriend = addressFriendTmp
|
||||||
|
|
||||||
let addressNameTmp = self.addressName
|
let addressNameTmp = self.addressName
|
||||||
|
|
||||||
let avatarModelTmp = ContactsManager.shared.avatarListModel.first(where: {
|
let avatarModelTmp = addressFriendTmp != nil
|
||||||
guard let friend = $0.friend else { return false }
|
? ContactsManager.shared.avatarListModel.first(where: {
|
||||||
return friend.name == addressFriendTmp.name && friend.address?.asStringUriOnly() == addressFriendTmp.address?.asStringUriOnly()
|
$0.friend!.name == addressFriendTmp!.name
|
||||||
|
&& $0.friend!.address!.asStringUriOnly() == addressFriendTmp!.address!.asStringUriOnly()
|
||||||
}) ?? ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
|
}) ?? ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
|
||||||
|
: ContactAvatarModel(friend: nil, name: self.addressName, address: self.address, withPresence: false)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.addressFriend = addressFriendTmp
|
self.addressFriend = addressFriendTmp
|
||||||
self.addressName = addressFriendTmp.name ?? addressNameTmp
|
self.addressName = addressFriendTmp!.name ?? addressNameTmp
|
||||||
self.avatarModel = avatarModelTmp
|
self.avatarModel = avatarModelTmp
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class HistoryListViewModel: ObservableObject {
|
class HistoryListViewModel: ObservableObject {
|
||||||
|
|
||||||
|
|
@ -149,23 +148,23 @@ class HistoryListViewModel: ObservableObject {
|
||||||
switch callStatus {
|
switch callStatus {
|
||||||
case Call.Status.Missed:
|
case Call.Status.Missed:
|
||||||
if isOutgoing {
|
if isOutgoing {
|
||||||
String(localized: "call_outgoing")
|
"Outgoing Call"
|
||||||
} else {
|
} else {
|
||||||
String(localized: "notification_missed_call_title")
|
"Missed Call"
|
||||||
}
|
}
|
||||||
|
|
||||||
case Call.Status.Success:
|
case Call.Status.Success:
|
||||||
if isOutgoing {
|
if isOutgoing {
|
||||||
String(localized: "call_outgoing")
|
"Outgoing Call"
|
||||||
} else {
|
} else {
|
||||||
String(localized: "call_audio_incoming")
|
"Incoming Call"
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if isOutgoing {
|
if isOutgoing {
|
||||||
String(localized: "call_outgoing")
|
"Outgoing Call"
|
||||||
} else {
|
} else {
|
||||||
String(localized: "call_audio_incoming")
|
"Incoming Call"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
// swiftlint:disable line_length
|
// swiftlint:disable line_length
|
||||||
class StartCallViewModel: ObservableObject {
|
class StartCallViewModel: ObservableObject {
|
||||||
|
|
@ -38,7 +37,8 @@ class StartCallViewModel: ObservableObject {
|
||||||
|
|
||||||
@Published var operationInProgress: Bool = false
|
@Published var operationInProgress: Bool = false
|
||||||
|
|
||||||
private var conferenceDelegate: ConferenceDelegate?
|
private var conferenceScheduler: ConferenceScheduler?
|
||||||
|
private var conferenceSchedulerDelegate: ConferenceSchedulerDelegate?
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
coreContext.doOnCoreQueue { core in
|
coreContext.doOnCoreQueue { core in
|
||||||
|
|
@ -67,7 +67,7 @@ class StartCallViewModel: ObservableObject {
|
||||||
let account = core.defaultAccount
|
let account = core.defaultAccount
|
||||||
if account == nil {
|
if account == nil {
|
||||||
Log.error(
|
Log.error(
|
||||||
"\(ConversationModel.TAG) No default account found, can't create group call!"
|
"\(StartCallViewModel.TAG) No default account found, can't create group call!"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -77,64 +77,86 @@ class StartCallViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
var participantsList: [Address] = []
|
let conferenceInfo = try Factory.Instance.createConferenceInfo()
|
||||||
|
conferenceInfo.organizer = account!.params?.identityAddress
|
||||||
|
conferenceInfo.subject = self.messageText
|
||||||
|
|
||||||
|
var participantsList: [ParticipantInfo] = []
|
||||||
self.participants.forEach { participant in
|
self.participants.forEach { participant in
|
||||||
participantsList.append(participant.address)
|
do {
|
||||||
|
let info = try Factory.Instance.createParticipantInfo(address: participant.address)
|
||||||
|
// For meetings, all participants must have Speaker role
|
||||||
|
info.role = Participant.Role.Speaker
|
||||||
|
participantsList.append(info)
|
||||||
|
} catch let error {
|
||||||
|
Log.error(
|
||||||
|
"\(StartCallViewModel.TAG) Can't create ParticipantInfo: \(error)"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.participants.removeAll()
|
self.participants.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conferenceInfo.addParticipantInfos(participantInfos: participantsList)
|
||||||
|
|
||||||
Log.info(
|
Log.info(
|
||||||
"\(ConversationModel.TAG) Creating group call with subject \(self.messageText) and \(participantsList.count) participant(s)"
|
"\(StartCallViewModel.TAG) Creating group call with subject \(self.messageText) and \(participantsList.count) participant(s)"
|
||||||
)
|
)
|
||||||
|
|
||||||
if let conference = LinphoneUtils.createGroupCall(core: core, account: account, subject: self.messageText) {
|
self.conferenceScheduler = try core.createConferenceScheduler(account: account)
|
||||||
self.conferenceAddDelegate(core: core, conference: conference)
|
if self.conferenceScheduler != nil {
|
||||||
let callParams = try? core.createCallParams(call: nil)
|
self.conferenceAddDelegate(core: core, conferenceScheduler: self.conferenceScheduler!)
|
||||||
if let callParams = callParams {
|
// Will trigger the conference creation/update automatically
|
||||||
callParams.videoEnabled = true
|
self.conferenceScheduler!.info = conferenceInfo
|
||||||
callParams.videoDirection = .RecvOnly
|
|
||||||
|
|
||||||
Log.info("\(ConversationModel.TAG) Inviting \(participantsList.count) participant(s) into newly created conference")
|
|
||||||
|
|
||||||
try conference.inviteParticipants(addresses: participantsList, params: callParams)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
TelecomManager.shared.participantsInvited = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch let error {
|
||||||
Log.error(
|
Log.error(
|
||||||
"\(ConversationModel.TAG) createGroupCall: \(error)"
|
"\(StartCallViewModel.TAG) createGroupCall: \(error)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func conferenceAddDelegate(core: Core, conference: Conference) {
|
func conferenceAddDelegate(core: Core, conferenceScheduler: ConferenceScheduler) {
|
||||||
self.conferenceDelegate = ConferenceDelegateStub(onStateChanged: { (conference: Conference, state: Conference.State) in
|
self.conferenceSchedulerDelegate = ConferenceSchedulerDelegateStub(onStateChanged: { (conferenceScheduler: ConferenceScheduler, state: ConferenceScheduler.State) in
|
||||||
Log.info("\(StartCallViewModel.TAG) Conference state is \(state)")
|
Log.info("\(StartCallViewModel.TAG) Conference scheduler state is \(state)")
|
||||||
if state == .Created {
|
if state == ConferenceScheduler.State.Ready {
|
||||||
NotificationCenter.default.post(name: Notification.Name("CallViewModelReset"), object: self)
|
conferenceScheduler.removeDelegate(delegate: self.conferenceSchedulerDelegate!)
|
||||||
|
self.conferenceSchedulerDelegate = nil
|
||||||
|
|
||||||
|
let conferenceAddress = conferenceScheduler.info?.uri
|
||||||
|
if conferenceAddress != nil {
|
||||||
|
Log.info(
|
||||||
|
"\(StartCallViewModel.TAG) Conference info created, address is \(conferenceAddress?.asStringUriOnly() ?? "Error conference address")"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.startVideoCall(core: core, conferenceAddress: conferenceAddress!)
|
||||||
|
} else {
|
||||||
|
Log.error("\(StartCallViewModel.TAG) Conference info URI is null!")
|
||||||
|
|
||||||
|
ToastViewModel.shared.toastMessage = "Failed_to_create_group_call_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
}
|
}
|
||||||
} else if state == .CreationFailed {
|
} else if state == ConferenceScheduler.State.Error {
|
||||||
|
conferenceScheduler.removeDelegate(delegate: self.conferenceSchedulerDelegate!)
|
||||||
|
self.conferenceSchedulerDelegate = nil
|
||||||
Log.error("\(StartCallViewModel.TAG) Failed to create group call!")
|
Log.error("\(StartCallViewModel.TAG) Failed to create group call!")
|
||||||
|
|
||||||
|
ToastViewModel.shared.toastMessage = "Failed_to_create_group_call_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.toastMessage = "Failed_to_create_group_call_error"
|
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
conferenceScheduler.addDelegate(delegate: self.conferenceSchedulerDelegate!)
|
||||||
if self.conferenceDelegate != nil {
|
|
||||||
conference.addDelegate(delegate: self.conferenceDelegate!)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startVideoCall(core: Core, conferenceAddress: Address) {
|
func startVideoCall(core: Core, conferenceAddress: Address) {
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,8 @@ struct AddParticipantsFragment: View {
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.default_text_style_orange_800(styleSize: 16)
|
.default_text_style_orange_800(styleSize: 16)
|
||||||
.padding(.top, 20)
|
.padding(.top, 20)
|
||||||
|
Text("\($addParticipantsViewModel.participantsToAdd.count) selected participants")
|
||||||
Text(String(format: String(localized: "selected_participants_count"), $addParticipantsViewModel.participantsToAdd.count.description))
|
.default_text_style_300(styleSize: 12)
|
||||||
.default_text_style_300(styleSize: 12)
|
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
@ -218,7 +217,7 @@ struct AddParticipantsFragment: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
Text("generic_address_picker_suggestions_list_title")
|
Text("Suggestions")
|
||||||
.default_text_style_800(styleSize: 16)
|
.default_text_style_800(styleSize: 16)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
@ -259,51 +258,27 @@ struct AddParticipantsFragment: View {
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
if index < contactsManager.lastSearchSuggestions.count
|
if index < contactsManager.lastSearchSuggestions.count
|
||||||
&& contactsManager.lastSearchSuggestions[index].address != nil {
|
&& contactsManager.lastSearchSuggestions[index].address != nil
|
||||||
if contactsManager.lastSearchSuggestions[index].address!.displayName != nil {
|
&& contactsManager.lastSearchSuggestions[index].address!.username != nil {
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: contactsManager.lastSearchSuggestions[index].address!.displayName!,
|
Image(uiImage: contactsManager.textToImage(
|
||||||
lastName: ""))
|
firstName: contactsManager.lastSearchSuggestions[index].address!.username!,
|
||||||
.resizable()
|
lastName: ""))
|
||||||
.frame(width: 45, height: 45)
|
.resizable()
|
||||||
.clipShape(Circle())
|
.frame(width: 45, height: 45)
|
||||||
|
.clipShape(Circle())
|
||||||
Text(contactsManager.lastSearchSuggestions[index].address?.displayName ?? "")
|
|
||||||
.default_text_style(styleSize: 16)
|
Text(contactsManager.lastSearchSuggestions[index].address?.username ?? "")
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.default_text_style(styleSize: 16)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
} else if contactsManager.lastSearchSuggestions[index].address!.username != nil {
|
.foregroundStyle(Color.orangeMain500)
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: contactsManager.lastSearchSuggestions[index].address!.username!,
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(contactsManager.lastSearchSuggestions[index].address!.username ?? "")
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
} else {
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Image("profil-picture-default")
|
Image("profil-picture-default")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 45, height: 45)
|
.frame(width: 45, height: 45)
|
||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
|
|
||||||
Text("username_error")
|
Text("Username error")
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.foregroundStyle(Color.orangeMain500)
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,7 @@ struct MeetingFragment: View {
|
||||||
let model = MeetingViewModel()
|
let model = MeetingViewModel()
|
||||||
model.subject = "Meeting subject"
|
model.subject = "Meeting subject"
|
||||||
model.conferenceUri = "linphone.com/lalalal.fr"
|
model.conferenceUri = "linphone.com/lalalal.fr"
|
||||||
model.description = ""
|
model.description = "description du meeting ça va être la bringue wesh wesh gros bien ou bien ça roule"
|
||||||
return MeetingFragment(meetingViewModel: model
|
return MeetingFragment(meetingViewModel: model
|
||||||
, meetingsListViewModel: MeetingsListViewModel()
|
, meetingsListViewModel: MeetingsListViewModel()
|
||||||
, isShowScheduleMeetingFragment: .constant(true)
|
, isShowScheduleMeetingFragment: .constant(true)
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ struct MeetingsListBottomSheet: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Button("dialog_close") {
|
Button("Close") {
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
showingSheet.toggle()
|
showingSheet.toggle()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -412,9 +412,7 @@ struct ScheduleMeetingFragment: View {
|
||||||
func getDatePopup(isTimeSelection: Bool) -> some View {
|
func getDatePopup(isTimeSelection: Bool) -> some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text(setFromDate ?
|
Text("Select \(setFromDate ? "start" : "end") \(isTimeSelection ? "time" : "date")")
|
||||||
(isTimeSelection ? String(localized: "meeting_schedule_pick_start_time_title") : String(localized: "meeting_schedule_pick_start_date_title"))
|
|
||||||
: String(localized: "meeting_schedule_pick_end_time_title"))
|
|
||||||
.default_text_style_800(styleSize: 16)
|
.default_text_style_800(styleSize: 16)
|
||||||
.frame(alignment: .leading)
|
.frame(alignment: .leading)
|
||||||
.padding(.bottom, 2)
|
.padding(.bottom, 2)
|
||||||
|
|
@ -435,7 +433,7 @@ struct ScheduleMeetingFragment: View {
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Text("dialog_cancel")
|
Text("Cancel")
|
||||||
.default_text_style_orange_500(styleSize: 16)
|
.default_text_style_orange_500(styleSize: 16)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if isTimeSelection {
|
if isTimeSelection {
|
||||||
|
|
@ -444,7 +442,7 @@ struct ScheduleMeetingFragment: View {
|
||||||
showDatePicker.toggle()
|
showDatePicker.toggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text("dialog_ok")
|
Text("Ok")
|
||||||
.default_text_style_orange_500(styleSize: 16)
|
.default_text_style_orange_500(styleSize: 16)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
pickDate()
|
pickDate()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class MeetingModel: ObservableObject {
|
class MeetingModel: ObservableObject {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,23 +181,14 @@ class MeetingViewModel: ObservableObject {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
self.errorMsg = (self.displayedMeeting != nil) ? "Could not edit conference" : "Could not create conference"
|
self.errorMsg = (self.displayedMeeting != nil) ? "Could not edit conference" : "Could not create conference"
|
||||||
ToastViewModel.shared.toastMessage = (self.displayedMeeting != nil) ? "meeting_failed_to_edit_toast" : "meeting_failed_to_schedule_toast"
|
// TODO: show error toast
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
}
|
}
|
||||||
} else if state == ConferenceScheduler.State.Ready {
|
} else if state == ConferenceScheduler.State.Ready {
|
||||||
let conferenceAddress = self.conferenceScheduler?.info?.uri
|
let conferenceAddress = self.conferenceScheduler?.info?.uri
|
||||||
if let confInfoToEdit = self.conferenceInfoToEdit {
|
if let confInfoToEdit = self.conferenceInfoToEdit {
|
||||||
Log.info("\(MeetingViewModel.TAG) Conference info \(confInfoToEdit.uri?.asStringUriOnly() ?? "'nil'") has been updated")
|
Log.info("\(MeetingViewModel.TAG) Conference info \(confInfoToEdit.uri?.asStringUriOnly() ?? "'nil'") has been updated")
|
||||||
DispatchQueue.main.async {
|
|
||||||
ToastViewModel.shared.toastMessage = "Success_meeting_info_updated_toast"
|
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Log.info("\(MeetingViewModel.TAG) Conference info created, address will be \(conferenceAddress?.asStringUriOnly() ?? "'nil'")")
|
Log.info("\(MeetingViewModel.TAG) Conference info created, address will be \(conferenceAddress?.asStringUriOnly() ?? "'nil'")")
|
||||||
DispatchQueue.main.async {
|
|
||||||
ToastViewModel.shared.toastMessage = "Success_meeting_info_created_toast"
|
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.sendInvitations {
|
if self.sendInvitations {
|
||||||
|
|
@ -211,9 +202,6 @@ class MeetingViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if state == ConferenceScheduler.State.Updating {
|
} else if state == ConferenceScheduler.State.Updating {
|
||||||
DispatchQueue.main.async {
|
|
||||||
ToastViewModel.shared.displayToast = true
|
|
||||||
}
|
|
||||||
self.sendIcsInvitation(core: core)
|
self.sendIcsInvitation(core: core)
|
||||||
}
|
}
|
||||||
}, onInvitationsSent: { (_: ConferenceScheduler, failedInvitations: [Address]) in
|
}, onInvitationsSent: { (_: ConferenceScheduler, failedInvitations: [Address]) in
|
||||||
|
|
@ -223,7 +211,7 @@ class MeetingViewModel: ObservableObject {
|
||||||
} else if failedInvitations.count == self.participants.count {
|
} else if failedInvitations.count == self.participants.count {
|
||||||
Log.error("\(MeetingViewModel.TAG) No invitation sent!")
|
Log.error("\(MeetingViewModel.TAG) No invitation sent!")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.toastMessage = "meeting_failed_to_send_invites_toast"
|
ToastViewModel.shared.toastMessage = "Failed_meeting_invitations_not_sent"
|
||||||
ToastViewModel.shared.displayToast = true
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -236,7 +224,7 @@ class MeetingViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
Log.warn("\(MeetingViewModel.TAG) \(failedInvitations.count) invitations couldn't have been sent to: \(failInvList)")
|
Log.warn("\(MeetingViewModel.TAG) \(failedInvitations.count) invitations couldn't have been sent to: \(failInvList)")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.toastMessage = "meeting_failed_to_send_part_of_invites_toast"
|
ToastViewModel.shared.toastMessage = "Error: \(failedInvitations.count) invitations couldn't be sent to \(failInvList)"
|
||||||
ToastViewModel.shared.displayToast = true
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,19 +262,6 @@ class MeetingViewModel: ObservableObject {
|
||||||
if let conferenceInfo = (self.displayedMeeting != nil ? self.displayedMeeting!.confInfo : try? Factory.Instance.createConferenceInfo()) {
|
if let conferenceInfo = (self.displayedMeeting != nil ? self.displayedMeeting!.confInfo : try? Factory.Instance.createConferenceInfo()) {
|
||||||
let localAccount = core.defaultAccount
|
let localAccount = core.defaultAccount
|
||||||
conferenceInfo.organizer = localAccount?.params?.identityAddress
|
conferenceInfo.organizer = localAccount?.params?.identityAddress
|
||||||
|
|
||||||
// Allows to have a chat room within the conference
|
|
||||||
conferenceInfo.setCapability(streamType: StreamType.Text, enable: true)
|
|
||||||
|
|
||||||
// Enable end-to-end encryption if client supports it
|
|
||||||
if LinphoneUtils.isEndToEndEncryptedChatAvailable(core: core) {
|
|
||||||
Log.info("\(MeetingViewModel.TAG) Requesting EndToEnd security level for conference")
|
|
||||||
conferenceInfo.securityLevel = Conference.SecurityLevel.EndToEnd
|
|
||||||
} else {
|
|
||||||
Log.info("\(MeetingViewModel.TAG) Requesting PointToPoint security level for conference")
|
|
||||||
conferenceInfo.securityLevel = Conference.SecurityLevel.PointToPoint
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fillConferenceInfo(confInfo: conferenceInfo)
|
self.fillConferenceInfo(confInfo: conferenceInfo)
|
||||||
self.resetConferenceSchedulerAndListeners(core: core)
|
self.resetConferenceSchedulerAndListeners(core: core)
|
||||||
self.conferenceScheduler?.account = localAccount
|
self.conferenceScheduler?.account = localAccount
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ class MeetingsListViewModel: ObservableObject {
|
||||||
todayIdx = currentIdx
|
todayIdx = currentIdx
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.sync {
|
||||||
self.todayIdx = todayIdx
|
self.todayIdx = todayIdx
|
||||||
self.meetingsList = meetingsListTmp
|
self.meetingsList = meetingsListTmp
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,672 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
|
||||||
*
|
|
||||||
* This file is part of linphone-iphone
|
|
||||||
*
|
|
||||||
* 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import UniformTypeIdentifiers
|
|
||||||
|
|
||||||
// swiftlint:disable type_body_length
|
|
||||||
struct AccountProfileFragment: View {
|
|
||||||
|
|
||||||
@ObservedObject var contactsManager = ContactsManager.shared
|
|
||||||
@ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared
|
|
||||||
|
|
||||||
@ObservedObject var accountProfileViewModel: AccountProfileViewModel
|
|
||||||
@ObservedObject var registerViewModel: RegisterViewModel
|
|
||||||
|
|
||||||
@Binding var isShowAccountProfileFragment: Bool
|
|
||||||
@State var detailIsOpen: Bool = true
|
|
||||||
@State var deviceIsOpen: Bool = false
|
|
||||||
|
|
||||||
@State private var showPhotoPicker = false
|
|
||||||
@State private var selectedImage: UIImage?
|
|
||||||
@State private var removedImage = false
|
|
||||||
@State private var isShowPopup = false
|
|
||||||
@State private var isShowLogoutPopup = false
|
|
||||||
@State private var flag = true
|
|
||||||
|
|
||||||
@FocusState var isDisplayNameFocused: Bool
|
|
||||||
|
|
||||||
private let avatarSize = 100.0
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationView {
|
|
||||||
ZStack {
|
|
||||||
VStack(spacing: 1) {
|
|
||||||
Rectangle()
|
|
||||||
.foregroundColor(Color.orangeMain500)
|
|
||||||
.edgesIgnoringSafeArea(.top)
|
|
||||||
.frame(height: 0)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Image("caret-left")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.padding(.top, 4)
|
|
||||||
.padding(.leading, -10)
|
|
||||||
.onTapGesture {
|
|
||||||
accountProfileViewModel.saveChangesWhenLeaving()
|
|
||||||
withAnimation {
|
|
||||||
if isShowAccountProfileFragment {
|
|
||||||
isShowAccountProfileFragment = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text("manage_account_title")
|
|
||||||
.default_text_style_orange_800(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(.top, 4)
|
|
||||||
.lineLimit(1)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.frame(height: 50)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.bottom, 4)
|
|
||||||
.background(.white)
|
|
||||||
|
|
||||||
ScrollView {
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
if accountProfileViewModel.accountModelIndex != nil && CoreContext.shared.accounts.count > accountProfileViewModel.accountModelIndex! {
|
|
||||||
let accountModel = CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex!]
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
if #unavailable(iOS 16.0) {
|
|
||||||
Rectangle()
|
|
||||||
.foregroundColor(Color.gray100)
|
|
||||||
.frame(height: 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
if accountModel.avatarModel != nil
|
|
||||||
&& accountModel.photoAvatarModel != nil
|
|
||||||
&& !accountModel.photoAvatarModel!.isEmpty
|
|
||||||
&& selectedImage == nil && !removedImage {
|
|
||||||
|
|
||||||
AsyncImage(url: CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex!].imagePathAvatar) { image in
|
|
||||||
switch image {
|
|
||||||
case .empty:
|
|
||||||
ProgressView()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
case .success(let image):
|
|
||||||
image
|
|
||||||
.resizable()
|
|
||||||
.aspectRatio(contentMode: .fill)
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
case .failure:
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: accountModel.avatarModel?.name ?? "",
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
@unknown default:
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if selectedImage == nil {
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: accountModel.avatarModel?.name ?? "",
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
} else {
|
|
||||||
Image(uiImage: selectedImage!)
|
|
||||||
.resizable()
|
|
||||||
.aspectRatio(contentMode: .fill)
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
}
|
|
||||||
|
|
||||||
if accountModel.avatarModel != nil
|
|
||||||
&& accountModel.photoAvatarModel != nil
|
|
||||||
&& !accountModel.photoAvatarModel!.isEmpty
|
|
||||||
&& (accountModel.photoAvatarModel!.suffix(11) != "default.png" || selectedImage != nil)
|
|
||||||
&& !removedImage {
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
showPhotoPicker = true
|
|
||||||
}, label: {
|
|
||||||
HStack {
|
|
||||||
Image("pencil-simple")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
|
|
||||||
Text("manage_account_edit_picture")
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.trailing, 10)
|
|
||||||
.sheet(isPresented: $showPhotoPicker) {
|
|
||||||
PhotoPicker(filter: .images, limit: 1) { results in
|
|
||||||
PhotoPicker.convertToUIImageArray(fromResults: results) { imagesOrNil, errorOrNil in
|
|
||||||
if let error = errorOrNil {
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
if let images = imagesOrNil {
|
|
||||||
if let first = images.first {
|
|
||||||
selectedImage = first
|
|
||||||
removedImage = false
|
|
||||||
saveImage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.edgesIgnoringSafeArea(.all)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
removedImage = true
|
|
||||||
selectedImage = nil
|
|
||||||
saveImage()
|
|
||||||
}, label: {
|
|
||||||
HStack {
|
|
||||||
Image("trash-simple")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
|
|
||||||
Text("manage_account_remove_picture")
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.padding(.top, 10)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Button(action: {
|
|
||||||
showPhotoPicker = true
|
|
||||||
}, label: {
|
|
||||||
HStack {
|
|
||||||
Image("camera")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
|
|
||||||
Text("manage_account_add_picture")
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.padding(.top, 10)
|
|
||||||
.sheet(isPresented: $showPhotoPicker) {
|
|
||||||
PhotoPicker(filter: .images, limit: 1) { results in
|
|
||||||
PhotoPicker.convertToUIImageArray(fromResults: results) { imagesOrNil, errorOrNil in
|
|
||||||
if let error = errorOrNil {
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
if let images = imagesOrNil {
|
|
||||||
if let first = images.first {
|
|
||||||
selectedImage = first
|
|
||||||
removedImage = false
|
|
||||||
showPhotoPicker = false
|
|
||||||
saveImage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.edgesIgnoringSafeArea(.all)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(minHeight: 150)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.bottom, 2)
|
|
||||||
.background(Color.gray100)
|
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Text("manage_account_details_title")
|
|
||||||
.default_text_style_800(styleSize: 18)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Image(detailIsOpen ? "caret-up" : "caret-down")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
}
|
|
||||||
.padding(.top, 30)
|
|
||||||
.padding(.bottom, 10)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.background(Color.gray100)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
detailIsOpen.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if detailIsOpen {
|
|
||||||
if accountModel.avatarModel != nil {
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
VStack(spacing: 30) {
|
|
||||||
HStack {
|
|
||||||
Text(String(localized: "sip_address") + ":")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
|
|
||||||
Text(accountModel.avatarModel!.address)
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.lineLimit(1)
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
UIPasteboard.general.setValue(
|
|
||||||
accountModel.avatarModel!.address,
|
|
||||||
forPasteboardType: UTType.plainText.identifier
|
|
||||||
)
|
|
||||||
|
|
||||||
ToastViewModel.shared.toastMessage = "Success_address_copied_into_clipboard"
|
|
||||||
ToastViewModel.shared.displayToast.toggle()
|
|
||||||
}, label: {
|
|
||||||
Image("copy")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("sip_address_display_name")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
TextField(accountModel.displayNameAvatar, text: Binding(
|
|
||||||
get: { accountModel.displayNameAvatar },
|
|
||||||
set: { newValue in
|
|
||||||
accountModel.displayNameAvatar = newValue
|
|
||||||
}
|
|
||||||
))
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.frame(height: 25)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.background(.white)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(isDisplayNameFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.focused($isDisplayNameFocused)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
HStack {
|
|
||||||
Text("manage_account_international_prefix")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
.lineLimit(1)
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
isShowPopup = true
|
|
||||||
}, label: {
|
|
||||||
Image("question")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
})
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
}
|
|
||||||
Menu {
|
|
||||||
Picker("", selection: $accountProfileViewModel.dialPlanValueSelected) {
|
|
||||||
ForEach(registerViewModel.dialPlansLabelList, id: \.self) { dialPlan in
|
|
||||||
Text(dialPlan).tag(dialPlan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onChange(of: accountProfileViewModel.dialPlanValueSelected) { newValue in
|
|
||||||
accountProfileViewModel.updateDialPlan(newDialPlan: newValue)
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Text(accountProfileViewModel.dialPlanValueSelected)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
|
|
||||||
Image("caret-down")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
}
|
|
||||||
.frame(height: 25)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.background(.white)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.vertical, 30)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
|
||||||
.background(.white)
|
|
||||||
.cornerRadius(15)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.zIndex(-1)
|
|
||||||
.transition(.move(edge: .top))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
VStack(spacing: 15) {
|
|
||||||
HStack(spacing: 20) {
|
|
||||||
Toggle("", isOn: Binding(
|
|
||||||
get: { accountModel.isRegistrered },
|
|
||||||
set: { _ in
|
|
||||||
accountProfileViewModel.toggleRegister()
|
|
||||||
}
|
|
||||||
))
|
|
||||||
.labelsHidden()
|
|
||||||
|
|
||||||
Text(accountModel.humanReadableRegistrationState)
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(accountModel.summary)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
}
|
|
||||||
.padding(.vertical, 30)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
|
||||||
.background(.white)
|
|
||||||
.cornerRadius(15)
|
|
||||||
.padding(.all)
|
|
||||||
.background(Color.gray100)
|
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Text("manage_account_devices_title")
|
|
||||||
.default_text_style_800(styleSize: 18)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Image(deviceIsOpen ? "caret-up" : "caret-down")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.background(Color.gray100)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
deviceIsOpen.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if deviceIsOpen {
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
VStack(spacing: 15) {
|
|
||||||
ForEach(accountModel.devices.indices, id: \.self) { index in
|
|
||||||
VStack {
|
|
||||||
HStack {
|
|
||||||
Image(accountModel.devices[index].isMobileDevice ? "device-mobile-camera" : "desktop")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
|
|
||||||
Text(accountModel.devices[index].deviceName)
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
deviceIsOpen = false
|
|
||||||
accountModel.removeDevice(deviceIndex: index)
|
|
||||||
deviceIsOpen = true
|
|
||||||
}, label: {
|
|
||||||
HStack {
|
|
||||||
Image("trash-simple")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
|
|
||||||
Text("manage_account_device_remove")
|
|
||||||
.default_text_style_orange_500(styleSize: 14)
|
|
||||||
.frame(height: 35)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.background(Color.orangeMain100)
|
|
||||||
.cornerRadius(60)
|
|
||||||
}
|
|
||||||
.padding(.bottom, 10)
|
|
||||||
|
|
||||||
Text("manage_account_device_last_connection")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Image("calendar-blank")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
|
|
||||||
Text(accountModel.devices[index].lastDate)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
|
|
||||||
Image("clock")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
|
|
||||||
Text(accountModel.devices[index].lastTime)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.all, 20)
|
|
||||||
.background(Color.gray100)
|
|
||||||
.cornerRadius(15)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.all, 20)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.overlay(
|
|
||||||
VStack {
|
|
||||||
if accountModel.devices.indices.isEmpty {
|
|
||||||
Text("manage_account_no_device")
|
|
||||||
.default_text_style_500(styleSize: 16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.all)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.background(.white)
|
|
||||||
.cornerRadius(15)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.zIndex(-2)
|
|
||||||
.transition(.move(edge: .top))
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Text("contact_details_actions_title")
|
|
||||||
.default_text_style_800(styleSize: 18)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(.top, 20)
|
|
||||||
.padding(.bottom, 10)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.background(Color.gray100)
|
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
VStack(spacing: 18) {
|
|
||||||
if accountProfileViewModel.accountModelIndex != nil && CoreContext.shared.accounts.count > accountProfileViewModel.accountModelIndex! {
|
|
||||||
NavigationLink(
|
|
||||||
destination:
|
|
||||||
AccountSettingsFragment(
|
|
||||||
accountModel: CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex!]
|
|
||||||
),
|
|
||||||
label: {
|
|
||||||
HStack {
|
|
||||||
Image("gear")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
|
|
||||||
Text("manage_account_settings")
|
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Divider()
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
isShowLogoutPopup = true
|
|
||||||
}, label: {
|
|
||||||
HStack {
|
|
||||||
Image("sign-out")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
|
|
||||||
Text("manage_account_delete")
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.padding(.vertical, 20)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
|
||||||
.background(.white)
|
|
||||||
.cornerRadius(15)
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
|
||||||
.frame(maxWidth: sharedMainViewModel.maxWidth)
|
|
||||||
.onAppear {
|
|
||||||
accountModel.requestDevicesList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
}
|
|
||||||
.background(Color.gray100)
|
|
||||||
}
|
|
||||||
.background(Color.gray100)
|
|
||||||
|
|
||||||
if self.isShowPopup {
|
|
||||||
PopupView(isShowPopup: $isShowPopup,
|
|
||||||
title: Text("manage_account_international_prefix"),
|
|
||||||
content: Text("manage_account_dialog_international_prefix_help_message"),
|
|
||||||
titleFirstButton: nil,
|
|
||||||
actionFirstButton: {},
|
|
||||||
titleSecondButton: Text("dialog_ok"),
|
|
||||||
actionSecondButton: {
|
|
||||||
self.isShowPopup.toggle()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
|
||||||
.onTapGesture {
|
|
||||||
self.isShowPopup.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.isShowLogoutPopup {
|
|
||||||
let localizedString = NSLocalizedString("manage_account_dialog_remove_account_message", comment: "")
|
|
||||||
|
|
||||||
let components = localizedString.components(separatedBy: " ")
|
|
||||||
let textPart = components.dropLast().joined(separator: " ")
|
|
||||||
|
|
||||||
let contentPopup1 = Text(textPart + " ")
|
|
||||||
let contentPopup2 = Text("[https://sip.linphone.org](https://sip.linphone.org)").underline()
|
|
||||||
|
|
||||||
PopupView(
|
|
||||||
isShowPopup: $isShowLogoutPopup,
|
|
||||||
title: Text("manage_account_dialog_remove_account_title"),
|
|
||||||
content: contentPopup1 + contentPopup2,
|
|
||||||
titleFirstButton: Text("dialog_cancel"),
|
|
||||||
actionFirstButton: {
|
|
||||||
self.isShowLogoutPopup.toggle()
|
|
||||||
},
|
|
||||||
titleSecondButton: Text("manage_account_delete"),
|
|
||||||
actionSecondButton: {
|
|
||||||
if accountProfileViewModel.accountModelIndex != nil {
|
|
||||||
CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex!].logout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
|
||||||
.onTapGesture {
|
|
||||||
self.isShowLogoutPopup.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.navigationTitle("")
|
|
||||||
.navigationBarHidden(true)
|
|
||||||
}
|
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveImage() {
|
|
||||||
let accountModel = CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex ?? 0]
|
|
||||||
let usernameTmp = CoreContext.shared.accounts[accountProfileViewModel.accountModelIndex ?? 0].usernaneAvatar
|
|
||||||
|
|
||||||
accountProfileViewModel.saveImage(
|
|
||||||
image: selectedImage
|
|
||||||
?? ContactsManager.shared.textToImage(
|
|
||||||
firstName: accountModel.avatarModel!.name, lastName: ""),
|
|
||||||
name: usernameTmp,
|
|
||||||
prefix: ((selectedImage == nil) ? "-default" : ""))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// swiftlint:enable type_body_length
|
|
||||||