mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 02:58:07 +00:00
Add contacts list
This commit is contained in:
parent
146682e555
commit
9ef96bbd78
27 changed files with 1376 additions and 376 deletions
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
886500223A8E518D3EE5FCB7 /* Pods_Linphone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A334B8FDAD2893691A734BE /* Pods_Linphone.framework */; };
|
||||
D706BA822ADD72D100278F45 /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */; };
|
||||
D70C93DE2AC2D0F60063CA3B /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */; };
|
||||
D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D717071D2AC5922E0037746F /* ColorExtension.swift */; };
|
||||
|
|
@ -18,6 +19,9 @@
|
|||
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABC82ABC6FD700B41C10 /* CoreContext.swift */; };
|
||||
D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABCB2ABC769C00B41C10 /* AssistantView.swift */; };
|
||||
D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */; };
|
||||
D71FCA7F2AE1397200D2E43E /* ContactsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FCA7E2AE1397200D2E43E /* ContactsListViewModel.swift */; };
|
||||
D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FCA802AE14CFC00D2E43E /* ContactsListFragment.swift */; };
|
||||
D71FCA832AE14D6E00D2E43E /* ContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FCA822AE14D6E00D2E43E /* ContactFragment.swift */; };
|
||||
D72250632ADE9615008FB426 /* HistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72250622ADE9615008FB426 /* HistoryViewModel.swift */; };
|
||||
D72250692ADFBF2D008FB426 /* SideMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72250682ADFBF2D008FB426 /* SideMenu.swift */; };
|
||||
D72343302ACEFEF8009AA24E /* QrCodeScannerFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D723432F2ACEFEF8009AA24E /* QrCodeScannerFragment.swift */; };
|
||||
|
|
@ -34,12 +38,14 @@
|
|||
D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D74C9D002ACB098C0021626A /* PermissionManager.swift */; };
|
||||
D750D3392AD3E6EE00EC99C5 /* PopupLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */; };
|
||||
D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7702EF12AC7205000557C00 /* WelcomeView.swift */; };
|
||||
D78290B82ADD3910004AA85C /* ContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290B72ADD3910004AA85C /* ContactFragment.swift */; };
|
||||
D777DBB32AE12C5900565A99 /* ContactsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D777DBB22AE12C5900565A99 /* ContactsManager.swift */; };
|
||||
D78290B82ADD3910004AA85C /* ContactsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290B72ADD3910004AA85C /* ContactsFragment.swift */; };
|
||||
D78290BB2ADD40B2004AA85C /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */; };
|
||||
D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FBC2ACC2DB60081A588 /* ContactsView.swift */; };
|
||||
D7A03FC02ACC2E390081A588 /* HistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FBF2ACC2E390081A588 /* HistoryView.swift */; };
|
||||
D7A03FC62ACC458A0081A588 /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A03FC52ACC458A0081A588 /* SplashScreen.swift */; };
|
||||
D7A2EDD62AC18115005D90FC /* SharedMainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A2EDD52AC18115005D90FC /* SharedMainViewModel.swift */; };
|
||||
D7D1698C2AE66FA500109A5C /* MagicSearchSingleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1698B2AE66FA500109A5C /* MagicSearchSingleton.swift */; };
|
||||
D7D24D132AC1B4E800C6F35B /* NotoSans-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */; };
|
||||
D7D24D142AC1B4E800C6F35B /* NotoSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */; };
|
||||
D7D24D152AC1B4E800C6F35B /* NotoSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */; };
|
||||
|
|
@ -48,14 +54,15 @@
|
|||
D7D24D182AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D122AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf */; };
|
||||
D7DA67622ACCB2FA00E95002 /* LoginFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67612ACCB2FA00E95002 /* LoginFragment.swift */; };
|
||||
D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */; };
|
||||
D7E6D0492AE933AD00A57AAF /* FavoriteContactsListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6D0482AE933AD00A57AAF /* FavoriteContactsListFragment.swift */; };
|
||||
D7E6D04B2AE9347D00A57AAF /* FavoriteContactsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7E6D04A2AE9347D00A57AAF /* FavoriteContactsListViewModel.swift */; };
|
||||
D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EAACCE2AD6ED8000AA6A8A /* PermissionsFragment.swift */; };
|
||||
D7FB55112AD447FD00A5AB15 /* RegisterFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FB55102AD447FD00A5AB15 /* RegisterFragment.swift */; };
|
||||
F4BB8DFBA0FF08430EBA9351 /* Pods_Linphone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F5B27C5576B1EAED2F205EB /* Pods_Linphone.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
377E0B5C2B1F38192E694334 /* Pods-Linphone.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Linphone.debug.xcconfig"; path = "Target Support Files/Pods-Linphone/Pods-Linphone.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
6F5B27C5576B1EAED2F205EB /* Pods_Linphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Linphone.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1A334B8FDAD2893691A734BE /* Pods_Linphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Linphone.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1DE4CD5FD6E1F01639F27E3B /* Pods-Linphone.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Linphone.release.xcconfig"; path = "Target Support Files/Pods-Linphone/Pods-Linphone.release.xcconfig"; sourceTree = "<group>"; };
|
||||
D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
|
||||
D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||
D717071D2AC5922E0037746F /* ColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExtension.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -69,6 +76,9 @@
|
|||
D719ABC82ABC6FD700B41C10 /* CoreContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreContext.swift; sourceTree = "<group>"; };
|
||||
D719ABCB2ABC769C00B41C10 /* AssistantView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistantView.swift; sourceTree = "<group>"; };
|
||||
D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLoginViewModel.swift; sourceTree = "<group>"; };
|
||||
D71FCA7E2AE1397200D2E43E /* ContactsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsListViewModel.swift; sourceTree = "<group>"; };
|
||||
D71FCA802AE14CFC00D2E43E /* ContactsListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsListFragment.swift; sourceTree = "<group>"; };
|
||||
D71FCA822AE14D6E00D2E43E /* ContactFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactFragment.swift; sourceTree = "<group>"; };
|
||||
D72250622ADE9615008FB426 /* HistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewModel.swift; sourceTree = "<group>"; };
|
||||
D72250682ADFBF2D008FB426 /* SideMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenu.swift; sourceTree = "<group>"; };
|
||||
D723432F2ACEFEF8009AA24E /* QrCodeScannerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrCodeScannerFragment.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -85,13 +95,15 @@
|
|||
D74C9D002ACB098C0021626A /* PermissionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionManager.swift; sourceTree = "<group>"; };
|
||||
D750D3382AD3E6EE00EC99C5 /* PopupLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupLoadingView.swift; sourceTree = "<group>"; };
|
||||
D7702EF12AC7205000557C00 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
|
||||
D78290B72ADD3910004AA85C /* ContactFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactFragment.swift; sourceTree = "<group>"; };
|
||||
D777DBB22AE12C5900565A99 /* ContactsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsManager.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>"; };
|
||||
D7A03FBC2ACC2DB60081A588 /* ContactsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsView.swift; sourceTree = "<group>"; };
|
||||
D7A03FBF2ACC2E390081A588 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = "<group>"; };
|
||||
D7A03FC52ACC458A0081A588 /* SplashScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashScreen.swift; sourceTree = "<group>"; };
|
||||
D7A2EDD52AC18115005D90FC /* SharedMainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedMainViewModel.swift; sourceTree = "<group>"; };
|
||||
D7A2EDDA2AC19EEC005D90FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
D7D1698B2AE66FA500109A5C /* MagicSearchSingleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagicSearchSingleton.swift; sourceTree = "<group>"; };
|
||||
D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Medium.ttf"; sourceTree = "<group>"; };
|
||||
D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Regular.ttf"; sourceTree = "<group>"; };
|
||||
D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Light.ttf"; sourceTree = "<group>"; };
|
||||
|
|
@ -100,9 +112,11 @@
|
|||
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>"; };
|
||||
D7DA67632ACCB31700E95002 /* ProfileModeFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModeFragment.swift; sourceTree = "<group>"; };
|
||||
D7E6D0482AE933AD00A57AAF /* FavoriteContactsListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteContactsListFragment.swift; sourceTree = "<group>"; };
|
||||
D7E6D04A2AE9347D00A57AAF /* FavoriteContactsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteContactsListViewModel.swift; sourceTree = "<group>"; };
|
||||
D7EAACCE2AD6ED8000AA6A8A /* PermissionsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsFragment.swift; sourceTree = "<group>"; };
|
||||
D7FB55102AD447FD00A5AB15 /* RegisterFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterFragment.swift; sourceTree = "<group>"; };
|
||||
F76FB87556A3109F61F9E2D5 /* Pods-Linphone.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Linphone.release.xcconfig"; path = "Target Support Files/Pods-Linphone/Pods-Linphone.release.xcconfig"; sourceTree = "<group>"; };
|
||||
FB718F405DAF7B9993AEB878 /* Pods-Linphone.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Linphone.debug.xcconfig"; path = "Target Support Files/Pods-Linphone/Pods-Linphone.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -110,17 +124,17 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F4BB8DFBA0FF08430EBA9351 /* Pods_Linphone.framework in Frameworks */,
|
||||
886500223A8E518D3EE5FCB7 /* Pods_Linphone.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
52EFCC310713B3CA01062945 /* Frameworks */ = {
|
||||
1CD95087B17CAD149119B7C2 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6F5B27C5576B1EAED2F205EB /* Pods_Linphone.framework */,
|
||||
1A334B8FDAD2893691A734BE /* Pods_Linphone.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -128,8 +142,8 @@
|
|||
A31AF2AB8C6A3D7B7EA3B424 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
377E0B5C2B1F38192E694334 /* Pods-Linphone.debug.xcconfig */,
|
||||
F76FB87556A3109F61F9E2D5 /* Pods-Linphone.release.xcconfig */,
|
||||
FB718F405DAF7B9993AEB878 /* Pods-Linphone.debug.xcconfig */,
|
||||
1DE4CD5FD6E1F01639F27E3B /* Pods-Linphone.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -140,6 +154,7 @@
|
|||
D717071D2AC5922E0037746F /* ColorExtension.swift */,
|
||||
D717071F2AC5989C0037746F /* TextExtension.swift */,
|
||||
D74C9D002ACB098C0021626A /* PermissionManager.swift */,
|
||||
D7D1698B2AE66FA500109A5C /* MagicSearchSingleton.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -150,7 +165,7 @@
|
|||
D719ABB52ABC67BF00B41C10 /* Linphone */,
|
||||
D719ABB42ABC67BF00B41C10 /* Products */,
|
||||
A31AF2AB8C6A3D7B7EA3B424 /* Pods */,
|
||||
52EFCC310713B3CA01062945 /* Frameworks */,
|
||||
1CD95087B17CAD149119B7C2 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
|
@ -167,6 +182,7 @@
|
|||
children = (
|
||||
D7A03FC52ACC458A0081A588 /* SplashScreen.swift */,
|
||||
D719ABB62ABC67BF00B41C10 /* LinphoneApp.swift */,
|
||||
D777DBB12AE12C4000565A99 /* Contacts */,
|
||||
D719ABC72ABC6FB200B41C10 /* Core */,
|
||||
D719ABC52ABC6EE800B41C10 /* UI */,
|
||||
D717071C2AC591EF0037746F /* Utils */,
|
||||
|
|
@ -285,10 +301,21 @@
|
|||
path = Welcome;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D777DBB12AE12C4000565A99 /* Contacts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D777DBB22AE12C5900565A99 /* ContactsManager.swift */,
|
||||
);
|
||||
path = Contacts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D78290B62ADD38F9004AA85C /* Fragments */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D78290B72ADD3910004AA85C /* ContactFragment.swift */,
|
||||
D78290B72ADD3910004AA85C /* ContactsFragment.swift */,
|
||||
D71FCA802AE14CFC00D2E43E /* ContactsListFragment.swift */,
|
||||
D71FCA822AE14D6E00D2E43E /* ContactFragment.swift */,
|
||||
D7E6D0482AE933AD00A57AAF /* FavoriteContactsListFragment.swift */,
|
||||
);
|
||||
path = Fragments;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -297,6 +324,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */,
|
||||
D71FCA7E2AE1397200D2E43E /* ContactsListViewModel.swift */,
|
||||
D7E6D04A2AE9347D00A57AAF /* FavoriteContactsListViewModel.swift */,
|
||||
);
|
||||
path = ViewModel;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -363,12 +392,12 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D719ABC22ABC67BF00B41C10 /* Build configuration list for PBXNativeTarget "Linphone" */;
|
||||
buildPhases = (
|
||||
6FE8573A5CFC1DA89D3172B5 /* [CP] Check Pods Manifest.lock */,
|
||||
BE9432280D0A11AA770A50FD /* [CP] Check Pods Manifest.lock */,
|
||||
D719ABAF2ABC67BF00B41C10 /* Sources */,
|
||||
D719ABB02ABC67BF00B41C10 /* Frameworks */,
|
||||
D719ABB12ABC67BF00B41C10 /* Resources */,
|
||||
D7FB55122AD53FE200A5AB15 /* Run Script */,
|
||||
230129DD87A6EBB04DF458AD /* [CP] Embed Pods Frameworks */,
|
||||
D5CA1ECD620857DB91E334A5 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -432,24 +461,7 @@
|
|||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
230129DD87A6EBB04DF458AD /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
6FE8573A5CFC1DA89D3172B5 /* [CP] Check Pods Manifest.lock */ = {
|
||||
BE9432280D0A11AA770A50FD /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
|
|
@ -471,6 +483,23 @@
|
|||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
D5CA1ECD620857DB91E334A5 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
D7FB55122AD53FE200A5AB15 /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
|
|
@ -499,16 +528,21 @@
|
|||
files = (
|
||||
D71707202AC5989C0037746F /* TextExtension.swift in Sources */,
|
||||
D719ABB92ABC67BF00B41C10 /* ContentView.swift in Sources */,
|
||||
D71FCA832AE14D6E00D2E43E /* ContactFragment.swift in Sources */,
|
||||
D750D3392AD3E6EE00EC99C5 /* PopupLoadingView.swift in Sources */,
|
||||
D7E6D0492AE933AD00A57AAF /* FavoriteContactsListFragment.swift in Sources */,
|
||||
D706BA822ADD72D100278F45 /* DeviceRotationViewModifier.swift in Sources */,
|
||||
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */,
|
||||
D7EAACCF2AD6ED8000AA6A8A /* PermissionsFragment.swift in Sources */,
|
||||
D777DBB32AE12C5900565A99 /* ContactsManager.swift in Sources */,
|
||||
D7A03FBD2ACC2DB60081A588 /* ContactsView.swift in Sources */,
|
||||
D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */,
|
||||
D78290BB2ADD40B2004AA85C /* ContactViewModel.swift in Sources */,
|
||||
D72992392ADD7F68003AF125 /* HistoryContactFragment.swift in Sources */,
|
||||
D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */,
|
||||
D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */,
|
||||
D71FCA7F2AE1397200D2E43E /* ContactsListViewModel.swift in Sources */,
|
||||
D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */,
|
||||
D719ABB72ABC67BF00B41C10 /* LinphoneApp.swift in Sources */,
|
||||
D72250632ADE9615008FB426 /* HistoryViewModel.swift in Sources */,
|
||||
D7A2EDD62AC18115005D90FC /* SharedMainViewModel.swift in Sources */,
|
||||
|
|
@ -522,12 +556,14 @@
|
|||
D72343322ACEFF58009AA24E /* QRScannerController.swift in Sources */,
|
||||
D72343342ACEFFC3009AA24E /* QRScanner.swift in Sources */,
|
||||
D72343302ACEFEF8009AA24E /* QrCodeScannerFragment.swift in Sources */,
|
||||
D7D1698C2AE66FA500109A5C /* MagicSearchSingleton.swift in Sources */,
|
||||
D72250692ADFBF2D008FB426 /* SideMenu.swift in Sources */,
|
||||
D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */,
|
||||
D78290B82ADD3910004AA85C /* ContactFragment.swift in Sources */,
|
||||
D78290B82ADD3910004AA85C /* ContactsFragment.swift in Sources */,
|
||||
D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */,
|
||||
D74C9CFC2ACACF370021626A /* WelcomePage3Fragment.swift in Sources */,
|
||||
D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */,
|
||||
D7E6D04B2AE9347D00A57AAF /* FavoriteContactsListViewModel.swift in Sources */,
|
||||
D74C9CFA2ACACF2D0021626A /* WelcomePage2Fragment.swift in Sources */,
|
||||
D74C9CFF2ACAEC5E0021626A /* PopupView.swift in Sources */,
|
||||
D7DA67622ACCB2FA00E95002 /* LoginFragment.swift in Sources */,
|
||||
|
|
@ -652,7 +688,7 @@
|
|||
};
|
||||
D719ABC32ABC67BF00B41C10 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 377E0B5C2B1F38192E694334 /* Pods-Linphone.debug.xcconfig */;
|
||||
baseConfigurationReference = FB718F405DAF7B9993AEB878 /* Pods-Linphone.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
|
|
@ -668,7 +704,8 @@
|
|||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = Linphone/Info.plist;
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "Share photos with your friends and customize avatars";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "";
|
||||
INFOPLIST_KEY_NSContactsUsageDescription = "Make calls with your friends";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Share photos with your friends and customize avatars";
|
||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||
|
|
@ -696,7 +733,7 @@
|
|||
};
|
||||
D719ABC42ABC67BF00B41C10 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = F76FB87556A3109F61F9E2D5 /* Pods-Linphone.release.xcconfig */;
|
||||
baseConfigurationReference = 1DE4CD5FD6E1F01639F27E3B /* Pods-Linphone.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
|
|
@ -712,7 +749,8 @@
|
|||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = Linphone/Info.plist;
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "Share photos with your friends and customize avatars";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "";
|
||||
INFOPLIST_KEY_NSContactsUsageDescription = "Make calls with your friends";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "Share photos with your friends and customize avatars";
|
||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||
|
|
|
|||
77
Linphone.xcodeproj/xcshareddata/xcschemes/Linphone.xcscheme
Normal file
77
Linphone.xcodeproj/xcshareddata/xcschemes/Linphone.xcscheme
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1500"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D719ABB22ABC67BF00B41C10"
|
||||
BuildableName = "Linphone.app"
|
||||
BlueprintName = "Linphone"
|
||||
ReferencedContainer = "container:Linphone.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D719ABB22ABC67BF00B41C10"
|
||||
BuildableName = "Linphone.app"
|
||||
BlueprintName = "Linphone"
|
||||
ReferencedContainer = "container:Linphone.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D719ABB22ABC67BF00B41C10"
|
||||
BuildableName = "Linphone.app"
|
||||
BlueprintName = "Linphone"
|
||||
ReferencedContainer = "container:Linphone.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
21
Linphone/Assets.xcassets/caret-up.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/caret-up.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "caret-up.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
1
Linphone/Assets.xcassets/caret-up.imageset/caret-up.svg
vendored
Normal file
1
Linphone/Assets.xcassets/caret-up.imageset/caret-up.svg
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 256 B |
21
Linphone/Assets.xcassets/check.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/check.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "check.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
1
Linphone/Assets.xcassets/check.imageset/check.svg
vendored
Normal file
1
Linphone/Assets.xcassets/check.imageset/check.svg
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 239 B |
21
Linphone/Assets.xcassets/green-check.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/green-check.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "green-check.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
3
Linphone/Assets.xcassets/green-check.imageset/green-check.svg
vendored
Normal file
3
Linphone/Assets.xcassets/green-check.imageset/green-check.svg
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.789 6.72975L9.26952 18.2492C9.20266 18.3162 9.12325 18.3693 9.03585 18.4055C8.94845 18.4417 8.85476 18.4604 8.76014 18.4604C8.66553 18.4604 8.57184 18.4417 8.48444 18.4055C8.39704 18.3693 8.31763 18.3162 8.25077 18.2492L3.21099 13.2095C3.0759 13.0744 3 12.8911 3 12.7001C3 12.509 3.0759 12.3258 3.21099 12.1907C3.34609 12.0556 3.52931 11.9797 3.72037 11.9797C3.91142 11.9797 4.09465 12.0556 4.22975 12.1907L8.76014 16.722L19.7703 5.71099C19.9053 5.5759 20.0886 5.5 20.2796 5.5C20.4707 5.5 20.6539 5.5759 20.789 5.71099C20.9241 5.84609 21 6.02931 21 6.22037C21 6.41142 20.9241 6.59465 20.789 6.72975Z" fill="#4FAE80"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 734 B |
21
Linphone/Assets.xcassets/x.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/x.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "x.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
1
Linphone/Assets.xcassets/x.imageset/x.svg
vendored
Normal file
1
Linphone/Assets.xcassets/x.imageset/x.svg
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 308 B |
250
Linphone/Contacts/ContactsManager.swift
Normal file
250
Linphone/Contacts/ContactsManager.swift
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of Linphone
|
||||
*
|
||||
* 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 Contacts
|
||||
import SwiftUI
|
||||
|
||||
final class ContactsManager: ObservableObject {
|
||||
|
||||
static let shared = ContactsManager()
|
||||
|
||||
private var coreContext = CoreContext.shared
|
||||
|
||||
@Published var contacts: [Contact] = []
|
||||
|
||||
private let nativeAddressBookFriendList = "Native address-book"
|
||||
let linphoneAddressBookFirendList = "Linphone address-book"
|
||||
|
||||
private init() {
|
||||
fetchContacts()
|
||||
}
|
||||
|
||||
func fetchContacts() {
|
||||
contacts.removeAll()
|
||||
DispatchQueue.global().async {
|
||||
let store = CNContactStore()
|
||||
store.requestAccess(for: .contacts) { (granted, error) in
|
||||
if let error = error {
|
||||
print("failed to request access", error)
|
||||
return
|
||||
}
|
||||
if granted {
|
||||
let keys = [CNContactEmailAddressesKey, CNContactPhoneNumbersKey,
|
||||
CNContactFamilyNameKey, CNContactGivenNameKey, CNContactNicknameKey,
|
||||
CNContactPostalAddressesKey, CNContactIdentifierKey,
|
||||
CNInstantMessageAddressUsernameKey, CNContactInstantMessageAddressesKey,
|
||||
CNContactImageDataKey, CNContactThumbnailImageDataKey, CNContactOrganizationNameKey]
|
||||
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
|
||||
do {
|
||||
try store.enumerateContacts(with: request, usingBlock: { (contact, _) in
|
||||
|
||||
DispatchQueue.main.sync {
|
||||
self.contacts.append(
|
||||
Contact(
|
||||
firstName: contact.givenName,
|
||||
lastName: contact.familyName,
|
||||
organizationName: contact.organizationName,
|
||||
displayName: contact.nickname,
|
||||
sipAddresses: contact.instantMessageAddresses.map { $0.value.service == "SIP" ? $0.value.username : "" },
|
||||
phoneNumbers: contact.phoneNumbers.map { PhoneNumber(numLabel: $0.label ?? "", num: $0.value.stringValue)},
|
||||
imageData: self.saveImage(
|
||||
image:
|
||||
UIImage(data: contact.thumbnailImageData ?? Data())
|
||||
?? self.textToImage(firstName: contact.givenName.isEmpty
|
||||
&& contact.familyName.isEmpty
|
||||
&& contact.phoneNumbers.first?.value.stringValue != nil
|
||||
? contact.phoneNumbers.first!.value.stringValue
|
||||
: contact.givenName, lastName: contact.familyName),
|
||||
name: contact.identifier)
|
||||
)
|
||||
)
|
||||
}
|
||||
self.contacts.sort(by: {
|
||||
$0.firstName.folding(
|
||||
options: .diacriticInsensitive, locale: .current
|
||||
) < $1.firstName.folding(
|
||||
options: .diacriticInsensitive, locale: .current
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
|
||||
} else {
|
||||
print("access denied")
|
||||
}
|
||||
}
|
||||
|
||||
var friends: [Friend] = []
|
||||
|
||||
self.contacts.forEach { contact in
|
||||
do {
|
||||
let friend = try self.coreContext.mCore.createFriend()
|
||||
friend.edit()
|
||||
try friend.setName(newValue: contact.firstName + " " + contact.lastName)
|
||||
friend.organization = contact.organizationName
|
||||
|
||||
var friendAddresses: [Address] = []
|
||||
contact.sipAddresses.forEach { sipAddress in
|
||||
let address = self.coreContext.mCore.interpretUrl(url: sipAddress, applyInternationalPrefix: true)
|
||||
|
||||
if address != nil && ((friendAddresses.firstIndex(where: {$0.asString() == address?.asString()})) == nil) {
|
||||
friend.addAddress(address: address!)
|
||||
friendAddresses.append(address!)
|
||||
}
|
||||
}
|
||||
|
||||
var friendPhoneNumbers: [PhoneNumber] = []
|
||||
contact.phoneNumbers.forEach { phone in
|
||||
do {
|
||||
if (friendPhoneNumbers.firstIndex(where: {$0.numLabel == phone.numLabel})) == nil {
|
||||
let phoneNumber = try Factory.Instance.createFriendPhoneNumber(phoneNumber: phone.num, label: phone.numLabel)
|
||||
friend.addPhoneNumberWithLabel(phoneNumber: phoneNumber)
|
||||
friendPhoneNumbers.append(phone)
|
||||
}
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
}
|
||||
|
||||
let contactImage = contact.imageData.dropFirst(8)
|
||||
friend.photo = "file:/" + contactImage
|
||||
|
||||
friend.done()
|
||||
friends.append(friend)
|
||||
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
}
|
||||
|
||||
if self.coreContext.mCore.globalState == GlobalState.Shutdown || self.coreContext.mCore.globalState == GlobalState.Off {
|
||||
print("$TAG Core is being stopped or already destroyed, abort")
|
||||
} else if friends.isEmpty {
|
||||
print("$TAG No friend created!")
|
||||
} else {
|
||||
print("$TAG ${friends.size} friends created")
|
||||
|
||||
let fetchedFriends = friends
|
||||
|
||||
let nativeFriendList = self.coreContext.mCore.getFriendListByName(name: self.nativeAddressBookFriendList)
|
||||
var friendList = nativeFriendList
|
||||
if friendList == nil {
|
||||
do {
|
||||
friendList = try self.coreContext.mCore.createFriendList()
|
||||
} catch let error {
|
||||
print("Failed to enumerate contact", error)
|
||||
}
|
||||
}
|
||||
|
||||
if friendList!.displayName == nil || friendList!.displayName!.isEmpty {
|
||||
print(
|
||||
"$TAG Friend list [$nativeAddressBookFriendList] didn't exist yet, let's create it"
|
||||
)
|
||||
|
||||
friendList?.databaseStorageEnabled = false // We don't want to store local address-book in DB
|
||||
|
||||
friendList!.displayName = self.nativeAddressBookFriendList
|
||||
self.coreContext.mCore.addFriendList(list: friendList!)
|
||||
} else {
|
||||
print(
|
||||
"$TAG Friend list [$LINPHONE_ADDRESS_BOOK_FRIEND_LIST] found, removing existing friends if any"
|
||||
)
|
||||
friendList!.friends.forEach { friend in
|
||||
_ = friendList!.removeFriend(linphoneFriend: friend)
|
||||
}
|
||||
}
|
||||
|
||||
fetchedFriends.forEach { friend in
|
||||
_ = friendList!.addLocalFriend(linphoneFriend: friend)
|
||||
}
|
||||
|
||||
friends.removeAll()
|
||||
|
||||
print("$TAG Friends added")
|
||||
|
||||
friendList!.updateSubscriptions()
|
||||
print("$TAG Subscription(s) updated")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func saveImage(image: UIImage, name: String) -> String {
|
||||
guard let data = image.jpegData(compressionQuality: 1) ?? image.pngData() else {
|
||||
return ""
|
||||
}
|
||||
let directory = FileManager.default.temporaryDirectory
|
||||
print("FileManagerFileManager \(directory.absoluteString)")
|
||||
do {
|
||||
try data.write(to: directory.appendingPathComponent(name + ".png"))
|
||||
return directory.appendingPathComponent(name + ".png").absoluteString
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func textToImage(firstName: String, lastName: String) -> UIImage {
|
||||
|
||||
let lblNameInitialize = UILabel()
|
||||
lblNameInitialize.frame.size = CGSize(width: 100.0, height: 100.0)
|
||||
lblNameInitialize.font = UIFont(name: "NotoSans-ExtraBold", size: 40)
|
||||
lblNameInitialize.textColor = UIColor(Color.grayMain2c600)
|
||||
|
||||
var textToDisplay = ""
|
||||
if firstName.first != nil {
|
||||
textToDisplay += String(firstName.first!)
|
||||
}
|
||||
if lastName.first != nil {
|
||||
textToDisplay += String(lastName.first!)
|
||||
}
|
||||
|
||||
lblNameInitialize.text = textToDisplay.uppercased()
|
||||
lblNameInitialize.textAlignment = .center
|
||||
lblNameInitialize.backgroundColor = UIColor(Color.grayMain2c200)
|
||||
lblNameInitialize.layer.cornerRadius = 10.0
|
||||
|
||||
var IBImgViewUserProfile = UIImage()
|
||||
UIGraphicsBeginImageContext(lblNameInitialize.frame.size)
|
||||
lblNameInitialize.layer.render(in: UIGraphicsGetCurrentContext()!)
|
||||
IBImgViewUserProfile = UIGraphicsGetImageFromCurrentImageContext()!
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return IBImgViewUserProfile
|
||||
}
|
||||
}
|
||||
|
||||
struct PhoneNumber {
|
||||
var numLabel: String
|
||||
var num: String
|
||||
}
|
||||
|
||||
struct Contact: Identifiable {
|
||||
var id = UUID()
|
||||
var firstName: String
|
||||
var lastName: String
|
||||
var organizationName: String
|
||||
var displayName: String
|
||||
var sipAddresses: [String] = []
|
||||
var phoneNumbers: [PhoneNumber] = []
|
||||
var imageData: String
|
||||
}
|
||||
|
|
@ -40,6 +40,9 @@ final class CoreContext: ObservableObject {
|
|||
let factory = Factory.Instance
|
||||
let configDir = factory.getConfigDir(context: nil)
|
||||
try? mCore = Factory.Instance.createCore(configPath: "\(configDir)/MyConfig", factoryConfigPath: "", systemContext: nil)
|
||||
|
||||
mCore.friendsDatabasePath = "\(configDir)/friends.db"
|
||||
|
||||
try? mCore.start()
|
||||
|
||||
// Create a Core listener to listen for the callback we need
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ struct LinphoneApp: App {
|
|||
var body: some Scene {
|
||||
WindowGroup {
|
||||
if isActive {
|
||||
ContentView(sharedMainViewModel: SharedMainViewModel(), contactViewModel: ContactViewModel(), historyViewModel: HistoryViewModel())
|
||||
ContentView(sharedMainViewModel: SharedMainViewModel(), contactViewModel: ContactViewModel(), historyViewModel: HistoryViewModel())
|
||||
.toast(isShowing: $coreContext.toastMessage)
|
||||
} else {
|
||||
SplashScreen(isActive: $isActive)
|
||||
|
|
|
|||
|
|
@ -36,9 +36,6 @@
|
|||
},
|
||||
"**Notifications** : Pour vous informé quand vous recevez un message ou un appel." : {
|
||||
|
||||
},
|
||||
"%lld" : {
|
||||
|
||||
},
|
||||
"%lld Book (Example)" : {
|
||||
"extractionState" : "manual",
|
||||
|
|
@ -95,6 +92,9 @@
|
|||
},
|
||||
"Accept all" : {
|
||||
|
||||
},
|
||||
"All contacts" : {
|
||||
|
||||
},
|
||||
"assistant_account_login" : {
|
||||
"extractionState" : "manual",
|
||||
|
|
@ -160,6 +160,9 @@
|
|||
},
|
||||
"Error" : {
|
||||
|
||||
},
|
||||
"Favourites" : {
|
||||
|
||||
},
|
||||
"History Contact fragment" : {
|
||||
|
||||
|
|
@ -252,6 +255,12 @@
|
|||
},
|
||||
"Sécurisé" : {
|
||||
|
||||
},
|
||||
"See all" : {
|
||||
|
||||
},
|
||||
"See Linphone contact" : {
|
||||
|
||||
},
|
||||
"sip.linphone.org" : {
|
||||
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ struct PermissionsFragment: View {
|
|||
.padding(.horizontal)
|
||||
|
||||
Button {
|
||||
permissionManager.contactsRequestPermission()
|
||||
permissionManager.cameraRequestPermission()
|
||||
} label: {
|
||||
Text("D'accord")
|
||||
|
|
|
|||
|
|
@ -23,58 +23,12 @@ struct ContactsView: View {
|
|||
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
@State private var selectedIndex = 0
|
||||
|
||||
var objects: [Int] = [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39
|
||||
]
|
||||
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
VStack(spacing: 0) {
|
||||
VStack {
|
||||
List {
|
||||
ForEach(objects, id: \.self) { index in
|
||||
Button {
|
||||
withAnimation {
|
||||
contactViewModel.contactTitle = String(index)
|
||||
}
|
||||
} label: {
|
||||
Text("\(index)")
|
||||
.frame( maxWidth: .infinity, alignment: .leading)
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.overlay(
|
||||
VStack {
|
||||
if objects.isEmpty {
|
||||
Spacer()
|
||||
Image("illus-belledonne1")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.clipped()
|
||||
.padding(.all)
|
||||
Text("No contacts for the moment...")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(.all)
|
||||
)
|
||||
}
|
||||
}
|
||||
.onRotate { newOrientation in
|
||||
orientation = newOrientation
|
||||
}
|
||||
|
||||
ContactsFragment(contactViewModel: contactViewModel)
|
||||
|
||||
Button {
|
||||
// Action
|
||||
|
|
|
|||
|
|
@ -20,58 +20,50 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ContactFragment: View {
|
||||
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
|
||||
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
if !(orientation == .landscapeLeft || orientation == .landscapeRight || UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
|
||||
HStack {
|
||||
Image("caret-left")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
.padding(.top, 20)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
contactViewModel.contactTitle = ""
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.leading)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(contactViewModel.contactTitle)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
List {
|
||||
ForEach(1...40, id: \.self) { index in
|
||||
Button {
|
||||
contactViewModel.contactTitle = String(index)
|
||||
} label: {
|
||||
Text("\(index)")
|
||||
.frame( maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.onRotate { newOrientation in
|
||||
orientation = newOrientation
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
|
||||
HStack {
|
||||
Image("caret-left")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
.padding(.top, 20)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
contactViewModel.contactTitle = ""
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.leading)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("Contact Fragment " + contactViewModel.contactTitle)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.onRotate { newOrientation in
|
||||
orientation = newOrientation
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContactFragment(contactViewModel: ContactViewModel())
|
||||
ContactFragment(contactViewModel: ContactViewModel())
|
||||
}
|
||||
|
|
|
|||
80
Linphone/UI/Main/Contacts/Fragments/ContactsFragment.swift
Normal file
80
Linphone/UI/Main/Contacts/Fragments/ContactsFragment.swift
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 ContactsFragment: View {
|
||||
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
|
||||
@State var isFavoriteOpen: Bool = true
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack(alignment: .center) {
|
||||
Text("Favourites")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(isFavoriteOpen ? "caret-up" : "caret-down")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
.padding(.top, 30)
|
||||
.padding(.horizontal, 16)
|
||||
.background(.white)
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
isFavoriteOpen.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
if isFavoriteOpen {
|
||||
FavoriteContactsListFragment(contactViewModel: contactViewModel, favoriteContactsListViewModel: FavoriteContactsListViewModel())
|
||||
.zIndex(-1)
|
||||
.transition(.move(edge: .top))
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text("All contacts")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.top, 10)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
ContactsListFragment(contactViewModel: contactViewModel, contactsListViewModel: ContactsListViewModel())
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.onRotate { newOrientation in
|
||||
orientation = newOrientation
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContactsFragment(contactViewModel: ContactViewModel())
|
||||
}
|
||||
115
Linphone/UI/Main/Contacts/Fragments/ContactsListFragment.swift
Normal file
115
Linphone/UI/Main/Contacts/Fragments/ContactsListFragment.swift
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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 linphonesw
|
||||
|
||||
struct ContactsListFragment: View {
|
||||
|
||||
@ObservedObject var magicSearch = MagicSearchSingleton.shared
|
||||
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var contactsListViewModel: ContactsListViewModel
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
List {
|
||||
ForEach(0..<magicSearch.lastSearch.count, id: \.self) { index in
|
||||
Button {
|
||||
withAnimation {
|
||||
contactViewModel.contactTitle = (magicSearch.lastSearch[index].friend?.name)!
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
if index == 0 || magicSearch.lastSearch[index].friend?.name!.lowercased().folding(options: .diacriticInsensitive, locale: .current).first !=
|
||||
magicSearch.lastSearch[index-1].friend?.name!.lowercased().folding(options: .diacriticInsensitive, locale: .current).first {
|
||||
Text(String((magicSearch.lastSearch[index].friend?.name!.uppercased().folding(options: .diacriticInsensitive, locale: .current).first)!))
|
||||
.contact_text_style_500(styleSize: 20)
|
||||
.frame(width: 18)
|
||||
.padding(.leading, -5)
|
||||
.padding(.trailing, 10)
|
||||
} else {
|
||||
Text("")
|
||||
.contact_text_style_500(styleSize: 20)
|
||||
.frame(width: 18)
|
||||
.padding(.leading, -5)
|
||||
.padding(.trailing, 10)
|
||||
}
|
||||
|
||||
if magicSearch.lastSearch[index].friend!.photo != nil && !magicSearch.lastSearch[index].friend!.photo!.isEmpty {
|
||||
AsyncImage(url: URL(string: magicSearch.lastSearch[index].friend!.photo!)) { image in
|
||||
switch image {
|
||||
case .empty:
|
||||
ProgressView()
|
||||
.frame(width: 45, height: 45)
|
||||
case .success(let image):
|
||||
image
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
case .failure:
|
||||
Image("profile-image-example")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
@unknown default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Image("profile-image-example")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
Text((magicSearch.lastSearch[index].friend?.name)!)
|
||||
.default_text_style(styleSize: 16)
|
||||
.frame( maxWidth: .infinity, alignment: .leading)
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.overlay(
|
||||
VStack {
|
||||
if magicSearch.lastSearch.isEmpty {
|
||||
Spacer()
|
||||
Image("illus-belledonne1")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.clipped()
|
||||
.padding(.all)
|
||||
Text("No contacts for the moment...")
|
||||
.default_text_style_800(styleSize: 16)
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(.all)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContactsListFragment(contactViewModel: ContactViewModel(), contactsListViewModel: ContactsListViewModel())
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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 FavoriteContactsListFragment: View {
|
||||
|
||||
@ObservedObject var magicSearch = MagicSearchSingleton.shared
|
||||
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var favoriteContactsListViewModel: FavoriteContactsListViewModel
|
||||
|
||||
var body: some View {
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
ForEach(0..<magicSearch.lastSearch.count, id: \.self) { index in
|
||||
Button {
|
||||
withAnimation {
|
||||
contactViewModel.contactTitle = (magicSearch.lastSearch[index].friend?.name)!
|
||||
}
|
||||
} label: {
|
||||
VStack {
|
||||
if magicSearch.lastSearch[index].friend!.photo != nil && !magicSearch.lastSearch[index].friend!.photo!.isEmpty {
|
||||
AsyncImage(url: URL(string: magicSearch.lastSearch[index].friend!.photo!)) { image in
|
||||
switch image {
|
||||
case .empty:
|
||||
ProgressView()
|
||||
.frame(width: 45, height: 45)
|
||||
case .success(let image):
|
||||
image
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
case .failure:
|
||||
Image("profile-image-example")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
@unknown default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Image("profile-image-example")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
Text((magicSearch.lastSearch[index].friend?.name)!)
|
||||
.default_text_style(styleSize: 16)
|
||||
.frame( maxWidth: .infinity, alignment: .center)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: 70)
|
||||
}
|
||||
}
|
||||
.padding(.all, 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
FavoriteContactsListFragment(contactViewModel: ContactViewModel(), favoriteContactsListViewModel: FavoriteContactsListViewModel())
|
||||
}
|
||||
|
|
@ -17,11 +17,11 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
class ContactViewModel: ObservableObject {
|
||||
|
||||
@Published var contactTitle: String = ""
|
||||
|
||||
@Published var contactTitle: String = ""
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
class ContactsListViewModel: ObservableObject {
|
||||
|
||||
private var magicSearch = MagicSearchSingleton.shared
|
||||
private var coreContext = CoreContext.shared
|
||||
|
||||
init() {
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
class FavoriteContactsListViewModel: ObservableObject {
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
|
@ -18,252 +18,421 @@
|
|||
*/
|
||||
|
||||
import SwiftUI
|
||||
import linphonesw
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@ObservedObject var sharedMainViewModel: SharedMainViewModel
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
|
||||
@State var index = 0
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
@State var menuOpen: Bool = false
|
||||
|
||||
var body: some View {
|
||||
if !sharedMainViewModel.welcomeViewDisplayed {
|
||||
WelcomeView(sharedMainViewModel: sharedMainViewModel)
|
||||
} else if coreContext.mCore.defaultAccount == nil || sharedMainViewModel.displayProfileMode {
|
||||
AssistantView(sharedMainViewModel: sharedMainViewModel)
|
||||
} else {
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
if orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
|
||||
VStack {
|
||||
Group {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.index = 0
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("address-book")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 0 {
|
||||
Text("Contacts")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Contacts")
|
||||
.default_text_style(styleSize: 10)
|
||||
var contactManager = ContactsManager.shared
|
||||
var magicSearch = MagicSearchSingleton.shared
|
||||
|
||||
@ObservedObject var sharedMainViewModel: SharedMainViewModel
|
||||
@ObservedObject var contactViewModel: ContactViewModel
|
||||
@ObservedObject var historyViewModel: HistoryViewModel
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
|
||||
@State var index = 0
|
||||
@State private var orientation = UIDevice.current.orientation
|
||||
@State var sideMenuIsOpen: Bool = false
|
||||
|
||||
@State private var searchIsActive = false
|
||||
@State private var text = ""
|
||||
@FocusState private var focusedField: Bool
|
||||
@State var isMenuOpen: Bool = false
|
||||
|
||||
var body: some View {
|
||||
if !sharedMainViewModel.welcomeViewDisplayed {
|
||||
WelcomeView(sharedMainViewModel: sharedMainViewModel)
|
||||
} else if coreContext.mCore.defaultAccount == nil || sharedMainViewModel.displayProfileMode {
|
||||
AssistantView(sharedMainViewModel: sharedMainViewModel)
|
||||
} else {
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 0) {
|
||||
if orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
|
||||
VStack {
|
||||
Group {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.index = 0
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("address-book")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 0 {
|
||||
Text("Contacts")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Contacts")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
self.index = 1
|
||||
contactViewModel.contactTitle = ""
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("phone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 1 {
|
||||
Text("Calls")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Calls")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.frame(width: 75)
|
||||
.padding(.leading,
|
||||
orientation == .landscapeRight && geometry.safeAreaInsets.bottom > 0
|
||||
? -geometry.safeAreaInsets.leading
|
||||
: 0)
|
||||
}
|
||||
|
||||
VStack(spacing: 0) {
|
||||
if searchIsActive == false {
|
||||
HStack {
|
||||
Image("profile-image-example")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
.clipShape(Circle())
|
||||
.onTapGesture {
|
||||
openMenu()
|
||||
}
|
||||
|
||||
Text(index == 0 ? "Contacts" : "Calls")
|
||||
.default_text_style_white_800(styleSize: 20)
|
||||
.padding(.leading, 10)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
withAnimation {
|
||||
searchIsActive.toggle()
|
||||
}
|
||||
} label: {
|
||||
Image("search")
|
||||
}
|
||||
|
||||
Menu {
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = true
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See all")
|
||||
Spacer()
|
||||
if magicSearch.allContact {
|
||||
Image("green-check")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
self.index = 1
|
||||
contactViewModel.contactTitle = ""
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("phone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 1 {
|
||||
Text("Calls")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Calls")
|
||||
.default_text_style(styleSize: 10)
|
||||
|
||||
Button {
|
||||
isMenuOpen = false
|
||||
magicSearch.allContact = false
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("See Linphone contact")
|
||||
Spacer()
|
||||
if !magicSearch.allContact {
|
||||
Image("green-check")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.frame(width: 75)
|
||||
.padding(.leading,
|
||||
orientation == .landscapeRight && geometry.safeAreaInsets.bottom > 0
|
||||
? -geometry.safeAreaInsets.leading
|
||||
: 0)
|
||||
}
|
||||
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Image("profile-image-example")
|
||||
.resizable()
|
||||
.frame(width: 40, height: 40)
|
||||
.clipShape(Circle())
|
||||
} label: {
|
||||
Image(index == 0 ? "filtres" : "more")
|
||||
}
|
||||
.padding(.leading)
|
||||
.onTapGesture {
|
||||
openMenu()
|
||||
isMenuOpen = true
|
||||
}
|
||||
|
||||
Text(index == 0 ? "Contacts" : "Calls")
|
||||
.default_text_style_white_800(styleSize: 20)
|
||||
.padding(.leading, 10)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Image("search")
|
||||
}
|
||||
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Image(index == 0 ? "filtres" : "more")
|
||||
}
|
||||
.padding(.leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.background(Color.orangeMain500)
|
||||
|
||||
if self.index == 0 {
|
||||
ContactsView(contactViewModel: contactViewModel, historyViewModel: historyViewModel)
|
||||
} else if self.index == 1 {
|
||||
HistoryView()
|
||||
}
|
||||
}
|
||||
.frame(maxWidth:
|
||||
(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
? geometry.size.width/100*40
|
||||
: .infinity
|
||||
)
|
||||
.background(
|
||||
Color.white
|
||||
.shadow(color: Color.gray200, radius: 4, x: 0, y: 0)
|
||||
.mask(Rectangle().padding(.horizontal, -8))
|
||||
)
|
||||
|
||||
if orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) {
|
||||
HStack {
|
||||
Group {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.index = 0
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("address-book")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 0 {
|
||||
Text("Contacts")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Contacts")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
.padding(.top)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
self.index = 1
|
||||
contactViewModel.contactTitle = ""
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("phone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 1 {
|
||||
Text("Calls")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Calls")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
.padding(.top)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 15)
|
||||
.background(
|
||||
Color.white
|
||||
.shadow(color: Color.gray200, radius: 4, x: 0, y: 0)
|
||||
.mask(Rectangle().padding(.top, -8))
|
||||
)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 5)
|
||||
.background(Color.orangeMain500)
|
||||
} else {
|
||||
HStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
self.focusedField = false
|
||||
searchIsActive.toggle()
|
||||
}
|
||||
|
||||
text = ""
|
||||
magicSearch.currentFilter = ""
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
} label: {
|
||||
Image("caret-left")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
|
||||
if #available(iOS 16.0, *) {
|
||||
TextEditor(text: Binding(
|
||||
get: {
|
||||
return text
|
||||
},
|
||||
set: { value in
|
||||
var newValue = value
|
||||
if value.contains("\n") {
|
||||
newValue = value.replacingOccurrences(of: "\n", with: "")
|
||||
}
|
||||
text = newValue
|
||||
}
|
||||
))
|
||||
.default_text_style_white_700(styleSize: 15)
|
||||
.padding(.all, 6)
|
||||
.accentColor(.white)
|
||||
.scrollContentBackground(.hidden)
|
||||
.focused($focusedField)
|
||||
.onAppear {
|
||||
self.focusedField = true
|
||||
}
|
||||
.onChange(of: text) { newValue in
|
||||
magicSearch.currentFilter = newValue
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
} else {
|
||||
TextEditor(text: Binding(
|
||||
get: {
|
||||
return text
|
||||
},
|
||||
set: { value in
|
||||
var newValue = value
|
||||
if value.contains("\n") {
|
||||
newValue = value.replacingOccurrences(of: "\n", with: "")
|
||||
}
|
||||
text = newValue
|
||||
}
|
||||
))
|
||||
.default_text_style_white_700(styleSize: 15)
|
||||
.padding(.all, 6)
|
||||
.accentColor(.white)
|
||||
.focused($focusedField)
|
||||
.onAppear {
|
||||
self.focusedField = true
|
||||
}
|
||||
.onChange(of: text) { newValue in
|
||||
magicSearch.currentFilter = newValue
|
||||
magicSearch.searchForContacts(
|
||||
sourceFlags: MagicSearch.Source.Friends.rawValue | MagicSearch.Source.LdapServers.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text = ""
|
||||
} label: {
|
||||
Image("x")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
.padding(.leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 5)
|
||||
.background(Color.orangeMain500)
|
||||
}
|
||||
|
||||
if self.index == 0 {
|
||||
ContactsView(contactViewModel: contactViewModel, historyViewModel: historyViewModel)
|
||||
} else if self.index == 1 {
|
||||
HistoryView()
|
||||
}
|
||||
}
|
||||
.frame(maxWidth:
|
||||
(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
? geometry.size.width/100*40
|
||||
: .infinity
|
||||
)
|
||||
.background(
|
||||
Color.white
|
||||
.shadow(color: Color.gray200, radius: 4, x: 0, y: 0)
|
||||
.mask(Rectangle().padding(.horizontal, -8))
|
||||
)
|
||||
|
||||
if orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) && !searchIsActive {
|
||||
HStack {
|
||||
Group {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
self.index = 0
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("address-book")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 0 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 0 {
|
||||
Text("Contacts")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Contacts")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
.padding(.top)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
self.index = 1
|
||||
contactViewModel.contactTitle = ""
|
||||
}, label: {
|
||||
VStack {
|
||||
Image("phone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(self.index == 1 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||
.frame(width: 25, height: 25)
|
||||
if self.index == 1 {
|
||||
Text("Calls")
|
||||
.default_text_style_700(styleSize: 10)
|
||||
} else {
|
||||
Text("Calls")
|
||||
.default_text_style(styleSize: 10)
|
||||
}
|
||||
}
|
||||
})
|
||||
.padding(.top)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 15)
|
||||
.background(
|
||||
Color.white
|
||||
.shadow(color: Color.gray200, radius: 4, x: 0, y: 0)
|
||||
.mask(Rectangle().padding(.top, -8))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !contactViewModel.contactTitle.isEmpty || !historyViewModel.historyTitle.isEmpty {
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
.frame(maxWidth:
|
||||
(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
? (geometry.size.width/100*40) + 75
|
||||
: 0
|
||||
)
|
||||
if self.index == 0 {
|
||||
ContactFragment(contactViewModel: contactViewModel)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
} else if self.index == 1 {
|
||||
HistoryContactFragment()
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
.ignoresSafeArea(.keyboard)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
&& searchIsActive {
|
||||
self.focusedField = false
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if !(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
&& searchIsActive {
|
||||
self.focusedField = true
|
||||
}
|
||||
}
|
||||
.padding(.leading,
|
||||
orientation == .landscapeRight && geometry.safeAreaInsets.bottom > 0
|
||||
? -geometry.safeAreaInsets.leading
|
||||
: 0)
|
||||
.transition(.move(edge: .trailing))
|
||||
.zIndex(1)
|
||||
}
|
||||
|
||||
SideMenu(
|
||||
width: geometry.size.width / 5 * 4,
|
||||
isOpen: self.sideMenuIsOpen,
|
||||
menuClose: self.openMenu,
|
||||
safeAreaInsets: geometry.safeAreaInsets
|
||||
)
|
||||
.ignoresSafeArea(.all)
|
||||
.zIndex(2)
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if isMenuOpen {
|
||||
Color.white.opacity(0.001)
|
||||
.ignoresSafeArea()
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.onTapGesture {
|
||||
isMenuOpen = false
|
||||
}
|
||||
|
||||
if !contactViewModel.contactTitle.isEmpty || !historyViewModel.historyTitle.isEmpty {
|
||||
HStack(spacing: 0) {
|
||||
Spacer()
|
||||
.frame(maxWidth:
|
||||
(orientation == .landscapeLeft
|
||||
|| orientation == .landscapeRight
|
||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)
|
||||
? (geometry.size.width/100*40) + 75
|
||||
: 0
|
||||
)
|
||||
if self.index == 0 {
|
||||
ContactFragment(contactViewModel: contactViewModel)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
} else if self.index == 1 {
|
||||
HistoryContactFragment()
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(Color.gray100)
|
||||
}
|
||||
}
|
||||
.padding(.leading,
|
||||
orientation == .landscapeRight && geometry.safeAreaInsets.bottom > 0
|
||||
? -geometry.safeAreaInsets.leading
|
||||
: 0)
|
||||
.transition(.move(edge: .trailing))
|
||||
}
|
||||
|
||||
SideMenu(
|
||||
width: geometry.size.width / 5 * 4,
|
||||
isOpen: self.menuOpen,
|
||||
menuClose: self.openMenu,
|
||||
safeAreaInsets: geometry.safeAreaInsets
|
||||
)
|
||||
.ignoresSafeArea(.all)
|
||||
}
|
||||
}
|
||||
.onRotate { newOrientation in
|
||||
orientation = newOrientation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openMenu() {
|
||||
withAnimation {
|
||||
self.menuOpen.toggle()
|
||||
}
|
||||
}
|
||||
.onRotate { newOrientation in
|
||||
if (!contactViewModel.contactTitle.isEmpty || !historyViewModel.historyTitle.isEmpty) && searchIsActive {
|
||||
self.focusedField = false
|
||||
} else if searchIsActive {
|
||||
self.focusedField = true
|
||||
}
|
||||
orientation = newOrientation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openMenu() {
|
||||
withAnimation {
|
||||
self.sideMenuIsOpen.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
|
|
|
|||
70
Linphone/Utils/MagicSearchSingleton.swift
Normal file
70
Linphone/Utils/MagicSearchSingleton.swift
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
final class MagicSearchSingleton: ObservableObject {
|
||||
|
||||
static let shared = MagicSearchSingleton()
|
||||
private var coreContext = CoreContext.shared
|
||||
|
||||
private var magicSearch: MagicSearch!
|
||||
var magicSearchDelegate: MagicSearchDelegate?
|
||||
|
||||
@objc var currentFilter: String = ""
|
||||
var previousFilter: String?
|
||||
|
||||
var needUpdateLastSearchContacts = false
|
||||
|
||||
@Published var lastSearch: [SearchResult] = []
|
||||
|
||||
private var limitSearchToLinphoneAccounts = true
|
||||
|
||||
@Published var allContact = false
|
||||
private var domainDefaultAccount = ""
|
||||
|
||||
private init() {
|
||||
domainDefaultAccount = coreContext.mCore.defaultAccount!.params!.domain!
|
||||
|
||||
magicSearch = try? coreContext.mCore.createMagicSearch()
|
||||
magicSearch.limitedSearch = false
|
||||
|
||||
magicSearchDelegate = MagicSearchDelegateStub(onSearchResultsReceived: { (magicSearch: MagicSearch) in
|
||||
self.needUpdateLastSearchContacts = true
|
||||
self.lastSearch = magicSearch.lastSearch
|
||||
})
|
||||
|
||||
magicSearch.addDelegate(delegate: magicSearchDelegate!)
|
||||
}
|
||||
|
||||
func searchForContacts(sourceFlags: Int) {
|
||||
if let oldFilter = previousFilter {
|
||||
if oldFilter.count > currentFilter.count || oldFilter != currentFilter {
|
||||
magicSearch.resetSearchCache()
|
||||
}
|
||||
}
|
||||
previousFilter = currentFilter
|
||||
|
||||
magicSearch.getContactsListAsync(
|
||||
filter: currentFilter,
|
||||
domain: allContact ? "" : domainDefaultAccount,
|
||||
sourceFlags: sourceFlags,
|
||||
aggregation: MagicSearch.Aggregation.Friend)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import Foundation
|
||||
import Photos
|
||||
import Contacts
|
||||
|
||||
class PermissionManager: ObservableObject {
|
||||
|
||||
|
|
@ -26,6 +27,7 @@ class PermissionManager: ObservableObject {
|
|||
|
||||
@Published var photoLibraryPermissionGranted = false
|
||||
@Published var cameraPermissionGranted = false
|
||||
@Published var contactsPermissionGranted = false
|
||||
|
||||
private init() {}
|
||||
|
||||
|
|
@ -44,4 +46,13 @@ class PermissionManager: ObservableObject {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func contactsRequestPermission() {
|
||||
let store = CNContactStore()
|
||||
store.requestAccess(for: .contacts) { success, _ in
|
||||
DispatchQueue.main.async {
|
||||
self.contactsPermissionGranted = success
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,4 +136,9 @@ extension View {
|
|||
self.font(Font.custom("NotoSans-Regular", size: styleSize))
|
||||
.foregroundStyle(Color.grayMain2c600)
|
||||
}
|
||||
|
||||
func contact_text_style_500(styleSize: CGFloat) -> some View {
|
||||
self.font(Font.custom("NotoSans-Medium", size: styleSize))
|
||||
.foregroundStyle(Color.grayMain2c400)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue