Compare commits
No commits in common. "master" and "6.1.0-alpha" have entirely different histories.
master
...
6.1.0-alph
102
CHANGELOG.md
|
|
@ -10,121 +10,51 @@ Group changes to describe their impact on the project, as follows:
|
||||||
Fixed for any bug fixes.
|
Fixed for any bug fixes.
|
||||||
Security to invite users to upgrade in case of vulnerabilities.
|
Security to invite users to upgrade in case of vulnerabilities.
|
||||||
|
|
||||||
|
## [6.0.0] - 2025-03-11
|
||||||
## [6.1.0] - 2025-12-08
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- LDAP and CardDAV settings
|
|
||||||
- Advanced settings in third-party SIP account login view
|
|
||||||
- Phone number calls in contact details
|
|
||||||
- Recording player
|
|
||||||
- Recording list
|
|
||||||
- Automatic Git commit, branch, and tag info for Help views
|
|
||||||
- Message deletion feature
|
|
||||||
- Message editing feature
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Launch Screen (Splash Screen)
|
|
||||||
- Updated translations from Weblate
|
|
||||||
- Updated SPM dependencies
|
|
||||||
- Disabled meetings view when audio/video conference factory address is missing
|
|
||||||
- Moved disable_chat_feature to UI section
|
|
||||||
- Updated configuration files
|
|
||||||
- Updated last message text in conversation list
|
|
||||||
- Updated PopupView UI
|
|
||||||
- Display core call logs instead of account call logs when the user has only one account
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- International prefix reset in settings
|
|
||||||
- Prevent editing of read-only (LDAP) contacts
|
|
||||||
- Crash when editing a contact (safe unwrapping of friend/photo)
|
|
||||||
- EditContactFragment view and “+” allowed in dialer
|
|
||||||
- Dial plan selector and default dial plan
|
|
||||||
- Encryption update when call state changes
|
|
||||||
- Unread message counter update in onMessageRetracted
|
|
||||||
- French translation of message_content_deleted
|
|
||||||
- Stop composing when the user stops typing
|
|
||||||
- Refresh presence info in history detail
|
|
||||||
- Refresh displayed friend when the contacts list is updated
|
|
||||||
- Prefix handling in interpretUrl when calling a phone number
|
|
||||||
- SIP contacts filter
|
|
||||||
|
|
||||||
|
|
||||||
## [6.0.2] - 2025-09-26
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Advanced settings to third-party SIP account login view
|
|
||||||
- Burger button to open the side menu
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Layout icon in conference call
|
|
||||||
- Translations from Weblate
|
|
||||||
- Disable meetings view when audio/video conference factory address is missing
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- EditContactFragment view and allow '+' in number dialer
|
|
||||||
- Dial plan selector and dial plan default
|
|
||||||
- Crash when editing a contact by safely unwrapping friend/photo
|
|
||||||
- Meeting scheduler
|
|
||||||
|
|
||||||
|
|
||||||
## [6.0.1] - 2025-09-12
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Done button toolbar to number pads
|
|
||||||
- Help view to login page
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- textToImage updated to generate image on the core queue
|
|
||||||
- Send DTMF execution moved to the core queue
|
|
||||||
- Use saveImage on core queue
|
|
||||||
- Use point_to_point string for encrypted calls in conference
|
|
||||||
- Hide VFS setting
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Avatar photo refresh
|
|
||||||
- onEphemeralMessageTimerStarted callback
|
|
||||||
- Crash in updateEncryption by safely handling optional currentCall
|
|
||||||
- Sorted list in MagicSearch when friend is nil
|
|
||||||
- Friend list refresh triggered by onPresenceReceived
|
|
||||||
- Crash when adding or removing SIP addresses and phone numbers in EditContactFragment
|
|
||||||
- awaitDataWrite execution on main queue
|
|
||||||
- Crash by copying Friend addresses and phone numbers before removal
|
|
||||||
- Ensure core is On before stopping it on background entry
|
|
||||||
- textToImage crash
|
|
||||||
|
|
||||||
|
|
||||||
## [6.0.0] - 2025-09-01
|
|
||||||
|
|
||||||
6.0.0 release is a complete rework of Linphone, with a fully redesigned UI, so it is impossible to list everything here.
|
6.0.0 release is a complete rework of Linphone, with a fully redesigned UI, so it is impossible to list everything here.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Separated threads: Contrary to previous versions, our SDK is now running in it's own thread, meaning it won't freeze the UI anymore in case of heavy work.
|
- Separated threads: Contrary to previous versions, our SDK is now running in it's own thread, meaning it won't freeze the UI anymore in case of heavy work, thus reducing the number of ANR and greatly increasing the fluidity of the app.
|
||||||
- Asymmetrical video : you no longer need to send your own camera feed to receive the one from the remote end of the call, and vice versa.
|
- Asymmetrical video : you no longer need to send your own camera feed to receive the one from the remote end of the call, and vice versa.
|
||||||
- Improved multi account: you'll only see history, conversations, meetings etc... related to currently selected account, and you can switch the default account in two clicks.
|
- Improved multi account: you'll only see history, conversations, meetings etc... related to currently selected account, and you can switch the default account in two clicks.
|
||||||
|
- Call transfer: Blind & Attended call transfer have been merged into one: during a call, if you initiate a transfer action, either pick another call to do the attended transfer or select a contact from the list (you can input a SIP URI not already in the suggestions list) to start a blind transfer.
|
||||||
- User can only send up to 12 files in a single chat message.
|
- User can only send up to 12 files in a single chat message.
|
||||||
- IMDNs are now only sent to the message sender, preventing huge traffic in large groups, and thus the delivery status icon for received messages is now hidden in groups (as it was in 1-1 conversations).
|
- IMDNs are now only sent to the message sender, preventing huge traffic in large groups, and thus the delivery status icon for received messages is now hidden in groups (as it was in 1-1 conversations).
|
||||||
- Settings: a lot of them are gone, the one that are still there have been reworked to increase user friendliness.
|
- Settings: a lot of them are gone, the one that are still there have been reworked to increase user friendliness.
|
||||||
- Default screen (between contacts, call history, conversations & meetings list) will change depending on where you were when the app was paused or killed, and you will return to that last visited screen on the next startup.
|
- Default screen (between contacts, call history, conversations & meetings list) will change depending on where you were when the app was paused or killed, and you will return to that last visited screen on the next startup.
|
||||||
|
- Gradle files have been migrated from Groovy to Kotlin DSL, and dependencies are now in a separated file (libs.versions.toml).
|
||||||
- Account creation no longer allows you to use your phone number as username, but it is still required to provide it to receive activation code by SMS.
|
- Account creation no longer allows you to use your phone number as username, but it is still required to provide it to receive activation code by SMS.
|
||||||
- Minimum supported iOS version is now 15.
|
- Minimum supported iOS version is now 15.
|
||||||
- Some settings have changed name and/or section in linphonerc file.
|
- Some settings have changed name and/or section in linphonerc file.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Contacts trust: contacts for which all devices have been validated through a ZRTP call with SAS exchange are now highlighted with a blue circle (and with a red one in case of mistrust). That trust is now handled at contact level (instead of conversation level in previous versions).
|
||||||
|
- Media & documents exchanged in a conversation can be easily found through a dedicated screen.
|
||||||
|
- A brand new chat message search feature has been added to conversations.
|
||||||
- You can now react to a chat message using any emoji.
|
- You can now react to a chat message using any emoji.
|
||||||
|
- If next message is also a voice recording, playback will automatically start after the currently playing one ends.
|
||||||
- Chat while in call: a shortcut to a conversation screen with the remote.
|
- Chat while in call: a shortcut to a conversation screen with the remote.
|
||||||
- Chat while in a conference: if the conference has a text stream enabled, you can chat with the other participants of the conference while it lasts. At the end, you'll find the messages history in the call history (and not in the list of conversations).
|
- Chat while in a conference: if the conference has a text stream enabled, you can chat with the other participants of the conference while it lasts. At the end, you'll find the messages history in the call history (and not in the list of conversations).
|
||||||
|
- Auto export of media to native gallery even when auto download is enabled (but still not if VFS is enabled nor for ephemeral messages).
|
||||||
|
- Save / export document & media from ephemeral messages will be disabled, and secure policy that prevents screenshots will be enforced in file viewer even if the setting is disabled.
|
||||||
|
- Notification showing upload/download of files shared through chat will let user know the progress and keep the app alive during that process.
|
||||||
|
- Screen sharing in conference: only desktop app starting with 6.0 version is able to start it, but on mobiles you'll be able to see it.
|
||||||
- Security focus: security & trust is more visible than ever, and unsecure conversations & calls are even more visible than before.
|
- Security focus: security & trust is more visible than ever, and unsecure conversations & calls are even more visible than before.
|
||||||
- OpenID: when used with a SSO compliant SIP server (such as Flexisip), we support single-sign-on login.
|
- OpenID: when used with a SSO compliant SIP server (such as Flexisip), we support single-sign-on login.
|
||||||
- MWI support: display and allow to call your voicemail when you have new messages (if supported by your VoIP provider and properly configured in your account params).
|
- MWI support: display and allow to call your voicemail when you have new messages (if supported by your VoIP provider and properly configured in your account params).
|
||||||
- CCMP support: if you configure a CCMP server URL in your accounts params, it will be used when scheduling meetings & to fetch list of meetings you've organized/been invited to.
|
- CCMP support: if you configure a CCMP server URL in your accounts params, it will be used when scheduling meetings & to fetch list of meetings you've organized/been invited to.
|
||||||
- Devices list: check on which device your sip.linphone.org account is connected and the last connection date & time (like on subscribe.linphone.org).
|
- Devices list: check on which device your sip.linphone.org account is connected and the last connection date & time (like on subscribe.linphone.org).
|
||||||
|
- Protobuf dependency to allow logging native crashes stack traces at next app startup.
|
||||||
- Dialer & in-call numpad show letters under the digit.
|
- Dialer & in-call numpad show letters under the digit.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- Dialer: the previous home screen (dialer) has been removed, you'll find it as an input option in the new start call screen.
|
- Dialer: the previous home screen (dialer) has been removed, you'll find it as an input option in the new start call screen.
|
||||||
- Peer-to-peer: a SIP account (sip.linphone.org or other) is now required.
|
- Peer-to-peer: a SIP account (sip.linphone.org or other) is now required.
|
||||||
|
- Contacts: we no longer add contacts created in-app in the native addressbook (WRITE_CONTACTS permission was removed), but we still import them if you grant us the READ_CONTACTS permission.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- AAudio driver no longer causes delay when switching between devices (SDK fix).
|
||||||
|
|
||||||
## [5.2.0] - 2023-28-12
|
## [5.2.0] - 2023-28-12
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
|
|
@ -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,208H48V96H208V208Zm-96-88v64a8,8,0,0,1-16,0V132.94l-4.42,2.22a8,8,0,0,1-7.16-14.32l16-8A8,8,0,0,1,112,120Zm59.16,30.45L152,176h16a8,8,0,0,1,0,16H136a8,8,0,0,1-6.4-12.8l28.78-38.37A8,8,0,1,0,145.07,132a8,8,0,1,1-13.85-8A24,24,0,0,1,176,136,23.76,23.76,0,0,1,171.16,150.45Z"></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,208H48V96H208V208Zm-96-88v64a8,8,0,0,1-16,0V132.94l-4.42,2.22a8,8,0,0,1-7.16-14.32l16-8A8,8,0,0,1,112,120Zm59.16,30.45L152,176h16a8,8,0,0,1,0,16H136a8,8,0,0,1-6.4-12.8l28.78-38.37A8,8,0,1,0,145.07,132a8,8,0,1,1-13.85-8A24,24,0,0,1,176,136,23.76,23.76,0,0,1,171.16,150.45Z"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 604 B After Width: | Height: | Size: 604 B |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "layout.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="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,16V96H40V56ZM40,112H96v88H40Zm176,88H112V112H216v88Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 274 B |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "list.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="M224,128a8,8,0,0,1-8,8H40a8,8,0,0,1,0-16H216A8,8,0,0,1,224,128ZM40,72H216a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16ZM216,184H40a8,8,0,0,0,0,16H216a8,8,0,0,0,0-16Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 277 B |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "music-notes.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" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><circle cx="180" cy="164" r="28" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><circle cx="52" cy="196" r="28" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="208" y1="72" x2="80" y2="104" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><polyline points="80 196 80 56 208 24 208 164" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 664 B |
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M222.37,158.46l-47.11-21.11-.13-.06a16,16,0,0,0-15.17,1.4,8.12,8.12,0,0,0-.75.56L134.87,160c-15.42-7.49-31.34-23.29-38.83-38.51l20.78-24.71c.2-.25.39-.5.57-.77a16,16,0,0,0,1.32-15.06l0-.12L97.54,33.64a16,16,0,0,0-16.62-9.52A56.26,56.26,0,0,0,32,80c0,79.4,64.6,144,144,144a56.26,56.26,0,0,0,55.88-48.92A16,16,0,0,0,222.37,158.46ZM176,208A128.14,128.14,0,0,1,48,80,40.2,40.2,0,0,1,82.87,40a.61.61,0,0,0,0,.12l21,47L83.2,111.86a6.13,6.13,0,0,0-.57.77,16,16,0,0,0-1,15.7c9.06,18.53,27.73,37.06,46.46,46.11a16,16,0,0,0,15.75-1.14,8.44,8.44,0,0,0,.74-.56L168.89,152l47,21.05h0s.08,0,.11,0A40.21,40.21,0,0,1,176,208Z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" fill="#4e6074" viewBox="0 0 256 256"><path d="M222.37,158.46l-47.11-21.11-.13-.06a16,16,0,0,0-15.17,1.4,8.12,8.12,0,0,0-.75.56L134.87,160c-15.42-7.49-31.34-23.29-38.83-38.51l20.78-24.71c.2-.25.39-.5.57-.77a16,16,0,0,0,1.32-15.06l0-.12L97.54,33.64a16,16,0,0,0-16.62-9.52A56.26,56.26,0,0,0,32,80c0,79.4,64.6,144,144,144a56.26,56.26,0,0,0,55.88-48.92A16,16,0,0,0,222.37,158.46ZM176,208A128.14,128.14,0,0,1,48,80,40.2,40.2,0,0,1,82.87,40a.61.61,0,0,0,0,.12l21,47L83.2,111.86a6.13,6.13,0,0,0-.57.77,16,16,0,0,0-1,15.7c9.06,18.53,27.73,37.06,46.46,46.11a16,16,0,0,0,15.75-1.14,8.44,8.44,0,0,0,.74-.56L168.89,152l47,21.05h0s.08,0,.11,0A40.21,40.21,0,0,1,176,208Z"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 733 B After Width: | Height: | Size: 735 B |
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M176,160a39.89,39.89,0,0,0-28.62,12.09l-46.1-29.63a39.8,39.8,0,0,0,0-28.92l46.1-29.63a40,40,0,1,0-8.66-13.45l-46.1,29.63a40,40,0,1,0,0,55.82l46.1,29.63A40,40,0,1,0,176,160Zm0-128a24,24,0,1,1-24,24A24,24,0,0,1,176,32ZM64,152a24,24,0,1,1,24-24A24,24,0,0,1,64,152Zm112,72a24,24,0,1,1,24-24A24,24,0,0,1,176,224Z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#4e6074" viewBox="0 0 256 256"><path d="M176,160a39.89,39.89,0,0,0-28.62,12.09l-46.1-29.63a39.8,39.8,0,0,0,0-28.92l46.1-29.63a40,40,0,1,0-8.66-13.45l-46.1,29.63a40,40,0,1,0,0,55.82l46.1,29.63A40,40,0,1,0,176,160Zm0-128a24,24,0,1,1-24,24A24,24,0,0,1,176,32ZM64,152a24,24,0,1,1,24-24A24,24,0,0,1,64,152Zm112,72a24,24,0,1,1,24-24A24,24,0,0,1,176,224Z"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 431 B After Width: | Height: | Size: 431 B |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "trash.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="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 415 B |
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "voicemail.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="M200,72a56,56,0,0,0-39.14,96H95.14A56,56,0,1,0,56,184H200a56,56,0,0,0,0-112ZM16,128a40,40,0,1,1,40,40A40,40,0,0,1,16,128Zm184,40a40,40,0,1,1,40-40A40,40,0,0,1,200,168Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 291 B |
|
|
@ -113,8 +113,6 @@ final class ContactsManager: ObservableObject {
|
||||||
core.addFriendList(list: tempRemoteFriendList)
|
core.addFriendList(list: tempRemoteFriendList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.refreshCardDavContacts()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let store = CNContactStore()
|
let store = CNContactStore()
|
||||||
|
|
@ -156,22 +154,25 @@ final class ContactsManager: ObservableObject {
|
||||||
|
|
||||||
let imageThumbnail = UIImage(data: contact.thumbnailImageData ?? Data())
|
let imageThumbnail = UIImage(data: contact.thumbnailImageData ?? Data())
|
||||||
if let image = imageThumbnail {
|
if let image = imageThumbnail {
|
||||||
self.saveImage(
|
DispatchQueue.main.async {
|
||||||
image: image,
|
self.saveImage(
|
||||||
name: contact.givenName + contact.familyName,
|
image: image,
|
||||||
prefix: "",
|
name: contact.givenName + contact.familyName,
|
||||||
contact: newContact, linphoneFriend: self.nativeAddressBookFriendList, existingFriend: nil) {
|
prefix: "",
|
||||||
dispatchGroup.leave()
|
contact: newContact, linphoneFriend: self.nativeAddressBookFriendList, existingFriend: nil) {
|
||||||
}
|
dispatchGroup.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let image = self.textToImage(firstName: contact.givenName, lastName: contact.familyName)
|
self.textToImageInMainThread(firstName: contact.givenName, lastName: contact.familyName) { image in
|
||||||
self.saveImage(
|
self.saveImage(
|
||||||
image: image,
|
image: image,
|
||||||
name: contact.givenName + contact.familyName,
|
name: contact.givenName + contact.familyName,
|
||||||
prefix: "-default",
|
prefix: "-default",
|
||||||
contact: newContact, linphoneFriend: self.nativeAddressBookFriendList, existingFriend: nil) {
|
contact: newContact, linphoneFriend: self.nativeAddressBookFriendList, existingFriend: nil) {
|
||||||
dispatchGroup.leave()
|
dispatchGroup.leave()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -196,165 +197,154 @@ final class ContactsManager: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func textToImage(firstName: String?, lastName: String?) -> UIImage {
|
func textToImageInMainThread(firstName: String, lastName: String, completion: @escaping (UIImage) -> Void) {
|
||||||
let firstInitial = firstName?.first.map { String($0) } ?? ""
|
DispatchQueue.main.async {
|
||||||
let lastInitial = lastName?.first.map { String($0) } ?? ""
|
let lblNameInitialize = UILabel()
|
||||||
let textToDisplay = (firstInitial + lastInitial).uppercased()
|
lblNameInitialize.frame.size = CGSize(width: 200.0, height: 200.0)
|
||||||
|
lblNameInitialize.font = UIFont(name: "NotoSans-ExtraBold", size: 80)
|
||||||
let size = CGSize(width: 200, height: 200)
|
lblNameInitialize.textColor = UIColor(Color.grayMain2c600)
|
||||||
let renderer = UIGraphicsImageRenderer(size: size)
|
|
||||||
|
|
||||||
return renderer.image { _ in
|
|
||||||
let rect = CGRect(origin: .zero, size: size)
|
|
||||||
|
|
||||||
UIColor(Color.grayMain2c200).setFill()
|
let textToDisplay = (firstName.first.map { String($0) } ?? "") + (lastName.first.map { String($0) } ?? "")
|
||||||
UIBezierPath(roundedRect: rect, cornerRadius: 10).fill()
|
|
||||||
|
|
||||||
let paragraph = NSMutableParagraphStyle()
|
lblNameInitialize.text = textToDisplay.uppercased()
|
||||||
paragraph.alignment = .center
|
lblNameInitialize.textAlignment = .center
|
||||||
|
lblNameInitialize.backgroundColor = UIColor(Color.grayMain2c200)
|
||||||
let attributes: [NSAttributedString.Key: Any] = [
|
lblNameInitialize.layer.cornerRadius = 10.0
|
||||||
.font: UIFont(name: "NotoSans-ExtraBold", size: 80) ?? UIFont.boldSystemFont(ofSize: 80),
|
lblNameInitialize.clipsToBounds = true
|
||||||
.foregroundColor: UIColor(Color.grayMain2c600),
|
|
||||||
.paragraphStyle: paragraph
|
UIGraphicsBeginImageContext(lblNameInitialize.frame.size)
|
||||||
]
|
defer { UIGraphicsEndImageContext() }
|
||||||
|
|
||||||
let textSize = textToDisplay.size(withAttributes: attributes)
|
guard let context = UIGraphicsGetCurrentContext() else {
|
||||||
let textRect = CGRect(
|
completion(UIImage())
|
||||||
x: (size.width - textSize.width) / 2,
|
return
|
||||||
y: (size.height - textSize.height) / 2,
|
}
|
||||||
width: textSize.width,
|
|
||||||
height: textSize.height
|
lblNameInitialize.layer.render(in: context)
|
||||||
)
|
let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
|
||||||
|
completion(image)
|
||||||
textToDisplay.draw(in: textRect, withAttributes: attributes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageFromBase64(_ base64String: String) -> UIImage? {
|
func textToImage(firstName: String, lastName: String) -> UIImage {
|
||||||
let cleanedString: String
|
let lblNameInitialize = UILabel()
|
||||||
if let range = base64String.range(of: "base64,") {
|
lblNameInitialize.frame.size = CGSize(width: 200.0, height: 200.0)
|
||||||
cleanedString = String(base64String[range.upperBound...])
|
lblNameInitialize.font = UIFont(name: "NotoSans-ExtraBold", size: 80)
|
||||||
} else {
|
lblNameInitialize.textColor = UIColor(Color.grayMain2c600)
|
||||||
cleanedString = base64String
|
|
||||||
|
var textToDisplay = ""
|
||||||
|
if firstName.first != nil {
|
||||||
|
textToDisplay += String(firstName.first!)
|
||||||
|
}
|
||||||
|
if lastName.first != nil {
|
||||||
|
textToDisplay += String(lastName.first!)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let imageData = Data(base64Encoded: cleanedString, options: .ignoreUnknownCharacters) else {
|
lblNameInitialize.text = textToDisplay.uppercased()
|
||||||
print("Error: failed to decode Base64 string")
|
lblNameInitialize.textAlignment = .center
|
||||||
return nil
|
lblNameInitialize.backgroundColor = UIColor(Color.grayMain2c200)
|
||||||
}
|
lblNameInitialize.layer.cornerRadius = 10.0
|
||||||
|
|
||||||
return UIImage(data: imageData)
|
var IBImgViewUserProfile = UIImage()
|
||||||
|
UIGraphicsBeginImageContext(lblNameInitialize.frame.size)
|
||||||
|
lblNameInitialize.layer.render(in: UIGraphicsGetCurrentContext()!)
|
||||||
|
IBImgViewUserProfile = UIGraphicsGetImageFromCurrentImageContext()!
|
||||||
|
UIGraphicsEndImageContext()
|
||||||
|
|
||||||
|
return IBImgViewUserProfile
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveImage(image: UIImage, name: String, prefix: String, contact: Contact, linphoneFriend: String, existingFriend: Friend?, editingFriend: Bool = false, completion: @escaping () -> Void) {
|
func saveImage(image: UIImage, name: String, prefix: String, contact: Contact, linphoneFriend: String, existingFriend: Friend?, completion: @escaping () -> Void) {
|
||||||
guard let data = image.jpegData(compressionQuality: 1) ?? image.pngData() else {
|
guard let data = image.jpegData(compressionQuality: 1) ?? image.pngData() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let base64Tmp = existingFriend?.friendList?.type == .CardDAV || linphoneAddressBookFriendList != CorePreferences.friendListInWhichStoreNewlyCreatedFriends
|
awaitDataWrite(data: data, name: name, prefix: prefix) { _, result in
|
||||||
|
self.saveFriend(result: result, contact: contact, existingFriend: existingFriend) { resultFriend in
|
||||||
awaitDataWrite(data: data, name: name, prefix: prefix, base64: base64Tmp) { result in
|
if resultFriend != nil {
|
||||||
if existingFriend?.friendList?.type != .CardDAV
|
if linphoneFriend != self.nativeAddressBookFriendList && existingFriend == nil {
|
||||||
|| (existingFriend?.friendList?.type == .CardDAV && linphoneFriend == self.linphoneAddressBookFriendList)
|
if let linphoneFL = self.linphoneFriendList, linphoneFriend == linphoneFL.displayName {
|
||||||
|| (editingFriend && linphoneFriend == CorePreferences.friendListInWhichStoreNewlyCreatedFriends) {
|
_ = linphoneFL.addFriend(linphoneFriend: resultFriend!)
|
||||||
self.saveFriend(result: result, contact: contact, existingFriend: existingFriend) { resultFriend in
|
} else if let linphoneFL = self.tempRemoteFriendList {
|
||||||
self.coreContext.doOnCoreQueue { core in
|
_ = linphoneFL.addFriend(linphoneFriend: resultFriend!)
|
||||||
if let friend = resultFriend {
|
}
|
||||||
if linphoneFriend != self.nativeAddressBookFriendList && existingFriend == nil {
|
} else if existingFriend == nil {
|
||||||
if let linphoneFL = self.linphoneFriendList, linphoneFriend == linphoneFL.displayName {
|
if let friendListTmp = self.friendList {
|
||||||
_ = linphoneFL.addFriend(linphoneFriend: friend)
|
_ = friendListTmp.addLocalFriend(linphoneFriend: resultFriend!)
|
||||||
} else if let linphoneFL = core.friendsLists.first(where: { $0.type == .CardDAV && $0.displayName == CorePreferences.friendListInWhichStoreNewlyCreatedFriends }) {
|
|
||||||
if linphoneFL.type == .CardDAV {
|
|
||||||
_ = linphoneFL.addFriend(linphoneFriend: friend)
|
|
||||||
}
|
|
||||||
} else if let linphoneFL = self.tempRemoteFriendList {
|
|
||||||
if friend.friendList?.type != .CardDAV {
|
|
||||||
_ = linphoneFL.addFriend(linphoneFriend: friend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if existingFriend == nil {
|
|
||||||
if let friendListTmp = self.friendList {
|
|
||||||
_ = friendListTmp.addLocalFriend(linphoneFriend: friend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
completion()
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func saveFriend(result: String, contact: Contact, existingFriend: Friend?, completion: @escaping (Friend?) -> Void) {
|
func saveFriend(result: String, contact: Contact, existingFriend: Friend?, completion: @escaping (Friend?) -> Void) {
|
||||||
self.coreContext.doOnCoreQueue { core in
|
self.coreContext.doOnCoreQueue { core in
|
||||||
do {
|
do {
|
||||||
// Create or use existing friend
|
|
||||||
let friend = try existingFriend ?? core.createFriend()
|
let friend = try existingFriend ?? core.createFriend()
|
||||||
|
|
||||||
// Strong capture in closure to avoid threading issues
|
|
||||||
friend.edit()
|
friend.edit()
|
||||||
|
|
||||||
friend.nativeUri = contact.identifier
|
friend.nativeUri = contact.identifier
|
||||||
try friend.setName(newValue: contact.firstName + " " + contact.lastName)
|
try friend.setName(newValue: contact.firstName + " " + contact.lastName)
|
||||||
|
|
||||||
// Safely update vCard
|
let friendvCard = friend.vcard
|
||||||
if let vcard = friend.vcard {
|
|
||||||
vcard.givenName = contact.firstName
|
if friendvCard != nil {
|
||||||
vcard.familyName = contact.lastName
|
friendvCard!.givenName = contact.firstName
|
||||||
|
friendvCard!.familyName = contact.lastName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend.organization = contact.organizationName
|
||||||
|
|
||||||
|
var friendAddresses: [Address] = []
|
||||||
|
friend.addresses.forEach({ address in
|
||||||
|
friend.removeAddress(address: address)
|
||||||
|
})
|
||||||
|
contact.sipAddresses.forEach { sipAddress in
|
||||||
|
if !sipAddress.isEmpty {
|
||||||
|
let address = core.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] = []
|
||||||
|
friend.phoneNumbersWithLabel.forEach({ phoneNumber in
|
||||||
|
friend.removePhoneNumberWithLabel(phoneNumber: phoneNumber)
|
||||||
|
})
|
||||||
|
contact.phoneNumbers.forEach { phone in
|
||||||
|
do {
|
||||||
|
if (friendPhoneNumbers.firstIndex(where: {$0.num == phone.num})) == nil {
|
||||||
|
let labelDrop = String(phone.numLabel.dropFirst(4).dropLast(4))
|
||||||
|
let phoneNumber = try Factory.Instance.createFriendPhoneNumber(phoneNumber: phone.num, label: labelDrop)
|
||||||
|
friend.addPhoneNumberWithLabel(phoneNumber: phoneNumber)
|
||||||
|
friendPhoneNumbers.append(phone)
|
||||||
|
}
|
||||||
|
} catch let error {
|
||||||
|
print("\(#function) - Failed to create friend phone number for \(phone.numLabel):", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend.photo = "file:/" + result
|
||||||
friend.organization = contact.organizationName
|
friend.organization = contact.organizationName
|
||||||
friend.jobTitle = contact.jobTitle
|
friend.jobTitle = contact.jobTitle
|
||||||
|
|
||||||
// Clear existing addresses and add new ones
|
|
||||||
friend.addresses.forEach { friend.removeAddress(address: $0) }
|
|
||||||
for sipAddress in contact.sipAddresses where !sipAddress.isEmpty {
|
|
||||||
if let address = core.interpretUrl(url: sipAddress, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)),
|
|
||||||
!friend.addresses.contains(where: { $0.asString() == address.asString() }) {
|
|
||||||
friend.addAddress(address: address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear existing phone numbers and add new ones
|
|
||||||
friend.phoneNumbersWithLabel.forEach { friend.removePhoneNumberWithLabel(phoneNumber: $0) }
|
|
||||||
for phone in contact.phoneNumbers {
|
|
||||||
do {
|
|
||||||
let labelDrop = String(phone.numLabel.dropFirst(4).dropLast(4))
|
|
||||||
let phoneNumber = try Factory.Instance.createFriendPhoneNumber(phoneNumber: phone.num, label: labelDrop)
|
|
||||||
friend.addPhoneNumberWithLabel(phoneNumber: phoneNumber)
|
|
||||||
} catch {
|
|
||||||
print("saveFriend - Failed to create friend phone number for \(phone.numLabel):", error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set photo
|
|
||||||
friend.photo = (friend.friendList?.type != .CardDAV && self.linphoneAddressBookFriendList == CorePreferences.friendListInWhichStoreNewlyCreatedFriends ? "file:/" : "") + result
|
|
||||||
|
|
||||||
// Linphone subscription settings
|
|
||||||
try friend.setSubscribesenabled(newValue: false)
|
try friend.setSubscribesenabled(newValue: false)
|
||||||
try friend.setIncsubscribepolicy(newValue: .SPDeny)
|
try friend.setIncsubscribepolicy(newValue: .SPDeny)
|
||||||
|
|
||||||
// Commit changes
|
|
||||||
friend.done()
|
friend.done()
|
||||||
|
|
||||||
// Notify completion safely
|
|
||||||
completion(friend)
|
completion(friend)
|
||||||
} catch {
|
} catch let error {
|
||||||
print("saveFriend - Failed to save friend:", error)
|
print("Failed to enumerate contact", error)
|
||||||
completion(nil)
|
completion(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func getImagePath(friendPhotoPath: String) -> URL {
|
func getImagePath(friendPhotoPath: String) -> URL {
|
||||||
let friendPath = String(friendPhotoPath.dropFirst(6))
|
let friendPath = String(friendPhotoPath.dropFirst(6))
|
||||||
|
|
@ -364,40 +354,25 @@ final class ContactsManager: ObservableObject {
|
||||||
return imagePath
|
return imagePath
|
||||||
}
|
}
|
||||||
|
|
||||||
func awaitDataWrite(data: Data, name: String, prefix: String, base64: Bool? = false, completion: @escaping (String) -> Void) {
|
func awaitDataWrite(data: Data, name: String, prefix: String, completion: @escaping ((), String) -> Void) {
|
||||||
guard let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
|
let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
|
||||||
completion("")
|
|
||||||
return
|
if directory != nil {
|
||||||
}
|
DispatchQueue.main.async {
|
||||||
if base64 == false {
|
do {
|
||||||
do {
|
if let urlName = URL(string: name + prefix) {
|
||||||
let fileName = name + prefix + ".png"
|
let imagePath = urlName.absoluteString.replacingOccurrences(of: "%", with: "")
|
||||||
|
|
||||||
let fileURL = directory.appendingPathComponent(fileName.replacingOccurrences(of: " ", with: ""))
|
let decodedData: () = try data.write(to: directory!.appendingPathComponent(imagePath + ".png"))
|
||||||
|
|
||||||
try data.write(to: fileURL)
|
completion(decodedData, imagePath + ".png")
|
||||||
completion(fileName.replacingOccurrences(of: " ", with: ""))
|
} else {
|
||||||
} catch {
|
completion((), "")
|
||||||
print("Error writing image: \(error)")
|
}
|
||||||
completion("")
|
} catch {
|
||||||
}
|
print("Error: ", error)
|
||||||
} else {
|
completion((), "")
|
||||||
do {
|
|
||||||
let fileName = name + prefix + ".png"
|
|
||||||
|
|
||||||
let fileURL = directory.appendingPathComponent(fileName.replacingOccurrences(of: " ", with: ""))
|
|
||||||
|
|
||||||
try data.write(to: fileURL)
|
|
||||||
|
|
||||||
if prefix.isEmpty {
|
|
||||||
let base64 = data.base64EncodedString()
|
|
||||||
completion("data:image/jpeg;base64,\(base64)")
|
|
||||||
} else {
|
|
||||||
completion("")
|
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
print("Error writing image: \(error)")
|
|
||||||
completion("")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -424,22 +399,15 @@ final class ContactsManager: ObservableObject {
|
||||||
var friend: Friend?
|
var friend: Friend?
|
||||||
|
|
||||||
if let friendList = self.friendList {
|
if let friendList = self.friendList {
|
||||||
friend = friendList.friends.first(where: { $0.addresses.contains(where: { $0.asStringUriOnly() == sipUri }) || $0.phoneNumbers.contains(where: { $0 == address.username }) })
|
friend = friendList.friends.first(where: { $0.addresses.contains(where: { $0.asStringUriOnly() == sipUri }) })
|
||||||
}
|
}
|
||||||
|
|
||||||
if friend == nil, let linphoneFriendList = self.linphoneFriendList {
|
if friend == nil, let linphoneFriendList = self.linphoneFriendList {
|
||||||
friend = linphoneFriendList.friends.first(where: { $0.addresses.contains(where: { $0.asStringUriOnly() == sipUri }) || $0.phoneNumbers.contains(where: { $0 == address.username }) })
|
friend = linphoneFriendList.friends.first(where: { $0.addresses.contains(where: { $0.asStringUriOnly() == sipUri }) })
|
||||||
}
|
}
|
||||||
if friend == nil, let tempRemoteFriendList = self.tempRemoteFriendList {
|
if friend == nil, let tempRemoteFriendList = self.tempRemoteFriendList {
|
||||||
friend = tempRemoteFriendList.friends.first(where: { $0.addresses.contains(where: { $0.asStringUriOnly() == sipUri }) || $0.phoneNumbers.contains(where: { $0 == address.username }) })
|
friend = tempRemoteFriendList.friends.first(where: { $0.addresses.contains(where: { $0.asStringUriOnly() == sipUri }) })
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreContext.shared.mCore.friendsLists.forEach { friendList in
|
|
||||||
if friendList.type == .CardDAV {
|
|
||||||
friend = friendList.friends.first(where: { $0.addresses.contains(where: { $0.asStringUriOnly() == sipUri }) || $0.phoneNumbers.contains(where: { $0 == address.username }) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return friend
|
return friend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,13 +425,6 @@ final class ContactsManager: ObservableObject {
|
||||||
friendList.updateSubscriptions()
|
friendList.updateSubscriptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let friendListDelegateToDelete = self.friendListDelegate {
|
|
||||||
CoreContext.shared.mCore.friendsLists.forEach { friendList in
|
|
||||||
friendList.removeDelegate(delegate: friendListDelegateToDelete)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.friendListDelegate = nil
|
|
||||||
|
|
||||||
let friendListDelegateTmp = FriendListDelegateStub(
|
let friendListDelegateTmp = FriendListDelegateStub(
|
||||||
onContactCreated: { (friendList: FriendList, linphoneFriend: Friend) in
|
onContactCreated: { (friendList: FriendList, linphoneFriend: Friend) in
|
||||||
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onContactCreated")
|
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onContactCreated")
|
||||||
|
|
@ -475,143 +436,56 @@ final class ContactsManager: ObservableObject {
|
||||||
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onContactUpdated")
|
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onContactUpdated")
|
||||||
},
|
},
|
||||||
onSyncStatusChanged: { (friendList: FriendList, status: FriendList.SyncStatus?, message: String?) in
|
onSyncStatusChanged: { (friendList: FriendList, status: FriendList.SyncStatus?, message: String?) in
|
||||||
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onSyncStatusChanged \(friendList.displayName ?? "No Display Name") -- Status: \(status != nil ? String(describing: status!) : "No Status")")
|
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onSyncStatusChanged")
|
||||||
if status == .Successful {
|
if status == .Successful {
|
||||||
if friendList.displayName != self.nativeAddressBookFriendList && friendList.displayName != self.linphoneAddressBookFriendList {
|
if friendList.displayName != self.nativeAddressBookFriendList && friendList.displayName != self.linphoneAddressBookFriendList {
|
||||||
if let tempRemoteFriendList = self.tempRemoteFriendList {
|
if let tempRemoteFriendList = self.tempRemoteFriendList {
|
||||||
tempRemoteFriendList.friends.forEach { friend in
|
tempRemoteFriendList.friends.forEach { friend in
|
||||||
if let friendAddress = friend.address,
|
_ = tempRemoteFriendList.removeFriend(linphoneFriend: friend)
|
||||||
friendList.friends.contains(where: { $0.address?.weakEqual(address2: friendAddress) == true }) {
|
|
||||||
_ = tempRemoteFriendList.removeFriend(linphoneFriend: friend)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dispatchGroup = DispatchGroup()
|
let dispatchGroup = DispatchGroup()
|
||||||
|
|
||||||
friendList.friends.forEach { friend in
|
friendList.friends.forEach { friend in
|
||||||
dispatchGroup.enter()
|
dispatchGroup.enter()
|
||||||
let addressTmp = friend.address?.clone()?.asStringUriOnly() ?? ""
|
let addressTmp = friend.address?.clone()?.asStringUriOnly() ?? ""
|
||||||
|
|
||||||
let newContact = Contact(
|
let newContact = Contact(
|
||||||
identifier: UUID().uuidString,
|
identifier: UUID().uuidString,
|
||||||
firstName: friend.firstName ?? addressTmp,
|
firstName: friend.name ?? addressTmp,
|
||||||
lastName: friend.lastName ?? "",
|
lastName: "",
|
||||||
organizationName: friend.organization ?? "",
|
organizationName: "",
|
||||||
jobTitle: friend.jobTitle ?? "",
|
jobTitle: "",
|
||||||
displayName: friend.address?.displayName ?? "",
|
displayName: friend.address?.displayName ?? "",
|
||||||
sipAddresses: friend.addresses.map { $0.asStringUriOnly() },
|
sipAddresses: friend.addresses.map { $0.asStringUriOnly() },
|
||||||
phoneNumbers: friend.phoneNumbersWithLabel.map { PhoneNumber(numLabel: $0.label ?? "", num: $0.phoneNumber)},
|
phoneNumbers: [],
|
||||||
imageData: ""
|
imageData: ""
|
||||||
)
|
)
|
||||||
|
|
||||||
let image: UIImage?
|
self.textToImageInMainThread(firstName: friend.name ?? addressTmp, lastName: "") { image in
|
||||||
|
|
||||||
if let photo = friend.photo, !photo.isEmpty, friendList.type == .CardDAV {
|
|
||||||
if let imageTmp = self.imageFromBase64(photo) {
|
|
||||||
image = imageTmp
|
|
||||||
if let image = image {
|
|
||||||
self.saveImage(
|
|
||||||
image: image,
|
|
||||||
name: friend.name ?? addressTmp,
|
|
||||||
prefix: "",
|
|
||||||
contact: newContact, linphoneFriend: friendList.displayName ?? "No Display Name", existingFriend: friend.friendList?.type == .CardDAV ? friend : nil) {
|
|
||||||
dispatchGroup.leave()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
image = self.textToImage(firstName: friend.name ?? addressTmp, lastName: "")
|
|
||||||
if let image = image {
|
|
||||||
self.saveImage(
|
|
||||||
image: image,
|
|
||||||
name: friend.name ?? addressTmp,
|
|
||||||
prefix: "-default",
|
|
||||||
contact: newContact, linphoneFriend: friendList.displayName ?? "No Display Name", existingFriend: friend.friendList?.type == .CardDAV ? friend : nil) {
|
|
||||||
dispatchGroup.leave()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
image = self.textToImage(firstName: friend.name ?? addressTmp, lastName: "")
|
|
||||||
if let image = image {
|
|
||||||
self.saveImage(
|
|
||||||
image: image,
|
|
||||||
name: friend.name ?? addressTmp,
|
|
||||||
prefix: "-default",
|
|
||||||
contact: newContact, linphoneFriend: friendList.displayName ?? "No Display Name", existingFriend: friend.friendList?.type == .CardDAV ? friend : nil) {
|
|
||||||
dispatchGroup.leave()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchGroup.notify(queue: .main) {
|
|
||||||
self.coreContext.doOnCoreQueue { _ in
|
|
||||||
MagicSearchSingleton.shared.searchForContacts()
|
|
||||||
if let linphoneFL = self.tempRemoteFriendList {
|
|
||||||
linphoneFL.updateSubscriptions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPresenceReceived: { (friendList: FriendList, friends: [Friend?]) in
|
|
||||||
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onPresenceReceived \(friends.count)")
|
|
||||||
if (friendList.isSubscriptionBodyless) {
|
|
||||||
Log.info("\(ContactsManager.TAG) Bodyless friendlist \(friendList.displayName ?? "No Display Name") presence received")
|
|
||||||
|
|
||||||
if friendList.displayName != self.nativeAddressBookFriendList && friendList.displayName != self.linphoneAddressBookFriendList {
|
|
||||||
if let tempRemoteFriendList = self.tempRemoteFriendList {
|
|
||||||
tempRemoteFriendList.friends.forEach { friend in
|
|
||||||
if let friendAddress = friend.address,
|
|
||||||
friends.contains(where: { $0?.address?.weakEqual(address2: friendAddress) == true }) {
|
|
||||||
_ = tempRemoteFriendList.removeFriend(linphoneFriend: friend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let dispatchGroup = DispatchGroup()
|
|
||||||
|
|
||||||
friends.forEach { friend in
|
|
||||||
dispatchGroup.enter()
|
|
||||||
if let friend = friend {
|
|
||||||
let addressTmp = friend.address?.clone()?.asStringUriOnly() ?? ""
|
|
||||||
Log.debug("\(ContactsManager.TAG) Newly discovered SIP Address \(addressTmp) for friend \(friend.name ?? "No Name") in bodyless list \(friendList.displayName ?? "No Display Name")")
|
|
||||||
|
|
||||||
let newContact = Contact(
|
|
||||||
identifier: UUID().uuidString,
|
|
||||||
firstName: friend.name ?? addressTmp,
|
|
||||||
lastName: "",
|
|
||||||
organizationName: "",
|
|
||||||
jobTitle: "",
|
|
||||||
displayName: friend.address?.displayName ?? "",
|
|
||||||
sipAddresses: friend.addresses.map { $0.asStringUriOnly() },
|
|
||||||
phoneNumbers: [],
|
|
||||||
imageData: ""
|
|
||||||
)
|
|
||||||
|
|
||||||
let image = self.textToImage(firstName: friend.name ?? addressTmp, lastName: "")
|
|
||||||
self.saveImage(
|
self.saveImage(
|
||||||
image: image,
|
image: image,
|
||||||
name: friend.name ?? addressTmp,
|
name: friend.name ?? addressTmp,
|
||||||
prefix: "-default",
|
prefix: "-default",
|
||||||
contact: newContact, linphoneFriend: friendList.displayName ?? "No Display Name", existingFriend: nil) {
|
contact: newContact, linphoneFriend: friendList.displayName ?? "No Display Name", existingFriend: nil) {
|
||||||
dispatchGroup.leave()
|
dispatchGroup.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchGroup.notify(queue: .main) {
|
dispatchGroup.notify(queue: .main) {
|
||||||
self.coreContext.doOnCoreQueue { _ in
|
MagicSearchSingleton.shared.searchForContacts()
|
||||||
MagicSearchSingleton.shared.searchForContacts()
|
if let linphoneFL = self.tempRemoteFriendList {
|
||||||
if let linphoneFL = self.tempRemoteFriendList {
|
linphoneFL.updateSubscriptions()
|
||||||
linphoneFL.updateSubscriptions()
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onPresenceReceived: { (friendList: FriendList, friends: [Friend?]) in
|
||||||
|
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onPresenceReceived \(friends.count)")
|
||||||
|
},
|
||||||
onNewSipAddressDiscovered: { (friendList: FriendList, linphoneFriend: Friend, sipUri: String) in
|
onNewSipAddressDiscovered: { (friendList: FriendList, linphoneFriend: Friend, sipUri: String) in
|
||||||
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onNewSipAddressDiscovered \(linphoneFriend.name ?? "")")
|
Log.info("\(ContactsManager.TAG) FriendListDelegateStub onNewSipAddressDiscovered \(linphoneFriend.name ?? "")")
|
||||||
var addedAvatarListModel: [ContactAvatarModel] = []
|
var addedAvatarListModel: [ContactAvatarModel] = []
|
||||||
|
|
@ -648,8 +522,6 @@ final class ContactsManager: ObservableObject {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self.friendListDelegate = friendListDelegateTmp
|
|
||||||
|
|
||||||
CoreContext.shared.mCore.friendsLists.forEach { friendList in
|
CoreContext.shared.mCore.friendsLists.forEach { friendList in
|
||||||
friendList.addDelegate(delegate: friendListDelegateTmp)
|
friendList.addDelegate(delegate: friendListDelegateTmp)
|
||||||
}
|
}
|
||||||
|
|
@ -658,11 +530,6 @@ final class ContactsManager: ObservableObject {
|
||||||
|
|
||||||
func addCoreDelegate(core: Core) {
|
func addCoreDelegate(core: Core) {
|
||||||
self.coreContext.doOnCoreQueue { _ in
|
self.coreContext.doOnCoreQueue { _ in
|
||||||
if let coreDelegate = self.coreDelegate {
|
|
||||||
core.removeDelegate(delegate: coreDelegate)
|
|
||||||
self.coreDelegate = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
self.coreDelegate = CoreDelegateStub(
|
self.coreDelegate = CoreDelegateStub(
|
||||||
onFriendListCreated: { (_: Core, friendList: FriendList) in
|
onFriendListCreated: { (_: Core, friendList: FriendList) in
|
||||||
Log.info("\(ContactsManager.TAG) Friend list \(friendList.displayName) created")
|
Log.info("\(ContactsManager.TAG) Friend list \(friendList.displayName) created")
|
||||||
|
|
@ -691,28 +558,15 @@ final class ContactsManager: ObservableObject {
|
||||||
for contact in avatarListModel {
|
for contact in avatarListModel {
|
||||||
contact.$starred
|
contact.$starred
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
self?.starredChangeTrigger = UUID()
|
self?.starredChangeTrigger = UUID() // 🔁 Déclenche le refresh de la vue
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSubscriptionsLinphoneList() {
|
func updateSubscriptionsLinphoneList() {
|
||||||
self.coreContext.doOnCoreQueue { _ in
|
if let linphoneFL = self.linphoneFriendList {
|
||||||
if let linphoneFL = self.linphoneFriendList {
|
linphoneFL.updateSubscriptions()
|
||||||
linphoneFL.updateSubscriptions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func refreshCardDavContacts() {
|
|
||||||
self.coreContext.doOnCoreQueue { core in
|
|
||||||
core.friendsLists.forEach{ friendList in
|
|
||||||
if (friendList.type == .CardDAV) {
|
|
||||||
Log.info("\(ContactsManager.TAG) Found CardDAV friend list \(friendList.displayName), starting update")
|
|
||||||
friendList.synchronizeFriendsFromServer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,6 @@ class CoreContext: ObservableObject {
|
||||||
|
|
||||||
var digestAuthInfoPendingPasswordUpdate: AuthInfo?
|
var digestAuthInfoPendingPasswordUpdate: AuthInfo?
|
||||||
|
|
||||||
@Published var reloadID = UUID()
|
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
do {
|
do {
|
||||||
try initialiseCore()
|
try initialiseCore()
|
||||||
|
|
@ -108,11 +106,12 @@ class CoreContext: ObservableObject {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if isConnected {
|
if isConnected {
|
||||||
Log.info("Network is now satisfied")
|
Log.info("Network is now satisfied")
|
||||||
ToastViewModel.shared.show("Success_toast_network_connected")
|
ToastViewModel.shared.toastMessage = "Success_toast_network_connected"
|
||||||
} else {
|
} else {
|
||||||
Log.error("Network is now \(path.status)")
|
Log.error("Network is now \(path.status)")
|
||||||
ToastViewModel.shared.show("Unavailable_network")
|
ToastViewModel.shared.toastMessage = "Unavailable_network"
|
||||||
}
|
}
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
self.networkStatusIsConnected = isConnected
|
self.networkStatusIsConnected = isConnected
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +121,7 @@ class CoreContext: ObservableObject {
|
||||||
|
|
||||||
coreQueue.async {
|
coreQueue.async {
|
||||||
LoggingService.Instance.logLevel = LogLevel.Debug
|
LoggingService.Instance.logLevel = LogLevel.Debug
|
||||||
Factory.Instance.logCollectionPath = Factory.Instance.getDataDir(context: UnsafeMutablePointer<Int8>(mutating: (Config.appGroupName as NSString).utf8String))
|
Factory.Instance.logCollectionPath = Factory.Instance.getConfigDir(context: nil)
|
||||||
Factory.Instance.enableLogCollection(state: LogCollectionState.Enabled)
|
Factory.Instance.enableLogCollection(state: LogCollectionState.Enabled)
|
||||||
|
|
||||||
Log.info("Checking if linphonerc file exists already. If not, creating one as a copy of linphonerc-default")
|
Log.info("Checking if linphonerc file exists already. If not, creating one as a copy of linphonerc-default")
|
||||||
|
|
@ -149,21 +148,13 @@ class CoreContext: ObservableObject {
|
||||||
self.mCore.callkitEnabled = true
|
self.mCore.callkitEnabled = true
|
||||||
self.mCore.pushNotificationEnabled = true
|
self.mCore.pushNotificationEnabled = true
|
||||||
|
|
||||||
let appGitVersion = AppGitInfo.commit
|
let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String
|
||||||
let appGitBranch = AppGitInfo.branch
|
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||||
let appGitTag = AppGitInfo.tag
|
|
||||||
let sdkGitVersion = linphonesw.sdkVersion
|
|
||||||
var sdkGitBranch = linphonesw.sdkBranch
|
|
||||||
|
|
||||||
if sdkGitBranch.hasPrefix("remotes/origin/") {
|
let userAgent = "LinphoneiOS/\(version ?? "6.0.0") (\(UIDevice.current.localizedModel.replacingOccurrences(of: "'", with: ""))) LinphoneSDK"
|
||||||
sdkGitBranch = String(sdkGitBranch.dropFirst("remotes/origin/".count))
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.info("Git Info — App: \(appGitTag + "-" + appGitVersion) [\(appGitBranch)] | SDK: \(sdkGitVersion) [\(sdkGitBranch)]")
|
|
||||||
|
|
||||||
let userAgent = "LinphoneiOS/\(appGitTag) (\(UIDevice.current.localizedModel.replacingOccurrences(of: "'", with: ""))) LinphoneSDK"
|
|
||||||
self.mCore.setUserAgent(name: userAgent, version: self.coreVersion)
|
self.mCore.setUserAgent(name: userAgent, version: self.coreVersion)
|
||||||
|
self.mCore.videoCaptureEnabled = true
|
||||||
|
self.mCore.videoDisplayEnabled = true
|
||||||
self.mCore.videoPreviewEnabled = false
|
self.mCore.videoPreviewEnabled = false
|
||||||
self.mCore.fecEnabled = true
|
self.mCore.fecEnabled = true
|
||||||
|
|
||||||
|
|
@ -209,26 +200,6 @@ class CoreContext: ObservableObject {
|
||||||
self.forceRemotePushToMatchVoipPushSettings(account: acc)
|
self.forceRemotePushToMatchVoipPushSettings(account: acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
let container = FileUtil.sharedContainerUrl()
|
|
||||||
let recordingsDir = container.appendingPathComponent("Library/Recordings")
|
|
||||||
|
|
||||||
let fm = FileManager.default
|
|
||||||
|
|
||||||
if !fm.fileExists(atPath: recordingsDir.path) {
|
|
||||||
do {
|
|
||||||
try fm.createDirectory(
|
|
||||||
at: recordingsDir,
|
|
||||||
withIntermediateDirectories: true,
|
|
||||||
attributes: nil
|
|
||||||
)
|
|
||||||
print("Recordings directory created.")
|
|
||||||
} catch {
|
|
||||||
print("Error creating directory: \(error)")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print("Recordings directory already exists.")
|
|
||||||
}
|
|
||||||
|
|
||||||
self.mCoreDelegate = CoreDelegateStub(onGlobalStateChanged: { (core: Core, state: GlobalState, _: String) in
|
self.mCoreDelegate = CoreDelegateStub(onGlobalStateChanged: { (core: Core, state: GlobalState, _: String) in
|
||||||
if state == GlobalState.On {
|
if state == GlobalState.On {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
@ -237,14 +208,20 @@ class CoreContext: ObservableObject {
|
||||||
let pushEnvironment = ""
|
let pushEnvironment = ""
|
||||||
#endif
|
#endif
|
||||||
for account in core.accountList {
|
for account in core.accountList {
|
||||||
|
|
||||||
|
let newParams = account.params?.clone()
|
||||||
if account.params?.pushNotificationConfig?.provider != ("apns" + pushEnvironment) {
|
if account.params?.pushNotificationConfig?.provider != ("apns" + pushEnvironment) {
|
||||||
let newParams = account.params?.clone()
|
|
||||||
|
|
||||||
Log.info("Account \(String(describing: newParams?.identityAddress?.asStringUriOnly())) - updating apple push provider from \(String(describing: newParams?.pushNotificationConfig?.provider)) to apns\(pushEnvironment)")
|
Log.info("Account \(String(describing: newParams?.identityAddress?.asStringUriOnly())) - updating apple push provider from \(String(describing: newParams?.pushNotificationConfig?.provider)) to apns\(pushEnvironment)")
|
||||||
newParams?.pushNotificationConfig?.provider = "apns" + pushEnvironment
|
newParams?.pushNotificationConfig?.provider = "apns" + pushEnvironment
|
||||||
|
|
||||||
account.params = newParams
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if account.params?.internationalPrefix == nil {
|
||||||
|
Log.info("Account \(account.displayName()): no international prefix set, adding 33 FRA by default: \(account.params?.internationalPrefix ?? "NIL")")
|
||||||
|
newParams?.internationalPrefix = "33"
|
||||||
|
newParams?.internationalPrefixIsoCountryCode = "FRA"
|
||||||
|
newParams?.useInternationalPrefixForCallsAndChats = true
|
||||||
|
}
|
||||||
|
account.params = newParams
|
||||||
}
|
}
|
||||||
|
|
||||||
self.actionsToPerformOnCoreQueueWhenCoreIsStarted.forEach { $0(core) }
|
self.actionsToPerformOnCoreQueueWhenCoreIsStarted.forEach { $0(core) }
|
||||||
|
|
@ -316,17 +293,18 @@ class CoreContext: ObservableObject {
|
||||||
Log.info("[CoreContext] Transferred call \(transferred.remoteAddress!.asStringUriOnly()) state changed \(callState)")
|
Log.info("[CoreContext] Transferred call \(transferred.remoteAddress!.asStringUriOnly()) state changed \(callState)")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if callState == Call.State.Connected {
|
if callState == Call.State.Connected {
|
||||||
ToastViewModel.shared.show("Success_toast_call_transfer_successful")
|
ToastViewModel.shared.toastMessage = "Success_toast_call_transfer_successful"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
} else if callState == Call.State.OutgoingProgress {
|
} else if callState == Call.State.OutgoingProgress {
|
||||||
ToastViewModel.shared.show("Success_toast_call_transfer_in_progress")
|
ToastViewModel.shared.toastMessage = "Success_toast_call_transfer_in_progress"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
} else if callState == Call.State.End || callState == Call.State.Error {
|
} else if callState == Call.State.End || callState == Call.State.Error {
|
||||||
ToastViewModel.shared.show("Failed_toast_call_transfer_failed")
|
ToastViewModel.shared.toastMessage = "Failed_toast_call_transfer_failed"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, onConfiguringStatus: { (_: Core, status: ConfiguringState, message: String) in
|
}, onConfiguringStatus: { (_: Core, status: ConfiguringState, message: String) in
|
||||||
Log.info("New configuration state is \(status) = \(message)\n")
|
Log.info("New configuration state is \(status) = \(message)\n")
|
||||||
let themeMainColor = CorePreferences.themeMainColor
|
|
||||||
SharedMainViewModel.shared.updateConfigChanges()
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if status == ConfiguringState.Successful {
|
if status == ConfiguringState.Successful {
|
||||||
var accountModels: [AccountModel] = []
|
var accountModels: [AccountModel] = []
|
||||||
|
|
@ -334,15 +312,14 @@ class CoreContext: ObservableObject {
|
||||||
accountModels.append(AccountModel(account: account, core: self.mCore))
|
accountModels.append(AccountModel(account: account, core: self.mCore))
|
||||||
}
|
}
|
||||||
self.accounts = accountModels
|
self.accounts = accountModels
|
||||||
ThemeManager.shared.applyTheme(named: themeMainColor)
|
|
||||||
self.reloadID = UUID()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, onLogCollectionUploadStateChanged: { (_: Core, _: Core.LogCollectionUploadState, info: String) in
|
}, onLogCollectionUploadStateChanged: { (_: Core, _: Core.LogCollectionUploadState, info: String) in
|
||||||
if info.starts(with: "https") {
|
if info.starts(with: "https") {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
UIPasteboard.general.setValue(info, forPasteboardType: UTType.plainText.identifier)
|
UIPasteboard.general.setValue(info, forPasteboardType: UTType.plainText.identifier)
|
||||||
ToastViewModel.shared.show("Success_send_logs")
|
ToastViewModel.shared.toastMessage = "Success_send_logs"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in
|
}, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in
|
||||||
|
|
@ -392,7 +369,8 @@ class CoreContext: ObservableObject {
|
||||||
self.loggedIn = false
|
self.loggedIn = false
|
||||||
if self.networkStatusIsConnected {
|
if self.networkStatusIsConnected {
|
||||||
// If network is disconnected, a toast message with key "Unavailable_network" should already be displayed
|
// If network is disconnected, a toast message with key "Unavailable_network" should already be displayed
|
||||||
ToastViewModel.shared.show("Registration_failed")
|
ToastViewModel.shared.toastMessage = "Registration_failed"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -428,31 +406,6 @@ class CoreContext: ObservableObject {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.accounts = accountModels
|
self.accounts = accountModels
|
||||||
}
|
}
|
||||||
}, onMessageWaitingIndicationChanged: { (core: Core, event: Event, mwi: MessageWaitingIndication) in
|
|
||||||
if (mwi.hasMessageWaiting()) {
|
|
||||||
let summaries = mwi.summaries
|
|
||||||
Log.info(
|
|
||||||
"[CoreContext][onMessageWaitingIndicationChanged] MWI NOTIFY received, messages are waiting (\(summaries.count) summaries)"
|
|
||||||
)
|
|
||||||
|
|
||||||
if let defaultAccount = core.defaultAccount?.params?.identityAddress, let mwiAccount = mwi.accountAddress, defaultAccount.weakEqual(address2: mwiAccount){
|
|
||||||
if !summaries.isEmpty {
|
|
||||||
let summary = summaries.first
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
withAnimation {
|
|
||||||
SharedMainViewModel.shared.waitingMessageCount = Int(summary?.nbNew ?? 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.info("[CoreContext][onMessageWaitingIndicationChanged] MWI NOTIFY received, no message is waiting")
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
withAnimation {
|
|
||||||
SharedMainViewModel.shared.waitingMessageCount = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
self.mCore.addDelegate(delegate: self.mCoreDelegate)
|
self.mCore.addDelegate(delegate: self.mCoreDelegate)
|
||||||
|
|
@ -471,24 +424,26 @@ class CoreContext: ObservableObject {
|
||||||
|
|
||||||
func onEnterForeground() {
|
func onEnterForeground() {
|
||||||
coreQueue.async {
|
coreQueue.async {
|
||||||
Log.info("[onEnterForegroundOrBackground] Entering foreground")
|
// We can't rely on defaultAccount?.params?.isPublishEnabled
|
||||||
|
// as it will be modified by the SDK when changing the presence status
|
||||||
|
|
||||||
try? self.mCore.start()
|
try? self.mCore.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func onEnterBackground() {
|
func onEnterBackground() {
|
||||||
coreQueue.async {
|
coreQueue.async {
|
||||||
Log.info("[onEnterForegroundOrBackground] Entering background, un-PUBLISHING presence info")
|
// We can't rely on defaultAccount?.params?.isPublishEnabled
|
||||||
|
// as it will be modified by the SDK when changing the presence status
|
||||||
|
Log.info("App is in background, un-PUBLISHING presence info")
|
||||||
|
|
||||||
self.updatePresence(core: self.mCore, presence: .Offline)
|
// We don't use ConsolidatedPresence.Busy but Offline to do an unsubscribe,
|
||||||
|
// Flexisip will handle the Busy status depending on other devices
|
||||||
|
self.updatePresence(core: self.mCore, presence: ConsolidatedPresence.Offline)
|
||||||
self.mCore.iterate()
|
self.mCore.iterate()
|
||||||
|
|
||||||
if self.mCore.currentCall == nil && self.mCore.globalState == .On {
|
if self.mCore.currentCall == nil {
|
||||||
Log.info("[onEnterForegroundOrBackground] Stopping core because no active calls")
|
|
||||||
self.mCore.stop()
|
self.mCore.stop()
|
||||||
} else {
|
|
||||||
Log.info("[onEnterForegroundOrBackground] Skipped stop: core not fully On or active call in progress")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,7 @@ class CorePreferences {
|
||||||
|
|
||||||
static var checkForUpdateServerUrl: String {
|
static var checkForUpdateServerUrl: String {
|
||||||
get {
|
get {
|
||||||
let raw = Config.get().getString(section: "misc", key: "version_check_url_root", defaultString: "")
|
return Config.get().getString(section: "misc", key: "version_check_url_root", defaultString: "")
|
||||||
return safeString(raw, defaultValue: "")
|
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
Config.get().setString(section: "misc", key: "version_check_url_root", value: newValue)
|
Config.get().setString(section: "misc", key: "version_check_url_root", value: newValue)
|
||||||
|
|
@ -87,8 +86,7 @@ class CorePreferences {
|
||||||
|
|
||||||
static var deviceName: String {
|
static var deviceName: String {
|
||||||
get {
|
get {
|
||||||
let raw = Config.get().getString(section: "app", key: "device", defaultString: "").trimmingCharacters(in: .whitespaces)
|
return Config.get().getString(section: "app", key: "device", defaultString: "").trimmingCharacters(in: .whitespaces)
|
||||||
return safeString(raw, defaultValue: "")
|
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
Config.get().setString(section: "app", key: "device", value: newValue.trimmingCharacters(in: .whitespaces))
|
Config.get().setString(section: "app", key: "device", value: newValue.trimmingCharacters(in: .whitespaces))
|
||||||
|
|
@ -133,23 +131,13 @@ class CorePreferences {
|
||||||
|
|
||||||
static var contactsFilter: String {
|
static var contactsFilter: String {
|
||||||
get {
|
get {
|
||||||
let raw = Config.get().getString(section: "ui", key: "contacts_filter", defaultString: "")
|
return Config.get().getString(section: "ui", key: "contacts_filter", defaultString: "")
|
||||||
return safeString(raw, defaultValue: "")
|
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
Config.get().setString(section: "ui", key: "contacts_filter", value: newValue)
|
Config.get().setString(section: "ui", key: "contacts_filter", value: newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var disableAddContact: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "ui", key: "disable_add_contact", defaultValue: false)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "ui", key: "disable_add_contact", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var showFavoriteContacts: Bool {
|
static var showFavoriteContacts: Bool {
|
||||||
get {
|
get {
|
||||||
return Config.get().getBool(section: "ui", key: "show_favorites_contacts", defaultValue: true)
|
return Config.get().getBool(section: "ui", key: "show_favorites_contacts", defaultValue: true)
|
||||||
|
|
@ -159,15 +147,6 @@ class CorePreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var friendListInWhichStoreNewlyCreatedFriends: String {
|
|
||||||
get {
|
|
||||||
return Config.get().getString(section: "app", key: "friend_list_to_store_newly_created_contacts", defaultString: "Linphone address-book")
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setString(section: "app", key: "friend_list_to_store_newly_created_contacts", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var voiceRecordingMaxDuration: Int {
|
static var voiceRecordingMaxDuration: Int {
|
||||||
get {
|
get {
|
||||||
return Config.get().getInt(section: "app", key: "voice_recording_max_duration", defaultValue: 600000)
|
return Config.get().getInt(section: "app", key: "voice_recording_max_duration", defaultValue: 600000)
|
||||||
|
|
@ -198,20 +177,13 @@ class CorePreferences {
|
||||||
|
|
||||||
static var themeMainColor: String {
|
static var themeMainColor: String {
|
||||||
get {
|
get {
|
||||||
let raw = Config.get().getString(section: "ui", key: "theme_main_color", defaultString: "orange")
|
return Config.get().getString(section: "ui", key: "theme_main_color", defaultString: "orange")
|
||||||
return safeString(raw, defaultValue: "orange")
|
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
Config.get().setString(section: "ui", key: "theme_main_color", value: newValue)
|
Config.get().setString(section: "ui", key: "theme_main_color", value: newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var themeAboutPictureUrl: String? {
|
|
||||||
get {
|
|
||||||
return Config.get().getString(section: "ui", key: "theme_about_picture_url", defaultString: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var darkModeAllowed: Bool {
|
static var darkModeAllowed: Bool {
|
||||||
return Config.get().getBool(section: "ui", key: "dark_mode_allowed", defaultValue: true)
|
return Config.get().getBool(section: "ui", key: "dark_mode_allowed", defaultValue: true)
|
||||||
}
|
}
|
||||||
|
|
@ -272,22 +244,21 @@ class CorePreferences {
|
||||||
|
|
||||||
static var defaultDomain: String {
|
static var defaultDomain: String {
|
||||||
get {
|
get {
|
||||||
let raw = Config.get().getString(section: "app", key: "default_domain", defaultString: "sip.linphone.org")
|
return Config.get().getString(section: "app", key: "default_domain", defaultString: "sip.linphone.org")
|
||||||
return safeString(raw, defaultValue: "sip.linphone.org")
|
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
Config.get().setString(section: "app", key: "default_domain", value: newValue)
|
Config.get().setString(section: "app", key: "default_domain", value: newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var disableChatFeature: Bool {
|
static var disableChatFeature: Bool {
|
||||||
get {
|
get {
|
||||||
return Config.get().getBool(section: "ui", key: "disable_chat_feature", defaultValue: false)
|
return Config.get().getBool(section: "app", key: "disable_chat_feature", defaultValue: false)
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
Config.get().setBool(section: "ui", key: "disable_chat_feature", value: newValue)
|
Config.get().setBool(section: "app", key: "disable_chat_feature", value: newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var disableMeetings: Bool {
|
static var disableMeetings: Bool {
|
||||||
get {
|
get {
|
||||||
|
|
@ -298,15 +269,6 @@ class CorePreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var hideSipAddresses: Bool {
|
|
||||||
get {
|
|
||||||
return Config.get().getBool(section: "ui", key: "hide_sip_addresses", defaultValue: false)
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
Config.get().setBool(section: "ui", key: "hide_sip_addresses", value: newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func copy(from: String, to: String, overrideIfExists: Bool = false) {
|
private func copy(from: String, to: String, overrideIfExists: Bool = false) {
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
if fileManager.fileExists(atPath: to), !overrideIfExists {
|
if fileManager.fileExists(atPath: to), !overrideIfExists {
|
||||||
|
|
@ -321,12 +283,4 @@ class CorePreferences {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func safeString(_ raw: String?, defaultValue: String = "") -> String {
|
|
||||||
guard let raw = raw else { return defaultValue }
|
|
||||||
if let data = raw.data(using: .utf8) {
|
|
||||||
return String(decoding: data, as: UTF8.self)
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public enum AppGitInfo {
|
|
||||||
public static let branch = "master"
|
|
||||||
public static let commit = "dc5f131ce"
|
|
||||||
public static let tag = "6.1.0-alpha"
|
|
||||||
}
|
|
||||||
|
|
@ -4,16 +4,6 @@
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>org.linphone.phone</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>linphone-mention</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Editor</string>
|
<string>Editor</string>
|
||||||
|
|
@ -125,6 +115,12 @@
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
|
<true/>
|
||||||
|
<key>ITSEncryptionExportComplianceCode</key>
|
||||||
|
<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>
|
||||||
|
|
@ -143,6 +139,9 @@
|
||||||
<string>audio</string>
|
<string>audio</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UILaunchScreen</key>
|
<key>UILaunchScreen</key>
|
||||||
<false/>
|
<dict>
|
||||||
|
<key>UIImageName</key>
|
||||||
|
<string>linphone</string>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24128" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
||||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<scene sceneID="EHf-IW-A2E">
|
|
||||||
<objects>
|
|
||||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="6L3-wz-ibv" image="linphone">
|
|
||||||
<rect key="frame" x="0" y="0" width="240" height="128"/>
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
|
||||||
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
|
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
<constraints>
|
|
||||||
<!-- Center horizontally -->
|
|
||||||
<constraint firstItem="6L3-wz-ibv" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="centerXConstraint"/>
|
|
||||||
<!-- Center vertically -->
|
|
||||||
<constraint firstItem="6L3-wz-ibv" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="centerYConstraint"/>
|
|
||||||
<!-- Fixed width -->
|
|
||||||
<constraint firstItem="6L3-wz-ibv" firstAttribute="width" constant="240" id="widthConstraint"/>
|
|
||||||
<!-- Fixed height -->
|
|
||||||
<constraint firstItem="6L3-wz-ibv" firstAttribute="height" constant="128" id="heightConstraint"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
<resources>
|
|
||||||
<image name="linphone" width="100" height="102"/>
|
|
||||||
</resources>
|
|
||||||
</document>
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import linphonesw
|
import linphonesw
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
import Intents
|
|
||||||
|
|
||||||
let accountTokenNotification = Notification.Name("AccountCreationTokenReceived")
|
let accountTokenNotification = Notification.Name("AccountCreationTokenReceived")
|
||||||
var displayedChatroomPeerAddr: String?
|
var displayedChatroomPeerAddr: String?
|
||||||
|
|
@ -109,28 +108,7 @@ class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDele
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func application(_ application: UIApplication,
|
|
||||||
continue userActivity: NSUserActivity,
|
|
||||||
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
|
||||||
|
|
||||||
guard let interaction = userActivity.interaction,
|
|
||||||
let intent = interaction.intent as? INStartCallIntent,
|
|
||||||
let person = intent.contacts?.first,
|
|
||||||
let number = person.personHandle?.value else { return false }
|
|
||||||
|
|
||||||
let isVideo = intent.callCapability == .videoCall
|
|
||||||
|
|
||||||
Log.info("[AppDelegate][INStartCallIntent] Generic call intent received for number: \(number) isVideo: \(isVideo)")
|
|
||||||
|
|
||||||
CoreContext.shared.performActionOnCoreQueueWhenCoreIsStarted { core in
|
|
||||||
if let address = core.interpretUrl(url: number, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)) {
|
|
||||||
TelecomManager.shared.doCallOrJoinConf(address: address, isVideo: isVideo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ application: UIApplication) {
|
func applicationWillTerminate(_ application: UIApplication) {
|
||||||
|
|
@ -262,22 +240,6 @@ struct RootView: View {
|
||||||
pendingURL = url
|
pendingURL = url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onContinueUserActivity("INStartCallIntent") { activity in
|
|
||||||
guard let interaction = activity.interaction,
|
|
||||||
let intent = interaction.intent as? INStartCallIntent,
|
|
||||||
let person = intent.contacts?.first,
|
|
||||||
let number = person.personHandle?.value else { return }
|
|
||||||
|
|
||||||
let isVideo = intent.callCapability == .videoCall
|
|
||||||
|
|
||||||
Log.info("[INStartCallIntent] Generic call intent received for number: \(number) isVideo: \(isVideo)")
|
|
||||||
|
|
||||||
coreContext.performActionOnCoreQueueWhenCoreIsStarted { core in
|
|
||||||
if let address = core.interpretUrl(url: number, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)) {
|
|
||||||
telecomManager.doCallOrJoinConf(address: address, isVideo: isVideo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -297,7 +259,6 @@ struct MainViewSwitcher: View {
|
||||||
let sharedMainViewModel: SharedMainViewModel
|
let sharedMainViewModel: SharedMainViewModel
|
||||||
@Binding var pendingURL: URL?
|
@Binding var pendingURL: URL?
|
||||||
let appDelegate: AppDelegate
|
let appDelegate: AppDelegate
|
||||||
@ObservedObject private var colors = ColorProvider.shared
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
selectedMainView()
|
selectedMainView()
|
||||||
|
|
@ -316,6 +277,5 @@ struct MainViewSwitcher: View {
|
||||||
navigationManager.openChatRoom(callId: callId, peerAddr: peerAddr, localAddr: localAddr)
|
navigationManager.openChatRoom(callId: callId, peerAddr: peerAddr, localAddr: localAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.id(colors.theme.name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"assistant_sip_account_transport_protocol" = "Transport";
|
"assistant_sip_account_transport_protocol" = "Transport";
|
||||||
"contact_call_action" = "Volat";
|
"contact_call_action" = "Volat";
|
||||||
"conversation_action_call" = "Volat";
|
"conversation_action_call" = "Volat";
|
||||||
"dialog_confirm" = "Confirm";
|
"dialog_ok" = "OK";
|
||||||
"drawer_menu_manage_account" = "Spravovat profil";
|
"drawer_menu_manage_account" = "Spravovat profil";
|
||||||
"help_error_checking_version_toast_message" = "Během kontroly aktualizací nastala chyba";
|
"help_error_checking_version_toast_message" = "Během kontroly aktualizací nastala chyba";
|
||||||
"settings_contacts_carddav_name_title" = "Zobrazené jméno";
|
"settings_contacts_carddav_name_title" = "Zobrazené jméno";
|
||||||
|
|
@ -474,9 +474,9 @@
|
||||||
"message_copied_to_clipboard_toast" = "Zpráva zkopírována do schránky";
|
"message_copied_to_clipboard_toast" = "Zpráva zkopírována do schránky";
|
||||||
"message_delivery_info_read_title" = "Přečteno";
|
"message_delivery_info_read_title" = "Přečteno";
|
||||||
"message_delivery_info_received_title" = "Přijato";
|
"message_delivery_info_received_title" = "Přijato";
|
||||||
"message_meeting_invitation_cancelled_notification" = "Schůzka byla zrušena";
|
"message_meeting_invitation_cancelled_notification" = "📅 Schůzka byla zrušena";
|
||||||
"message_meeting_invitation_notification" = "Jste pozváni na schůzku";
|
"message_meeting_invitation_notification" = "📅 Jste pozváni na schůzku";
|
||||||
"message_meeting_invitation_updated_notification" = "Schůzka byla aktualizována";
|
"message_meeting_invitation_updated_notification" = "📅 Schůzka byla aktualizována";
|
||||||
"message_reactions_info_all_title" = "Reakce";
|
"message_reactions_info_all_title" = "Reakce";
|
||||||
"network_reachable_again" = "Síť je znovu dostupná";
|
"network_reachable_again" = "Síť je znovu dostupná";
|
||||||
"menu_block_number" = "Blokovat číslo";
|
"menu_block_number" = "Blokovat číslo";
|
||||||
|
|
@ -522,4 +522,3 @@
|
||||||
"conversations_files_waiting_to_be_shared_single" = "1 soubor čekající na sdílení";
|
"conversations_files_waiting_to_be_shared_single" = "1 soubor čekající na sdílení";
|
||||||
"conversations_files_waiting_to_be_shared_multiple" = "%@ souborů čekajících na sdílení";
|
"conversations_files_waiting_to_be_shared_multiple" = "%@ souborů čekajících na sdílení";
|
||||||
"conversation_ephemeral_messages_duration_multiple_days" = "%d dnů";
|
"conversation_ephemeral_messages_duration_multiple_days" = "%d dnů";
|
||||||
"authentication_id" = "ID pro ověření (je-li odlišné)";
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"call_stats_audio_title" = "Audio";
|
"call_stats_audio_title" = "Audio";
|
||||||
"Error" = "Fehler";
|
"Error" = "Fehler";
|
||||||
"settings_advanced_accept_early_media_title" = "Frühe Medienübertragung erlauben";
|
"settings_advanced_accept_early_media_title" = "'Early Media' erlauben";
|
||||||
"settings_advanced_allow_outgoing_early_media_title" = "Ausgehende frühe Medianübertragung erlauben";
|
"settings_advanced_allow_outgoing_early_media_title" = "Ausgehende 'Early Media' erlauben";
|
||||||
"settings_advanced_audio_codecs_title" = "Audio-Codecs";
|
"settings_advanced_audio_codecs_title" = "Audio-Codecs";
|
||||||
"settings_advanced_audio_devices_title" = "Audiogeräte";
|
"settings_advanced_audio_devices_title" = "Audiogeräte";
|
||||||
"settings_advanced_device_id" = "Gerät ID";
|
"settings_advanced_device_id" = "Gerät ID";
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
"assistant_account_login_forbidden_error" = "Falscher Benutzername oder Passwort";
|
"assistant_account_login_forbidden_error" = "Falscher Benutzername oder Passwort";
|
||||||
"assistant_account_register" = "Registrieren";
|
"assistant_account_register" = "Registrieren";
|
||||||
"assistant_account_register_push_notification_not_received_error" = "Push Benachrichtigung mit Authentifizierungstoken nicht innerhalb von 5 Sekunden empfangen, bitte versuchen Sie es später erneut";
|
"assistant_account_register_push_notification_not_received_error" = "Push Benachrichtigung mit Authentifizierungstoken nicht innerhalb von 5 Sekunden empfangen, bitte versuchen Sie es später erneut";
|
||||||
"assistant_account_register_unexpected_error" = "Unerwarteter Fehler ist aufgetreten, bitte versuchen Sie es später erneut";
|
"assistant_account_register_unexpected_error" = "Unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.";
|
||||||
"assistant_already_have_an_account" = "Haben Sie bereits ein Konto?";
|
"assistant_already_have_an_account" = "Haben Sie bereits ein Konto?";
|
||||||
"assistant_create_account_using_email_on_our_web_platform" = "Erstellen Sie mit Ihrer E-Mail ein Konto bei:";
|
"assistant_create_account_using_email_on_our_web_platform" = "Erstellen Sie mit Ihrer E-Mail ein Konto bei:";
|
||||||
"assistant_dialog_confirm_phone_number_title" = "Telefonnummer bestätigen";
|
"assistant_dialog_confirm_phone_number_title" = "Telefonnummer bestätigen";
|
||||||
|
|
@ -67,10 +67,10 @@
|
||||||
"call_action_change_layout" = "Layout";
|
"call_action_change_layout" = "Layout";
|
||||||
"call_action_go_to_calls_list" = "Anrufliste";
|
"call_action_go_to_calls_list" = "Anrufliste";
|
||||||
"call_action_hang_up" = "Auflegen";
|
"call_action_hang_up" = "Auflegen";
|
||||||
"call_action_pause_call" = "Halten";
|
"call_action_pause_call" = "Pause";
|
||||||
"call_action_record_call" = "Aufnahmen";
|
"call_action_record_call" = "Aufnahmen";
|
||||||
"call_action_resume_call" = "Fortsetzen";
|
"call_action_resume_call" = "Fortsetzen";
|
||||||
"call_action_show_dialer" = "Tastatur";
|
"call_action_show_dialer" = "Wähler";
|
||||||
"call_action_show_messages" = "Nachrichten";
|
"call_action_show_messages" = "Nachrichten";
|
||||||
"call_action_start_new_call" = "Neuer Anruf";
|
"call_action_start_new_call" = "Neuer Anruf";
|
||||||
"call_audio_device_type_earpiece" = "Ohrhörer";
|
"call_audio_device_type_earpiece" = "Ohrhörer";
|
||||||
|
|
@ -142,7 +142,7 @@
|
||||||
"conversation_action_delete" = "Gespräch löschen";
|
"conversation_action_delete" = "Gespräch löschen";
|
||||||
"conversation_action_leave_group" = "Verlasse die Gruppe";
|
"conversation_action_leave_group" = "Verlasse die Gruppe";
|
||||||
"conversation_action_mark_as_read" = "Als gelesen markieren";
|
"conversation_action_mark_as_read" = "Als gelesen markieren";
|
||||||
"conversation_action_mute" = "Stummschalten";
|
"conversation_action_mute" = "Stumm";
|
||||||
"conversation_action_unmute" = "Stumm aufheben";
|
"conversation_action_unmute" = "Stumm aufheben";
|
||||||
"conversation_add_participants_title" = "Teilnehmer hinzufügen";
|
"conversation_add_participants_title" = "Teilnehmer hinzufügen";
|
||||||
"conversation_dialog_edit_subject" = "Gesprächsbetreff bearbeiten";
|
"conversation_dialog_edit_subject" = "Gesprächsbetreff bearbeiten";
|
||||||
|
|
@ -156,7 +156,7 @@
|
||||||
"conversation_ephemeral_messages_duration_three_days" = "3 Tage";
|
"conversation_ephemeral_messages_duration_three_days" = "3 Tage";
|
||||||
"conversation_ephemeral_messages_subtitle" = "Neue Nachrichten werden automatisch gelöscht, sobald sie von allen gelesen wurden.\nWählen Sie eine Dauer:";
|
"conversation_ephemeral_messages_subtitle" = "Neue Nachrichten werden automatisch gelöscht, sobald sie von allen gelesen wurden.\nWählen Sie eine Dauer:";
|
||||||
"conversation_ephemeral_messages_title" = "Kurzlebiger Nachrichten";
|
"conversation_ephemeral_messages_title" = "Kurzlebiger Nachrichten";
|
||||||
"conversation_event_conference_created" = "Sie sind der Gruppe beigetreten";
|
"conversation_event_conference_created" = "Sie haben der Gruppe beigetreten";
|
||||||
"conversation_event_conference_destroyed" = "Sie haben der Gruppe verlassen";
|
"conversation_event_conference_destroyed" = "Sie haben der Gruppe verlassen";
|
||||||
"conversation_event_ephemeral_messages_disabled" = "Kurzlebige Nachrichten wurden deaktiviert";
|
"conversation_event_ephemeral_messages_disabled" = "Kurzlebige Nachrichten wurden deaktiviert";
|
||||||
"conversation_event_ephemeral_messages_enabled" = "Kurzlebige Nachrichten wurden aktiviert";
|
"conversation_event_ephemeral_messages_enabled" = "Kurzlebige Nachrichten wurden aktiviert";
|
||||||
|
|
@ -192,7 +192,7 @@
|
||||||
"dialog_deny" = "Ablehnen";
|
"dialog_deny" = "Ablehnen";
|
||||||
"dialog_install" = "Installieren";
|
"dialog_install" = "Installieren";
|
||||||
"dialog_no" = "Nein";
|
"dialog_no" = "Nein";
|
||||||
"dialog_confirm" = "Confirm";
|
"dialog_ok" = "OK";
|
||||||
"dialog_yes" = "Ja";
|
"dialog_yes" = "Ja";
|
||||||
"drawer_menu_account_connection_status_cleared" = "Deaktiviert";
|
"drawer_menu_account_connection_status_cleared" = "Deaktiviert";
|
||||||
"drawer_menu_account_connection_status_connected" = "Verbunden";
|
"drawer_menu_account_connection_status_connected" = "Verbunden";
|
||||||
|
|
@ -288,17 +288,17 @@
|
||||||
"menu_delete_selected_item" = "Löschen";
|
"menu_delete_selected_item" = "Löschen";
|
||||||
"menu_forward_chat_message" = "Weiterleiten";
|
"menu_forward_chat_message" = "Weiterleiten";
|
||||||
"menu_invite" = "Einladen";
|
"menu_invite" = "Einladen";
|
||||||
"menu_reply_to_chat_message" = "Antworten";
|
"menu_reply_to_chat_message" = "Antwort";
|
||||||
"menu_resend_chat_message" = "Erneut senden";
|
"menu_resend_chat_message" = "Erneut senden";
|
||||||
"menu_see_existing_contact" = "Siehe Kontakt";
|
"menu_see_existing_contact" = "Siehe Kontakt";
|
||||||
"menu_show_imdn" = "Zustelldetails";
|
"menu_show_imdn" = "Lieferstatus";
|
||||||
"message_delivery_info_error_title" = "Fehler";
|
"message_delivery_info_error_title" = "Fehler";
|
||||||
"message_forwarded_label" = "Weitergeleitet";
|
"message_forwarded_label" = "Weitergeleitet";
|
||||||
"message_reaction_click_to_remove_label" = "Zum Entfernen klicken";
|
"message_reaction_click_to_remove_label" = "Zum Entfernen klicken";
|
||||||
"network_not_reachable" = "Sie sind nicht mit dem Internet verbunden";
|
"network_not_reachable" = "Sie sind nicht mit dem Internet verbunden";
|
||||||
"new_conversation_create_group" = "Gruppengespräch erstellen";
|
"new_conversation_create_group" = "Gruppengespräch erstellen";
|
||||||
"new_conversation_search_bar_filter_hint" = "Kontakt suchen";
|
"new_conversation_search_bar_filter_hint" = "Kontakt suchen";
|
||||||
"new_conversation_title" = "Neuer Chat";
|
"new_conversation_title" = "Neues Gespräch";
|
||||||
"next" = "Weiter";
|
"next" = "Weiter";
|
||||||
"notification_missed_call_title" = "Verpasster Anruf";
|
"notification_missed_call_title" = "Verpasster Anruf";
|
||||||
"operation_in_progress_overlay" = "Vorgang wird ausgeführt, bitte warten";
|
"operation_in_progress_overlay" = "Vorgang wird ausgeführt, bitte warten";
|
||||||
|
|
@ -318,29 +318,29 @@
|
||||||
"settings_calls_auto_record_title" = "Automatische Anrufaufzeichnung starten";
|
"settings_calls_auto_record_title" = "Automatische Anrufaufzeichnung starten";
|
||||||
"settings_calls_calibrate_echo_canceller_title" = "Echokompensator kalibrieren";
|
"settings_calls_calibrate_echo_canceller_title" = "Echokompensator kalibrieren";
|
||||||
"settings_calls_change_ringtone_title" = "Klingelton ändern";
|
"settings_calls_change_ringtone_title" = "Klingelton ändern";
|
||||||
"settings_calls_echo_canceller_subtitle" = "Verhindert, dass das Echo am entfernten Ende gehört wird, wenn kein Hardware Echokompensator verfügbar ist";
|
"settings_calls_echo_canceller_subtitle" = "Verhindert, dass das Echo am entfernten Ende gehört wird, wenn kein Hardware Echokompensator verfügbar ist.";
|
||||||
"settings_calls_echo_canceller_title" = "Verwenden Sie die Software Echounterdrückung";
|
"settings_calls_echo_canceller_title" = "Verwenden Sie die Software Echounterdrückung";
|
||||||
"settings_calls_enable_fec_title" = "Video FEC aktivieren";
|
"settings_calls_enable_fec_title" = "Video FEC aktivieren";
|
||||||
"settings_calls_enable_video_title" = "Video aktivieren";
|
"settings_calls_enable_video_title" = "Video aktivieren";
|
||||||
"settings_calls_title" = "Anrufe";
|
"settings_calls_title" = "Anrufe";
|
||||||
"settings_calls_vibrate_while_ringing_title" = "Vibrieren während ein eingehender Anruf klingelt";
|
"settings_calls_vibrate_while_ringing_title" = "Vibrieren während ein eingehender Anruf klingelt";
|
||||||
"settings_contacts_add_carddav_server_title" = "CardDAV-Adressbuch hinzufügen";
|
"settings_contacts_add_carddav_server_title" = "CardDAV Adressbuch hinzufügen";
|
||||||
"settings_contacts_add_ldap_server_title" = "LDAP-Server hinzufügen";
|
"settings_contacts_add_ldap_server_title" = "LDAP Server hinzufügen";
|
||||||
"settings_contacts_carddav_name_title" = "Anzeigename";
|
"settings_contacts_carddav_name_title" = "Anzeigename";
|
||||||
"settings_contacts_carddav_password_title" = "Passwort";
|
"settings_contacts_carddav_password_title" = "Passwort";
|
||||||
"settings_contacts_carddav_server_url_title" = "Server-URL";
|
"settings_contacts_carddav_server_url_title" = "Server URL";
|
||||||
"settings_contacts_carddav_sync_error_toast" = "Synchronisierungfehler!";
|
"settings_contacts_carddav_sync_error_toast" = "Synchronisierungfehler!";
|
||||||
"settings_contacts_carddav_username_title" = "Benutzername";
|
"settings_contacts_carddav_username_title" = "Benutzername";
|
||||||
"settings_contacts_edit_carddav_server_title" = "CardDAV-Adressbuch bearbeiten";
|
"settings_contacts_edit_carddav_server_title" = "CardDAV Adressbuch bearbeiten";
|
||||||
"settings_contacts_edit_ldap_server_title" = "LDAP-Server bearbeiten";
|
"settings_contacts_edit_ldap_server_title" = "LDAP Server bearbeiten";
|
||||||
"settings_contacts_ldap_bind_dn_title" = "DN binden";
|
"settings_contacts_ldap_bind_dn_title" = "DN binden";
|
||||||
"settings_contacts_ldap_password_title" = "Passwort";
|
"settings_contacts_ldap_password_title" = "Passwort";
|
||||||
"settings_contacts_ldap_search_base_title" = "Suchbasis (darf nicht leer sein)";
|
"settings_contacts_ldap_search_base_title" = "Filter";
|
||||||
"settings_contacts_ldap_server_url_title" = "Server-URL (darf nicht leer sein)";
|
"settings_contacts_ldap_server_url_title" = "Server URL (darf nicht leer sein)";
|
||||||
"settings_contacts_ldap_use_tls_title" = "TLS verwenden";
|
"settings_contacts_ldap_use_tls_title" = "TLS verwenden";
|
||||||
"settings_contacts_title" = "Kontakte";
|
"settings_contacts_title" = "Kontakte";
|
||||||
"settings_conversations_auto_download_title" = "Dateien automatisch herunterladen";
|
"settings_conversations_auto_download_title" = "Dateien automatisch heruntergeladene";
|
||||||
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Gespräche beim Schließen der Benachrichtigung als gelesen markieren";
|
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Gespräche beim Schließen der Nachrichtenbenachrichtigung als gelesen markieren";
|
||||||
"settings_conversations_title" = "Gespräche";
|
"settings_conversations_title" = "Gespräche";
|
||||||
"settings_meetings_default_layout_title" = "Standardlayout";
|
"settings_meetings_default_layout_title" = "Standardlayout";
|
||||||
"settings_meetings_layout_active_speaker_label" = "Aktiver Lautsprecher";
|
"settings_meetings_layout_active_speaker_label" = "Aktiver Lautsprecher";
|
||||||
|
|
@ -358,154 +358,8 @@
|
||||||
"sip_address_display_name" = "Anzeigename";
|
"sip_address_display_name" = "Anzeigename";
|
||||||
"sip_address_domain" = "Domäne";
|
"sip_address_domain" = "Domäne";
|
||||||
"start" = "Start";
|
"start" = "Start";
|
||||||
"web_platform_register_email_url" = "https://subscribe.linphone.org/register/email";
|
"web_platform_register_email_url" = "TURN aktivieren";
|
||||||
"welcome_page_3_title" = "Open source";
|
"welcome_page_3_title" = "Open source";
|
||||||
"welcome_page_title" = "Willkommen";
|
"welcome_page_title" = "Willkommen";
|
||||||
"conversation_end_to_end_encrypted_event_subtitle" = "Nachrichten in dieser Gespr sind Ende-zu-Ende verschlüsselt. Nur Ihr Gesprächspartner kann sie entschlüsseln.";
|
"conversation_end_to_end_encrypted_event_subtitle" = "Nachrichten in dieser Gespr sind Ende-zu-Ende verschlüsselt. Nur Ihr Gesprächspartner kann sie entschlüsseln.";
|
||||||
"conversation_end_to_end_encrypted_event_title" = "Ende-zu-Ende verschlüsselte Gespräch";
|
"conversation_end_to_end_encrypted_event_title" = "Ende-zu-Ende verschlüsselte Gespräch";
|
||||||
"assistant_permissions_post_notifications_title" = "**Postbenachrichtigungen:** Um informiert zu werden, wenn Sie eine Nachricht oder einen Anruf erhalten.";
|
|
||||||
"assistant_permissions_access_camera_title" = "**Auf Kamera zugreifen:** Zum Aufnehmen von Videos während Videoanrufen und Konferenzen.";
|
|
||||||
"assistant_permissions_read_contacts_title" = "**Kontakte lesen:** Um Ihre Kontakte anzuzeigen und herauszufinden, wer %@ verwendet.";
|
|
||||||
"account_settings_dialog_invalid_password_message" = "Verbindung fehlgeschlagen, da die Authentifizierung für das Konto fehlt oder ungültig ist.\n%@.\n\nSie können Ihr Passwort erneut eingeben oder Ihre Kontokonfiguration in den Einstellungen überprüfen.";
|
|
||||||
"assistant_account_creation_sms_confirmation_explanation" = "Wir haben einen Bestätigungscode an Ihre Telefonnummer %@ gesendet. Bitte geben Sie unten den Bestätigungscode ein:";
|
|
||||||
"assistant_dialog_confirm_phone_number_message" = "Möchten Sie wirklich die Telefonnummer %@ verwenden?";
|
|
||||||
"assistant_dialog_general_terms_and_privacy_policy_message" = "Indem Sie fortfahren, akzeptieren Sie unsere %@ und %@.";
|
|
||||||
"assistant_forgotten_password" = "Passwort vergessen?";
|
|
||||||
"assistant_invalid_uri_toast" = "Ungültige URI";
|
|
||||||
"assistant_permissions_record_audio_title" = "**Audio aufnehmen:** Damit Ihr Gesprächspartner Sie hören kann und um Sprachnachrichten aufzunehmen.";
|
|
||||||
"assistant_permissions_subtitle" = "Um %@ in vollem Umfang nutzen zu können, müssen Sie uns die folgenden Berechtigungen erteilen:";
|
|
||||||
"history_list_empty_with_filter_history" = "Keine Einträge entsprechen Ihrer Suche";
|
|
||||||
"history_title" = "Anrufliste";
|
|
||||||
"IM_MSG" = "Sie haben eine Nachricht erhalten";
|
|
||||||
"Interoperable" = "Interoperabel";
|
|
||||||
"conversation_event_participant_added" = "%@ ist beigetreten";
|
|
||||||
"drawer_menu_account_connection_status_refreshing" = "Aktualisieren...";
|
|
||||||
"failed_meeting_ics_invitation_not_sent_toast" = "ICS-Besprechungseinladungen an Teilnehmer konnten nicht gesendet werden";
|
|
||||||
"You will change this mode later" = "Sie werden diesen Modus später ändern";
|
|
||||||
"welcome_carousel_skip" = "Überspringen";
|
|
||||||
"welcome_page_2_message" = "Ihre Kommunikation ist dank unserer **Ende-zu-Ende-Verschlüsselung** sicher.";
|
|
||||||
"welcome_page_subtitle" = "in %@";
|
|
||||||
"conversation_event_participant_removed" = "%@ hat verlassen";
|
|
||||||
"username_error" = "Benutzername-Fehler";
|
|
||||||
": %@" = ": %@";
|
|
||||||
"*" = "*";
|
|
||||||
"**%@**" = "**%@**";
|
|
||||||
"#" = "#";
|
|
||||||
"%@" = "%@";
|
|
||||||
"%lld" = "%lld";
|
|
||||||
"%lld %@" = "%1$lld %2$@";
|
|
||||||
"%lld%%" = "%lld%%";
|
|
||||||
"+" = "+";
|
|
||||||
"|" = "|";
|
|
||||||
"❤️" = "❤️";
|
|
||||||
"👍" = "👍";
|
|
||||||
"😂" = "😂";
|
|
||||||
"😢" = "😢";
|
|
||||||
"😮" = "😮";
|
|
||||||
"0" = "0";
|
|
||||||
"1" = "1";
|
|
||||||
"2" = "2";
|
|
||||||
"3" = "3";
|
|
||||||
"4" = "4";
|
|
||||||
"5" = "5";
|
|
||||||
"6" = "6";
|
|
||||||
"7" = "7";
|
|
||||||
"8" = "8";
|
|
||||||
"9" = "9";
|
|
||||||
"assistant_third_party_sip_account_create_linphone_account" = "Ich möchte lieber ein Konto erstellen";
|
|
||||||
"assistant_third_party_sip_account_warning_explanation" = "Für einige Funktionen, wie z. B. Gruppennachrichten, Videokonferenzen usw., ist ein %@-Konto erforderlich.\n\nDiese Funktionen sind ausgeblendet, wenn Sie sich mit einem SIP-Konto eines Drittanbieters registrieren.\n\nUm diese Funktion in einem kommerziellen Projekt zu aktivieren, kontaktieren Sie uns bitte.";
|
|
||||||
"call_action_attended_transfer" = "Begleitete Übertragung";
|
|
||||||
"call_audio_device_type_bluetooth" = "Bluetooth (%@)";
|
|
||||||
"call_can_be_trusted_toast" = "Authentifiziertes Gerät";
|
|
||||||
"call_dialog_zrtp_validate_trust_message" = "Zu Ihrer Sicherheit müssen wir Ihr Endgerät authentifizieren. Bitte tauschen Sie Ihre Codes aus:";
|
|
||||||
"call_dialog_zrtp_validate_trust_warning_message" = "Zu Ihrer Sicherheit müssen wir Ihr Endgerät authentifizieren. Bitte tauschen Sie Ihre Codes aus:";
|
|
||||||
"Ce mode vous permet d’être interopérable avec d’autres services SIP.\nVos communications seront chiffrées de point à point. " = "Dieser Modus ermöglicht die Interoperabilität mit anderen SIP-Diensten.\nIhre Kommunikation wird Punkt-zu-Punkt verschlüsselt. ";
|
|
||||||
"Chiffrement de bout en bout de tous vos échanges, grâce au mode default vos communications sont à l’abri des regards." = "Ende-zu-Ende-Verschlüsselung aller Ihrer Kommunikationen. Dank des Standardmodus sind Ihre Kommunikationen vor neugierigen Blicken geschützt.";
|
|
||||||
"conference_name_error" = "Konferenznamen Fehler";
|
|
||||||
"contact_details_numbers_and_addresses_title" = "Telefonnummern & SIP-Adressen";
|
|
||||||
"contact_dialog_delete_title" = "%@ löschen?";
|
|
||||||
"call_transfer_current_call_title" = "Anruf weiterleiten";
|
|
||||||
"call_zrtp_sas_validation_skip" = "Überspringen";
|
|
||||||
"calls_count_label" = "%@ Anrufe";
|
|
||||||
"contact_video_call_action" = "Videoanruf";
|
|
||||||
"contacts_list_filter_popup_see_linphone_only" = "%@ Kontakte siehen";
|
|
||||||
"conversation_composing_label_multiple" = "%@ schreiben…";
|
|
||||||
"conversation_composing_label_single" = "%@ schreibt…";
|
|
||||||
"conversation_ephemeral_messages_duration_multiple_days" = "%d Tage";
|
|
||||||
"conversation_event_admin_set" = "%@ ist Administrator";
|
|
||||||
"conversation_event_admin_unset" = "$@ ist nicht mehr Administrator";
|
|
||||||
"conversation_event_device_added" = "neues Gerät für %@";
|
|
||||||
"conversation_event_device_removed" = "Gerät für %@ entfernt";
|
|
||||||
"conversation_event_ephemeral_messages_lifetime_changed" = "Kurzlebige Nachrichten Lebensdauer beträgt jetzt %@";
|
|
||||||
"conversation_event_subject_changed" = "neues Betreff: %@";
|
|
||||||
"conversations_files_waiting_to_be_shared_single" = "1 Datei wartet auf Freigabe";
|
|
||||||
"conversations_files_waiting_to_be_shared_multiple" = "%@ Dateien warten auf Freigabe";
|
|
||||||
"conversation_info_participants_list_title" = "Gruppenteilnehmer (%d)";
|
|
||||||
"conversation_message_meeting_cancelled_label" = "Das Besprechung wurde abgesagt!";
|
|
||||||
"conversation_one_to_one_hidden_subject" = "Dummy-Betreff";
|
|
||||||
"conversation_reply_to_message_title" = "Antwort auf: ";
|
|
||||||
"debug_logs_copied_to_clipboard_toast" = "Debug-Protokolle in die Zwischenablage kopiert";
|
|
||||||
"Default" = "Standard";
|
|
||||||
"Default mode" = "Standardmodus";
|
|
||||||
"dialog_close" = "Schließen";
|
|
||||||
"DTLS" = "DTLS";
|
|
||||||
"GC_MSG" = "Sie wurden zu einem Chatroom hinzugefügt";
|
|
||||||
"help_dialog_update_available_message" = "Eine neue Version %@ ist verfügbar. Möchten Sie aktualisieren?";
|
|
||||||
"manage_account_dialog_international_prefix_help_message" = "Wählen Sie Ihr Land aus, um Linphone die Zuordnung Ihrer Kontakte zuzulassen.";
|
|
||||||
"meeting_call_remove_no_participants" = "Zur Zeit kein Teilnehmer…";
|
|
||||||
"meeting_call_remove_participant_confirmation_message" = "Sind Sie sicher, dass Sie %@ entfernen möchten?";
|
|
||||||
"meeting_call_remove_participant_confirmation_title" = "Einen Teilnehmer entfernen";
|
|
||||||
"meeting_exported_as_calendar_event" = "Besprechung zum iPhone-Kalender hinzugefügt";
|
|
||||||
"meeting_failed_to_edit_toast" = "Das Bearbeiten der Besprechung ist fehlgeschlagen";
|
|
||||||
"meeting_schedule_failed_no_subject_or_participant_toast" = "Zum Erstellen eines Meetings ist ein Betreff und mindestens ein Teilnehmer erforderlich";
|
|
||||||
"meeting_waiting_room_joining_subtitle" = "Sie werden in Kürze verbunden sein";
|
|
||||||
"meetings_list_empty" = "Zur Zeit keine Besprechung…";
|
|
||||||
"menu_block_address" = "Die Adresse blockieren";
|
|
||||||
"menu_block_number" = "Die Nummer blockieren";
|
|
||||||
"menu_copy_sip_address" = "SIP-Addresse kopieren";
|
|
||||||
"message_copied_to_clipboard_toast" = "Nachricht in die Zwischenablage kopiert";
|
|
||||||
"message_delivery_info_read_title" = "Gelesen";
|
|
||||||
"message_delivery_info_received_title" = "Empfangen";
|
|
||||||
"message_delivery_info_sent_title" = "Gesendet";
|
|
||||||
"message_meeting_invitation_cancelled_notification" = "Die Besprechung wurde abgesagt";
|
|
||||||
"message_meeting_invitation_notification" = "Sie sind zu einer Besprechung eingeladen";
|
|
||||||
"message_meeting_invitation_updated_notification" = "Besprechung wurde aktualisiert";
|
|
||||||
"message_reactions_info_all_title" = "Reaktionen";
|
|
||||||
"network_reachable_again" = "Netzwerk ist nun wieder erreichbar";
|
|
||||||
"None" = "Kein";
|
|
||||||
"notification_chat_message_reaction_received" = "%@ hat mit %@ auf: %@ reagiert";
|
|
||||||
"notification_chat_message_received_title" = "Nachricht erhalten";
|
|
||||||
"Personnalize your profil mode" = "Ihren Profilmodus personalisieren";
|
|
||||||
"picker_categories" = "Kategorien";
|
|
||||||
"qr_code_validated" = "QR-Code validiert";
|
|
||||||
"selected_participants_count" = "%@ ausgewählte Teilnehmer";
|
|
||||||
"settings_calls_calibrate_echo_canceller_done" = "%@ ms";
|
|
||||||
"settings_contacts_carddav_deleted_toast" = "CardDAV-Konto gelöscht";
|
|
||||||
"settings_contacts_carddav_mandatory_field_not_filled_toast" = "Bitte geben Sie mindestens den Anzeigenamen und die Server-URL ein";
|
|
||||||
"settings_contacts_carddav_realm_title" = "Authentifizierungsbereich";
|
|
||||||
"settings_contacts_carddav_sync_successful_toast" = "Synchronisierung erfolgreich";
|
|
||||||
"settings_contacts_carddav_use_as_default_title" = "Neu erstellte Kontakte hier speichern";
|
|
||||||
"settings_contacts_ldap_bind_user_password_title" = "Benutzerkennwort binden";
|
|
||||||
"settings_contacts_ldap_max_results_title" = "Maximale Ergebnisse";
|
|
||||||
"settings_contacts_ldap_request_timeout_title" = "Request timeout";
|
|
||||||
"settings_contacts_ldap_search_filter_title" = "Filter";
|
|
||||||
"sip_address" = "SIP-Adresse";
|
|
||||||
"SRTP" = "SRTP";
|
|
||||||
"TCP" = "TCP";
|
|
||||||
"Temp Help" = "Temporärer-Hilfe";
|
|
||||||
"text_copied_to_clipboard_toast" = "Text in die Zwischenablage kopiert";
|
|
||||||
"TLS" = "TLS";
|
|
||||||
"UDP" = "UDP";
|
|
||||||
"uri_handler_bad_call_address_failed_toast" = "Anruf nicht möglich, ungültige Adresse";
|
|
||||||
"uri_handler_bad_config_address_failed_toast" = "Konfiguration konnte nicht abgerufen werden, ungültige Adresse";
|
|
||||||
"uri_handler_call_failed_toast" = "Anruf fehlgeschlagen";
|
|
||||||
"uri_handler_config_failed_toast" = "Konfiguration fehlgeschlagen";
|
|
||||||
"ZRTP" = "ZRTP";
|
|
||||||
"welcome_page_1_message" = "Eine **sichere** **Open Source** Kommunikations-App aus Frankreich.";
|
|
||||||
"welcome_page_3_message" = "Eine **kostenlose** Open-Source Anwendung seit **2001**.";
|
|
||||||
"help_about_contribute_translations_title" = "Zur Übersetzung von Linphone beitragen";
|
|
||||||
"help_about_privacy_policy_subtitle" = "Welche Informationen Linphone sammelt und nutzt";
|
|
||||||
"help_about_title" = "Über Linphone";
|
|
||||||
"help_about_user_guide_title" = "Linphone Benutzerhandbuch";
|
|
||||||
"help_about_open_source_licenses_title" = "GNU General Public License v3.0";
|
|
||||||
"help_about_open_source_licenses_subtitle" = "© Belledonne Communications 2010-2024";
|
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,6 @@
|
||||||
"account_settings_im_encryption_mandatory_title" = "IM encryption mandatory";
|
"account_settings_im_encryption_mandatory_title" = "IM encryption mandatory";
|
||||||
"account_settings_lime_server_url_title" = "E2E encryption keys server URL";
|
"account_settings_lime_server_url_title" = "E2E encryption keys server URL";
|
||||||
"account_settings_mwi_uri_title" = "MWI server URI (Message Waiting Indicator)";
|
"account_settings_mwi_uri_title" = "MWI server URI (Message Waiting Indicator)";
|
||||||
"account_settings_apply_international_prefix_title" = "Format phone numbers using international prefix";
|
|
||||||
"account_settings_replace_plus_by_00_title" = "Replace + by 00 when formatting phone numbers";
|
|
||||||
"account_settings_nat_policy_title" = "NAT policy settings";
|
"account_settings_nat_policy_title" = "NAT policy settings";
|
||||||
"account_settings_outbound_proxy_title" = "Outbound proxy";
|
"account_settings_outbound_proxy_title" = "Outbound proxy";
|
||||||
"account_settings_push_notification_not_available_title" = "Push notifications aren't available!";
|
"account_settings_push_notification_not_available_title" = "Push notifications aren't available!";
|
||||||
|
|
@ -95,7 +93,6 @@
|
||||||
"assistant_third_party_sip_account_warning_explanation" = "Some features require a %@ account, such as group messaging, video conferences…\n\nThese features are hidden when you register with a third party SIP account.\n\nTo enable it in a commercial project, please contact us.";
|
"assistant_third_party_sip_account_warning_explanation" = "Some features require a %@ account, such as group messaging, video conferences…\n\nThese features are hidden when you register with a third party SIP account.\n\nTo enable it in a commercial project, please contact us.";
|
||||||
"assistant_third_party_sip_account_warning_ok" = "I understand";
|
"assistant_third_party_sip_account_warning_ok" = "I understand";
|
||||||
"assistant_web_platform_link" = "subscribe.linphone.org";
|
"assistant_web_platform_link" = "subscribe.linphone.org";
|
||||||
"authentication_id" = "Authentication ID (if different)";
|
|
||||||
"bottom_navigation_calls_label" = "Calls";
|
"bottom_navigation_calls_label" = "Calls";
|
||||||
"bottom_navigation_contacts_label" = "Contacts";
|
"bottom_navigation_contacts_label" = "Contacts";
|
||||||
"bottom_navigation_conversations_label" = "Conversations";
|
"bottom_navigation_conversations_label" = "Conversations";
|
||||||
|
|
@ -190,7 +187,6 @@
|
||||||
"contacts_list_favourites_title" = "Favourites";
|
"contacts_list_favourites_title" = "Favourites";
|
||||||
"contacts_list_filter_popup_see_all" = "See all";
|
"contacts_list_filter_popup_see_all" = "See all";
|
||||||
"contacts_list_filter_popup_see_linphone_only" = "See %@ contacts";
|
"contacts_list_filter_popup_see_linphone_only" = "See %@ contacts";
|
||||||
"contacts_list_filter_popup_see_sip_only" = "See SIP contacts";
|
|
||||||
"conversation_action_call" = "Call";
|
"conversation_action_call" = "Call";
|
||||||
"conversation_action_configure_ephemeral_messages" = "Configure ephemeral messages";
|
"conversation_action_configure_ephemeral_messages" = "Configure ephemeral messages";
|
||||||
"conversation_action_delete" = "Delete conversation";
|
"conversation_action_delete" = "Delete conversation";
|
||||||
|
|
@ -204,13 +200,6 @@
|
||||||
"conversation_dialog_edit_subject" = "Edit conversation subject";
|
"conversation_dialog_edit_subject" = "Edit conversation subject";
|
||||||
"conversation_dialog_set_subject" = "Set conversation subject";
|
"conversation_dialog_set_subject" = "Set conversation subject";
|
||||||
"conversation_dialog_subject_hint" = "Conversation subject";
|
"conversation_dialog_subject_hint" = "Conversation subject";
|
||||||
"conversation_editing_message_title" = "Message being edited";
|
|
||||||
"conversation_message_edited_label" = "Edited";
|
|
||||||
"conversation_dialog_delete_chat_message_title" = "Delete this message?";
|
|
||||||
"conversation_dialog_delete_locally_label" = "For me";
|
|
||||||
"conversation_dialog_delete_for_everyone_label" = "For everyone";
|
|
||||||
"conversation_message_content_deleted_label" = "This message has been deleted";
|
|
||||||
"conversation_message_content_deleted_by_us_label" = "You have deleted this message";
|
|
||||||
"conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security";
|
"conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security";
|
||||||
"conversation_end_to_end_encrypted_event_title" = "End-to-end encrypted conversation";
|
"conversation_end_to_end_encrypted_event_title" = "End-to-end encrypted conversation";
|
||||||
"conversation_end_to_end_encrypted_event_subtitle" = "Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them.";
|
"conversation_end_to_end_encrypted_event_subtitle" = "Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them.";
|
||||||
|
|
@ -251,21 +240,14 @@
|
||||||
"conversation_info_participant_is_admin_label" = "Admin";
|
"conversation_info_participant_is_admin_label" = "Admin";
|
||||||
"conversation_info_participants_list_title" = "Group members (%d)";
|
"conversation_info_participants_list_title" = "Group members (%d)";
|
||||||
"conversation_invalid_participant_due_to_security_mode_toast" = "Can't create conversation with a participant not on the same domain due to security restrictions!";
|
"conversation_invalid_participant_due_to_security_mode_toast" = "Can't create conversation with a participant not on the same domain due to security restrictions!";
|
||||||
"conversation_menu_search_in_messages" = "Search";
|
|
||||||
"conversation_menu_go_to_info" = "Conversation info";
|
|
||||||
"conversation_menu_configure_ephemeral_messages" = "Ephemeral messages";
|
"conversation_menu_configure_ephemeral_messages" = "Ephemeral messages";
|
||||||
"conversation_menu_media_files" = "Media";
|
"conversation_menu_go_to_info" = "Conversation info";
|
||||||
"conversation_menu_documents_files" = "Documents";
|
|
||||||
"conversation_message_forward_cancelled_toast" = "Message forward was cancelled";
|
"conversation_message_forward_cancelled_toast" = "Message forward was cancelled";
|
||||||
"conversation_message_forwarded_toast" = "Message was forwarded";
|
"conversation_message_forwarded_toast" = "Message was forwarded";
|
||||||
"conversation_message_meeting_cancelled_label" = "Meeting has been cancelled!";
|
"conversation_message_meeting_cancelled_label" = "Meeting has been cancelled!";
|
||||||
"conversation_message_meeting_updated_label" = "Meeting has been updated";
|
"conversation_message_meeting_updated_label" = "Meeting has been updated";
|
||||||
"conversation_one_to_one_hidden_subject" = "Dummy subject";
|
"conversation_one_to_one_hidden_subject" = "Dummy subject";
|
||||||
"conversation_participants_list_empty" = "No participants found";
|
|
||||||
"conversation_participants_list_header" = "Participants";
|
|
||||||
"conversation_reply_to_message_title" = "Replying to: ";
|
"conversation_reply_to_message_title" = "Replying to: ";
|
||||||
"conversation_search_no_match_found" = "No matching result found";
|
|
||||||
"conversation_search_results_limit_reached_label" = "Search results limit reached, refine your search";
|
|
||||||
"conversation_text_field_hint" = "Say something…";
|
"conversation_text_field_hint" = "Say something…";
|
||||||
"conversations_list_empty" = "No conversation for the moment…";
|
"conversations_list_empty" = "No conversation for the moment…";
|
||||||
"conversation_take_picture_label" = "Take picture";
|
"conversation_take_picture_label" = "Take picture";
|
||||||
|
|
@ -284,7 +266,7 @@
|
||||||
"dialog_deny" = "Deny";
|
"dialog_deny" = "Deny";
|
||||||
"dialog_install" = "Install";
|
"dialog_install" = "Install";
|
||||||
"dialog_no" = "No";
|
"dialog_no" = "No";
|
||||||
"dialog_confirm" = "Confirm";
|
"dialog_ok" = "OK";
|
||||||
"dialog_yes" = "Yes";
|
"dialog_yes" = "Yes";
|
||||||
"drawer_menu_account_connection_status_cleared" = "Disabled";
|
"drawer_menu_account_connection_status_cleared" = "Disabled";
|
||||||
"drawer_menu_account_connection_status_connected" = "Connected";
|
"drawer_menu_account_connection_status_connected" = "Connected";
|
||||||
|
|
@ -408,26 +390,21 @@
|
||||||
"menu_delete_selected_item" = "Delete";
|
"menu_delete_selected_item" = "Delete";
|
||||||
"menu_forward_chat_message" = "Forward";
|
"menu_forward_chat_message" = "Forward";
|
||||||
"menu_invite" = "Invite";
|
"menu_invite" = "Invite";
|
||||||
"menu_edit_chat_message" = "Edit";
|
|
||||||
"menu_reply_to_chat_message" = "Reply";
|
"menu_reply_to_chat_message" = "Reply";
|
||||||
"menu_resend_chat_message" = "Re-send";
|
"menu_resend_chat_message" = "Re-send";
|
||||||
"menu_see_existing_contact" = "See contact";
|
"menu_see_existing_contact" = "See contact";
|
||||||
"menu_show_imdn" = "Delivery status";
|
"menu_show_imdn" = "Delivery status";
|
||||||
"menu_export_selected_item" = "Download";
|
|
||||||
"menu_share_selected_item" = "Share";
|
|
||||||
"message_copied_to_clipboard_toast" = "Message copied into clipboard";
|
"message_copied_to_clipboard_toast" = "Message copied into clipboard";
|
||||||
"message_delivery_info_error_title" = "Error";
|
"message_delivery_info_error_title" = "Error";
|
||||||
"message_delivery_info_read_title" = "Read";
|
"message_delivery_info_read_title" = "Read";
|
||||||
"message_delivery_info_received_title" = "Received";
|
"message_delivery_info_received_title" = "Received";
|
||||||
"message_delivery_info_sent_title" = "Sent";
|
"message_delivery_info_sent_title" = "Sent";
|
||||||
"message_forwarded_label" = "Forwarded";
|
"message_forwarded_label" = "Forwarded";
|
||||||
"message_meeting_invitation_cancelled_notification" = "Meeting has been cancelled";
|
"message_meeting_invitation_cancelled_notification" = "📅 Meeting has been cancelled";
|
||||||
"message_meeting_invitation_notification" = "You are invited to a meeting";
|
"message_meeting_invitation_notification" = "📅 You are invited to a meeting";
|
||||||
"message_meeting_invitation_updated_notification" = "Meeting has been updated";
|
"message_meeting_invitation_updated_notification" = "📅 Meeting has been updated";
|
||||||
"message_reaction_click_to_remove_label" = "Click to remove";
|
"message_reaction_click_to_remove_label" = "Click to remove";
|
||||||
"message_reactions_info_all_title" = "Reactions";
|
"message_reactions_info_all_title" = "Reactions";
|
||||||
"mwi_messages_are_waiting_single" = "1 new voice message";
|
|
||||||
"mwi_messages_are_waiting_multiple" = "%@ new voice messages";
|
|
||||||
"network_not_reachable" = "You aren't connected to internet";
|
"network_not_reachable" = "You aren't connected to internet";
|
||||||
"network_reachable_again" = "Network is now reachable again";
|
"network_reachable_again" = "Network is now reachable again";
|
||||||
"new_conversation_create_group" = "Create a group conversation";
|
"new_conversation_create_group" = "Create a group conversation";
|
||||||
|
|
@ -446,10 +423,6 @@
|
||||||
"picker_categories" = "Categories";
|
"picker_categories" = "Categories";
|
||||||
"qr_code_validated" = "QR code validated";
|
"qr_code_validated" = "QR code validated";
|
||||||
"recordings_title" = "Recordings";
|
"recordings_title" = "Recordings";
|
||||||
"recordings_list_empty" = "No recording for the moment…";
|
|
||||||
"recordings_list_empty" = "No recording for the moment…";
|
|
||||||
"recordings_list_empty" = "No recording for the moment…";
|
|
||||||
"recordings_list_empty" = "No recording for the moment…";
|
|
||||||
"selected_participants_count" = "%@ selected participants";
|
"selected_participants_count" = "%@ selected participants";
|
||||||
"settings_advanced_accept_early_media_title" = "Accept early media";
|
"settings_advanced_accept_early_media_title" = "Accept early media";
|
||||||
"settings_advanced_allow_outgoing_early_media_title" = "Allow outgoing early media";
|
"settings_advanced_allow_outgoing_early_media_title" = "Allow outgoing early media";
|
||||||
|
|
@ -493,22 +466,15 @@
|
||||||
"settings_contacts_carddav_username_title" = "Username";
|
"settings_contacts_carddav_username_title" = "Username";
|
||||||
"settings_contacts_edit_carddav_server_title" = "Edit CardDAV address book";
|
"settings_contacts_edit_carddav_server_title" = "Edit CardDAV address book";
|
||||||
"settings_contacts_edit_ldap_server_title" = "Edit LDAP server";
|
"settings_contacts_edit_ldap_server_title" = "Edit LDAP server";
|
||||||
"settings_contacts_ldap_enabled_title" = "Enabled";
|
|
||||||
"settings_contacts_ldap_server_url_title" = "Server URL (can't be empty)";
|
|
||||||
"settings_contacts_ldap_bind_dn_title" = "Bind DN";
|
"settings_contacts_ldap_bind_dn_title" = "Bind DN";
|
||||||
|
"settings_contacts_ldap_bind_user_password_title" = "Bind user password";
|
||||||
|
"settings_contacts_ldap_max_results_title" = "Maximum results";
|
||||||
"settings_contacts_ldap_password_title" = "Password";
|
"settings_contacts_ldap_password_title" = "Password";
|
||||||
"settings_contacts_ldap_use_tls_title" = "Use TLS";
|
"settings_contacts_ldap_request_timeout_title" = "Request timeout";
|
||||||
"settings_contacts_ldap_search_base_title" = "Search base (can't be empty)";
|
"settings_contacts_ldap_search_base_title" = "Search base (can't be empty)";
|
||||||
"settings_contacts_ldap_search_filter_title" = "Filter";
|
"settings_contacts_ldap_search_filter_title" = "Filter";
|
||||||
"settings_contacts_ldap_max_results_title" = "Max results";
|
"settings_contacts_ldap_server_url_title" = "Server URL (can't be empty)";
|
||||||
"settings_contacts_ldap_request_timeout_title" = "Timeout (in seconds)";
|
"settings_contacts_ldap_use_tls_title" = "Use TLS";
|
||||||
"settings_contacts_ldap_request_delay_title" = "Delay between two queries (in milliseconds)";
|
|
||||||
"settings_contacts_ldap_min_characters_title" = "Min characters to start a query";
|
|
||||||
"settings_contacts_ldap_name_attributes_title" = "Name attributes";
|
|
||||||
"settings_contacts_ldap_sip_attributes_title" = "SIP attributes";
|
|
||||||
"settings_contacts_ldap_sip_domain_title" = "SIP domain";
|
|
||||||
"settings_contacts_ldap_error_toast" = "A error occurred, LDAP server not saved!";
|
|
||||||
"settings_contacts_ldap_empty_server_error_toast" = "Server URL can't be empty";
|
|
||||||
"settings_contacts_title" = "Contacts";
|
"settings_contacts_title" = "Contacts";
|
||||||
"settings_conversations_auto_download_title" = "Auto-download files";
|
"settings_conversations_auto_download_title" = "Auto-download files";
|
||||||
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Mark conversation as read when dismissing message notification";
|
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Mark conversation as read when dismissing message notification";
|
||||||
|
|
@ -544,8 +510,6 @@
|
||||||
"uri_handler_config_success_toast" = "Configuration successfully applied";
|
"uri_handler_config_success_toast" = "Configuration successfully applied";
|
||||||
"username" = "Username";
|
"username" = "Username";
|
||||||
"username_error" = "Username error";
|
"username_error" = "Username error";
|
||||||
"Voicemail" = "Voicemail";
|
|
||||||
"New message" = "New message";
|
|
||||||
"web_platform_forgotten_password_url" = "https://subscribe.linphone.org/";
|
"web_platform_forgotten_password_url" = "https://subscribe.linphone.org/";
|
||||||
"web_platform_register_email_url" = "https://subscribe.linphone.org/register/email";
|
"web_platform_register_email_url" = "https://subscribe.linphone.org/register/email";
|
||||||
"website_contact_url" = "https://linphone.org/contact";
|
"website_contact_url" = "https://linphone.org/contact";
|
||||||
|
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
||||||
"settings_calls_change_ringtone_title" = "Cambiar tono de llamada";
|
|
||||||
"settings_calls_echo_canceller_subtitle" = "Evita que el eco se escuche en el extremo remoto si no hay un cancelador de eco de hardware disponible";
|
|
||||||
"settings_calls_echo_canceller_title" = "Utilizar el cancelador de eco de software";
|
|
||||||
"settings_calls_enable_fec_title" = "Habilitar FEC de vídeo";
|
|
||||||
"settings_calls_enable_video_title" = "Habilitar video";
|
|
||||||
"settings_calls_title" = "Llamadas";
|
|
||||||
"settings_calls_vibrate_while_ringing_title" = "Vibrar mientras suena la llamada entrante";
|
|
||||||
"settings_contacts_add_carddav_server_title" = "Agregar libreta de direcciones CardDAV";
|
|
||||||
"settings_contacts_add_ldap_server_title" = "Agregar servidor LDAP";
|
|
||||||
"settings_contacts_carddav_name_title" = "Nombre para mostrar";
|
|
||||||
"settings_contacts_carddav_password_title" = "Clave";
|
|
||||||
"settings_contacts_carddav_server_url_title" = "URL del servidor";
|
|
||||||
"settings_contacts_carddav_sync_error_toast" = "Error de sincronización!";
|
|
||||||
"settings_contacts_carddav_username_title" = "Nombre de Usuario";
|
|
||||||
"settings_contacts_edit_carddav_server_title" = "Editar libreta de direcciones CardDAV";
|
|
||||||
"settings_contacts_edit_ldap_server_title" = "Editar servidor LDAP";
|
|
||||||
"settings_contacts_ldap_password_title" = "Clave";
|
|
||||||
"settings_contacts_title" = "Contactos";
|
|
||||||
"settings_conversations_auto_download_title" = "Descarga automática de archivos";
|
|
||||||
"dialog_no" = "No";
|
|
||||||
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Marcar la conversación como leída al descartar la notificación del mensaje";
|
|
||||||
"settings_conversations_title" = "Conversaciones";
|
|
||||||
"settings_meetings_default_layout_title" = "Diseño predeterminado";
|
|
||||||
"settings_meetings_layout_active_speaker_label" = "Altavoz activo";
|
|
||||||
"settings_meetings_layout_mosaic_label" = "Mosaico";
|
|
||||||
"settings_meetings_title" = "Reuniones";
|
|
||||||
"settings_network_allow_ipv6" = "Permitir IPv6";
|
|
||||||
"settings_network_title" = "Red";
|
|
||||||
"settings_network_use_wifi_only" = "Use solo redes Wi-fi";
|
|
||||||
"settings_security_enable_vfs_subtitle" = "Advertencia: una vez habilitado, ¡no se puede deshabilitar!";
|
|
||||||
"settings_security_enable_vfs_title" = "Cifrar todo";
|
|
||||||
"settings_security_prevent_screenshots_title" = "Evitar que se grabe la interfaz";
|
|
||||||
"settings_security_title" = "Seguridad";
|
|
||||||
"settings_title" = "Ajustes";
|
|
||||||
"sip_address_copied_to_clipboard_toast" = "Dirección SIP copiada al portapapeles";
|
|
||||||
"sip_address_display_name" = "Nombre para mostrar";
|
|
||||||
"sip_address_domain" = "Dominio";
|
|
||||||
"start" = "Iniciar";
|
|
||||||
"uri_handler_config_success_toast" = "Configuración aplicada satisfactoriamente";
|
|
||||||
"username" = "Nombre de Usuario";
|
|
||||||
"welcome_page_2_title" = "Segura";
|
|
||||||
"welcome_page_3_title" = "código abierto";
|
|
||||||
"welcome_page_title" = "Bienvenido";
|
|
||||||
"account_settings_dialog_invalid_password_hint" = "Clave";
|
|
||||||
"account_settings_title" = "Configuración de la cuenta";
|
|
||||||
"assistant_account_create" = "Crear";
|
|
||||||
"assistant_account_creation_wrong_phone_number" = "Número equivocado?";
|
|
||||||
"assistant_account_login" = "Acceso";
|
|
||||||
"assistant_account_login_forbidden_error" = "Nombre de usuario o contraseña incorrectos";
|
|
||||||
"assistant_account_register" = "Registrar";
|
|
||||||
"assistant_account_register_push_notification_not_received_error" = "Notificación push con token de autenticación no recibida en 5 segundos, inténtelo nuevamente más tarde";
|
|
||||||
"assistant_account_register_unexpected_error" = "Se produjo un error inesperado, inténtelo de nuevo más tarde";
|
|
||||||
"assistant_already_have_an_account" = "Ya tienes una cuenta?";
|
|
||||||
"assistant_create_account_using_email_on_our_web_platform" = "Crea una cuenta con tu correo electrónico en:";
|
|
||||||
"assistant_dialog_confirm_phone_number_title" = "Confirmar número de teléfono";
|
|
||||||
"assistant_dialog_general_terms_and_privacy_policy_title" = "Términos generales y política de privacidad";
|
|
||||||
"assistant_dialog_general_terms_label" = "términos generales";
|
|
||||||
"assistant_dialog_privacy_policy_label" = "política de privacidad";
|
|
||||||
"assistant_login_third_party_sip_account" = "Utilice una cuenta SIP de terceros";
|
|
||||||
"assistant_no_account_yet" = "Aún no tienes cuenta?";
|
|
||||||
"assistant_permissions_grant_all_of_them" = "Ok";
|
|
||||||
"assistant_permissions_skip_permissions" = "Hazlo más tarde";
|
|
||||||
"assistant_permissions_title" = "Conceder permisos";
|
|
||||||
"assistant_qr_code_invalid_toast" = "Código QR no válido!";
|
|
||||||
"assistant_scan_qr_code" = "Escanear código QR";
|
|
||||||
"assistant_sip_account_transport_protocol" = "Transporte";
|
|
||||||
"assistant_third_party_sip_account_warning_ok" = "Yo entiendo";
|
|
||||||
"authentication_id" = "ID de autenticación (si es diferente)";
|
|
||||||
"bottom_navigation_calls_label" = "Llamadas";
|
|
||||||
"bottom_navigation_contacts_label" = "Contactos";
|
|
||||||
"bottom_navigation_conversations_label" = "Conversaciones";
|
|
||||||
"bottom_navigation_meetings_label" = "Reuniones";
|
|
||||||
"call_stats_media_encryption_title" = "Cifrado de medios";
|
|
||||||
"conference_layout_grid" = "Mosaico";
|
|
||||||
"contact_call_action" = "Llamar";
|
|
||||||
"contact_details_delete" = "Eliminar";
|
|
||||||
"conversation_action_call" = "Llamar";
|
|
||||||
"conversation_action_mark_as_read" = "Marcar como leído";
|
|
||||||
"conversation_ephemeral_messages_duration_disabled" = "Deshabilitado";
|
|
||||||
"dialog_accept" = "Aceptar";
|
|
||||||
"dialog_call" = "Llamar";
|
|
||||||
"dialog_cancel" = "Cancelar";
|
|
||||||
"dialog_continue" = "Continuar";
|
|
||||||
"dialog_deny" = "Denegar";
|
|
||||||
"dialog_install" = "Instalar";
|
|
||||||
"dialog_ok" = "Ok";
|
|
||||||
"dialog_yes" = "Si";
|
|
||||||
"drawer_menu_account_connection_status_cleared" = "Deshabilitado";
|
|
||||||
"drawer_menu_account_connection_status_connected" = "Conectado";
|
|
||||||
"drawer_menu_account_connection_status_failed" = "Error";
|
|
||||||
"drawer_menu_account_connection_status_progress" = "Conectando…";
|
|
||||||
"drawer_menu_add_account" = "Agregar una cuenta";
|
|
||||||
"drawer_menu_manage_account" = "Administrar el perfil";
|
|
||||||
"drawer_menu_no_account_configured_yet" = "Cuenta no configurada aun";
|
|
||||||
"Error" = "Error";
|
|
||||||
"help_about_advanced_title" = "Avanzado";
|
|
||||||
"help_about_check_for_update" = "Comprobar actualización";
|
|
||||||
"help_about_privacy_policy_title" = "Política de privacidad";
|
|
||||||
"help_about_user_guide_subtitle" = "Aprenda a dominar todas las funciones de la aplicación, paso a paso.";
|
|
||||||
"help_about_version_title" = "Versión";
|
|
||||||
"help_dialog_update_available_title" = "Actualización disponible";
|
|
||||||
"help_error_checking_version_toast_message" = "Se produjo un error al buscar actualizaciones";
|
|
||||||
"help_quit_title" = "Salir de la aplicación";
|
|
||||||
"help_title" = "Ayuda";
|
|
||||||
"help_troubleshooting_app_version_title" = "Versión de la aplicación";
|
|
||||||
"help_troubleshooting_clean_logs" = "Limpiar registros";
|
|
||||||
"help_troubleshooting_clear_native_friends_in_database" = "Borrar contactos importados de la libreta de direcciones nativa";
|
|
||||||
"help_troubleshooting_debug_logs_cleaned_toast_message" = "Se han limpiado los registros de depuración";
|
|
||||||
"help_troubleshooting_debug_logs_upload_error_toast_message" = "No se pudieron cargar los registros de depuración";
|
|
||||||
"help_troubleshooting_firebase_project_title" = "ID del proyecto de Firebase";
|
|
||||||
"help_troubleshooting_print_logs_in_logcat" = "Imprimir registros en logcat";
|
|
||||||
"help_troubleshooting_sdk_version_title" = "Versión SDK";
|
|
||||||
"help_troubleshooting_share_logs" = "Compartir registros";
|
|
||||||
"help_troubleshooting_share_logs_dialog_title" = "Compartir enlaces de registros de depuración usando…";
|
|
||||||
"help_troubleshooting_show_config_file" = "Mostrar configuracion";
|
|
||||||
"help_troubleshooting_title" = "Solución de problemas";
|
|
||||||
"help_version_up_to_date_toast_message" = "Su versión esta actualizada";
|
|
||||||
"Interoperable mode" = "Modo interoperable";
|
|
||||||
"manage_account_add_picture" = "Agregar una imagen";
|
|
||||||
"manage_account_delete" = "Cerrar sesión";
|
|
||||||
"manage_account_details_title" = "Detalles";
|
|
||||||
"manage_account_device_remove" = "Remover";
|
|
||||||
"manage_account_devices_title" = "Dispositivos";
|
|
||||||
"manage_account_edit_picture" = "Editar imagen";
|
|
||||||
"manage_account_international_prefix" = "Prefijo internacional";
|
|
||||||
"manage_account_no_device" = "No se encontró ningún dispositivo…";
|
|
||||||
"manage_account_remove_picture" = "Remover imagen";
|
|
||||||
"manage_account_settings" = "Configuración de la cuenta";
|
|
||||||
"manage_account_status_cleared_summary" = "La cuenta ha sido desactivada, no recibirás ninguna llamada ni mensaje.";
|
|
||||||
"manage_account_status_connected_summary" = "Esta cuenta está en línea, cualquiera puede llamarte.";
|
|
||||||
"manage_account_status_failed_summary" = "Error al conectar la cuenta, comprueba tu configuración.";
|
|
||||||
"manage_account_status_progress_summary" = "La cuenta se está conectando al servidor, por favor espere…";
|
|
||||||
"manage_account_title" = "Administrar cuenta";
|
|
||||||
"meeting_waiting_room_cancel" = "Cancelar";
|
|
||||||
"menu_delete_selected_item" = "Eliminar";
|
|
||||||
"menu_reply_to_chat_message" = "Responder";
|
|
||||||
"message_delivery_info_error_title" = "Error";
|
|
||||||
"next" = "Siguiente";
|
|
||||||
"notification_missed_call_title" = "Llamada perdida";
|
|
||||||
"or" = "o";
|
|
||||||
"password" = "Clave";
|
|
||||||
"phone_number" = "Número de teléfono";
|
|
||||||
"settings_advanced_accept_early_media_title" = "Acepta los primeros medios de comunicación";
|
|
||||||
"settings_advanced_allow_outgoing_early_media_title" = "Permitir la salida temprana de medios";
|
|
||||||
"settings_advanced_audio_codecs_title" = "Códecs de audio";
|
|
||||||
"settings_advanced_audio_devices_title" = "Dispositivos de audio";
|
|
||||||
"settings_advanced_device_id" = "Identificación de Dispositivo";
|
|
||||||
"settings_advanced_device_id_hint" = "Solo caracteres alfanuméricos";
|
|
||||||
"settings_advanced_download_apply_remote_provisioning" = "Descargar y aplicar";
|
|
||||||
"settings_advanced_input_audio_device_title" = "Dispositivo de audio de entrada predeterminado";
|
|
||||||
"settings_advanced_media_encryption_mandatory_title" = "Cifrado obligatorio de los medios de comunicación";
|
|
||||||
"settings_advanced_output_audio_device_title" = "Dispositivo de salida de audio predeterminado";
|
|
||||||
"settings_advanced_remote_provisioning_url" = "URL de aprovisionamiento remoto";
|
|
||||||
"settings_advanced_title" = "Configuración avanzada";
|
|
||||||
"settings_advanced_upload_server_url" = "URL del servidor para compartir archivos";
|
|
||||||
"settings_advanced_video_codecs_title" = "Códecs de video";
|
|
||||||
"settings_calls_auto_record_title" = "Iniciar automáticamente la grabación de llamadas";
|
|
||||||
"settings_calls_calibrate_echo_canceller_done_no_echo" = "sin eco";
|
|
||||||
"settings_calls_calibrate_echo_canceller_failed" = "fallido";
|
|
||||||
"settings_calls_calibrate_echo_canceller_in_progress" = "en progreso";
|
|
||||||
"settings_calls_calibrate_echo_canceller_title" = "Calibrar el cancelador de eco";
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
"next" = "Hurrengoa";
|
|
||||||
"account_settings_dialog_invalid_password_hint" = "Pasahitza";
|
|
||||||
"dialog_accept" = "Onartu";
|
|
||||||
"dialog_deny" = "Ukatu";
|
|
||||||
"or" = "edo";
|
|
||||||
"password" = "Pasahitza";
|
|
||||||
"phone_number" = "Telefono zenbakia";
|
|
||||||
"settings_advanced_device_id" = "Gailuaren IDa";
|
|
||||||
"settings_contacts_carddav_name_title" = "Erakusteko izena";
|
|
||||||
"settings_contacts_carddav_password_title" = "Pasahitza";
|
|
||||||
"settings_contacts_carddav_username_title" = "Erabiltzaile izena";
|
|
||||||
"settings_contacts_ldap_password_title" = "Pasahitza";
|
|
||||||
"sip_address_display_name" = "Erakusteko izena";
|
|
||||||
"sip_address_domain" = "Domeinua";
|
|
||||||
"start" = "Hasi";
|
|
||||||
"username" = "Erabiltzaile izena";
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
"account_settings_audio_video_conference_factory_uri_title" = "Audio/video kokous, tehdas URI";
|
|
||||||
"account_settings_dialog_invalid_password_title" = "Todennus tarvitaan";
|
|
||||||
": %@" = ": %@";
|
|
||||||
"[linphone.org/contact](https://linphone.org/contact)" = "[linphone.org/contact](https://linphone.org/contact)";
|
|
||||||
"*" = "*";
|
|
||||||
"**%@**" = "**%@**";
|
|
||||||
"#" = "#";
|
|
||||||
"%@" = "%@";
|
|
||||||
"%lld" = "%lld";
|
|
||||||
"%lld %@" = "%1$lld %2$@";
|
|
||||||
"+" = "+";
|
|
||||||
"|" = "|";
|
|
||||||
"❤️" = "❤️";
|
|
||||||
"👍" = "👍";
|
|
||||||
"%lld%%" = "%lld%%";
|
|
||||||
"😂" = "😂";
|
|
||||||
"😢" = "😢";
|
|
||||||
"😮" = "😮";
|
|
||||||
"0" = "0";
|
|
||||||
"1" = "1";
|
|
||||||
"2" = "2";
|
|
||||||
"3" = "3";
|
|
||||||
"4" = "4";
|
|
||||||
"5" = "5";
|
|
||||||
"6" = "6";
|
|
||||||
"7" = "7";
|
|
||||||
"8" = "8";
|
|
||||||
"9" = "9";
|
|
||||||
"account_settings_avpf_title" = "AVPF";
|
|
||||||
"ZRTP" = "ZRTP";
|
|
||||||
"[https://sip.linphone.org](https://sip.linphone.org)" = "[https://sip.linphone.org](https://sip.linphone.org)";
|
|
||||||
|
|
@ -47,8 +47,6 @@
|
||||||
"account_settings_im_encryption_mandatory_title" = "Chiffrement obligatoire des conversations";
|
"account_settings_im_encryption_mandatory_title" = "Chiffrement obligatoire des conversations";
|
||||||
"account_settings_lime_server_url_title" = "URL du serveur d'échange de clés de chiffrement";
|
"account_settings_lime_server_url_title" = "URL du serveur d'échange de clés de chiffrement";
|
||||||
"account_settings_mwi_uri_title" = "URI du serveur MWI (Message Waiting Indicator)";
|
"account_settings_mwi_uri_title" = "URI du serveur MWI (Message Waiting Indicator)";
|
||||||
"account_settings_apply_international_prefix_title" = "Formater les numéros en utilisant l'indicatif international";
|
|
||||||
"account_settings_replace_plus_by_00_title" = "Remplacer + par 00 lors du formatage des numéros de téléphone";
|
|
||||||
"account_settings_nat_policy_title" = "Paramètres de politique NAT";
|
"account_settings_nat_policy_title" = "Paramètres de politique NAT";
|
||||||
"account_settings_outbound_proxy_title" = "Serveur mandataire sortant";
|
"account_settings_outbound_proxy_title" = "Serveur mandataire sortant";
|
||||||
"account_settings_push_notification_not_available_title" = "Notifications push non disponibles";
|
"account_settings_push_notification_not_available_title" = "Notifications push non disponibles";
|
||||||
|
|
@ -95,7 +93,6 @@
|
||||||
"assistant_third_party_sip_account_warning_explanation" = "Certaines fonctionnalités telles que les conversations de groupe, les vidéo-conférences, etc… nécessitent un compte %@.\n\nCes fonctionnalités seront masquées si vous utilisez un compte SIP tiers.\n\nPour les activer dans un projet commercial, merci de nous contacter.";
|
"assistant_third_party_sip_account_warning_explanation" = "Certaines fonctionnalités telles que les conversations de groupe, les vidéo-conférences, etc… nécessitent un compte %@.\n\nCes fonctionnalités seront masquées si vous utilisez un compte SIP tiers.\n\nPour les activer dans un projet commercial, merci de nous contacter.";
|
||||||
"assistant_third_party_sip_account_warning_ok" = "J’ai compris";
|
"assistant_third_party_sip_account_warning_ok" = "J’ai compris";
|
||||||
"assistant_web_platform_link" = "subscribe.linphone.org";
|
"assistant_web_platform_link" = "subscribe.linphone.org";
|
||||||
"authentication_id" = "Identifiant de connexion (si différent)";
|
|
||||||
"bottom_navigation_calls_label" = "Appels";
|
"bottom_navigation_calls_label" = "Appels";
|
||||||
"bottom_navigation_contacts_label" = "Contacts";
|
"bottom_navigation_contacts_label" = "Contacts";
|
||||||
"bottom_navigation_conversations_label" = "Conversations";
|
"bottom_navigation_conversations_label" = "Conversations";
|
||||||
|
|
@ -190,7 +187,6 @@
|
||||||
"contacts_list_favourites_title" = "Favoris";
|
"contacts_list_favourites_title" = "Favoris";
|
||||||
"contacts_list_filter_popup_see_all" = "Tous les contacts";
|
"contacts_list_filter_popup_see_all" = "Tous les contacts";
|
||||||
"contacts_list_filter_popup_see_linphone_only" = "Contacts %@";
|
"contacts_list_filter_popup_see_linphone_only" = "Contacts %@";
|
||||||
"contacts_list_filter_popup_see_sip_only" = "Contacts SIP";
|
|
||||||
"conversation_action_call" = "Appeler";
|
"conversation_action_call" = "Appeler";
|
||||||
"conversation_action_configure_ephemeral_messages" = "Configurer les messages éphémères";
|
"conversation_action_configure_ephemeral_messages" = "Configurer les messages éphémères";
|
||||||
"conversation_action_delete" = "Supprimer la conversation";
|
"conversation_action_delete" = "Supprimer la conversation";
|
||||||
|
|
@ -204,13 +200,6 @@
|
||||||
"conversation_dialog_edit_subject" = "Renommer la conversation";
|
"conversation_dialog_edit_subject" = "Renommer la conversation";
|
||||||
"conversation_dialog_set_subject" = "Nommer la conversation";
|
"conversation_dialog_set_subject" = "Nommer la conversation";
|
||||||
"conversation_dialog_subject_hint" = "Nom de la conversation";
|
"conversation_dialog_subject_hint" = "Nom de la conversation";
|
||||||
"conversation_editing_message_title" = "Modification du message";
|
|
||||||
"conversation_message_edited_label" = "Modifié";
|
|
||||||
"conversation_dialog_delete_chat_message_title" = "Supprimer le message ?";
|
|
||||||
"conversation_dialog_delete_locally_label" = "Pour moi";
|
|
||||||
"conversation_dialog_delete_for_everyone_label" = "Pour tout le monde";
|
|
||||||
"conversation_message_content_deleted_label" = "Le message a été supprimé";
|
|
||||||
"conversation_message_content_deleted_by_us_label" = "Vous avez supprimé le message";
|
|
||||||
"conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security";
|
"conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security";
|
||||||
"conversation_end_to_end_encrypted_event_title" = "Conversation chiffrée de bout en bout";
|
"conversation_end_to_end_encrypted_event_title" = "Conversation chiffrée de bout en bout";
|
||||||
"conversation_end_to_end_encrypted_event_subtitle" = "Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer.";
|
"conversation_end_to_end_encrypted_event_subtitle" = "Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer.";
|
||||||
|
|
@ -251,21 +240,14 @@
|
||||||
"conversation_info_participant_is_admin_label" = "Administrateur";
|
"conversation_info_participant_is_admin_label" = "Administrateur";
|
||||||
"conversation_info_participants_list_title" = "Participants (%d)";
|
"conversation_info_participants_list_title" = "Participants (%d)";
|
||||||
"conversation_invalid_participant_due_to_security_mode_toast" = "Pour des raisons de sécurité, la création d'une conversation avec un participant d'un domaine tiers est désactivé.";
|
"conversation_invalid_participant_due_to_security_mode_toast" = "Pour des raisons de sécurité, la création d'une conversation avec un participant d'un domaine tiers est désactivé.";
|
||||||
"conversation_menu_search_in_messages" = "Chercher";
|
|
||||||
"conversation_menu_go_to_info" = "Informations";
|
|
||||||
"conversation_menu_configure_ephemeral_messages" = "Messages éphémères";
|
"conversation_menu_configure_ephemeral_messages" = "Messages éphémères";
|
||||||
"conversation_menu_media_files" = "Médias";
|
"conversation_menu_go_to_info" = "Informations";
|
||||||
"conversation_menu_documents_files" = "Documents";
|
|
||||||
"conversation_message_forward_cancelled_toast" = "Transfert annulé";
|
"conversation_message_forward_cancelled_toast" = "Transfert annulé";
|
||||||
"conversation_message_forwarded_toast" = "Message transféré";
|
"conversation_message_forwarded_toast" = "Message transféré";
|
||||||
"conversation_message_meeting_cancelled_label" = "La réunion a été annulée";
|
"conversation_message_meeting_cancelled_label" = "La réunion a été annulée";
|
||||||
"conversation_message_meeting_updated_label" = "La réunion a été mise à jour";
|
"conversation_message_meeting_updated_label" = "La réunion a été mise à jour";
|
||||||
"conversation_one_to_one_hidden_subject" = "Dummy subject";
|
"conversation_one_to_one_hidden_subject" = "Dummy subject";
|
||||||
"conversation_participants_list_empty" = "Aucun participant trouvé";
|
|
||||||
"conversation_participants_list_header" = "Participants";
|
|
||||||
"conversation_reply_to_message_title" = "En réponse à : ";
|
"conversation_reply_to_message_title" = "En réponse à : ";
|
||||||
"conversation_search_no_match_found" = "Aucun résultat trouvé";
|
|
||||||
"conversation_search_results_limit_reached_label" = "Nombre maximal de résultats atteint, affinez votre recherche";
|
|
||||||
"conversation_text_field_hint" = "Dites quelque chose…";
|
"conversation_text_field_hint" = "Dites quelque chose…";
|
||||||
"conversations_list_empty" = "Aucune conversation pour le moment…";
|
"conversations_list_empty" = "Aucune conversation pour le moment…";
|
||||||
"conversation_take_picture_label" = "Prendre une photo";
|
"conversation_take_picture_label" = "Prendre une photo";
|
||||||
|
|
@ -284,7 +266,7 @@
|
||||||
"dialog_deny" = "Refuser";
|
"dialog_deny" = "Refuser";
|
||||||
"dialog_install" = "Installer";
|
"dialog_install" = "Installer";
|
||||||
"dialog_no" = "Non";
|
"dialog_no" = "Non";
|
||||||
"dialog_confirm" = "Confirmer";
|
"dialog_ok" = "OK";
|
||||||
"dialog_yes" = "Oui";
|
"dialog_yes" = "Oui";
|
||||||
"drawer_menu_account_connection_status_cleared" = "Désactivé";
|
"drawer_menu_account_connection_status_cleared" = "Désactivé";
|
||||||
"drawer_menu_account_connection_status_connected" = "Connecté";
|
"drawer_menu_account_connection_status_connected" = "Connecté";
|
||||||
|
|
@ -355,7 +337,7 @@
|
||||||
"manage_account_international_prefix" = "Indicatif international";
|
"manage_account_international_prefix" = "Indicatif international";
|
||||||
"manage_account_no_device" = "Aucun appareil n'a été trouvé…";
|
"manage_account_no_device" = "Aucun appareil n'a été trouvé…";
|
||||||
"manage_account_remove_picture" = "Supprimer";
|
"manage_account_remove_picture" = "Supprimer";
|
||||||
"manage_account_settings" = "Paramètres";
|
"manage_account_settings" = "Mon compte";
|
||||||
"manage_account_status_cleared_summary" = "Compte désactivé, vous ne recevrez ni appel ni message.";
|
"manage_account_status_cleared_summary" = "Compte désactivé, vous ne recevrez ni appel ni message.";
|
||||||
"manage_account_status_connected_summary" = "Vous êtes en ligne, on peut vous joindre.";
|
"manage_account_status_connected_summary" = "Vous êtes en ligne, on peut vous joindre.";
|
||||||
"manage_account_status_failed_summary" = "Erreur de connexion, vérifiez vos paramètres.";
|
"manage_account_status_failed_summary" = "Erreur de connexion, vérifiez vos paramètres.";
|
||||||
|
|
@ -408,26 +390,21 @@
|
||||||
"menu_delete_selected_item" = "Supprimer";
|
"menu_delete_selected_item" = "Supprimer";
|
||||||
"menu_forward_chat_message" = "Transférer";
|
"menu_forward_chat_message" = "Transférer";
|
||||||
"menu_invite" = "Inviter";
|
"menu_invite" = "Inviter";
|
||||||
"menu_edit_chat_message" = "Modifier";
|
|
||||||
"menu_reply_to_chat_message" = "Répondre";
|
"menu_reply_to_chat_message" = "Répondre";
|
||||||
"menu_resend_chat_message" = "Ré-envoyer";
|
"menu_resend_chat_message" = "Ré-envoyer";
|
||||||
"menu_see_existing_contact" = "Voir le contact";
|
"menu_see_existing_contact" = "Voir le contact";
|
||||||
"menu_show_imdn" = "Info de réception";
|
"menu_show_imdn" = "Info de réception";
|
||||||
"menu_export_selected_item" = "Télécharger";
|
|
||||||
"menu_share_selected_item" = "Partager";
|
|
||||||
"message_copied_to_clipboard_toast" = "Message copié dans le presse-papier";
|
"message_copied_to_clipboard_toast" = "Message copié dans le presse-papier";
|
||||||
"message_delivery_info_error_title" = "En erreur";
|
"message_delivery_info_error_title" = "En erreur";
|
||||||
"message_delivery_info_read_title" = "Lu";
|
"message_delivery_info_read_title" = "Lu";
|
||||||
"message_delivery_info_received_title" = "Reçu";
|
"message_delivery_info_received_title" = "Reçu";
|
||||||
"message_delivery_info_sent_title" = "Envoyé";
|
"message_delivery_info_sent_title" = "Envoyé";
|
||||||
"message_forwarded_label" = "Transféré";
|
"message_forwarded_label" = "Transféré";
|
||||||
"message_meeting_invitation_cancelled_notification" = "Réunion annulée";
|
"message_meeting_invitation_cancelled_notification" = "📅 Réunion annulée";
|
||||||
"message_meeting_invitation_notification" = "Invitation à une réunion";
|
"message_meeting_invitation_notification" = "📅 Invitation à une réunion";
|
||||||
"message_meeting_invitation_updated_notification" = "Réunion mise à jour";
|
"message_meeting_invitation_updated_notification" = "📅 Réunion mise à jour";
|
||||||
"message_reaction_click_to_remove_label" = "Cliquez pour supprimer";
|
"message_reaction_click_to_remove_label" = "Cliquez pour supprimer";
|
||||||
"message_reactions_info_all_title" = "Réactions";
|
"message_reactions_info_all_title" = "Réactions";
|
||||||
"mwi_messages_are_waiting_single" = "1 message vocal en attente";
|
|
||||||
"mwi_messages_are_waiting_multiple" = "%@ messages vocaux en attente";
|
|
||||||
"network_not_reachable" = "Vous n’êtes pas connecté à internet";
|
"network_not_reachable" = "Vous n’êtes pas connecté à internet";
|
||||||
"network_reachable_again" = "Vous êtes à nouveau connecté à internet";
|
"network_reachable_again" = "Vous êtes à nouveau connecté à internet";
|
||||||
"new_conversation_create_group" = "Créer une conversation de groupe";
|
"new_conversation_create_group" = "Créer une conversation de groupe";
|
||||||
|
|
@ -446,7 +423,6 @@
|
||||||
"picker_categories" = "Catégories";
|
"picker_categories" = "Catégories";
|
||||||
"qr_code_validated" = "QR code validé";
|
"qr_code_validated" = "QR code validé";
|
||||||
"recordings_title" = "Enregistrements";
|
"recordings_title" = "Enregistrements";
|
||||||
"recordings_list_empty" = "Aucun appel enregistré…";
|
|
||||||
"selected_participants_count" = "%@ participants selectionnés";
|
"selected_participants_count" = "%@ participants selectionnés";
|
||||||
"settings_advanced_accept_early_media_title" = "Accepter l'early media";
|
"settings_advanced_accept_early_media_title" = "Accepter l'early media";
|
||||||
"settings_advanced_allow_outgoing_early_media_title" = "Autoriser l'early media pour les appels sortants";
|
"settings_advanced_allow_outgoing_early_media_title" = "Autoriser l'early media pour les appels sortants";
|
||||||
|
|
@ -490,22 +466,15 @@
|
||||||
"settings_contacts_carddav_username_title" = "Nom d'utilisateur";
|
"settings_contacts_carddav_username_title" = "Nom d'utilisateur";
|
||||||
"settings_contacts_edit_carddav_server_title" = "Editer le carnet d'adresse CardDAV";
|
"settings_contacts_edit_carddav_server_title" = "Editer le carnet d'adresse CardDAV";
|
||||||
"settings_contacts_edit_ldap_server_title" = "Editer le serveur LDAP";
|
"settings_contacts_edit_ldap_server_title" = "Editer le serveur LDAP";
|
||||||
"settings_contacts_ldap_enabled_title" = "Activé";
|
|
||||||
"settings_contacts_ldap_server_url_title" = "URL du serveur (ne peut être vide)";
|
|
||||||
"settings_contacts_ldap_bind_dn_title" = "Bind DN";
|
"settings_contacts_ldap_bind_dn_title" = "Bind DN";
|
||||||
|
"settings_contacts_ldap_bind_user_password_title" = "Mot de passe de l'utilisateur Bind";
|
||||||
|
"settings_contacts_ldap_max_results_title" = "Nombre de résultats maximum";
|
||||||
"settings_contacts_ldap_password_title" = "Mot de passe";
|
"settings_contacts_ldap_password_title" = "Mot de passe";
|
||||||
"settings_contacts_ldap_use_tls_title" = "Utiliser TLS";
|
"settings_contacts_ldap_request_timeout_title" = "Délai d'attente de la requête";
|
||||||
"settings_contacts_ldap_search_base_title" = "Base de recherche (ne peut être vide)";
|
"settings_contacts_ldap_search_base_title" = "Base de recherche (ne peut être vide)";
|
||||||
"settings_contacts_ldap_search_filter_title" = "Filtre";
|
"settings_contacts_ldap_search_filter_title" = "Filtre";
|
||||||
"settings_contacts_ldap_max_results_title" = "Nombre de résultats maximum";
|
"settings_contacts_ldap_server_url_title" = "URL du serveur (ne peut être vide)";
|
||||||
"settings_contacts_ldap_request_timeout_title" = "Durée maximum (en secondes)";
|
"settings_contacts_ldap_use_tls_title" = "Utiliser TLS";
|
||||||
"settings_contacts_ldap_request_delay_title" = "Délai entre 2 requêtes (en millisecondes)";
|
|
||||||
"settings_contacts_ldap_min_characters_title" = "Nombre minimum de caractères pour lancer la requête";
|
|
||||||
"settings_contacts_ldap_name_attributes_title" = "Attributs de nom";
|
|
||||||
"settings_contacts_ldap_sip_attributes_title" = "Attributs SIP";
|
|
||||||
"settings_contacts_ldap_sip_domain_title" = "Domaine SIP";
|
|
||||||
"settings_contacts_ldap_error_toast" = "Une erreur s'est produite, la configuration LDAP n'a pas été sauvegardée !";
|
|
||||||
"settings_contacts_ldap_empty_server_error_toast" = "L'URL du serveur ne peut être vide";
|
|
||||||
"settings_contacts_title" = "Contacts";
|
"settings_contacts_title" = "Contacts";
|
||||||
"settings_conversations_auto_download_title" = "Télécharger automatiquement les fichiers";
|
"settings_conversations_auto_download_title" = "Télécharger automatiquement les fichiers";
|
||||||
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Marquer la conversation comme lue lorsqu'une notification de message est supprimée";
|
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Marquer la conversation comme lue lorsqu'une notification de message est supprimée";
|
||||||
|
|
@ -541,8 +510,6 @@
|
||||||
"uri_handler_config_success_toast" = "Configuration appliquée avec succè";
|
"uri_handler_config_success_toast" = "Configuration appliquée avec succè";
|
||||||
"username" = "Nom d’utilisateur";
|
"username" = "Nom d’utilisateur";
|
||||||
"username_error" = "Erreur dans le nom d'utilisateur";
|
"username_error" = "Erreur dans le nom d'utilisateur";
|
||||||
"Voicemail" = "Messagerie vocale";
|
|
||||||
"New message" = "Nouveau message";
|
|
||||||
"web_platform_forgotten_password_url" = "https://subscribe.linphone.org/";
|
"web_platform_forgotten_password_url" = "https://subscribe.linphone.org/";
|
||||||
"web_platform_register_email_url" = "https://subscribe.linphone.org/register/email";
|
"web_platform_register_email_url" = "https://subscribe.linphone.org/register/email";
|
||||||
"website_contact_url" = "https://linphone.org/contact";
|
"website_contact_url" = "https://linphone.org/contact";
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
"account_settings_dialog_invalid_password_hint" = "Jelszó";
|
|
||||||
"assistant_account_create" = "Létrehoz";
|
|
||||||
"assistant_account_creation_wrong_phone_number" = "Rossz telefonszám?";
|
|
||||||
"assistant_account_login" = "Bejelentkezés";
|
|
||||||
"assistant_account_login_forbidden_error" = "Hibás felhasználónév vagy jelszó";
|
|
||||||
"assistant_account_register" = "Regisztráció";
|
|
||||||
"assistant_account_register_push_notification_not_received_error" = "A push értesítés az azonosító tokennel nem érkezett meg 5mp-en belül, próbálja meg később";
|
|
||||||
"assistant_account_register_unexpected_error" = "Váratlan hiba történt, próbálja meg később";
|
|
||||||
"assistant_already_have_an_account" = "Már van fiókja?";
|
|
||||||
"assistant_create_account_using_email_on_our_web_platform" = "Hozzon létre egy fiókot az email–címével:";
|
|
||||||
"assistant_dialog_confirm_phone_number_title" = "Erősítse meg a telefonszámot";
|
|
||||||
"assistant_dialog_general_terms_and_privacy_policy_title" = "Általános feltételek és biztonsági szabályok";
|
|
||||||
"assistant_dialog_general_terms_label" = "általános feltételek";
|
|
||||||
"assistant_dialog_privacy_policy_label" = "biztonsági szabályok";
|
|
||||||
"assistant_login_third_party_sip_account" = "Harmadik féltől származó SIP fiók használata";
|
|
||||||
"assistant_no_account_yet" = "Nincs még fiókja?";
|
|
||||||
"assistant_permissions_grant_all_of_them" = "OK";
|
|
||||||
"assistant_permissions_skip_permissions" = "Később";
|
|
||||||
"assistant_permissions_title" = "Engedélyek biztosítása";
|
|
||||||
"assistant_qr_code_invalid_toast" = "Érvénytelen QR–kód!";
|
|
||||||
"assistant_scan_qr_code" = "QR–kód beolvasása";
|
|
||||||
"assistant_sip_account_transport_protocol" = "SIP átvitel";
|
|
||||||
"assistant_third_party_sip_account_warning_ok" = "Elfogadom";
|
|
||||||
"authentication_id" = "Azonosító ID (ha különbözik)";
|
|
||||||
"bottom_navigation_calls_label" = "Hívások";
|
|
||||||
"bottom_navigation_contacts_label" = "Névjegyek";
|
|
||||||
"bottom_navigation_conversations_label" = "Üzenetek";
|
|
||||||
"bottom_navigation_meetings_label" = "Meetingek";
|
|
||||||
"contact_call_action" = "Hívás";
|
|
||||||
"contact_details_delete" = "Törlés";
|
|
||||||
"conversation_action_call" = "Hívás";
|
|
||||||
"conversation_action_mark_as_read" = "Jelölés olvasottnak";
|
|
||||||
"conversation_ephemeral_messages_duration_disabled" = "Letiltva";
|
|
||||||
"dialog_accept" = "Elfogad";
|
|
||||||
"dialog_call" = "Hívás";
|
|
||||||
"dialog_cancel" = "Mégse";
|
|
||||||
"dialog_continue" = "Folytatás";
|
|
||||||
"dialog_deny" = "Elutasít";
|
|
||||||
"dialog_install" = "Telepítés";
|
|
||||||
"dialog_no" = "Nem";
|
|
||||||
"dialog_ok" = "OK";
|
|
||||||
"dialog_yes" = "Igen";
|
|
||||||
"drawer_menu_account_connection_status_cleared" = "Letiltva";
|
|
||||||
"drawer_menu_account_connection_status_connected" = "Csatlakoztatva";
|
|
||||||
"drawer_menu_account_connection_status_failed" = "Hiba";
|
|
||||||
"drawer_menu_account_connection_status_progress" = "Csatlakozás…";
|
|
||||||
"drawer_menu_add_account" = "Fiók hozzáadása";
|
|
||||||
"drawer_menu_manage_account" = "Profil kezelése";
|
|
||||||
"drawer_menu_no_account_configured_yet" = "Nincs még fiók konfigurálva";
|
|
||||||
"Error" = "Hiba";
|
|
||||||
"help_about_advanced_title" = "Haladó";
|
|
||||||
"help_about_version_title" = "Verzió";
|
|
||||||
"help_title" = "Segítség";
|
|
||||||
"help_troubleshooting_clear_native_friends_in_database" = "Importált névjegyek törlése a telefon névjegyzékéből";
|
|
||||||
"help_troubleshooting_show_config_file" = "Mutassa a konfigurációt";
|
|
||||||
"manage_account_device_remove" = "Eltávolít";
|
|
||||||
"meeting_waiting_room_cancel" = "Mégse";
|
|
||||||
"menu_delete_selected_item" = "Törlés";
|
|
||||||
"menu_reply_to_chat_message" = "Válasz";
|
|
||||||
"message_delivery_info_error_title" = "Hiba";
|
|
||||||
"next" = "Következő";
|
|
||||||
"notification_missed_call_title" = "Nem fogadott hívás";
|
|
||||||
"or" = "vagy";
|
|
||||||
"password" = "Jelszó";
|
|
||||||
"phone_number" = "Telefonszám";
|
|
||||||
"settings_advanced_device_id" = "Eszköz ID";
|
|
||||||
"settings_calls_title" = "Hívások";
|
|
||||||
"settings_contacts_carddav_name_title" = "Megjelenített név";
|
|
||||||
"settings_contacts_carddav_password_title" = "Jelszó";
|
|
||||||
"settings_contacts_carddav_username_title" = "Felhasználónév";
|
|
||||||
"settings_contacts_ldap_password_title" = "Jelszó";
|
|
||||||
"settings_contacts_title" = "Névjegyek";
|
|
||||||
"settings_conversations_title" = "Üzenetek";
|
|
||||||
"settings_meetings_title" = "Meetingek";
|
|
||||||
"settings_security_enable_vfs_title" = "Titkosítás";
|
|
||||||
"settings_security_title" = "Biztonság";
|
|
||||||
"settings_title" = "Beállítások";
|
|
||||||
"sip_address_copied_to_clipboard_toast" = "SIP cím a vágólapra másolva";
|
|
||||||
"sip_address_display_name" = "Megjelenített név";
|
|
||||||
"sip_address_domain" = "Domain cím";
|
|
||||||
"start" = "Indítás";
|
|
||||||
"uri_handler_config_success_toast" = "A konfiguráció sikeresen alkalmazva";
|
|
||||||
"username" = "Felhasználónév";
|
|
||||||
"welcome_page_2_title" = "Biztonságos";
|
|
||||||
"welcome_page_3_title" = "Nyílt forráskódú";
|
|
||||||
"welcome_page_title" = "Üdvözöljük";
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
|
|
||||||
|
|
@ -1,363 +0,0 @@
|
||||||
"dialog_yes" = "Ja";
|
|
||||||
"Error" = "Fout";
|
|
||||||
"menu_copy_chat_message" = "Kopiëren";
|
|
||||||
"account_settings_audio_video_conference_factory_uri_title" = "URI van de Audio/video conference factory";
|
|
||||||
"account_settings_avpf_title" = "AVPF";
|
|
||||||
"account_settings_bundle_mode_title" = "Bundelmodus";
|
|
||||||
"account_settings_ccmp_server_url_title" = "URL van de CCMP-server";
|
|
||||||
"account_settings_conference_factory_uri_title" = "Conference factory URI";
|
|
||||||
"account_settings_cpim_in_basic_conversations_title" = "Gebruik CPIM in 'basis' gesprekken";
|
|
||||||
"account_settings_dialog_invalid_password_title" = "Authenticatie vereist";
|
|
||||||
"account_settings_dialog_invalid_password_hint" = "Wachtwoord";
|
|
||||||
"account_settings_enable_ice_title" = "Schakel ICE in";
|
|
||||||
"account_settings_enable_turn_title" = "Schakel TURN in";
|
|
||||||
"account_settings_expire_title" = "Vervalt (in seconden)";
|
|
||||||
"account_settings_im_encryption_mandatory_title" = "IM-versleuteling verplicht";
|
|
||||||
"account_settings_lime_server_url_title" = "URL van de E2E-versleuteling sleutel server";
|
|
||||||
"account_settings_mwi_uri_title" = "MWI-server URI (Message Waiting Indicator)";
|
|
||||||
"account_settings_nat_policy_title" = "NAT-beleid instellingen";
|
|
||||||
"account_settings_outbound_proxy_title" = "Uitgaande proxy";
|
|
||||||
"account_settings_push_notification_not_available_title" = "Pushmeldingen zijn niet beschikbaar!";
|
|
||||||
"account_settings_push_notification_title" = "Sta pushmeldingen toe";
|
|
||||||
"account_settings_sip_proxy_url_title" = "URL van de SIP-proxyserver";
|
|
||||||
"account_settings_stun_server_url_title" = "URL van de STUN/TURN-server";
|
|
||||||
"account_settings_title" = "Accountinstellingen";
|
|
||||||
"account_settings_turn_password_title" = "TURN-wachtwoord";
|
|
||||||
"account_settings_turn_username_title" = "TURN-gebruikersnaam";
|
|
||||||
"account_settings_update_password_title" = "Wachtwoord bijwerken";
|
|
||||||
"account_settings_voicemail_uri_title" = "Voicemail-URI";
|
|
||||||
"assistant_account_create" = "Maken";
|
|
||||||
"assistant_account_creation_wrong_phone_number" = "Verkeerd nummer?";
|
|
||||||
"assistant_account_login" = "Login";
|
|
||||||
"assistant_account_login_forbidden_error" = "Verkeerde gebruikersnaam of wachtwoord";
|
|
||||||
"assistant_account_register" = "Registreren";
|
|
||||||
"assistant_account_register_push_notification_not_received_error" = "Pushmelding met autorisatietoken niet binnen 5 seconden ontvangen, probeer het later opnieuw";
|
|
||||||
"assistant_account_register_unexpected_error" = "Er is een onverwachte fout opgetreden, probeer het later opnieuw";
|
|
||||||
"assistant_already_have_an_account" = "Heeft u al een account?";
|
|
||||||
"assistant_create_account_using_email_on_our_web_platform" = "Maak een account aan met uw e-mailadres op:";
|
|
||||||
"assistant_dialog_confirm_phone_number_title" = "Bevestig telefoonnummer";
|
|
||||||
"assistant_dialog_general_terms_and_privacy_policy_title" = "Algemene voorwaarden & privacybeleid";
|
|
||||||
"assistant_dialog_general_terms_label" = "algemene voorwaarden";
|
|
||||||
"assistant_dialog_privacy_policy_label" = "privacy beleid";
|
|
||||||
"assistant_login_third_party_sip_account" = "Gebruik een SIP-account van derden";
|
|
||||||
"assistant_no_account_yet" = "Nog geen account?";
|
|
||||||
"assistant_permissions_grant_all_of_them" = "OK";
|
|
||||||
"assistant_permissions_skip_permissions" = "Doe het later";
|
|
||||||
"assistant_permissions_title" = "Verleen machtigingen";
|
|
||||||
"assistant_qr_code_invalid_toast" = "Ongeldige QR-code!";
|
|
||||||
"assistant_scan_qr_code" = "Scan de QR-code";
|
|
||||||
"assistant_sip_account_transport_protocol" = "Vervoer";
|
|
||||||
"assistant_third_party_sip_account_warning_ok" = "Ik begrijp";
|
|
||||||
"authentication_id" = "Authenticatie-ID (indien anders)";
|
|
||||||
"bottom_navigation_calls_label" = "Oproepen";
|
|
||||||
"bottom_navigation_contacts_label" = "Contacten";
|
|
||||||
"bottom_navigation_conversations_label" = "Gesprekken";
|
|
||||||
"bottom_navigation_meetings_label" = "Vergaderingen";
|
|
||||||
"call_action_blind_transfer" = "Doorverbinden";
|
|
||||||
"call_action_change_layout" = "Indeling";
|
|
||||||
"call_action_go_to_calls_list" = "Oproeplijst";
|
|
||||||
"call_action_hang_up" = "Oproep beëindigen";
|
|
||||||
"call_action_pause_call" = "Pauzeren";
|
|
||||||
"call_action_record_call" = "Opnemen";
|
|
||||||
"call_action_resume_call" = "Hervatten";
|
|
||||||
"call_action_show_dialer" = "Toetsenbord";
|
|
||||||
"call_action_show_messages" = "Berichten";
|
|
||||||
"call_action_start_new_call" = "Nieuwe oproep";
|
|
||||||
"call_audio_device_type_earpiece" = "Oortelefoon";
|
|
||||||
"call_audio_device_type_headphones" = "Koptelefoon";
|
|
||||||
"call_audio_device_type_speaker" = "Spreker";
|
|
||||||
"call_audio_incoming" = "Inkomende oproep";
|
|
||||||
"call_dialog_zrtp_security_alert_message" = "De vertrouwelijkheid van deze oproep kan in gevaar zijn!";
|
|
||||||
"call_dialog_zrtp_security_alert_title" = "Beveiligingswaarschuwing";
|
|
||||||
"call_dialog_zrtp_security_alert_try_again" = "Probeer het opnieuw";
|
|
||||||
"call_dialog_zrtp_validate_trust_letters_do_not_match" = "Niets komt overeen";
|
|
||||||
"call_dialog_zrtp_validate_trust_local_code_label" = "Uw code:";
|
|
||||||
"call_dialog_zrtp_validate_trust_remote_code_label" = "Code van gesprekspartner:";
|
|
||||||
"call_dialog_zrtp_validate_trust_title" = "Valideer het apparaat";
|
|
||||||
"call_do_zrtp_sas_validation_again" = "Valideer ZRTP SAS opnieuw";
|
|
||||||
"call_history_deleted_toast" = "Gespreksgeschiedenis is verwijderd";
|
|
||||||
"call_not_encrypted" = "Oproep is niet versleuteld";
|
|
||||||
"call_outgoing" = "Uitgaande oproep";
|
|
||||||
"call_srtp_point_to_point_encrypted" = "Punt-naar-punt versleuteld door SRTP";
|
|
||||||
"call_state_connected" = "Actief";
|
|
||||||
"call_state_paused" = "Gepauzeerd";
|
|
||||||
"call_state_paused_by_remote" = "Gepauzeerd door externe partij";
|
|
||||||
"call_state_resuming" = "Hervatten…";
|
|
||||||
"call_stats_audio_title" = "Audio";
|
|
||||||
"call_stats_media_encryption_title" = "Media-versleuteling";
|
|
||||||
"call_stats_video_title" = "Video";
|
|
||||||
"call_transfer_failed_toast" = "Doorverbinden van oproep mislukt!";
|
|
||||||
"call_transfer_in_progress_toast" = "Oproep wordt doorverbonden";
|
|
||||||
"call_transfer_successful_toast" = "Oproep is succesvol doorverbonden";
|
|
||||||
"call_waiting_for_encryption_info" = "Wachten op versleuteling…";
|
|
||||||
"call_zrtp_end_to_end_encrypted" = "Eind-tot-eind versleuteld door ZRTP";
|
|
||||||
"call_zrtp_sas_validation_required" = "Validatie vereist";
|
|
||||||
"calls_list_dialog_merge_into_conference_label" = "Groepsgesprek aanmaken";
|
|
||||||
"calls_list_dialog_merge_into_conference_title" = "Alle oproepen samenvoegen in een groepsgesprek ?";
|
|
||||||
"calls_list_title" = "Oproeplijst";
|
|
||||||
"conference_action_screen_sharing" = "Scherm delen";
|
|
||||||
"conference_action_show_participants" = "Deelnemers";
|
|
||||||
"conference_call_empty" = "Wachten op andere deelnemers…";
|
|
||||||
"conference_failed_to_create_group_call_toast" = "Groepsoproep aanmaken mislukt!";
|
|
||||||
"conference_layout_active_speaker" = "Spreker";
|
|
||||||
"conference_layout_audio_only" = "Alleen audio";
|
|
||||||
"conference_layout_grid" = "Mozaïek";
|
|
||||||
"conference_participant_joining_text" = "Deelnemen…";
|
|
||||||
"conference_participant_paused_text" = "Gepauzeerd";
|
|
||||||
"conference_share_link_title" = "Uitnodiging delen";
|
|
||||||
"connection_error_for_non_default_account" = "Account(s) verbindingsfout";
|
|
||||||
"contact_call_action" = "Telefoongesprek";
|
|
||||||
"contact_details_actions_title" = "Andere acties";
|
|
||||||
"contact_details_add_to_favourites" = "Toevoegen aan favorieten";
|
|
||||||
"contact_details_delete" = "Wissen";
|
|
||||||
"contact_details_edit" = "Bewerken";
|
|
||||||
"contact_details_remove_from_favourites" = "Verwijderen uit favorieten";
|
|
||||||
"contact_details_share" = "Delen";
|
|
||||||
"contact_dialog_delete_message" = "Dit contact wordt definitief verwijderd.";
|
|
||||||
"contact_dialog_pick_phone_number_or_sip_address_title" = "Kies een nummer of een SIP-adres";
|
|
||||||
"contact_edit_title" = "Bewerk contact";
|
|
||||||
"contact_editor_company" = "Bedrijf";
|
|
||||||
"contact_editor_dialog_abort_confirmation_message" = "Alle wijzigingen gaan verloren";
|
|
||||||
"contact_editor_dialog_abort_confirmation_title" = "Wijzigingen niet opslaan?";
|
|
||||||
"contact_editor_first_name" = "Voornaam";
|
|
||||||
"contact_editor_job_title" = "Functietitel";
|
|
||||||
"contact_editor_last_name" = "Achternaam";
|
|
||||||
"contact_message_action" = "Bericht";
|
|
||||||
"contact_new_title" = "Nieuw contact";
|
|
||||||
"contacts_list_all_contacts_title" = "Alle contacten";
|
|
||||||
"contacts_list_empty" = "Geen contact op dit moment…";
|
|
||||||
"contacts_list_favourites_title" = "Favorieten";
|
|
||||||
"contacts_list_filter_popup_see_all" = "Zie alles";
|
|
||||||
"conversation_action_call" = "Telefoongesprek";
|
|
||||||
"conversation_action_configure_ephemeral_messages" = "Configureer kortstondige berichten";
|
|
||||||
"conversation_action_delete" = "Gesprek verwijderen";
|
|
||||||
"conversation_action_leave_group" = "Groep verlaten";
|
|
||||||
"conversation_action_mark_as_read" = "Markeer als gelezen";
|
|
||||||
"conversation_action_mute" = "Dempen";
|
|
||||||
"conversation_action_unmute" = "Dempen uitschakelen";
|
|
||||||
"conversation_add_participants_title" = "Deelnemers toevoegen";
|
|
||||||
"conversation_dialog_edit_subject" = "Gespreksonderwerp bewerken";
|
|
||||||
"conversation_dialog_set_subject" = "Gespreksonderwerp instellen";
|
|
||||||
"conversation_dialog_subject_hint" = "Gespreksonderwerp";
|
|
||||||
"conversation_end_to_end_encrypted_event_title" = "Eind-tot-eind versleuteld gesprek";
|
|
||||||
"conversation_end_to_end_encrypted_event_subtitle" = "Berichten in dit gesprek zijn eind-tot-eind versleuteld. Alleen je gesprekspartner kan ze decoderen.";
|
|
||||||
"conversation_ephemeral_messages_duration_disabled" = "Uitgeschakeld";
|
|
||||||
"conversation_ephemeral_messages_duration_one_day" = "1 dag";
|
|
||||||
"conversation_ephemeral_messages_duration_one_hour" = "1 uur";
|
|
||||||
"conversation_ephemeral_messages_duration_one_minute" = "1 minuut";
|
|
||||||
"conversation_ephemeral_messages_duration_one_week" = "1 week";
|
|
||||||
"conversation_ephemeral_messages_duration_three_days" = "3 dagen";
|
|
||||||
"conversation_ephemeral_messages_subtitle" = "Nieuwe berichten worden automatisch verwijderd zodra ze door iedereen zijn gelezen.\nKies een duur:";
|
|
||||||
"conversation_ephemeral_messages_title" = "Kortstondige berichten";
|
|
||||||
"conversation_event_conference_created" = "Je bent bij de groep gekomen";
|
|
||||||
"conversation_event_conference_destroyed" = "Je hebt de groep verlaten";
|
|
||||||
"conversation_event_ephemeral_messages_disabled" = "Tijdelijke berichten zijn uitgeschakeld";
|
|
||||||
"conversation_event_ephemeral_messages_enabled" = "Tijdelijke berichten zijn ingeschakeld";
|
|
||||||
"conversation_failed_to_create_toast" = "Gesprek aanmaken mislukt!";
|
|
||||||
"conversation_forward_message_title" = "Bericht doorsturen naar…";
|
|
||||||
"conversation_info_add_participants_label" = "Deelnemers toevoegen";
|
|
||||||
"conversation_info_admin_menu_remove_participant" = "Verwijderen uit de groep";
|
|
||||||
"conversation_info_admin_menu_set_participant_admin" = "Beheerrechten toekennen";
|
|
||||||
"conversation_info_admin_menu_unset_participant_admin" = "Beheerrechten intrekken";
|
|
||||||
"conversation_info_confirm_start_group_call_dialog_message" = "Alle deelnemers ontvangen een oproep.";
|
|
||||||
"conversation_info_confirm_start_group_call_dialog_title" = "Groepsoproep starten?";
|
|
||||||
"conversation_info_delete_history_action" = "Geschiedenis verwijderen";
|
|
||||||
"conversation_info_menu_add_to_contacts" = "Toevoegen aan contacten";
|
|
||||||
"conversation_info_menu_go_to_contact" = "Bekijk contactprofiel";
|
|
||||||
"conversation_info_participant_is_admin_label" = "Beheerder";
|
|
||||||
"conversation_invalid_participant_due_to_security_mode_toast" = "Gesprek kan niet worden aangemaakt met een deelnemer die zich niet op hetzelfde domein bevindt vanwege beveiligingsbeperkingen!";
|
|
||||||
"conversation_menu_configure_ephemeral_messages" = "Kortstondige berichten";
|
|
||||||
"conversation_menu_go_to_info" = "Gesprek informatie";
|
|
||||||
"conversation_message_forward_cancelled_toast" = "Doorsturen van bericht is geannuleerd";
|
|
||||||
"conversation_message_forwarded_toast" = "Bericht is doorgestuurd";
|
|
||||||
"conversation_message_meeting_updated_label" = "Vergadering is bijgewerkt";
|
|
||||||
"conversation_text_field_hint" = "Zeg iets…";
|
|
||||||
"conversations_list_empty" = "Geen gesprek op dit moment…";
|
|
||||||
"conversation_take_picture_label" = "Foto maken";
|
|
||||||
"conversation_pick_file_from_gallery_label" = "Galerij openen";
|
|
||||||
"conversation_pick_any_file_label" = "Bestand kiezen";
|
|
||||||
"conversation_file_cant_be_opened_error_toast" = "Bestand kan niet worden geopend!";
|
|
||||||
"default_account_disabled" = "Geselecteerd account is momenteel uitgeschakeld";
|
|
||||||
"dialog_accept" = "Accepteren";
|
|
||||||
"dialog_call" = "Telefoongesprek";
|
|
||||||
"dialog_cancel" = "Annuleren";
|
|
||||||
"dialog_continue" = "Doorgaan";
|
|
||||||
"dialog_deny" = "Ontkennen";
|
|
||||||
"dialog_install" = "Installeren";
|
|
||||||
"dialog_no" = "Nee";
|
|
||||||
"dialog_ok" = "OK";
|
|
||||||
"drawer_menu_account_connection_status_cleared" = "Uitgeschakeld";
|
|
||||||
"drawer_menu_account_connection_status_connected" = "Verbonden";
|
|
||||||
"drawer_menu_account_connection_status_failed" = "Fout";
|
|
||||||
"drawer_menu_account_connection_status_progress" = "Verbinden…";
|
|
||||||
"drawer_menu_add_account" = "Voeg een account toe";
|
|
||||||
"drawer_menu_manage_account" = "Beheer het profiel";
|
|
||||||
"drawer_menu_no_account_configured_yet" = "Nog geen account geconfigureerd";
|
|
||||||
"generic_address_picker_suggestions_list_title" = "Suggesties";
|
|
||||||
"help_about_advanced_title" = "Geavanceerd";
|
|
||||||
"help_about_check_for_update" = "Controleer op updates";
|
|
||||||
"help_about_privacy_policy_title" = "Privacy beleid";
|
|
||||||
"help_about_version_title" = "Versie";
|
|
||||||
"help_dialog_update_available_title" = "Update beschikbaar";
|
|
||||||
"help_error_checking_version_toast_message" = "Er is een fout opgetreden tijdens het controleren op updates";
|
|
||||||
"help_quit_title" = "Afsluiten";
|
|
||||||
"help_title" = "Help";
|
|
||||||
"help_troubleshooting_app_version_title" = "App-versie";
|
|
||||||
"help_troubleshooting_clean_logs" = "Logboeken opschonen";
|
|
||||||
"help_troubleshooting_clear_native_friends_in_database" = "Geïmporteerde contacten uit het lokale adresboek verwijderen";
|
|
||||||
"help_troubleshooting_debug_logs_cleaned_toast_message" = "Debuglogs zijn opgeschoond";
|
|
||||||
"help_troubleshooting_debug_logs_upload_error_toast_message" = "Uploaden van debuglogs mislukt";
|
|
||||||
"help_troubleshooting_firebase_project_title" = "Firebase-project-ID";
|
|
||||||
"help_troubleshooting_print_logs_in_logcat" = "Logboeken afdrukken in logcat";
|
|
||||||
"help_troubleshooting_sdk_version_title" = "SDK-versie";
|
|
||||||
"help_troubleshooting_share_logs" = "Logs delen";
|
|
||||||
"help_troubleshooting_share_logs_dialog_title" = "Deel link naar foutopsporingslogs via…";
|
|
||||||
"help_troubleshooting_show_config_file" = "Configuratie weergeven";
|
|
||||||
"help_troubleshooting_title" = "Probleemoplossing";
|
|
||||||
"help_version_up_to_date_toast_message" = "Uw versie is up-to-date";
|
|
||||||
"history_call_start_create_group_call" = "Maak een groepsgesprek";
|
|
||||||
"history_call_start_search_bar_filter_hint" = "Zoek contact of gespreksgeschiedenis";
|
|
||||||
"history_call_start_title" = "Nieuwe oproep";
|
|
||||||
"history_dialog_delete_all_call_logs_message" = "Alle oproepen worden uit de geschiedenis verwijderd";
|
|
||||||
"history_dialog_delete_all_call_logs_title" = "Weet je zeker dat je alle gespreksgeschiedenis wilt verwijderen?";
|
|
||||||
"history_group_call_start_dialog_set_subject" = "Instellen onderwerp groepsoproep";
|
|
||||||
"history_group_call_start_dialog_subject_hint" = "Onderwerp groepsoproep";
|
|
||||||
"history_list_empty_history" = "Geen oproep op dit moment…";
|
|
||||||
"Interoperable mode" = "Interoperabele modus";
|
|
||||||
"list_filter_no_result_found" = "Geen resultaat gevonden…";
|
|
||||||
"manage_account_add_picture" = "Voeg een foto toe";
|
|
||||||
"manage_account_delete" = "Uitloggen";
|
|
||||||
"manage_account_details_title" = "Details";
|
|
||||||
"manage_account_device_last_connection" = "Laatste verbinding:";
|
|
||||||
"manage_account_device_remove" = "Weghalen";
|
|
||||||
"manage_account_devices_title" = "Apparaten";
|
|
||||||
"manage_account_dialog_remove_account_message" = "Als je je account permanent wilt verwijderen, ga naar: https://sip.linphone.org";
|
|
||||||
"manage_account_dialog_remove_account_title" = "Uitloggen uit je account?";
|
|
||||||
"manage_account_edit_picture" = "Bewerk foto";
|
|
||||||
"manage_account_international_prefix" = "Internationale prefix";
|
|
||||||
"manage_account_no_device" = "Geen apparaat gevonden…";
|
|
||||||
"manage_account_remove_picture" = "Verwijder foto";
|
|
||||||
"manage_account_settings" = "Accountinstellingen";
|
|
||||||
"manage_account_status_cleared_summary" = "Account is uitgeschakeld, je ontvangt geen oproepen of berichten.";
|
|
||||||
"manage_account_status_connected_summary" = "Dit account is online, iedereen kan je bellen.";
|
|
||||||
"manage_account_status_failed_summary" = "Verbinding met account mislukt, controleer je instellingen.";
|
|
||||||
"manage_account_status_progress_summary" = "Account is verbinding aan het maken met de server, even geduld aub…";
|
|
||||||
"manage_account_title" = "Account beheren";
|
|
||||||
"meeting_failed_to_schedule_toast" = "Vergadering inplannen mislukt!";
|
|
||||||
"meeting_failed_to_send_invites_toast" = "Het verzenden van alle uitnodigingen voor de vergadering is mislukt!";
|
|
||||||
"meeting_failed_to_send_part_of_invites_toast" = "Het verzenden van uitnodigingen naar sommige deelnemers van de vergadering is mislukt!";
|
|
||||||
"meeting_info_cancelled_toast" = "Vergadering is geannuleerd";
|
|
||||||
"meeting_info_created_toast" = "Vergadering is aangemaakt";
|
|
||||||
"meeting_info_delete" = "Vergadering verwijderen";
|
|
||||||
"meeting_info_deleted_toast" = "Vergadering is verwijderd";
|
|
||||||
"meeting_info_export_as_calendar_event" = "Agenda-item aanmaken";
|
|
||||||
"meeting_info_join_title" = "Sluit nu aan in de vergadering";
|
|
||||||
"meeting_info_organizer_label" = "Organisator";
|
|
||||||
"meeting_info_updated_toast" = "Vergadering is bijgewerkt";
|
|
||||||
"meeting_schedule_add_participants_title" = "Deelnemers toevoegen";
|
|
||||||
"meeting_schedule_cancel_dialog_message" = "Wil je de vergadering annuleren en een melding naar alle deelnemers sturen?";
|
|
||||||
"meeting_schedule_cancel_dialog_title" = "Vergadering annuleren?";
|
|
||||||
"meeting_schedule_description_hint" = "Beschrijving toevoegen";
|
|
||||||
"meeting_schedule_description_title" = "Beschrijving";
|
|
||||||
"meeting_schedule_edit_title" = "Vergadering bewerken";
|
|
||||||
"meeting_schedule_meeting_label" = "Vergadering";
|
|
||||||
"meeting_schedule_pick_end_time_title" = "Kies de eindtijd";
|
|
||||||
"meeting_schedule_pick_start_date_title" = "Kies de startdatum";
|
|
||||||
"meeting_schedule_pick_start_time_title" = "Kies de starttijd";
|
|
||||||
"meeting_schedule_send_invitations_title" = "Stuur uitnodiging naar deelnemers";
|
|
||||||
"meeting_schedule_subject_hint" = "Titel toevoegen…";
|
|
||||||
"meeting_schedule_timezone_title" = "Tijdzone";
|
|
||||||
"meeting_schedule_title" = "Nieuwe vergadering";
|
|
||||||
"meeting_waiting_room_cancel" = "Annuleren";
|
|
||||||
"meeting_waiting_room_join" = "Deelnemen";
|
|
||||||
"meeting_waiting_room_joining_title" = "Verbinding wordt tot stand gebracht";
|
|
||||||
"meetings_list_no_meeting_for_today" = "Er is geen vergadering gepland voor vandaag";
|
|
||||||
"menu_add_address_to_contacts" = "Toevoegen aan contacten";
|
|
||||||
"menu_copy_phone_number" = "Telefoonnummer kopiëren";
|
|
||||||
"menu_delete_history" = "Geschiedenis verwijderen";
|
|
||||||
"menu_delete_selected_item" = "Wissen";
|
|
||||||
"menu_forward_chat_message" = "Doorsturen";
|
|
||||||
"menu_invite" = "Uitnodigen";
|
|
||||||
"menu_reply_to_chat_message" = "Antwoord";
|
|
||||||
"menu_resend_chat_message" = "Opnieuw verzenden";
|
|
||||||
"menu_see_existing_contact" = "Bekijk contact";
|
|
||||||
"menu_show_imdn" = "Bezorgstatus";
|
|
||||||
"message_delivery_info_error_title" = "Fout";
|
|
||||||
"message_forwarded_label" = "Doorgestuurd";
|
|
||||||
"message_reaction_click_to_remove_label" = "Klik om te verwijderen";
|
|
||||||
"network_not_reachable" = "Je bent niet verbonden met internet";
|
|
||||||
"new_conversation_create_group" = "Maak een groepsgesprek";
|
|
||||||
"new_conversation_search_bar_filter_hint" = "Zoek contact";
|
|
||||||
"new_conversation_title" = "Nieuw gesprek";
|
|
||||||
"next" = "Volgende";
|
|
||||||
"notification_missed_call_title" = "Gemiste oproep";
|
|
||||||
"operation_in_progress_overlay" = "Bezig met bewerking, even geduld alstublieft";
|
|
||||||
"or" = "of";
|
|
||||||
"password" = "Wachtwoord";
|
|
||||||
"phone_number" = "Telefoonnummer";
|
|
||||||
"recordings_title" = "Opnames";
|
|
||||||
"settings_advanced_accept_early_media_title" = "Accepteer vroege media";
|
|
||||||
"settings_advanced_allow_outgoing_early_media_title" = "Sta uitgaande vroege media toe";
|
|
||||||
"settings_advanced_audio_codecs_title" = "Audiocodecs";
|
|
||||||
"settings_advanced_audio_devices_title" = "Audioapparaten";
|
|
||||||
"settings_advanced_device_id" = "Apparaat-ID";
|
|
||||||
"settings_advanced_device_id_hint" = "Alleen alfanumerieke tekens";
|
|
||||||
"settings_advanced_download_apply_remote_provisioning" = "Downloaden en toepassen";
|
|
||||||
"settings_advanced_input_audio_device_title" = "Standaard invoer audioapparaat";
|
|
||||||
"settings_advanced_media_encryption_mandatory_title" = "Media-versleuteling verplicht";
|
|
||||||
"settings_advanced_output_audio_device_title" = "Standaard uitvoer audioapparaat";
|
|
||||||
"settings_advanced_remote_provisioning_url" = "URL voor externe provisioning";
|
|
||||||
"settings_advanced_title" = "Geavanceerde instellingen";
|
|
||||||
"settings_advanced_upload_server_url" = "URL van de bestanddelingsserver";
|
|
||||||
"settings_advanced_video_codecs_title" = "Video-codecs";
|
|
||||||
"settings_calls_adaptive_rate_control_title" = "Adaptieve bitrate-regeling";
|
|
||||||
"settings_calls_auto_record_title" = "Oproepen automatisch opnemen";
|
|
||||||
"settings_calls_calibrate_echo_canceller_done_no_echo" = "Geen echo";
|
|
||||||
"settings_calls_calibrate_echo_canceller_failed" = "Mislukt";
|
|
||||||
"settings_calls_calibrate_echo_canceller_in_progress" = "In behandeling";
|
|
||||||
"settings_calls_calibrate_echo_canceller_title" = "Echo-onderdrukking kalibreren";
|
|
||||||
"settings_calls_change_ringtone_title" = "Beltoon wijzigen";
|
|
||||||
"settings_calls_echo_canceller_subtitle" = "Voorkomt echo aan de andere kant van de lijn als er geen hardwarematige echo-onderdrukking beschikbaar is";
|
|
||||||
"settings_calls_echo_canceller_title" = "Softwarematige echo-onderdrukking gebruiken";
|
|
||||||
"settings_calls_enable_fec_title" = "Video-FEC inschakelen";
|
|
||||||
"settings_calls_enable_video_title" = "Video inschakelen";
|
|
||||||
"settings_calls_title" = "Oproepen";
|
|
||||||
"settings_calls_vibrate_while_ringing_title" = "Trillen tijdens inkomend gesprek";
|
|
||||||
"settings_contacts_add_carddav_server_title" = "Voeg CardDAV-adresboek toe";
|
|
||||||
"settings_contacts_add_ldap_server_title" = "LDAP-server toevoegen";
|
|
||||||
"settings_contacts_carddav_name_title" = "Weergavenaam";
|
|
||||||
"settings_contacts_carddav_password_title" = "Wachtwoord";
|
|
||||||
"settings_contacts_carddav_server_url_title" = "Server-URL";
|
|
||||||
"settings_contacts_carddav_sync_error_toast" = "Synchronisatiefout!";
|
|
||||||
"settings_contacts_carddav_username_title" = "Gebruikersnaam";
|
|
||||||
"settings_contacts_edit_carddav_server_title" = "Bewerk CardDAV-adresboek";
|
|
||||||
"settings_contacts_edit_ldap_server_title" = "Bewerk LDAP-server";
|
|
||||||
"settings_contacts_ldap_bind_dn_title" = "Bind-DN";
|
|
||||||
"settings_contacts_ldap_password_title" = "Wachtwoord";
|
|
||||||
"settings_contacts_ldap_server_url_title" = "Server-URL (mag niet leeg zijn)";
|
|
||||||
"settings_contacts_ldap_use_tls_title" = "TLS gebruiken";
|
|
||||||
"settings_contacts_title" = "Contacten";
|
|
||||||
"settings_conversations_auto_download_title" = "Bestanden automatisch downloaden";
|
|
||||||
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Gesprek als gelezen markeren bij het sluiten van berichtmelding";
|
|
||||||
"settings_conversations_title" = "Gesprekken";
|
|
||||||
"settings_meetings_default_layout_title" = "Standaardindeling";
|
|
||||||
"settings_meetings_layout_active_speaker_label" = "Actieve spreker";
|
|
||||||
"settings_meetings_layout_mosaic_label" = "Mozaïek";
|
|
||||||
"settings_meetings_title" = "Vergaderingen";
|
|
||||||
"settings_network_allow_ipv6" = "Sta IPv6 toe";
|
|
||||||
"settings_network_title" = "Netwerk";
|
|
||||||
"settings_network_use_wifi_only" = "Gebruik alleen Wi-Fi-netwerken";
|
|
||||||
"settings_security_enable_vfs_subtitle" = "Waarschuwing: eenmaal ingeschakeld kan dit niet meer worden uitgeschakeld!";
|
|
||||||
"settings_security_enable_vfs_title" = "Versleutel alles";
|
|
||||||
"settings_security_prevent_screenshots_title" = "Voorkom dat de interface wordt opgenomen";
|
|
||||||
"settings_security_title" = "Beveiliging";
|
|
||||||
"settings_title" = "Instellingen";
|
|
||||||
"sip_address_copied_to_clipboard_toast" = "SIP-adres gekopieerd naar klembord";
|
|
||||||
"sip_address_display_name" = "Weergavenaam";
|
|
||||||
"sip_address_domain" = "Domein";
|
|
||||||
"start" = "Begin";
|
|
||||||
"uri_handler_config_success_toast" = "Configuratie succesvol toegepast";
|
|
||||||
"username" = "Gebruikersnaam";
|
|
||||||
"welcome_page_2_title" = "Beveiligd";
|
|
||||||
"welcome_page_3_title" = "Open-source";
|
|
||||||
"welcome_page_title" = "Welkom";
|
|
||||||
|
|
@ -1,525 +0,0 @@
|
||||||
"assistant_account_create" = "Criar";
|
|
||||||
"assistant_account_creation_wrong_phone_number" = "Número errado?";
|
|
||||||
"assistant_account_login" = "Login";
|
|
||||||
"assistant_account_login_forbidden_error" = "Nome de usuário ou senha incorretos";
|
|
||||||
"assistant_account_register" = "Registrar";
|
|
||||||
"assistant_account_register_push_notification_not_received_error" = "Notificação push com token de autenticação não recebida em 5 segundos, por favor, tente novamente mais tarde";
|
|
||||||
"assistant_account_register_unexpected_error" = "Ocorreu um erro inesperado, por favor, tente novamente mais tarde";
|
|
||||||
"conversation_ephemeral_messages_duration_disabled" = "Desativado";
|
|
||||||
"conversation_invalid_participant_due_to_security_mode_toast" = "Não é possível criar conversa com um participante que não está no mesmo domínio devido a restrições de segurança!";
|
|
||||||
"conversation_menu_configure_ephemeral_messages" = "Mensagens efêmeras";
|
|
||||||
"conversation_menu_go_to_info" = "Informações da conversa";
|
|
||||||
"conversation_message_forward_cancelled_toast" = "Encaminhamento de mensagem cancelado";
|
|
||||||
"conversations_list_empty" = "Nenhuma conversa no momento…";
|
|
||||||
"default_account_disabled" = "A conta selecionada está atualmente desativada";
|
|
||||||
"dialog_accept" = "Aceitar";
|
|
||||||
"dialog_call" = "Chamar";
|
|
||||||
"dialog_cancel" = "Cancelar";
|
|
||||||
"dialog_continue" = "Continuar";
|
|
||||||
"dialog_deny" = "Recusar";
|
|
||||||
"dialog_install" = "Instalar";
|
|
||||||
"Error" = "Erro";
|
|
||||||
"help_troubleshooting_clean_logs" = "Limpar logs";
|
|
||||||
"help_troubleshooting_clear_native_friends_in_database" = "Limpar contatos importados da agenda nativa";
|
|
||||||
"help_troubleshooting_debug_logs_cleaned_toast_message" = "Logs de depuração foram limpos";
|
|
||||||
"help_troubleshooting_debug_logs_upload_error_toast_message" = "Falha ao enviar logs de depuração";
|
|
||||||
"help_troubleshooting_firebase_project_title" = "ID do projeto Firebase";
|
|
||||||
"help_troubleshooting_print_logs_in_logcat" = "Imprimir logs no logcat";
|
|
||||||
"help_troubleshooting_sdk_version_title" = "Versão do SDK";
|
|
||||||
"help_troubleshooting_share_logs" = "Compartilhar logs";
|
|
||||||
"help_troubleshooting_share_logs_dialog_title" = "Compartilhar link de logs de depuração usando…";
|
|
||||||
"help_troubleshooting_show_config_file" = "Mostrar configuração";
|
|
||||||
"help_troubleshooting_title" = "Solução de problemas";
|
|
||||||
"help_version_up_to_date_toast_message" = "Sua versão está atualizada";
|
|
||||||
"history_call_start_create_group_call" = "Criar uma chamada em grupo";
|
|
||||||
"history_call_start_search_bar_filter_hint" = "Pesquisar contato ou histórico de chamadas";
|
|
||||||
"history_call_start_title" = "Nova chamada";
|
|
||||||
"history_dialog_delete_all_call_logs_message" = "Todas as chamadas serão removidas do histórico";
|
|
||||||
"history_dialog_delete_all_call_logs_title" = "Você realmente quer excluir todo o histórico de chamadas?";
|
|
||||||
"history_group_call_start_dialog_set_subject" = "Definir assunto da chamada em grupo";
|
|
||||||
"history_group_call_start_dialog_subject_hint" = "Assunto da chamada em grupo";
|
|
||||||
"history_list_empty_history" = "Nenhuma chamada no momento…";
|
|
||||||
"Interoperable mode" = "Modo interoperável";
|
|
||||||
"list_filter_no_result_found" = "Nenhum resultado encontrado…";
|
|
||||||
"manage_account_no_device" = "Nenhum dispositivo encontrado…";
|
|
||||||
"manage_account_remove_picture" = "Remover foto";
|
|
||||||
"manage_account_settings" = "Configurações da conta";
|
|
||||||
"manage_account_status_cleared_summary" = "A conta foi desativada, você não receberá nenhuma chamada ou mensagem.";
|
|
||||||
"manage_account_status_connected_summary" = "Esta conta está online, todos podem ligar para você.";
|
|
||||||
"manage_account_status_failed_summary" = "Falha na conexão da conta, verifique suas configurações.";
|
|
||||||
"meeting_failed_to_send_invites_toast" = "Falha ao enviar todos os convites para a reunião!";
|
|
||||||
"meeting_failed_to_send_part_of_invites_toast" = "Falha ao enviar convites para alguns participantes da reunião!";
|
|
||||||
"meeting_info_cancelled_toast" = "A reunião foi cancelada";
|
|
||||||
"meeting_info_created_toast" = "A reunião foi criada";
|
|
||||||
"meeting_info_delete" = "Excluir reunião";
|
|
||||||
"meeting_info_organizer_label" = "Organizador";
|
|
||||||
"meeting_info_updated_toast" = "A reunião foi atualizada";
|
|
||||||
"meeting_schedule_add_participants_title" = "Adicionar participantes";
|
|
||||||
"meeting_schedule_cancel_dialog_message" = "Você quer cancelar a reunião e enviar uma notificação para todos os participantes?";
|
|
||||||
"meeting_schedule_cancel_dialog_title" = "Cancelar a reunião?";
|
|
||||||
"meeting_schedule_description_hint" = "Adicionar descrição";
|
|
||||||
"meeting_schedule_description_title" = "Descrição";
|
|
||||||
"meeting_waiting_room_join" = "Entrar";
|
|
||||||
"meeting_waiting_room_joining_title" = "Conexão em andamento";
|
|
||||||
"meetings_list_no_meeting_for_today" = "Nenhuma reunião agendada para hoje";
|
|
||||||
"menu_add_address_to_contacts" = "Adicionar aos contatos";
|
|
||||||
"menu_copy_chat_message" = "Copiar";
|
|
||||||
"menu_copy_phone_number" = "Copiar número de telefone";
|
|
||||||
"menu_delete_history" = "Excluir histórico";
|
|
||||||
"menu_delete_selected_item" = "Excluir";
|
|
||||||
"menu_forward_chat_message" = "Encaminhar";
|
|
||||||
"menu_invite" = "Convidar";
|
|
||||||
"menu_reply_to_chat_message" = "Responder";
|
|
||||||
"password" = "Senha";
|
|
||||||
"settings_advanced_device_id_hint" = "Apenas caracteres alfanuméricos";
|
|
||||||
"settings_advanced_download_apply_remote_provisioning" = "Baixar e aplicar";
|
|
||||||
"settings_advanced_input_audio_device_title" = "Dispositivo de entrada de áudio padrão";
|
|
||||||
"settings_contacts_carddav_name_title" = "Nome de exibição";
|
|
||||||
"settings_contacts_carddav_password_title" = "Senha";
|
|
||||||
"settings_contacts_carddav_server_url_title" = "URL do servidor";
|
|
||||||
"settings_contacts_carddav_sync_error_toast" = "Erro na sincronização!";
|
|
||||||
"settings_contacts_carddav_username_title" = "Nome de usuário";
|
|
||||||
"settings_contacts_edit_carddav_server_title" = "Editar agenda CardDAV";
|
|
||||||
"settings_contacts_edit_ldap_server_title" = "Editar servidor LDAP";
|
|
||||||
"settings_contacts_ldap_bind_dn_title" = "Bind DN";
|
|
||||||
"settings_contacts_ldap_password_title" = "Senha";
|
|
||||||
"settings_meetings_layout_mosaic_label" = "Mosaico";
|
|
||||||
"welcome_page_2_title" = "Seguro";
|
|
||||||
"welcome_page_title" = "Bem vindo";
|
|
||||||
"account_settings_audio_video_conference_factory_uri_title" = "URI da fábrica de conferências de áudio/vídeo";
|
|
||||||
"account_settings_avpf_title" = "AVPF";
|
|
||||||
"account_settings_bundle_mode_title" = "Modo bundle";
|
|
||||||
"account_settings_ccmp_server_url_title" = "URL do servidor CCMP";
|
|
||||||
"account_settings_conference_factory_uri_title" = "URI da fábrica de conferências";
|
|
||||||
"account_settings_cpim_in_basic_conversations_title" = "Usar CPIM em conversas básicas";
|
|
||||||
"account_settings_dialog_invalid_password_title" = "Autenticação necessária";
|
|
||||||
"account_settings_dialog_invalid_password_hint" = "Senha";
|
|
||||||
"account_settings_enable_ice_title" = "Ativar ICE";
|
|
||||||
"account_settings_enable_turn_title" = "Ativar TURN";
|
|
||||||
"account_settings_expire_title" = "Expira (em segundos)";
|
|
||||||
"account_settings_im_encryption_mandatory_title" = "Criptografia de IM obrigatória";
|
|
||||||
"account_settings_lime_server_url_title" = "URL do servidor de chaves de criptografia E2E";
|
|
||||||
"account_settings_mwi_uri_title" = "URI do servidor MWI (Indicador de Mensagem em Espera)";
|
|
||||||
"account_settings_nat_policy_title" = "Configurações da política NAT";
|
|
||||||
"account_settings_outbound_proxy_title" = "Proxy de saída";
|
|
||||||
"account_settings_push_notification_not_available_title" = "As notificações push não estão disponíveis!";
|
|
||||||
"account_settings_push_notification_title" = "Permitir notificações push";
|
|
||||||
"account_settings_sip_proxy_url_title" = "URL do servidor proxy SIP";
|
|
||||||
"account_settings_stun_server_url_title" = "URL do servidor STUN/TURN";
|
|
||||||
"account_settings_title" = "Configurações da conta";
|
|
||||||
"account_settings_turn_password_title" = "Senha TURN";
|
|
||||||
"account_settings_turn_username_title" = "Nome de usuário TURN";
|
|
||||||
"account_settings_update_password_title" = "Atualizar senha";
|
|
||||||
"account_settings_voicemail_uri_title" = "URI do correio de voz";
|
|
||||||
"assistant_already_have_an_account" = "Já tem uma conta?";
|
|
||||||
"assistant_create_account_using_email_on_our_web_platform" = "Crie uma conta com seu e-mail em:";
|
|
||||||
"assistant_dialog_confirm_phone_number_title" = "Confirmar número de telefone";
|
|
||||||
"assistant_dialog_general_terms_and_privacy_policy_title" = "Termos gerais e política de privacidade";
|
|
||||||
"assistant_dialog_general_terms_label" = "termos gerais";
|
|
||||||
"assistant_dialog_privacy_policy_label" = "política de privacidade";
|
|
||||||
"assistant_login_third_party_sip_account" = "Usar uma conta SIP de terceiros";
|
|
||||||
"assistant_no_account_yet" = "Ainda não tem uma conta?";
|
|
||||||
"assistant_permissions_grant_all_of_them" = "OK";
|
|
||||||
"assistant_permissions_skip_permissions" = "Fazer isso mais tarde";
|
|
||||||
"assistant_permissions_title" = "Conceder permissões";
|
|
||||||
"assistant_qr_code_invalid_toast" = "QR code inválido!";
|
|
||||||
"assistant_scan_qr_code" = "Escanear QR code";
|
|
||||||
"assistant_sip_account_transport_protocol" = "Transporte";
|
|
||||||
"assistant_third_party_sip_account_warning_ok" = "Eu entendo";
|
|
||||||
"authentication_id" = "ID de autenticação (se diferente)";
|
|
||||||
"bottom_navigation_calls_label" = "Chamadas";
|
|
||||||
"bottom_navigation_contacts_label" = "Contatos";
|
|
||||||
"bottom_navigation_conversations_label" = "Conversas";
|
|
||||||
"bottom_navigation_meetings_label" = "Reuniões";
|
|
||||||
"call_action_blind_transfer" = "Transferir";
|
|
||||||
"call_action_change_layout" = "Layout";
|
|
||||||
"call_action_go_to_calls_list" = "Lista de chamadas";
|
|
||||||
"call_action_hang_up" = "Desligar";
|
|
||||||
"call_action_pause_call" = "Pausar";
|
|
||||||
"call_action_record_call" = "Gravar";
|
|
||||||
"call_action_resume_call" = "Retomar";
|
|
||||||
"call_action_show_dialer" = "Teclado";
|
|
||||||
"call_action_show_messages" = "Mensagens";
|
|
||||||
"call_action_start_new_call" = "Nova chamada";
|
|
||||||
"call_audio_device_type_earpiece" = "Fone de ouvido (auricular)";
|
|
||||||
"call_audio_device_type_headphones" = "Fones de ouvido";
|
|
||||||
"call_audio_device_type_speaker" = "Alto-falante";
|
|
||||||
"call_audio_incoming" = "Chamada recebida";
|
|
||||||
"call_dialog_zrtp_security_alert_message" = "A confidencialidade desta chamada pode estar comprometida!";
|
|
||||||
"call_dialog_zrtp_security_alert_title" = "Alerta de segurança";
|
|
||||||
"call_dialog_zrtp_security_alert_try_again" = "Tente novamente";
|
|
||||||
"call_dialog_zrtp_validate_trust_letters_do_not_match" = "Nada corresponde";
|
|
||||||
"call_dialog_zrtp_validate_trust_local_code_label" = "Seu código:";
|
|
||||||
"call_dialog_zrtp_validate_trust_remote_code_label" = "Código do correspondente:";
|
|
||||||
"call_dialog_zrtp_validate_trust_title" = "Valide o dispositivo";
|
|
||||||
"call_do_zrtp_sas_validation_again" = "Validar ZRTP SAS novamente";
|
|
||||||
"call_history_deleted_toast" = "O histórico foi excluído";
|
|
||||||
"call_not_encrypted" = "A chamada não está criptografada";
|
|
||||||
"call_outgoing" = "Chamada efetuada";
|
|
||||||
"call_srtp_point_to_point_encrypted" = "Criptografada ponto a ponto por SRTP";
|
|
||||||
"call_state_connected" = "Ativa";
|
|
||||||
"call_state_paused" = "Pausada";
|
|
||||||
"call_state_paused_by_remote" = "Pausada pelo remoto";
|
|
||||||
"call_state_resuming" = "Retomando…";
|
|
||||||
"call_stats_audio_title" = "Áudio";
|
|
||||||
"call_stats_media_encryption_title" = "Criptografia de mídia";
|
|
||||||
"call_stats_video_title" = "Vídeo";
|
|
||||||
"call_transfer_failed_toast" = "Falha na transferência da chamada!";
|
|
||||||
"call_transfer_in_progress_toast" = "A chamada está sendo transferida";
|
|
||||||
"call_transfer_successful_toast" = "A chamada foi transferida com sucesso";
|
|
||||||
"call_waiting_for_encryption_info" = "Aguardando criptografia…";
|
|
||||||
"call_zrtp_end_to_end_encrypted" = "Criptografada de ponta a ponta por ZRTP";
|
|
||||||
"call_zrtp_sas_validation_required" = "Validação necessária";
|
|
||||||
"calls_list_dialog_merge_into_conference_label" = "Criar conferência";
|
|
||||||
"calls_list_dialog_merge_into_conference_title" = "Mesclar todas as chamadas em conferência?";
|
|
||||||
"conference_layout_audio_only" = "Somente áudio";
|
|
||||||
"conference_layout_grid" = "Mosaico";
|
|
||||||
"conference_participant_joining_text" = "Entrando…";
|
|
||||||
"conference_participant_paused_text" = "Pausado";
|
|
||||||
"calls_list_title" = "Lista de chamadas";
|
|
||||||
"conference_action_screen_sharing" = "Compartilhar tela";
|
|
||||||
"conference_action_show_participants" = "Participantes";
|
|
||||||
"conference_call_empty" = "Aguardando outros participantes…";
|
|
||||||
"conference_failed_to_create_group_call_toast" = "Falha ao criar chamada em grupo!";
|
|
||||||
"conference_layout_active_speaker" = "Alto-falante";
|
|
||||||
"conference_share_link_title" = "Compartilhar convite";
|
|
||||||
"connection_error_for_non_default_account" = "Erro de conexão da(s) conta(s)";
|
|
||||||
"contact_call_action" = "Chamar";
|
|
||||||
"contact_details_actions_title" = "Outras ações";
|
|
||||||
"contact_details_add_to_favourites" = "Adicionar aos favoritos";
|
|
||||||
"contact_details_delete" = "Excluir";
|
|
||||||
"contact_details_edit" = "Editar";
|
|
||||||
"contact_details_remove_from_favourites" = "Remover dos favoritos";
|
|
||||||
"contact_details_share" = "Compartilhar";
|
|
||||||
"contact_dialog_delete_message" = "Este contato será removido definitivamente.";
|
|
||||||
"contact_dialog_pick_phone_number_or_sip_address_title" = "Escolha um número ou um endereço SIP";
|
|
||||||
"contact_edit_title" = "Editar contato";
|
|
||||||
"contact_editor_company" = "Empresa";
|
|
||||||
"contact_editor_dialog_abort_confirmation_message" = "Todas as alterações serão perdidas";
|
|
||||||
"contact_editor_dialog_abort_confirmation_title" = "Não salvar alterações?";
|
|
||||||
"contact_editor_first_name" = "Nome";
|
|
||||||
"contact_editor_job_title" = "Cargo";
|
|
||||||
"contact_editor_last_name" = "Sobrenome";
|
|
||||||
"contact_message_action" = "Mensagem";
|
|
||||||
"contact_new_title" = "Novo contato";
|
|
||||||
"contacts_list_all_contacts_title" = "Todos os contatos";
|
|
||||||
"contacts_list_empty" = "Nenhum contato no momento…";
|
|
||||||
"contacts_list_favourites_title" = "Favoritos";
|
|
||||||
"contacts_list_filter_popup_see_all" = "Ver todos";
|
|
||||||
"conversation_action_call" = "Chamar";
|
|
||||||
"conversation_action_configure_ephemeral_messages" = "Configurar mensagens efêmeras";
|
|
||||||
"conversation_action_delete" = "Excluir conversa";
|
|
||||||
"conversation_action_leave_group" = "Sair do grupo";
|
|
||||||
"conversation_action_mark_as_read" = "Marcar como lida";
|
|
||||||
"conversation_action_mute" = "Silenciar";
|
|
||||||
"conversation_action_unmute" = "Reativar som";
|
|
||||||
"conversation_add_participants_title" = "Adicionar participantes";
|
|
||||||
"conversation_dialog_edit_subject" = "Editar assunto da conversa";
|
|
||||||
"conversation_dialog_set_subject" = "Definir assunto da conversa";
|
|
||||||
"conversation_dialog_subject_hint" = "Assunto da conversa";
|
|
||||||
"conversation_end_to_end_encrypted_event_title" = "Conversa com criptografia de ponta a ponta";
|
|
||||||
"conversation_end_to_end_encrypted_event_subtitle" = "As mensagens nesta conversa são criptografadas de ponta a ponta. Apenas seu correspondente pode descriptografá-las.";
|
|
||||||
"conversation_ephemeral_messages_duration_one_day" = "1 dia";
|
|
||||||
"conversation_ephemeral_messages_duration_one_hour" = "1 hora";
|
|
||||||
"conversation_ephemeral_messages_duration_one_minute" = "1 minuto";
|
|
||||||
"conversation_ephemeral_messages_duration_one_week" = "1 semana";
|
|
||||||
"conversation_ephemeral_messages_duration_three_days" = "3 dias";
|
|
||||||
"conversation_ephemeral_messages_subtitle" = "Novas mensagens serão excluídas automaticamente assim que lidas por todos.\nEscolha uma duração:";
|
|
||||||
"conversation_ephemeral_messages_title" = "Mensagens efêmeras";
|
|
||||||
"conversation_event_conference_created" = "Você entrou no grupo";
|
|
||||||
"conversation_event_conference_destroyed" = "Você saiu do grupo";
|
|
||||||
"conversation_event_ephemeral_messages_disabled" = "Mensagens efêmeras foram desativadas";
|
|
||||||
"conversation_event_ephemeral_messages_enabled" = "Mensagens efêmeras foram ativadas";
|
|
||||||
"conversation_failed_to_create_toast" = "Falha ao criar conversa!";
|
|
||||||
"conversation_forward_message_title" = "Encaminhar mensagem para…";
|
|
||||||
"conversation_info_add_participants_label" = "Adicionar participantes";
|
|
||||||
"conversation_info_admin_menu_remove_participant" = "Remover do grupo";
|
|
||||||
"conversation_info_admin_menu_set_participant_admin" = "Dar direitos de admin";
|
|
||||||
"conversation_info_admin_menu_unset_participant_admin" = "Remover direitos de admin";
|
|
||||||
"conversation_info_confirm_start_group_call_dialog_message" = "Todos os participantes receberão uma chamada.";
|
|
||||||
"conversation_info_confirm_start_group_call_dialog_title" = "Iniciar uma chamada em grupo?";
|
|
||||||
"conversation_info_delete_history_action" = "Excluir histórico";
|
|
||||||
"conversation_info_menu_add_to_contacts" = "Adicionar aos contatos";
|
|
||||||
"conversation_message_forwarded_toast" = "A mensagem foi encaminhada";
|
|
||||||
"conversation_info_menu_go_to_contact" = "Ver perfil do contato";
|
|
||||||
"conversation_info_participant_is_admin_label" = "Admin";
|
|
||||||
"conversation_message_meeting_updated_label" = "A reunião foi atualizada";
|
|
||||||
"conversation_text_field_hint" = "Diga algo…";
|
|
||||||
"conversation_take_picture_label" = "Tirar foto";
|
|
||||||
"conversation_pick_file_from_gallery_label" = "Abrir galeria";
|
|
||||||
"conversation_pick_any_file_label" = "Escolher arquivo";
|
|
||||||
"conversation_file_cant_be_opened_error_toast" = "O arquivo não pode ser aberto!";
|
|
||||||
"dialog_no" = "Não";
|
|
||||||
"dialog_ok" = "OK";
|
|
||||||
"dialog_yes" = "Sim";
|
|
||||||
"drawer_menu_account_connection_status_cleared" = "Desativado";
|
|
||||||
"drawer_menu_account_connection_status_connected" = "Conectado";
|
|
||||||
"drawer_menu_account_connection_status_failed" = "Erro";
|
|
||||||
"drawer_menu_account_connection_status_progress" = "Conectando…";
|
|
||||||
"drawer_menu_add_account" = "Adicionar uma conta";
|
|
||||||
"drawer_menu_manage_account" = "Gerenciar o perfil";
|
|
||||||
"drawer_menu_no_account_configured_yet" = "Nenhuma conta configurada ainda";
|
|
||||||
"generic_address_picker_suggestions_list_title" = "Sugestões";
|
|
||||||
"help_about_advanced_title" = "Avançado";
|
|
||||||
"help_about_check_for_update" = "Verificar atualização";
|
|
||||||
"help_about_privacy_policy_title" = "Política de privacidade";
|
|
||||||
"help_about_user_guide_subtitle" = "Aprenda a dominar todos os recursos do aplicativo, passo a passo.";
|
|
||||||
"help_about_version_title" = "Versão";
|
|
||||||
"help_dialog_update_available_title" = "Atualização disponível";
|
|
||||||
"help_error_checking_version_toast_message" = "Ocorreu um erro ao verificar por atualizações";
|
|
||||||
"help_quit_title" = "Sair do aplicativo";
|
|
||||||
"help_title" = "Ajuda";
|
|
||||||
"help_troubleshooting_app_version_title" = "Versão do aplicativo";
|
|
||||||
"manage_account_add_picture" = "Adicionar uma foto";
|
|
||||||
"manage_account_delete" = "Sair";
|
|
||||||
"manage_account_details_title" = "Detalhes";
|
|
||||||
"manage_account_device_last_connection" = "Última conexão:";
|
|
||||||
"manage_account_device_remove" = "Remover";
|
|
||||||
"manage_account_devices_title" = "Dispositivos";
|
|
||||||
"manage_account_dialog_remove_account_message" = "Se você deseja excluir sua conta permanentemente, acesse: https://sip.linphone.org";
|
|
||||||
"manage_account_dialog_remove_account_title" = "Sair da sua conta?";
|
|
||||||
"manage_account_edit_picture" = "Editar foto";
|
|
||||||
"manage_account_international_prefix" = "Prefixo internacional";
|
|
||||||
"manage_account_status_progress_summary" = "A conta está se conectando ao servidor, por favor, aguarde…";
|
|
||||||
"manage_account_title" = "Gerenciar conta";
|
|
||||||
"meeting_failed_to_schedule_toast" = "Falha ao agendar reunião!";
|
|
||||||
"meeting_info_deleted_toast" = "A reunião foi excluída";
|
|
||||||
"meeting_info_export_as_calendar_event" = "Criar evento na agenda";
|
|
||||||
"meeting_info_join_title" = "Entrar na reunião agora";
|
|
||||||
"meeting_schedule_edit_title" = "Editar reunião";
|
|
||||||
"meeting_schedule_meeting_label" = "Reunião";
|
|
||||||
"meeting_schedule_pick_end_time_title" = "Escolha a hora de término";
|
|
||||||
"meeting_schedule_pick_start_date_title" = "Escolha a data de início";
|
|
||||||
"meeting_schedule_pick_start_time_title" = "Escolha a hora de início";
|
|
||||||
"meeting_schedule_send_invitations_title" = "Enviar convite aos participantes";
|
|
||||||
"meeting_schedule_subject_hint" = "Adicionar título…";
|
|
||||||
"meeting_schedule_timezone_title" = "Fuso horário";
|
|
||||||
"meeting_schedule_title" = "Nova reunião";
|
|
||||||
"meeting_waiting_room_cancel" = "Cancelar";
|
|
||||||
"menu_resend_chat_message" = "Reenviar";
|
|
||||||
"or" = "ou";
|
|
||||||
"phone_number" = "Número de telefone";
|
|
||||||
"menu_see_existing_contact" = "Ver contato";
|
|
||||||
"menu_show_imdn" = "Status de entrega";
|
|
||||||
"message_delivery_info_error_title" = "Erro";
|
|
||||||
"message_forwarded_label" = "Encaminhada";
|
|
||||||
"message_reaction_click_to_remove_label" = "Clique para remover";
|
|
||||||
"network_not_reachable" = "Você não está conectado à internet";
|
|
||||||
"new_conversation_create_group" = "Criar uma conversa em grupo";
|
|
||||||
"new_conversation_search_bar_filter_hint" = "Pesquisar contato";
|
|
||||||
"new_conversation_title" = "Nova conversa";
|
|
||||||
"next" = "Próximo";
|
|
||||||
"notification_missed_call_title" = "Chamada perdida";
|
|
||||||
"operation_in_progress_overlay" = "Operação em andamento, por favor, aguarde";
|
|
||||||
"recordings_title" = "Gravações";
|
|
||||||
"settings_advanced_accept_early_media_title" = "Aceitar mídia antecipada";
|
|
||||||
"settings_advanced_allow_outgoing_early_media_title" = "Permitir mídia antecipada de saída";
|
|
||||||
"settings_advanced_audio_codecs_title" = "Codecs de áudio";
|
|
||||||
"settings_advanced_audio_devices_title" = "Dispositivos de áudio";
|
|
||||||
"settings_advanced_device_id" = "ID do dispositivo";
|
|
||||||
"settings_advanced_media_encryption_mandatory_title" = "Criptografia de mídia obrigatória";
|
|
||||||
"settings_advanced_output_audio_device_title" = "Dispositivo de saída de áudio padrão";
|
|
||||||
"settings_advanced_remote_provisioning_url" = "URL de provisionamento remoto";
|
|
||||||
"settings_advanced_title" = "Configurações avançadas";
|
|
||||||
"settings_advanced_upload_server_url" = "URL do servidor de compartilhamento de arquivos";
|
|
||||||
"settings_advanced_video_codecs_title" = "Codecs de vídeo";
|
|
||||||
"settings_calls_adaptive_rate_control_title" = "Controle adaptativo de taxa";
|
|
||||||
"settings_calls_auto_record_title" = "Iniciar gravação de chamadas automaticamente";
|
|
||||||
"settings_calls_calibrate_echo_canceller_done_no_echo" = "sem eco";
|
|
||||||
"settings_calls_calibrate_echo_canceller_failed" = "falhou";
|
|
||||||
"settings_calls_calibrate_echo_canceller_in_progress" = "em andamento";
|
|
||||||
"settings_calls_calibrate_echo_canceller_title" = "Calibrar cancelador de eco";
|
|
||||||
"settings_calls_change_ringtone_title" = "Alterar toque";
|
|
||||||
"settings_calls_echo_canceller_subtitle" = "Impede que o eco seja ouvido pela outra parte se não houver um cancelador de eco de hardware disponível";
|
|
||||||
"settings_calls_echo_canceller_title" = "Usar cancelador de eco por software";
|
|
||||||
"settings_calls_enable_fec_title" = "Ativar FEC de vídeo";
|
|
||||||
"settings_calls_enable_video_title" = "Ativar vídeo";
|
|
||||||
"settings_calls_title" = "Chamadas";
|
|
||||||
"settings_calls_vibrate_while_ringing_title" = "Vibrar enquanto a chamada recebida está tocando";
|
|
||||||
"settings_contacts_add_carddav_server_title" = "Adicionar agenda CardDAV";
|
|
||||||
"settings_contacts_add_ldap_server_title" = "Adicionar servidor LDAP";
|
|
||||||
"settings_contacts_ldap_search_base_title" = "Base de busca (não pode ficar em branco)";
|
|
||||||
"settings_contacts_ldap_search_filter_title" = "Filtro";
|
|
||||||
"settings_contacts_ldap_server_url_title" = "URL do servidor (não pode ficar em branco)";
|
|
||||||
"settings_contacts_ldap_use_tls_title" = "Usar TLS";
|
|
||||||
"settings_contacts_title" = "Contatos";
|
|
||||||
"settings_conversations_auto_download_title" = "Baixar arquivos automaticamente";
|
|
||||||
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Marcar conversa como lida ao dispensar notificação de mensagem";
|
|
||||||
"settings_conversations_title" = "Conversas";
|
|
||||||
"settings_meetings_default_layout_title" = "Layout padrão";
|
|
||||||
"settings_meetings_layout_active_speaker_label" = "Orador ativo";
|
|
||||||
"settings_meetings_title" = "Reuniões";
|
|
||||||
"settings_network_allow_ipv6" = "Permitir IPv6";
|
|
||||||
"settings_network_title" = "Rede";
|
|
||||||
"settings_network_use_wifi_only" = "Usar apenas redes Wi-Fi";
|
|
||||||
"settings_security_enable_vfs_subtitle" = "Aviso: uma vez ativado, não pode ser desativado!";
|
|
||||||
"settings_security_enable_vfs_title" = "Criptografar tudo";
|
|
||||||
"settings_security_prevent_screenshots_title" = "Impedir que a interface seja gravada";
|
|
||||||
"settings_security_title" = "Segurança";
|
|
||||||
"settings_title" = "Configurações";
|
|
||||||
"sip_address_copied_to_clipboard_toast" = "Endereço SIP copiado para a área de transferência";
|
|
||||||
"sip_address_display_name" = "Nome de exibição";
|
|
||||||
"sip_address_domain" = "Domínio";
|
|
||||||
"start" = "Início";
|
|
||||||
"uri_handler_config_success_toast" = "Configuração aplicada com sucesso";
|
|
||||||
"username" = "Nome de usuário";
|
|
||||||
"welcome_page_3_title" = "Código aberto";
|
|
||||||
"[linphone.org/contact](https://linphone.org/contact)" = "[linphone.org/contact](https://linphone.org/contact)";
|
|
||||||
"*" = "*";
|
|
||||||
"**%@**" = "**%@**";
|
|
||||||
"#" = "#";
|
|
||||||
"%@" = "%@";
|
|
||||||
"%lld" = "%lld";
|
|
||||||
"%lld %@" = "%1$lld %2$@";
|
|
||||||
"%lld%%" = "%lld%%";
|
|
||||||
"+" = "+";
|
|
||||||
"|" = "|";
|
|
||||||
"❤️" = "❤️";
|
|
||||||
"👍" = "👍";
|
|
||||||
"😂" = "😂";
|
|
||||||
"😢" = "😢";
|
|
||||||
"😮" = "😮";
|
|
||||||
"0" = "0";
|
|
||||||
"1" = "1";
|
|
||||||
"2" = "2";
|
|
||||||
"3" = "3";
|
|
||||||
"4" = "4";
|
|
||||||
"5" = "5";
|
|
||||||
"6" = "6";
|
|
||||||
"7" = "7";
|
|
||||||
"8" = "8";
|
|
||||||
"9" = "9";
|
|
||||||
"account_settings_dialog_invalid_password_message" = "A conexão falhou porque a autenticação está ausente ou é inválida para a conta \n%@.\n\nVocê pode fornecer a senha novamente ou verificar a configuração da sua conta nas configurações.";
|
|
||||||
"assistant_account_creation_sms_confirmation_explanation" = "Enviamos um código de verificação para o seu número de telefone %@. Por favor, insira o código de verificação abaixo:";
|
|
||||||
"assistant_dialog_confirm_phone_number_message" = "Tem certeza de que deseja usar o número de telefone %@?";
|
|
||||||
"assistant_permissions_subtitle" = "Para aproveitar ao máximo o %@, precisamos que você nos conceda as seguintes permissões:";
|
|
||||||
"assistant_dialog_general_terms_and_privacy_policy_message" = "Ao continuar, você aceita nossos %@ e %@.";
|
|
||||||
"assistant_forgotten_password" = "Esqueceu a senha?";
|
|
||||||
"assistant_invalid_uri_toast" = "URI inválido";
|
|
||||||
"assistant_permissions_access_camera_title" = "**Acessar a câmera:** Para capturar vídeo durante videochamadas e conferências.";
|
|
||||||
"assistant_permissions_post_notifications_title" = "**Publicar notificações:** Para ser informado quando você receber uma mensagem ou uma chamada.";
|
|
||||||
"assistant_permissions_read_contacts_title" = "**Ler contatos:** Para exibir seus contatos e encontrar quem está usando o %@.";
|
|
||||||
"assistant_third_party_sip_account_create_linphone_account" = "Prefiro criar uma conta";
|
|
||||||
"assistant_web_platform_link" = "subscribe.linphone.org";
|
|
||||||
"call_action_attended_transfer" = "Transferência assistida";
|
|
||||||
"call_audio_device_type_bluetooth" = "Bluetooth (%@)";
|
|
||||||
"assistant_permissions_record_audio_title" = "**Gravar áudio:** Para que seu correspondente possa ouvi-lo e para gravar mensagens de voz.";
|
|
||||||
"assistant_third_party_sip_account_warning_explanation" = "Alguns recursos exigem uma conta %@, como mensagens em grupo, videoconferências…\n\nEsses recursos ficam ocultos quando você se registra com uma conta SIP de terceiros.\n\nPara habilitá-los em um projeto comercial, entre em contato conosco.";
|
|
||||||
"call_can_be_trusted_toast" = "Dispositivo autenticado";
|
|
||||||
"call_transfer_current_call_title" = "Transferir chamada";
|
|
||||||
"call_zrtp_sas_validation_skip" = "Pular";
|
|
||||||
"calls_count_label" = "%@ chamadas";
|
|
||||||
"Ce mode vous permet d’être interopérable avec d’autres services SIP.\nVos communications seront chiffrées de point à point. " = "Este modo permite que você seja interoperável com outros serviços SIP.\nSuas comunicações serão criptografadas de ponto a ponto. ";
|
|
||||||
"Chiffrement de bout en bout de tous vos échanges, grâce au mode default vos communications sont à l’abri des regards." = "Criptografia de ponta a ponta de todas as suas trocas, graças ao modo padrão, suas comunicações ficam protegidas de olhares indiscretos.";
|
|
||||||
"call_dialog_zrtp_validate_trust_warning_message" = "Para sua segurança, precisamos reautenticar o dispositivo do seu correspondente. Por favor, troquem novamente seus códigos:";
|
|
||||||
"call_dialog_zrtp_validate_trust_message" = "Para sua segurança, precisamos autenticar o dispositivo do seu correspondente. Por favor, troquem seus códigos:";
|
|
||||||
"conference_name_error" = "Erro no nome da conferência";
|
|
||||||
"contact_dialog_delete_title" = "Excluir %@?";
|
|
||||||
"contact_video_call_action" = "Chamada de vídeo";
|
|
||||||
"contacts_list_filter_popup_see_linphone_only" = "Ver contatos %@";
|
|
||||||
"conversation_composing_label_multiple" = "%@ estão digitando…";
|
|
||||||
"conversation_composing_label_single" = "%@ está digitando…";
|
|
||||||
"conversation_end_to_end_encrypted_bottom_sheet_link" = "https://linphone.org/en/features/#security";
|
|
||||||
"contact_details_numbers_and_addresses_title" = "Números de telefone e endereços SIP";
|
|
||||||
"conversation_ephemeral_messages_duration_multiple_days" = "%d dias";
|
|
||||||
"conversation_event_admin_set" = "%@ é admin";
|
|
||||||
"conversation_event_admin_unset" = "%@ não é mais admin";
|
|
||||||
"conversation_event_device_removed" = "Dispositivo de %@ removido";
|
|
||||||
"conversation_event_device_added" = "Novo dispositivo para %@";
|
|
||||||
"conversation_event_ephemeral_messages_lifetime_changed" = "A duração efêmera agora é %@";
|
|
||||||
"conversation_event_participant_added" = "%@ entrou";
|
|
||||||
"conversation_info_participants_list_title" = "Membros do grupo (%d)";
|
|
||||||
"conversation_message_meeting_cancelled_label" = "A reunião foi cancelada!";
|
|
||||||
"conversation_one_to_one_hidden_subject" = "Dummy subject";
|
|
||||||
"conversation_reply_to_message_title" = "Respondendo a: ";
|
|
||||||
"debug_logs_copied_to_clipboard_toast" = "Logs de depuração copiados para a área de transferência";
|
|
||||||
"Default" = "Padrão";
|
|
||||||
"Default mode" = "Modo padrão";
|
|
||||||
"dialog_close" = "Fechar";
|
|
||||||
"help_about_title" = "Sobre o Linphone";
|
|
||||||
"drawer_menu_account_connection_status_refreshing" = "Atualizando ...";
|
|
||||||
"DTLS" = "DTLS";
|
|
||||||
"failed_meeting_ics_invitation_not_sent_toast" = "Não foi possível enviar convites ICS da reunião para nenhum participante";
|
|
||||||
"GC_MSG" = "Você foi adicionado a uma sala de chat";
|
|
||||||
"help_about_contribute_translations_title" = "Contribua na tradução do Linphone";
|
|
||||||
"help_about_open_source_licenses_subtitle" = "© Belledonne Communications 2010-2024";
|
|
||||||
"help_about_open_source_licenses_title" = "GNU General Public License v3.0";
|
|
||||||
"help_about_privacy_policy_subtitle" = "Quais informações o Linphone coleta e usa";
|
|
||||||
"help_about_user_guide_title" = "Guia do usuário Linphone";
|
|
||||||
"help_dialog_update_available_message" = "Uma nova versão %@ está disponível. Você quer atualizar?";
|
|
||||||
"history_list_empty_with_filter_history" = "Nenhum registro corresponde à sua pesquisa";
|
|
||||||
"history_title" = "Histórico de chamadas";
|
|
||||||
"Interoperable" = "Interoperável";
|
|
||||||
"IM_MSG" = "Você recebeu uma mensagem";
|
|
||||||
"manage_account_dialog_international_prefix_help_message" = "Escolha seu país para permitir que o Linphone corresponda aos seus contatos.";
|
|
||||||
"meeting_call_remove_no_participants" = "Nenhum participante no momento…";
|
|
||||||
"meeting_call_remove_participant_confirmation_message" = "Tem certeza de que deseja remover %@ ?";
|
|
||||||
"meeting_call_remove_participant_confirmation_title" = "Remover um participante";
|
|
||||||
"meeting_exported_as_calendar_event" = "Reunião adicionada à agenda do iPhone";
|
|
||||||
"meeting_failed_to_edit_toast" = "Falha ao editar reunião";
|
|
||||||
"meeting_schedule_failed_no_subject_or_participant_toast" = "Um assunto e pelo menos um participante são necessários para criar uma reunião";
|
|
||||||
"meeting_waiting_room_joining_subtitle" = "Você entrará em breve";
|
|
||||||
"meetings_list_empty" = "Nenhuma reunião no momento…";
|
|
||||||
"menu_block_address" = "Bloquear o endereço";
|
|
||||||
"menu_block_number" = "Bloquear o número";
|
|
||||||
"menu_copy_sip_address" = "Copiar endereço SIP";
|
|
||||||
"message_copied_to_clipboard_toast" = "Mensagem copiada para a área de transferência";
|
|
||||||
"message_delivery_info_read_title" = "Lida";
|
|
||||||
"message_delivery_info_received_title" = "Recebida";
|
|
||||||
"message_delivery_info_sent_title" = "Enviada";
|
|
||||||
"message_meeting_invitation_cancelled_notification" = "📅 A reunião foi cancelada";
|
|
||||||
"message_meeting_invitation_notification" = "📅 Você foi convidado para uma reunião";
|
|
||||||
"message_meeting_invitation_updated_notification" = "📅 A reunião foi atualizada";
|
|
||||||
"message_reactions_info_all_title" = "Reações";
|
|
||||||
"network_reachable_again" = "A rede está acessível novamente";
|
|
||||||
"None" = "Nenhum";
|
|
||||||
"notification_chat_message_reaction_received" = "%@ reagiu com %@ para: %@";
|
|
||||||
"Personnalize your profil mode" = "Personalize o modo do seu perfil";
|
|
||||||
"picker_categories" = "Categorias";
|
|
||||||
"selected_participants_count" = "%@ participantes selecionados";
|
|
||||||
"qr_code_validated" = "QR code validado";
|
|
||||||
"settings_calls_calibrate_echo_canceller_done" = "%@ ms";
|
|
||||||
"settings_contacts_carddav_deleted_toast" = "Conta CardDAV excluída";
|
|
||||||
"settings_contacts_carddav_mandatory_field_not_filled_toast" = "Por favor, preencha pelo menos o nome de exibição e a URL do servidor";
|
|
||||||
"settings_contacts_carddav_sync_successful_toast" = "Sincronização bem-sucedida";
|
|
||||||
"settings_contacts_carddav_use_as_default_title" = "Armazenar contatos recém-criados aqui";
|
|
||||||
"settings_contacts_carddav_realm_title" = "Domínio de autenticação";
|
|
||||||
"settings_contacts_ldap_bind_user_password_title" = "Senha do usuário de autenticação";
|
|
||||||
"settings_contacts_ldap_max_results_title" = "Máximo de resultados";
|
|
||||||
"settings_contacts_ldap_request_timeout_title" = "Tempo limite da solicitação";
|
|
||||||
"sip_address" = "Endereço SIP";
|
|
||||||
"sip.linphone.org" = "sip.linphone.org";
|
|
||||||
"SRTP" = "SRTP";
|
|
||||||
"TCP" = "TCP";
|
|
||||||
"Temp Help" = "Ajuda Temp";
|
|
||||||
"text_copied_to_clipboard_toast" = "Texto copiado para a área de transferência";
|
|
||||||
"username_error" = "Erro no nome de usuário";
|
|
||||||
"web_platform_forgotten_password_url" = "https://subscribe.linphone.org/";
|
|
||||||
"website_contact_url" = "https://linphone.org/contact";
|
|
||||||
"website_download_url" = "https://linphone.org/linphone-softphone";
|
|
||||||
"website_user_guide_url" = "https://linphone.org/en/docs/";
|
|
||||||
"welcome_page_subtitle" = "ao %@";
|
|
||||||
"ZRTP" = "ZRTP";
|
|
||||||
"conversation_event_participant_removed" = "%@ saiu";
|
|
||||||
"conversation_event_subject_changed" = "Novo assunto: %@";
|
|
||||||
"conversations_files_waiting_to_be_shared_single" = "1 arquivo esperando para ser compartilhado";
|
|
||||||
"conversations_files_waiting_to_be_shared_multiple" = "%@ arquivos esperando para ser compartilhados";
|
|
||||||
"notification_chat_message_received_title" = "Mensagem recebida";
|
|
||||||
"TLS" = "TLS";
|
|
||||||
"UDP" = "UDP";
|
|
||||||
"uri_handler_bad_call_address_failed_toast" = "Não é possível ligar, endereço inválido";
|
|
||||||
"uri_handler_bad_config_address_failed_toast" = "Não é possível recuperar a configuração, endereço inválido";
|
|
||||||
"uri_handler_call_failed_toast" = "Falha na chamada";
|
|
||||||
"uri_handler_config_failed_toast" = "Falha na configuração";
|
|
||||||
"web_platform_register_email_url" = "https://subscribe.linphone.org/register/email";
|
|
||||||
"website_open_source_licences_usage_url" = "https://wiki.linphone.org/xwiki/wiki/public/view/Linphone/Third%20party%20components%20/";
|
|
||||||
"website_privacy_policy_url" = "https://linphone.org/en/privacy-policy";
|
|
||||||
"website_terms_and_conditions_url" = "https://www.linphone.org/en/terms-of-use";
|
|
||||||
"website_translate_weblate_url" = "https://weblate.linphone.org/";
|
|
||||||
"[https://sip.linphone.org](https://sip.linphone.org)" = "[https://sip.linphone.org](https://sip.linphone.org)";
|
|
||||||
": %@" = ": %@";
|
|
||||||
"welcome_carousel_skip" = "Pular";
|
|
||||||
"welcome_page_1_message" = "Um aplicativo de comunicação **seguro**, de **código aberto** e **francês**.";
|
|
||||||
"welcome_page_2_message" = "Suas comunicações estão seguras graças à nossa **criptografia de ponta a ponta**.";
|
|
||||||
"welcome_page_3_message" = "Um aplicativo **gratuito** e de código aberto desde **2001**.";
|
|
||||||
"You will change this mode later" = "Você mudará este modo mais tarde";
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
"[linphone.org/contact](https://linphone.org/contact)" = "[linphone.org/contact](https://linphone.org/contact)";
|
|
||||||
"*" = "*";
|
|
||||||
"**%@**" = "**%@**";
|
|
||||||
"#" = "#";
|
|
||||||
"%@" = "%@";
|
|
||||||
"%lld" = "%lld";
|
|
||||||
"[https://sip.linphone.org](https://sip.linphone.org)" = "[https://sip.linphone.org](https://sip.linphone.org)";
|
|
||||||
": %@" = ": %@";
|
|
||||||
|
|
@ -187,7 +187,7 @@
|
||||||
"dialog_deny" = "Отказать";
|
"dialog_deny" = "Отказать";
|
||||||
"dialog_install" = "Установить";
|
"dialog_install" = "Установить";
|
||||||
"dialog_no" = "Нет";
|
"dialog_no" = "Нет";
|
||||||
"dialog_confirm" = "ОК";
|
"dialog_ok" = "ОК";
|
||||||
"dialog_yes" = "Да";
|
"dialog_yes" = "Да";
|
||||||
"drawer_menu_account_connection_status_cleared" = "Отключить";
|
"drawer_menu_account_connection_status_cleared" = "Отключить";
|
||||||
"drawer_menu_account_connection_status_connected" = "Подключен";
|
"drawer_menu_account_connection_status_connected" = "Подключен";
|
||||||
|
|
@ -362,4 +362,3 @@
|
||||||
"username" = "Имя пользователя";
|
"username" = "Имя пользователя";
|
||||||
"conversation_end_to_end_encrypted_event_title" = "Сквозное шифрование беседы";
|
"conversation_end_to_end_encrypted_event_title" = "Сквозное шифрование беседы";
|
||||||
"conversation_end_to_end_encrypted_event_subtitle" = "Сообщения в этой беседе зашифрованы end-to-end шифрованием. Расшифровать их может только ваш собеседник.";
|
"conversation_end_to_end_encrypted_event_subtitle" = "Сообщения в этой беседе зашифрованы end-to-end шифрованием. Расшифровать их может только ваш собеседник.";
|
||||||
"authentication_id" = "Идентификатор аутентификации (если отличается)";
|
|
||||||
|
|
|
||||||
|
|
@ -1,382 +0,0 @@
|
||||||
"account_settings_title" = "Nastavenia účtu";
|
|
||||||
"account_settings_push_notification_not_available_title" = "Push notifikácie nie sú dostupné!";
|
|
||||||
"account_settings_im_encryption_mandatory_title" = "Šifrovanie správ je povinné";
|
|
||||||
"account_settings_sip_proxy_url_title" = "URL adresa SIP proxy servera";
|
|
||||||
"account_settings_outbound_proxy_title" = "Proxy server pre odchádzajúcu komunikáciu";
|
|
||||||
"account_settings_nat_policy_title" = "Nastavenia zásad NAT";
|
|
||||||
"account_settings_enable_ice_title" = "Povoliť ICE";
|
|
||||||
"account_settings_enable_turn_title" = "Povoliť TURN";
|
|
||||||
"account_settings_turn_username_title" = "TURN používateľské meno";
|
|
||||||
"account_settings_turn_password_title" = "TURN heslo";
|
|
||||||
"account_settings_avpf_title" = "AVPF (Profil audio-vizuálu so spätnou väzbou)";
|
|
||||||
"account_settings_expire_title" = "Platnosť (v sekundách)";
|
|
||||||
"account_settings_audio_video_conference_factory_uri_title" = "URI adresa pre audio/video hovory";
|
|
||||||
"account_settings_ccmp_server_url_title" = "RL adresa servera CCMP (Cisco CallManager Provisioning)";
|
|
||||||
"account_settings_lime_server_url_title" = "URL adresa servera pre kľúče koncového šifrovania";
|
|
||||||
"account_settings_bundle_mode_title" = "Režim zoskupenia";
|
|
||||||
"account_settings_mwi_uri_title" = "URI adresa servera MWI (Message Waiting Indicator)";
|
|
||||||
"account_settings_dialog_invalid_password_title" = "Vyžaduje sa overenie";
|
|
||||||
"bottom_navigation_contacts_label" = "Kontakty";
|
|
||||||
"bottom_navigation_calls_label" = "Hovory";
|
|
||||||
"account_settings_voicemail_uri_title" = "URI adresa hlasovej schránky";
|
|
||||||
"account_settings_update_password_title" = "Aktualizovať heslo";
|
|
||||||
"bottom_navigation_meetings_label" = "Schôdzky";
|
|
||||||
"contacts_list_empty" = "Momentálne žiadny kontakt…";
|
|
||||||
"contacts_list_favourites_title" = "Obľúbené";
|
|
||||||
"contacts_list_all_contacts_title" = "Všetky kontakty";
|
|
||||||
"drawer_menu_manage_account" = "Spravovať profil";
|
|
||||||
"drawer_menu_account_connection_status_connected" = "Pripojené";
|
|
||||||
"drawer_menu_account_connection_status_cleared" = "Zakázané";
|
|
||||||
"drawer_menu_account_connection_status_progress" = "Pripájanie…";
|
|
||||||
"drawer_menu_account_connection_status_failed" = "Chyba";
|
|
||||||
"drawer_menu_no_account_configured_yet" = "Žiadny účet zatiaľ nie je nastavený";
|
|
||||||
"drawer_menu_add_account" = "Pridať účet";
|
|
||||||
"help_about_check_for_update" = "Kontrola aktualizácie";
|
|
||||||
"help_about_advanced_title" = "Pokročilé";
|
|
||||||
"help_about_privacy_policy_title" = "Zásady ochrany súkromia";
|
|
||||||
"help_about_version_title" = "Verzia";
|
|
||||||
"help_version_up_to_date_toast_message" = "Vaša verzia je aktuálna";
|
|
||||||
"help_error_checking_version_toast_message" = "Počas kontroly aktualizácie nastala chyba";
|
|
||||||
"help_dialog_update_available_title" = "Je dostupná nová aktualizácia";
|
|
||||||
"help_quit_title" = "Ukončiť aplikáciu";
|
|
||||||
"help_troubleshooting_title" = "Riešenie problémov";
|
|
||||||
"help_troubleshooting_clean_logs" = "Vyčistiť záznamy";
|
|
||||||
"help_troubleshooting_print_logs_in_logcat" = "Zapisovať záznamy do logcatu";
|
|
||||||
"help_troubleshooting_share_logs" = "Zdieľať záznamy";
|
|
||||||
"help_troubleshooting_app_version_title" = "Verzia aplikácie";
|
|
||||||
"help_troubleshooting_sdk_version_title" = "Verzia SDK";
|
|
||||||
"help_troubleshooting_share_logs_dialog_title" = "Zdieľať odkaz na ladiace záznamy pomocou…";
|
|
||||||
"help_troubleshooting_debug_logs_cleaned_toast_message" = "Ladiace záznamy boli vyčistené";
|
|
||||||
"help_troubleshooting_debug_logs_upload_error_toast_message" = "Chyba pri nahrávaní ladiacich záznamov";
|
|
||||||
"help_troubleshooting_show_config_file" = "Zobraziť konfiguráciu";
|
|
||||||
"history_call_start_title" = "Nový hovor";
|
|
||||||
"history_call_start_search_bar_filter_hint" = "Hľadať kontakt alebo históriu hovoru";
|
|
||||||
"history_call_start_create_group_call" = "Vytvoriť skupinový hovor";
|
|
||||||
"history_group_call_start_dialog_set_subject" = "Nastaviť predmet skupinového hovoru";
|
|
||||||
"history_group_call_start_dialog_subject_hint" = "Predmet skupinového hovoru";
|
|
||||||
"history_dialog_delete_all_call_logs_title" = "Naozaj chcete zmazať celú históriu hovorov?";
|
|
||||||
"history_dialog_delete_all_call_logs_message" = "Z histórie budú odstránené všetky hovory";
|
|
||||||
"manage_account_details_title" = "Podrobnosti";
|
|
||||||
"manage_account_devices_title" = "Zariadenia";
|
|
||||||
"manage_account_edit_picture" = "Upraviť obrázok";
|
|
||||||
"manage_account_remove_picture" = "Odstrániť obrázok";
|
|
||||||
"manage_account_status_connected_summary" = "Tento účet je aktívny, každý Vám môže volať.";
|
|
||||||
"manage_account_international_prefix" = "Medzinárodný prefix";
|
|
||||||
"manage_account_settings" = "Nastavenia účtu";
|
|
||||||
"manage_account_delete" = "Odhlásiť sa";
|
|
||||||
"manage_account_device_last_connection" = "Posledné pripojenie:";
|
|
||||||
"manage_account_dialog_remove_account_title" = "Odhlásiť sa z Vášho účtu?";
|
|
||||||
"manage_account_dialog_remove_account_message" = "Pokiaľ si želáte nenávratne zmazať svoj účet, navštívte: https://sip.linphone.org";
|
|
||||||
"history_list_empty_history" = "Momentálne žiadny hovor…";
|
|
||||||
"manage_account_title" = "Spravovať účet";
|
|
||||||
"manage_account_status_failed_summary" = "Pripojenie účtu zlyhalo, skontrolujte nastavenia.";
|
|
||||||
"settings_advanced_title" = "Pokročilé nastavenia";
|
|
||||||
"settings_advanced_device_id_hint" = "Iba alfanumerické znaky";
|
|
||||||
"settings_advanced_upload_server_url" = "URL adresa servera pre zdieľanie súborov";
|
|
||||||
"settings_advanced_media_encryption_mandatory_title" = "Povinné šifrovanie médií";
|
|
||||||
"settings_advanced_accept_early_media_title" = "Prijímať zvuk pred spojením hovoru (early media)";
|
|
||||||
"settings_advanced_allow_outgoing_early_media_title" = "Prenášať zvuk pri odchádzajúcom hovore (early media)";
|
|
||||||
"settings_advanced_remote_provisioning_url" = "URL pre vzdialenú správu";
|
|
||||||
"settings_advanced_download_apply_remote_provisioning" = "Stiahnuť a použiť";
|
|
||||||
"settings_advanced_audio_devices_title" = "Zvukové zariadenia";
|
|
||||||
"settings_advanced_input_audio_device_title" = "Predvolené vstupné zvukové zariadenie";
|
|
||||||
"settings_advanced_output_audio_device_title" = "Predvolené výstupné zvukové zariadenie";
|
|
||||||
"settings_advanced_audio_codecs_title" = "Zvukové kodeky";
|
|
||||||
"settings_calls_calibrate_echo_canceller_in_progress" = "prebieha";
|
|
||||||
"settings_calls_calibrate_echo_canceller_done_no_echo" = "bez ozveny";
|
|
||||||
"settings_calls_calibrate_echo_canceller_failed" = "zlyhalo";
|
|
||||||
"settings_calls_adaptive_rate_control_title" = "Adaptívny kontrola rýchlosti";
|
|
||||||
"settings_calls_change_ringtone_title" = "Zmeniť vyzváňací tón";
|
|
||||||
"settings_advanced_video_codecs_title" = "Video kodeky";
|
|
||||||
"settings_calls_enable_video_title" = "Povoliť video";
|
|
||||||
"settings_calls_enable_fec_title" = "Povoliť FEC pre video";
|
|
||||||
"settings_calls_vibrate_while_ringing_title" = "Vibrovať počas prichádzajúceho hovoru";
|
|
||||||
"settings_contacts_add_ldap_server_title" = "Pridať LDAP server";
|
|
||||||
"settings_contacts_add_carddav_server_title" = "Pridať adresár CardDAV";
|
|
||||||
"settings_contacts_carddav_server_url_title" = "URL adresa servera";
|
|
||||||
"settings_contacts_carddav_sync_error_toast" = "Synchronizácia zlyhala!";
|
|
||||||
"settings_contacts_edit_ldap_server_title" = "Upraviť LDAP server";
|
|
||||||
"settings_contacts_edit_carddav_server_title" = "Upraviť adresár CardDAV";
|
|
||||||
"settings_contacts_ldap_bind_dn_title" = "Bind DN (pripájací identifikátor)";
|
|
||||||
"settings_title" = "Nastavenia";
|
|
||||||
"settings_security_title" = "Zabezpečenie";
|
|
||||||
"settings_security_enable_vfs_title" = "Šifrovať všetko";
|
|
||||||
"settings_security_enable_vfs_subtitle" = "Varovanie: po zapnutí sa už nedá zrušiť!";
|
|
||||||
"settings_security_prevent_screenshots_title" = "Zabrániť nahrávaniu rozhrania aplikácie";
|
|
||||||
"settings_conversations_auto_download_title" = "Automaticky sťahovať súbory";
|
|
||||||
"settings_conversations_mark_as_read_when_dismissing_notif_title" = "Označiť konverzáciu ako prečítanú pri zavretí oznámenia o správe";
|
|
||||||
"settings_contacts_title" = "Kontakty";
|
|
||||||
"settings_contacts_ldap_use_tls_title" = "Použiť TLS";
|
|
||||||
"settings_contacts_ldap_server_url_title" = "URL adresa servera (nesmie byť prázdne)";
|
|
||||||
"settings_meetings_title" = "Schôdzky";
|
|
||||||
"settings_meetings_default_layout_title" = "Predvolené rozloženie";
|
|
||||||
"settings_meetings_layout_active_speaker_label" = "Aktívny hovoriaci";
|
|
||||||
"settings_meetings_layout_mosaic_label" = "Mozaika";
|
|
||||||
"settings_network_title" = "Sieť";
|
|
||||||
"settings_network_use_wifi_only" = "Používať iba siete Wi-Fi";
|
|
||||||
"settings_network_allow_ipv6" = "Povoliť IPv6";
|
|
||||||
"settings_conversations_title" = "Konverzácie";
|
|
||||||
"help_troubleshooting_clear_native_friends_in_database" = "Vymazať importované kontakty zo systémového adresára";
|
|
||||||
"manage_account_status_cleared_summary" = "Účet bol zakázaný, nebudete môcť prijímať hovory ani správy.";
|
|
||||||
"account_settings_push_notification_title" = "Povoliť push notifikácie";
|
|
||||||
"bottom_navigation_conversations_label" = "Konverzácie";
|
|
||||||
"account_settings_stun_server_url_title" = "URL adresa servera STUN/TURN";
|
|
||||||
"settings_calls_echo_canceller_title" = "Použiť softvérové potlačenie ozveny";
|
|
||||||
"help_title" = "Pomoc";
|
|
||||||
"settings_calls_auto_record_title" = "Automaticky spustiť nahrávanie hovorov";
|
|
||||||
"help_about_user_guide_subtitle" = "Naučte sa krok za krokom ovládať všetky funkcie aplikácie.";
|
|
||||||
"help_troubleshooting_firebase_project_title" = "ID Firebase project";
|
|
||||||
"manage_account_status_progress_summary" = "Účet sa pripája k serveru, prosím, čakajte…";
|
|
||||||
"settings_calls_title" = "Hovory";
|
|
||||||
"settings_calls_echo_canceller_subtitle" = "Zabraňuje, aby ozvenu bolo počuť na vzdialenej strane, ak nie je k dispozícii hardvérové potlačenie ozveny";
|
|
||||||
"settings_calls_calibrate_echo_canceller_title" = "Kalibrovať potlačenie ozveny";
|
|
||||||
"manage_account_add_picture" = "Pridať obrázok";
|
|
||||||
"account_settings_cpim_in_basic_conversations_title" = "Použiť CPIM v \"základných\" konverzáciách";
|
|
||||||
"account_settings_conference_factory_uri_title" = "URI adresa pre vytváranie konferencií";
|
|
||||||
"manage_account_device_remove" = "Odstrániť";
|
|
||||||
"welcome_page_2_title" = "Zabezpečená";
|
|
||||||
"welcome_page_3_title" = "Otvorená";
|
|
||||||
"welcome_page_title" = "Vitajte";
|
|
||||||
"account_settings_dialog_invalid_password_hint" = "Heslo";
|
|
||||||
"assistant_account_create" = "Vytvoriť";
|
|
||||||
"assistant_account_creation_wrong_phone_number" = "Nesprávne číslo?";
|
|
||||||
"assistant_account_login" = "Prihlásenie";
|
|
||||||
"assistant_account_login_forbidden_error" = "Nesprávne používateľské meno alebo heslo";
|
|
||||||
"assistant_account_register" = "Registrácia";
|
|
||||||
"assistant_account_register_push_notification_not_received_error" = "Push notifikácia s autentifikačným tokenom nebola prijatá behom 5 sekúnd, skúste to, prosím, neskôr znovu";
|
|
||||||
"assistant_account_register_unexpected_error" = "Nastala neočakávaná chyba, skúste to, prosím, neskôr znovu";
|
|
||||||
"assistant_already_have_an_account" = "Máte už účet?";
|
|
||||||
"assistant_create_account_using_email_on_our_web_platform" = "Vytvorte účet pomocou svojej e-mailovej adresy na:";
|
|
||||||
"assistant_dialog_confirm_phone_number_title" = "Potvrdiť telefónne číslo";
|
|
||||||
"assistant_dialog_general_terms_and_privacy_policy_title" = "Všeobecné podmienky a zásady ochrany súkromia";
|
|
||||||
"assistant_dialog_general_terms_label" = "všeobecné podmienky";
|
|
||||||
"assistant_dialog_privacy_policy_label" = "zásady ochrany súkromia";
|
|
||||||
"assistant_login_third_party_sip_account" = "Použiť SIP účet tretej strany";
|
|
||||||
"assistant_no_account_yet" = "Nemáte ešte účet?";
|
|
||||||
"assistant_permissions_grant_all_of_them" = "OK";
|
|
||||||
"assistant_permissions_skip_permissions" = "Vykonať neskôr";
|
|
||||||
"assistant_permissions_title" = "Udeliť oprávnenia";
|
|
||||||
"assistant_qr_code_invalid_toast" = "Neplatný QR kód!";
|
|
||||||
"assistant_scan_qr_code" = "Naskenovať QR kód";
|
|
||||||
"assistant_sip_account_transport_protocol" = "Prenos";
|
|
||||||
"assistant_third_party_sip_account_warning_ok" = "Rozumiem";
|
|
||||||
"contact_call_action" = "Volať";
|
|
||||||
"contact_details_delete" = "Vymazať";
|
|
||||||
"conversation_action_call" = "Volať";
|
|
||||||
"conversation_action_mark_as_read" = "Označiť ako prečítané";
|
|
||||||
"dialog_accept" = "Prijať";
|
|
||||||
"dialog_call" = "Volať";
|
|
||||||
"dialog_cancel" = "Zrušiť";
|
|
||||||
"dialog_continue" = "Pokračovať";
|
|
||||||
"dialog_deny" = "Odmietnuť";
|
|
||||||
"dialog_install" = "Inštalovať";
|
|
||||||
"dialog_no" = "Nie";
|
|
||||||
"dialog_confirm" = "Confirm";
|
|
||||||
"dialog_yes" = "Áno";
|
|
||||||
"meeting_waiting_room_cancel" = "Zrušiť";
|
|
||||||
"menu_delete_selected_item" = "Vymazať";
|
|
||||||
"menu_reply_to_chat_message" = "Odpovedať";
|
|
||||||
"next" = "Ďalej";
|
|
||||||
"notification_missed_call_title" = "Zmeškaný hovor";
|
|
||||||
"or" = "alebo";
|
|
||||||
"password" = "Heslo";
|
|
||||||
"phone_number" = "Telefónne číslo";
|
|
||||||
"settings_advanced_device_id" = "ID zariadenia";
|
|
||||||
"settings_contacts_carddav_name_title" = "Zobrazované meno";
|
|
||||||
"settings_contacts_carddav_password_title" = "Heslo";
|
|
||||||
"settings_contacts_carddav_username_title" = "Používateľské meno";
|
|
||||||
"settings_contacts_ldap_password_title" = "Heslo";
|
|
||||||
"sip_address_copied_to_clipboard_toast" = "SIP adresa skopírovaná do schránky";
|
|
||||||
"sip_address_display_name" = "Zobrazované meno";
|
|
||||||
"sip_address_domain" = "Doména";
|
|
||||||
"start" = "Začať";
|
|
||||||
"uri_handler_config_success_toast" = "Konfigurácia bola úspešne nastavená";
|
|
||||||
"username" = "Používateľské meno";
|
|
||||||
"contacts_list_filter_popup_see_all" = "Zobraziť všetko";
|
|
||||||
"contact_new_title" = "Nový kontakt";
|
|
||||||
"contact_edit_title" = "Upraviť kontakt";
|
|
||||||
"contact_editor_first_name" = "Krstné meno";
|
|
||||||
"contact_editor_last_name" = "Priezvisko";
|
|
||||||
"contact_editor_company" = "Spoločnosť";
|
|
||||||
"contact_editor_job_title" = "Pracovná pozícia";
|
|
||||||
"contact_editor_dialog_abort_confirmation_title" = "Neuložiť zmeny?";
|
|
||||||
"contact_editor_dialog_abort_confirmation_message" = "Všetky zmeny budú stratené";
|
|
||||||
"contact_details_actions_title" = "Ďalšie akcie";
|
|
||||||
"contact_details_edit" = "Upraviť";
|
|
||||||
"contact_details_add_to_favourites" = "Pridať do obľúbených";
|
|
||||||
"contact_details_remove_from_favourites" = "Odstrániť z obľúbených";
|
|
||||||
"contact_details_share" = "Zdieľať";
|
|
||||||
"contact_dialog_delete_message" = "Tento kontakt bude definitívne odstránený.";
|
|
||||||
"contact_dialog_pick_phone_number_or_sip_address_title" = "Vyberte číslo alebo SIP adresu";
|
|
||||||
"contact_message_action" = "Správa";
|
|
||||||
"contact_video_call_action" = "Videohovor";
|
|
||||||
"conversation_action_mute" = "Stlmiť";
|
|
||||||
"conversation_action_unmute" = "Zrušiť stlmenie";
|
|
||||||
"conversation_action_delete" = "Vymazať konverzáciu";
|
|
||||||
"conversation_action_leave_group" = "Opustiť skupinu";
|
|
||||||
"conversation_ephemeral_messages_title" = "Dočasné (miznúce) správy";
|
|
||||||
"conversations_list_empty" = "Momentálne žiadna konverzácia…";
|
|
||||||
"conversation_action_configure_ephemeral_messages" = "Nastavenie dočasných (miznúcich) správ";
|
|
||||||
"conference_layout_grid" = "Mozaika";
|
|
||||||
"conversation_ephemeral_messages_duration_disabled" = "Zakázané";
|
|
||||||
"conversation_menu_configure_ephemeral_messages" = "Dočasné (miznúce) správy";
|
|
||||||
"Error" = "Chyba";
|
|
||||||
"Interoperable mode" = "Režim vzájomnej kompatibility";
|
|
||||||
"manage_account_no_device" = "Zariadenie sa nenašlo…";
|
|
||||||
"message_delivery_info_error_title" = "Chyba";
|
|
||||||
"call_action_start_new_call" = "Nový hovor";
|
|
||||||
"call_stats_media_encryption_title" = "Šifrovanie médií";
|
|
||||||
"settings_contacts_ldap_search_base_title" = "Počiatočný bod hľadania (nesmie byť prázdne)";
|
|
||||||
"conversation_ephemeral_messages_subtitle" = "Nové správy sa automaticky odstránia, keď si ich všetci prečítajú.\nVyberte dobu trvania:";
|
|
||||||
"conversation_ephemeral_messages_duration_one_minute" = "1 minúta";
|
|
||||||
"conversation_ephemeral_messages_duration_one_hour" = "1 hodina";
|
|
||||||
"conversation_ephemeral_messages_duration_one_day" = "1 deň";
|
|
||||||
"conversation_ephemeral_messages_duration_three_days" = "3 dni";
|
|
||||||
"conversation_ephemeral_messages_duration_one_week" = "1 týždeň";
|
|
||||||
"conversation_add_participants_title" = "Pridať účastníkov";
|
|
||||||
"conversation_end_to_end_encrypted_event_title" = "Koncové šifrovanie konverzácie";
|
|
||||||
"conversation_end_to_end_encrypted_event_subtitle" = "Správy v tejto konverzácii sú koncovo šifrované. Dešifrovať ich môže len váš partner v konverzácii.";
|
|
||||||
"conversation_dialog_set_subject" = "Nastaviť predmet konverzácie";
|
|
||||||
"conversation_dialog_edit_subject" = "Upraviť predmet konverzácie";
|
|
||||||
"conversation_dialog_subject_hint" = "Predmet konverzácie";
|
|
||||||
"conversation_event_ephemeral_messages_enabled" = "Dočasné (miznúce) správy boli povolené";
|
|
||||||
"conversation_event_ephemeral_messages_disabled" = "Dočasné (miznúce) správy boli zakázané";
|
|
||||||
"conversation_event_conference_created" = "Pridali ste sa ku skupine";
|
|
||||||
"conversation_event_conference_destroyed" = "Opustili ste skupinu";
|
|
||||||
"conversation_info_add_participants_label" = "Pridať účastníkov";
|
|
||||||
"conversation_info_delete_history_action" = "Vymazať históriu";
|
|
||||||
"conversation_info_admin_menu_remove_participant" = "Odstrániť zo skupiny";
|
|
||||||
"conversation_info_admin_menu_set_participant_admin" = "Udeliť práva administrátora";
|
|
||||||
"conversation_info_admin_menu_unset_participant_admin" = "Odobrať práva administrátora";
|
|
||||||
"conversation_info_menu_go_to_contact" = "Zobraziť profil kontaktu";
|
|
||||||
"conversation_info_menu_add_to_contacts" = "Pridať do kontaktov";
|
|
||||||
"conversation_info_confirm_start_group_call_dialog_message" = "Bude volané všetkým účastníkom.";
|
|
||||||
"conversation_text_field_hint" = "Napíšte niečo…";
|
|
||||||
"conversation_menu_go_to_info" = "Informácia o konverzácii";
|
|
||||||
"conversation_invalid_participant_due_to_security_mode_toast" = "Vytvorenie konverzácie s účastníkom z inej domény nie je z bezpečnostných dôvodov povolené!";
|
|
||||||
"conversation_take_picture_label" = "Odfotiť";
|
|
||||||
"conversation_pick_file_from_gallery_label" = "Otvoriť galériu";
|
|
||||||
"conversation_pick_any_file_label" = "Vybrať súbor";
|
|
||||||
"conversation_file_cant_be_opened_error_toast" = "Súbor sa nedá otvoriť!";
|
|
||||||
"new_conversation_title" = "Nová konverzácia";
|
|
||||||
"new_conversation_search_bar_filter_hint" = "Hľadať kontakt";
|
|
||||||
"new_conversation_create_group" = "Vytvoriť skupinovú konverzáciu";
|
|
||||||
"conversation_info_participant_is_admin_label" = "Administrátor";
|
|
||||||
"conversation_info_confirm_start_group_call_dialog_title" = "Zahájiť skupinový hovor?";
|
|
||||||
"conversation_failed_to_create_toast" = "Vytvorenie konverzácie zlyhalo!";
|
|
||||||
"call_action_go_to_calls_list" = "Zoznam hovorov";
|
|
||||||
"call_action_change_layout" = "Rozloženie";
|
|
||||||
"call_outgoing" = "Odchádzajúci hovor";
|
|
||||||
"call_audio_incoming" = "Prichádzajúci hovor";
|
|
||||||
"call_action_show_messages" = "Správy";
|
|
||||||
"call_action_pause_call" = "Pozastaviť";
|
|
||||||
"call_action_resume_call" = "Obnoviť";
|
|
||||||
"call_action_record_call" = "Nahrať";
|
|
||||||
"call_action_hang_up" = "Zavesiť";
|
|
||||||
"call_do_zrtp_sas_validation_again" = "Znovu overiť ZRTP SAS";
|
|
||||||
"call_not_encrypted" = "Hovor nie je šifrovaný";
|
|
||||||
"call_dialog_zrtp_validate_trust_local_code_label" = "Váš kód:";
|
|
||||||
"call_dialog_zrtp_validate_trust_remote_code_label" = "Kód protistrany:";
|
|
||||||
"call_dialog_zrtp_validate_trust_letters_do_not_match" = "Žiadna zhoda";
|
|
||||||
"call_dialog_zrtp_security_alert_title" = "Bezpečnostné upozornenie";
|
|
||||||
"call_dialog_zrtp_security_alert_try_again" = "Skúsiť znovu";
|
|
||||||
"call_dialog_zrtp_security_alert_message" = "Dôvernosť tohto hovoru môže byť ohrozená!";
|
|
||||||
"call_audio_device_type_earpiece" = "Slúchadlo";
|
|
||||||
"call_audio_device_type_speaker" = "Reproduktor";
|
|
||||||
"call_state_connected" = "Aktívny";
|
|
||||||
"call_state_paused" = "Pozastavené";
|
|
||||||
"call_state_paused_by_remote" = "Pozastavené vzdialenou stranou";
|
|
||||||
"call_state_resuming" = "Obnovujem…";
|
|
||||||
"call_stats_audio_title" = "Zvuk";
|
|
||||||
"call_zrtp_sas_validation_required" = "Vyžadované overenie";
|
|
||||||
"calls_list_title" = "Zoznam hovorov";
|
|
||||||
"calls_list_dialog_merge_into_conference_title" = "Zlúčiť všetky hovory do konferencie?";
|
|
||||||
"calls_list_dialog_merge_into_conference_label" = "Vytvoriť konferenciu";
|
|
||||||
"conversation_forward_message_title" = "Preposlať správu…";
|
|
||||||
"conversation_message_forwarded_toast" = "Správa bola preposlaná";
|
|
||||||
"conversation_message_forward_cancelled_toast" = "Preposlanie správy bolo zrušené";
|
|
||||||
"meeting_schedule_meeting_label" = "Schôdzka";
|
|
||||||
"meeting_schedule_pick_start_date_title" = "Zvoľte dátum začiatku";
|
|
||||||
"meeting_schedule_pick_start_time_title" = "Zvoľte čas začiatku";
|
|
||||||
"meeting_schedule_pick_end_time_title" = "Zvoľte čas konca";
|
|
||||||
"meeting_schedule_description_hint" = "Pridať popis";
|
|
||||||
"meeting_schedule_add_participants_title" = "Pridať účastníkov";
|
|
||||||
"meeting_info_join_title" = "Pripojiť sa k schôdzke teraz";
|
|
||||||
"meeting_info_organizer_label" = "Organizátor";
|
|
||||||
"meeting_info_export_as_calendar_event" = "Vytvoriť udalosť v kalendári";
|
|
||||||
"meeting_info_deleted_toast" = "Schôdzka bola vymazaná";
|
|
||||||
"meeting_schedule_description_title" = "Popis";
|
|
||||||
"meeting_schedule_edit_title" = "Upraviť schôdzku";
|
|
||||||
"meeting_schedule_cancel_dialog_title" = "Zrušiť schôdzku?";
|
|
||||||
"meeting_schedule_cancel_dialog_message" = "Želáte si zrušiť schôdzku a odoslať oznámenie všetkým účastníkom?";
|
|
||||||
"meeting_info_created_toast" = "Schôdzka bola vytvorená";
|
|
||||||
"meeting_info_updated_toast" = "Schôdzka bola aktualizovaná";
|
|
||||||
"meeting_failed_to_schedule_toast" = "Naplánovanie schôdzky zlyhalo!";
|
|
||||||
"meeting_failed_to_send_invites_toast" = "Odoslanie všetkých pozvánok na schôdzku zlyhalo!";
|
|
||||||
"meeting_schedule_title" = "Nová schôdzka";
|
|
||||||
"meeting_schedule_timezone_title" = "Časová zóna";
|
|
||||||
"meeting_waiting_room_join" = "Pripojiť sa";
|
|
||||||
"meeting_waiting_room_joining_title" = "Prebieha pripojenie";
|
|
||||||
"message_reaction_click_to_remove_label" = "Kliknutím odstránite";
|
|
||||||
"message_forwarded_label" = "Preposlané";
|
|
||||||
"call_waiting_for_encryption_info" = "Čaká sa na šifrovanie…";
|
|
||||||
"call_zrtp_end_to_end_encrypted" = "Koncovo šifrované pomocou ZRTP";
|
|
||||||
"call_dialog_zrtp_validate_trust_title" = "Overiť zariadenie";
|
|
||||||
"meeting_schedule_send_invitations_title" = "Odoslať pozvánku účastníkom";
|
|
||||||
"call_audio_device_type_headphones" = "Slúchadlá";
|
|
||||||
"meeting_schedule_subject_hint" = "Pridať názov…";
|
|
||||||
"call_action_show_dialer" = "Číselník (numerická klávesnica)";
|
|
||||||
"call_srtp_point_to_point_encrypted" = "Koncové šifrovanie pomocou SRTP";
|
|
||||||
"meetings_list_no_meeting_for_today" = "Nadnes nie je naplánovaná žiadna schôdzka";
|
|
||||||
"meeting_info_cancelled_toast" = "Schôdzka bola zrušená";
|
|
||||||
"meeting_failed_to_send_part_of_invites_toast" = "Odoslanie pozvánok na schôdzku niektorým účastníkom zlyhalo!";
|
|
||||||
"account_settings_dialog_invalid_password_message" = "Pripojenie sa nepodarilo, pretože chýba alebo je neplatné overenie účtu\n%@.\n\nPokúste sa znovu zadať heslo alebo skontrolovať konfiguráciu účtu v nastaveniach.";
|
|
||||||
": %@" = ": %@";
|
|
||||||
"[https://sip.linphone.org](https://sip.linphone.org)" = "[https://sip.linphone.org](https://sip.linphone.org)";
|
|
||||||
"[linphone.org/contact](https://linphone.org/contact)" = "[linphone.org/contact](https://linphone.org/contact)";
|
|
||||||
"*" = "*";
|
|
||||||
"assistant_dialog_general_terms_and_privacy_policy_message" = "Pokračovaním akceptujete naše %1$s a %2$s.";
|
|
||||||
"assistant_third_party_sip_account_create_linphone_account" = "Preferujem vytvorenie účtu";
|
|
||||||
"assistant_permissions_subtitle" = "Pre plné využívanie %@ potrebujeme udelenie nasledovných oprávnení:";
|
|
||||||
"assistant_account_creation_sms_confirmation_explanation" = "Poslali sme Vám overovací kód na Vaše telefónne číslo %@. Zadajte nižšie, prosím, verifikačný kód:";
|
|
||||||
"assistant_forgotten_password" = "Zabudnuté heslo?";
|
|
||||||
"assistant_invalid_uri_toast" = "Neplatná URI adresa";
|
|
||||||
"call_history_deleted_toast" = "História bola vymazaná";
|
|
||||||
"call_stats_video_title" = "Video (obraz)";
|
|
||||||
"call_transfer_in_progress_toast" = "Hovor je presmerovaný";
|
|
||||||
"call_transfer_failed_toast" = "Presmerovanie hovoru zlyhalo!";
|
|
||||||
"call_transfer_successful_toast" = "Hovor bol úspešne presmerovaný";
|
|
||||||
"conference_share_link_title" = "Zdieľať pozvánku";
|
|
||||||
"conference_call_empty" = "Čakanie na ďalších účastníkov…";
|
|
||||||
"conference_action_screen_sharing" = "Zdieľať obrazovku";
|
|
||||||
"conference_action_show_participants" = "Účastníci";
|
|
||||||
"conference_failed_to_create_group_call_toast" = "Vytvorenie skupinového hovoru zlyhalo!";
|
|
||||||
"conference_participant_joining_text" = "Pripojovanie…";
|
|
||||||
"conference_participant_paused_text" = "Pozastavené";
|
|
||||||
"conference_layout_active_speaker" = "Hovoriaci (rečník)";
|
|
||||||
"conference_layout_audio_only" = "Iba zvuk";
|
|
||||||
"connection_error_for_non_default_account" = "Chyba pripojenia účtu(-ov)";
|
|
||||||
"default_account_disabled" = "Zvolený účet je momentálne zakázaný";
|
|
||||||
"generic_address_picker_suggestions_list_title" = "Návrhy";
|
|
||||||
"help_about_title" = "O aplikácii Linphone";
|
|
||||||
"help_about_contribute_translations_title" = "Prispieť do prekladu Linphone";
|
|
||||||
"help_about_open_source_licenses_subtitle" = "© Belledonne Communications 2010-2024";
|
|
||||||
"help_about_open_source_licenses_title" = "GNU General Public License v3.0";
|
|
||||||
"list_filter_no_result_found" = "Žiadne výsledky…";
|
|
||||||
"menu_add_address_to_contacts" = "Pridať do kontaktov";
|
|
||||||
"menu_see_existing_contact" = "Zobraziť kontakt";
|
|
||||||
"menu_copy_phone_number" = "Kopírovať telefónne číslo";
|
|
||||||
"menu_delete_history" = "Vymazať históriu";
|
|
||||||
"menu_invite" = "Pozvať";
|
|
||||||
"menu_resend_chat_message" = "Znovu odoslať";
|
|
||||||
"menu_show_imdn" = "Stav doručenia";
|
|
||||||
"menu_forward_chat_message" = "Preposlať";
|
|
||||||
"menu_copy_chat_message" = "Kopírovať";
|
|
||||||
"network_not_reachable" = "Nie ste pripojený k internetu";
|
|
||||||
"operation_in_progress_overlay" = "Prebieha operácia, prosím, čakajte";
|
|
||||||
"recordings_title" = "Nahrávky";
|
|
||||||
"settings_contacts_ldap_max_results_title" = "Maximálny počet výsledkov";
|
|
||||||
"welcome_page_subtitle" = "v %@";
|
|
||||||
"call_action_blind_transfer" = "Presmerovať";
|
|
||||||
"conversation_message_meeting_updated_label" = "Schôdzka bola aktualizovaná";
|
|
||||||
"meeting_info_delete" = "Vymazať schôdzku";
|
|
||||||
|
|
@ -377,7 +377,7 @@
|
||||||
"conversation_ephemeral_messages_duration_one_week" = "1 тиждень";
|
"conversation_ephemeral_messages_duration_one_week" = "1 тиждень";
|
||||||
"conversation_ephemeral_messages_duration_three_days" = "3 доби";
|
"conversation_ephemeral_messages_duration_three_days" = "3 доби";
|
||||||
"conversation_ephemeral_messages_duration_one_day" = "1 доба";
|
"conversation_ephemeral_messages_duration_one_day" = "1 доба";
|
||||||
"dialog_confirm" = "ОК";
|
"dialog_ok" = "ОК";
|
||||||
"welcome_page_title" = "Ласкаво просимо";
|
"welcome_page_title" = "Ласкаво просимо";
|
||||||
": %@" = ": %@";
|
": %@" = ": %@";
|
||||||
"[https://sip.linphone.org](https://sip.linphone.org)" = "[https://sip.linphone.org](https://sip.linphone.org)";
|
"[https://sip.linphone.org](https://sip.linphone.org)" = "[https://sip.linphone.org](https://sip.linphone.org)";
|
||||||
|
|
@ -456,9 +456,9 @@
|
||||||
"message_delivery_info_read_title" = "Читати";
|
"message_delivery_info_read_title" = "Читати";
|
||||||
"message_delivery_info_received_title" = "Отримано";
|
"message_delivery_info_received_title" = "Отримано";
|
||||||
"message_delivery_info_sent_title" = "Відправлено";
|
"message_delivery_info_sent_title" = "Відправлено";
|
||||||
"message_meeting_invitation_cancelled_notification" = "Нараду скасовано";
|
"message_meeting_invitation_cancelled_notification" = "📅 Нараду скасовано";
|
||||||
"message_meeting_invitation_notification" = "Вас запрошено на нараду";
|
"message_meeting_invitation_notification" = "📅 Вас запрошено на нараду";
|
||||||
"message_meeting_invitation_updated_notification" = "Нараду оновлено";
|
"message_meeting_invitation_updated_notification" = "📅 Нараду оновлено";
|
||||||
"message_reactions_info_all_title" = "Реакції";
|
"message_reactions_info_all_title" = "Реакції";
|
||||||
"network_reachable_again" = "Мережа знову доступна";
|
"network_reachable_again" = "Мережа знову доступна";
|
||||||
"None" = "Жоден";
|
"None" = "Жоден";
|
||||||
|
|
@ -522,4 +522,3 @@
|
||||||
"conversations_files_waiting_to_be_shared_single" = "1 файл очікує на спільний доступ";
|
"conversations_files_waiting_to_be_shared_single" = "1 файл очікує на спільний доступ";
|
||||||
"conversations_files_waiting_to_be_shared_multiple" = "%@ файлів, що очікують на спільний доступ";
|
"conversations_files_waiting_to_be_shared_multiple" = "%@ файлів, що очікують на спільний доступ";
|
||||||
"conversation_ephemeral_messages_duration_multiple_days" = "%d днів";
|
"conversation_ephemeral_messages_duration_multiple_days" = "%d днів";
|
||||||
"authentication_id" = "Ідентифікатор автентифікації (якщо відрізняється)";
|
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@
|
||||||
"dialog_deny" = "拒绝";
|
"dialog_deny" = "拒绝";
|
||||||
"dialog_install" = "安装";
|
"dialog_install" = "安装";
|
||||||
"dialog_no" = "否";
|
"dialog_no" = "否";
|
||||||
"dialog_confirm" = "Confirm";
|
"dialog_ok" = "OK";
|
||||||
"dialog_yes" = "是";
|
"dialog_yes" = "是";
|
||||||
"drawer_menu_account_connection_status_cleared" = "禁用";
|
"drawer_menu_account_connection_status_cleared" = "禁用";
|
||||||
"drawer_menu_account_connection_status_connected" = "已连接";
|
"drawer_menu_account_connection_status_connected" = "已连接";
|
||||||
|
|
@ -362,5 +362,3 @@
|
||||||
"menu_show_imdn" = "送达状态";
|
"menu_show_imdn" = "送达状态";
|
||||||
"conversation_end_to_end_encrypted_event_title" = "端到端加密聊天";
|
"conversation_end_to_end_encrypted_event_title" = "端到端加密聊天";
|
||||||
"conversation_end_to_end_encrypted_event_subtitle" = "此聊天中的消息是端对端e2e加密的。只有您的对话方才能解密它们。";
|
"conversation_end_to_end_encrypted_event_subtitle" = "此聊天中的消息是端对端e2e加密的。只有您的对话方才能解密它们。";
|
||||||
"authentication_id" = "认证ID";
|
|
||||||
"settings_contacts_ldap_search_filter_title" = "搜索base(不能为空) 过滤";
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,10 @@
|
||||||
<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">srtp</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">
|
||||||
|
<entry name="friendlist_subscription_enabled" overwrite="true">1</entry>
|
||||||
|
</section>
|
||||||
</config>
|
</config>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
<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="remote_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>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ 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_rfc2833=1
|
||||||
use_info=1
|
use_info=1
|
||||||
rls_uri=sips:rls@sip.linphone.org
|
|
||||||
|
|
||||||
[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"
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,20 @@ zrtp_key_agreements_suites=MS_ZRTP_KEY_AGREEMENT_K255_MLK512,MS_ZRTP_KEY_AGREEME
|
||||||
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
|
||||||
|
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=0
|
disable_ringing=1
|
||||||
|
|
||||||
[audio]
|
[audio]
|
||||||
|
|
||||||
[video]
|
[video]
|
||||||
auto_resize_preview_to_keep_ratio=1
|
auto_resize_preview_to_keep_ratio=1
|
||||||
max_conference_size=vga
|
max_conference_size=vga
|
||||||
|
automatically_accept=1
|
||||||
|
automatically_initiate=0
|
||||||
|
|
||||||
[misc]
|
[misc]
|
||||||
enable_basic_to_client_group_chat_room_migration=0
|
enable_basic_to_client_group_chat_room_migration=0
|
||||||
|
|
@ -46,7 +49,6 @@ record_aware=1
|
||||||
|
|
||||||
[account_creator]
|
[account_creator]
|
||||||
url=https://subscribe.linphone.org/api/
|
url=https://subscribe.linphone.org/api/
|
||||||
backend=1
|
|
||||||
|
|
||||||
[lime]
|
[lime]
|
||||||
lime_update_threshold=86400
|
lime_update_threshold=86400
|
||||||
|
|
|
||||||
|
|
@ -20,19 +20,20 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct SplashScreen: View {
|
struct SplashScreen: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
GeometryReader { _ in
|
||||||
Color.white
|
VStack {
|
||||||
.ignoresSafeArea()
|
Spacer()
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Image("linphone")
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
|
||||||
Image("linphone")
|
|
||||||
.resizable()
|
|
||||||
.renderingMode(.template)
|
|
||||||
.aspectRatio(contentMode: .fit)
|
|
||||||
.frame(width: 240, height: 128)
|
|
||||||
.foregroundColor(ThemeManager.shared.currentTheme.main500)
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
||||||
.ignoresSafeArea(.all)
|
.ignoresSafeArea(.all)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -195,13 +195,17 @@ class TelecomManager: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeRecordFilePath(address: String) -> String {
|
private func makeRecordFilePath() -> String {
|
||||||
var filePath = "call_recording_sip_" + address.dropFirst(4) + "_on_"
|
var filePath = "recording_"
|
||||||
|
let now = Date()
|
||||||
|
let dateFormat = DateFormatter()
|
||||||
|
dateFormat.dateFormat = "E-d-MMM-yyyy-HH-mm-ss"
|
||||||
|
let date = dateFormat.string(from: now)
|
||||||
|
filePath = filePath.appending("\(date).mkv")
|
||||||
|
|
||||||
filePath = filePath.appending("\(Int(Date().timeIntervalSince1970)).mkv")
|
let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
|
||||||
|
let writablePath = paths[0]
|
||||||
let writablePath = FileUtil.sharedContainerUrl().appendingPathComponent("Library/Recordings/\(filePath)")
|
return writablePath.appending("/\(filePath)")
|
||||||
return writablePath.path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func doCall(core: Core, addr: Address, isSas: Bool, isVideo: Bool, isConference: Bool = false) throws {
|
func doCall(core: Core, addr: Address, isSas: Bool, isVideo: Bool, isConference: Bool = false) throws {
|
||||||
|
|
@ -233,7 +237,7 @@ class TelecomManager: ObservableObject {
|
||||||
// Log.directLog(BCTBX_LOG_DEBUG, text: "record file path: \(writablePath)")
|
// Log.directLog(BCTBX_LOG_DEBUG, text: "record file path: \(writablePath)")
|
||||||
// lcallParams.recordFile = writablePath
|
// lcallParams.recordFile = writablePath
|
||||||
|
|
||||||
lcallParams.recordFile = makeRecordFilePath(address: addr.asStringUriOnly())
|
lcallParams.recordFile = makeRecordFilePath()
|
||||||
|
|
||||||
if isSas {
|
if isSas {
|
||||||
lcallParams.mediaEncryption = .ZRTP
|
lcallParams.mediaEncryption = .ZRTP
|
||||||
|
|
@ -288,7 +292,7 @@ class TelecomManager: ObservableObject {
|
||||||
func acceptCall(core: Core, call: Call, hasVideo: Bool) {
|
func acceptCall(core: Core, call: Call, hasVideo: Bool) {
|
||||||
do {
|
do {
|
||||||
let callParams = try core.createCallParams(call: call)
|
let callParams = try core.createCallParams(call: call)
|
||||||
callParams.recordFile = makeRecordFilePath(address: call.remoteAddress?.asStringUriOnly() ?? "")
|
callParams.recordFile = makeRecordFilePath()
|
||||||
callParams.videoEnabled = hasVideo
|
callParams.videoEnabled = hasVideo
|
||||||
/*if (ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference")) {
|
/*if (ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference")) {
|
||||||
let low_bandwidth = (AppManager.network() == .network_2g)
|
let low_bandwidth = (AppManager.network() == .network_2g)
|
||||||
|
|
@ -338,13 +342,11 @@ class TelecomManager: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
func terminateCall(call: Call) {
|
func terminateCall(call: Call) {
|
||||||
CoreContext.shared.doOnCoreQueue { _ in
|
do {
|
||||||
do {
|
try call.terminate()
|
||||||
try call.terminate()
|
Log.info("Call terminated")
|
||||||
Log.info("Call terminated")
|
} catch {
|
||||||
} catch {
|
Log.error("Failed to terminate call failed because \(error)")
|
||||||
Log.error("Failed to terminate call failed because \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -433,7 +435,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 !callInProgress && participantsInvited {
|
||||||
if let remoteAddress = call.remoteAddress {
|
if let remoteAddress = call.remoteAddress {
|
||||||
let uuid = UUID()
|
let uuid = UUID()
|
||||||
|
|
@ -493,36 +494,38 @@ class TelecomManager: ObservableObject {
|
||||||
|
|
||||||
let isRecordingByRemoteTmp = call.remoteParams?.isRecording ?? false
|
let isRecordingByRemoteTmp = call.remoteParams?.isRecording ?? false
|
||||||
|
|
||||||
let displayName: String
|
if isRecordingByRemoteTmp && ToastViewModel.shared.toastMessage.isEmpty {
|
||||||
let friend = ContactsManager.shared.getFriendWithAddress(address: call.remoteAddress)
|
|
||||||
|
|
||||||
if let name = friend?.address?.displayName {
|
|
||||||
displayName = name
|
|
||||||
} else if let name = call.remoteAddress?.displayName {
|
|
||||||
displayName = name
|
|
||||||
} else if let username = call.remoteAddress?.username {
|
|
||||||
displayName = username
|
|
||||||
} else if let uri = call.remoteAddress?.asStringUriOnly() {
|
|
||||||
displayName = String(uri.dropFirst(4))
|
|
||||||
} else {
|
|
||||||
displayName = "Unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.isRecordingByRemote = isRecordingByRemoteTmp
|
|
||||||
|
|
||||||
if isRecordingByRemoteTmp {
|
var displayName = ""
|
||||||
ToastViewModel.shared.show("\(displayName) is recording")
|
let friend = ContactsManager.shared.getFriendWithAddress(address: call.remoteAddress!)
|
||||||
} else if let toast = ToastViewModel.shared.toast,
|
if friend != nil && friend!.address != nil && friend!.address!.displayName != nil {
|
||||||
toast.message.contains("is recording") {
|
displayName = friend!.address!.displayName!
|
||||||
ToastViewModel.shared.hide()
|
} else {
|
||||||
|
if call.remoteAddress!.displayName != nil {
|
||||||
|
displayName = call.remoteAddress!.displayName!
|
||||||
|
} else if call.remoteAddress!.username != nil {
|
||||||
|
displayName = call.remoteAddress!.username!
|
||||||
|
} else {
|
||||||
|
displayName = String(call.remoteAddress!.asStringUriOnly().dropFirst(4))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.isRecordingByRemote = isRecordingByRemoteTmp
|
||||||
|
ToastViewModel.shared.toastMessage = "\(displayName) is recording"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info("[Call] Call is recording by \(call.remoteAddress!.asStringUriOnly())")
|
||||||
}
|
}
|
||||||
|
|
||||||
if isRecordingByRemoteTmp {
|
if !isRecordingByRemoteTmp && ToastViewModel.shared.toastMessage.contains("is recording") {
|
||||||
Log.info("[Call] Call is recording by \(call.remoteAddress?.asStringUriOnly() ?? "")")
|
DispatchQueue.main.async {
|
||||||
} else {
|
self.isRecordingByRemote = isRecordingByRemoteTmp
|
||||||
Log.info("[Call] Recording is stopped by \(call.remoteAddress?.asStringUriOnly() ?? "")")
|
ToastViewModel.shared.toastMessage = ""
|
||||||
|
ToastViewModel.shared.displayToast = false
|
||||||
|
}
|
||||||
|
Log.info("[Call] Recording is stopped by \(call.remoteAddress!.asStringUriOnly())")
|
||||||
}
|
}
|
||||||
|
|
||||||
if cstate == Call.State.PausedByRemote {
|
if cstate == Call.State.PausedByRemote {
|
||||||
|
|
@ -620,11 +623,6 @@ class TelecomManager: ObservableObject {
|
||||||
|
|
||||||
Log.info("CallKit: outgoing call started connecting with uuid \(uuid!) and callId \(callId)")
|
Log.info("CallKit: outgoing call started connecting with uuid \(uuid!) and callId \(callId)")
|
||||||
providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid!)
|
providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid!)
|
||||||
} else if callId != "" && cstate == .OutgoingInit {
|
|
||||||
if let uuidTmp = providerDelegate.uuids["\(callId)"] {
|
|
||||||
providerDelegate.uuids.removeValue(forKey: callId)
|
|
||||||
providerDelegate.uuids.updateValue(uuidTmp, forKey: "")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
referedToCall = callId
|
referedToCall = callId
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Combine
|
|
||||||
|
|
||||||
struct LoginFragment: View {
|
struct LoginFragment: View {
|
||||||
|
|
||||||
@ObservedObject private var coreContext = CoreContext.shared
|
@ObservedObject private var coreContext = CoreContext.shared
|
||||||
|
|
||||||
@StateObject private var accountLoginViewModel = AccountLoginViewModel()
|
@StateObject private var accountLoginViewModel = AccountLoginViewModel()
|
||||||
@StateObject private var keyboard = KeyboardResponder()
|
|
||||||
|
|
||||||
@State private var isSecured: Bool = true
|
@State private var isSecured: Bool = true
|
||||||
|
|
||||||
|
|
@ -39,8 +37,6 @@ struct LoginFragment: View {
|
||||||
@State private var isLinkSIPActive = false
|
@State private var isLinkSIPActive = false
|
||||||
@State private var isLinkREGActive = false
|
@State private var isLinkREGActive = false
|
||||||
|
|
||||||
@State var isShowHelpFragment = false
|
|
||||||
|
|
||||||
var isShowBack = false
|
var isShowBack = false
|
||||||
|
|
||||||
var onBackPressed: (() -> Void)?
|
var onBackPressed: (() -> Void)?
|
||||||
|
|
@ -70,33 +66,25 @@ struct LoginFragment: View {
|
||||||
let contentPopup3 = Text(.init(splitMsg[1]))
|
let contentPopup3 = Text(.init(splitMsg[1]))
|
||||||
let contentPopup4 = Text(.init(privacyPolicy)).underline()
|
let contentPopup4 = Text(.init(privacyPolicy)).underline()
|
||||||
let contentPopup5 = Text(.init(splitMsg[2]))
|
let contentPopup5 = Text(.init(splitMsg[2]))
|
||||||
PopupView(
|
PopupView(isShowPopup: $isShowPopup,
|
||||||
isShowPopup: $isShowPopup,
|
title: Text("assistant_dialog_general_terms_and_privacy_policy_title"),
|
||||||
title: Text("assistant_dialog_general_terms_and_privacy_policy_title"),
|
content: contentPopup1 + contentPopup2 + contentPopup3 + contentPopup4 + contentPopup5,
|
||||||
content: contentPopup1 + contentPopup2 + contentPopup3 + contentPopup4 + contentPopup5,
|
titleFirstButton: Text("dialog_deny"),
|
||||||
titleFirstButton: nil,
|
actionFirstButton: {self.isShowPopup.toggle()},
|
||||||
actionFirstButton: {},
|
titleSecondButton: Text("dialog_accept"),
|
||||||
titleSecondButton: Text("dialog_accept"),
|
actionSecondButton: {acceptGeneralTerms()})
|
||||||
actionSecondButton: { acceptGeneralTerms() },
|
|
||||||
titleThirdButton: Text("dialog_deny"),
|
|
||||||
actionThirdButton: { self.isShowPopup.toggle() }
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
self.isShowPopup.toggle()
|
self.isShowPopup.toggle()
|
||||||
}
|
}
|
||||||
} else { // backup just in case
|
} else { // backup just in case
|
||||||
PopupView(
|
PopupView(isShowPopup: $isShowPopup,
|
||||||
isShowPopup: $isShowPopup,
|
title: Text("assistant_dialog_general_terms_and_privacy_policy_title"),
|
||||||
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))),
|
||||||
content: Text(.init(String(format: String(localized: "assistant_dialog_general_terms_and_privacy_policy_message"), generalTerms, privacyPolicy))),
|
titleFirstButton: Text("dialog_deny"),
|
||||||
titleFirstButton: nil,
|
actionFirstButton: {self.isShowPopup.toggle()},
|
||||||
actionFirstButton: {},
|
titleSecondButton: Text("dialog_accept"),
|
||||||
titleSecondButton: Text("dialog_accept"),
|
actionSecondButton: {acceptGeneralTerms()})
|
||||||
actionSecondButton: { acceptGeneralTerms() },
|
|
||||||
titleThirdButton: Text("dialog_deny"),
|
|
||||||
actionThirdButton: { self.isShowPopup.toggle() }
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
self.isShowPopup.toggle()
|
self.isShowPopup.toggle()
|
||||||
|
|
@ -105,14 +93,6 @@ struct LoginFragment: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isShowHelpFragment {
|
|
||||||
HelpFragment(
|
|
||||||
isShowHelpFragment: $isShowHelpFragment
|
|
||||||
)
|
|
||||||
.transition(.move(edge: .trailing))
|
|
||||||
.zIndex(3)
|
|
||||||
}
|
|
||||||
|
|
||||||
if coreContext.loggingInProgress {
|
if coreContext.loggingInProgress {
|
||||||
PopupLoadingView()
|
PopupLoadingView()
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
|
|
@ -149,26 +129,6 @@ struct LoginFragment: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button {
|
|
||||||
withAnimation {
|
|
||||||
isShowHelpFragment = true
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image("question")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
|
|
||||||
Text("help_title")
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.default_text_style_orange_600(styleSize: 15)
|
|
||||||
.frame(height: 35)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text("assistant_account_login")
|
Text("assistant_account_login")
|
||||||
|
|
@ -353,7 +313,7 @@ struct LoginFragment: View {
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
.padding(.horizontal, 10)
|
.padding(.horizontal, 10)
|
||||||
|
|
||||||
NavigationLink(destination: RegisterFragment(), isActive: $isLinkREGActive, label: { Text("assistant_account_register")
|
NavigationLink(destination: RegisterFragment(registerViewModel: RegisterViewModel()), isActive: $isLinkREGActive, label: { Text("assistant_account_register")
|
||||||
.default_text_style_white_600(styleSize: 20)
|
.default_text_style_white_600(styleSize: 20)
|
||||||
.frame(height: 35)
|
.frame(height: 35)
|
||||||
})
|
})
|
||||||
|
|
@ -387,7 +347,6 @@ struct LoginFragment: View {
|
||||||
.clipped()
|
.clipped()
|
||||||
}
|
}
|
||||||
.frame(minHeight: geometry.size.height)
|
.frame(minHeight: geometry.size.height)
|
||||||
.padding(.bottom, keyboard.currentHeight)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func acceptGeneralTerms() {
|
func acceptGeneralTerms() {
|
||||||
|
|
|
||||||
|
|
@ -145,21 +145,20 @@ struct ProfileModeFragment: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.isShowPopup {
|
if self.isShowPopup {
|
||||||
PopupView(
|
PopupView(isShowPopup: $isShowPopup,
|
||||||
isShowPopup: $isShowPopup,
|
title: Text(isShowPopupForDefault ? "Default mode" : "Interoperable mode"),
|
||||||
title: Text(isShowPopupForDefault ? "Default mode" : "Interoperable mode"),
|
content: Text(
|
||||||
content: Text(
|
isShowPopupForDefault
|
||||||
isShowPopupForDefault
|
? "Texte explicatif du default mode : lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
? "Texte explicatif du default mode : lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
+ "Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula."
|
||||||
+ "Etiam velit sapien, egestas sit amet dictum eget, condimentum a ligula."
|
: "Texte explicatif du interoperable mode : lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
||||||
: "Texte explicatif du interoperable mode : lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
+ " 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: nil,
|
actionSecondButton: {
|
||||||
actionSecondButton: {},
|
self.isShowPopup.toggle()
|
||||||
titleThirdButton: Text("dialog_close"),
|
}
|
||||||
actionThirdButton: { self.isShowPopup.toggle() }
|
|
||||||
)
|
)
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,10 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct RegisterFragment: View {
|
struct RegisterFragment: View {
|
||||||
|
|
||||||
|
@ObservedObject var registerViewModel: RegisterViewModel
|
||||||
@ObservedObject var sharedMainViewModel = SharedMainViewModel.shared
|
@ObservedObject var sharedMainViewModel = SharedMainViewModel.shared
|
||||||
|
|
||||||
@StateObject private var registerViewModel = RegisterViewModel()
|
|
||||||
|
|
||||||
@StateObject private var keyboard = KeyboardResponder()
|
|
||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
@State private var isSecured: Bool = true
|
@State private var isSecured: Bool = true
|
||||||
|
|
@ -57,21 +55,22 @@ struct RegisterFragment: View {
|
||||||
let titlePopup = Text("assistant_dialog_confirm_phone_number_title")
|
let titlePopup = Text("assistant_dialog_confirm_phone_number_title")
|
||||||
let contentPopup = Text(String(format: String(localized: "assistant_dialog_confirm_phone_number_message"), registerViewModel.phoneNumber))
|
let contentPopup = Text(String(format: String(localized: "assistant_dialog_confirm_phone_number_message"), registerViewModel.phoneNumber))
|
||||||
|
|
||||||
|
|
||||||
PopupView(
|
PopupView(
|
||||||
isShowPopup: $isShowPopup,
|
isShowPopup: $isShowPopup,
|
||||||
title: titlePopup,
|
title: titlePopup,
|
||||||
content: contentPopup,
|
content: contentPopup,
|
||||||
titleFirstButton: nil,
|
titleFirstButton: Text("dialog_cancel"),
|
||||||
actionFirstButton: {},
|
actionFirstButton: {
|
||||||
|
self.isShowPopup = false
|
||||||
|
},
|
||||||
titleSecondButton: Text("dialog_continue"),
|
titleSecondButton: Text("dialog_continue"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
self.isShowPopup = false
|
self.isShowPopup = false
|
||||||
registerViewModel.createInProgress = true
|
registerViewModel.createInProgress = true
|
||||||
registerViewModel.startAccountCreation()
|
registerViewModel.startAccountCreation()
|
||||||
registerViewModel.phoneNumberConfirmedByUser()
|
registerViewModel.phoneNumberConfirmedByUser()
|
||||||
},
|
}
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
|
||||||
actionThirdButton: { self.isShowPopup = false },
|
|
||||||
)
|
)
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|
@ -270,13 +269,13 @@ struct RegisterFragment: View {
|
||||||
})
|
})
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
.padding(.vertical, 10)
|
.padding(.vertical, 10)
|
||||||
.background((registerViewModel.username.isEmpty || registerViewModel.dialPlanValueSelected == "---" || registerViewModel.phoneNumber.isEmpty || registerViewModel.passwd.isEmpty) ? Color.orangeMain100 : Color.orangeMain500)
|
.background((registerViewModel.username.isEmpty || registerViewModel.phoneNumber.isEmpty || registerViewModel.passwd.isEmpty) ? Color.orangeMain100 : Color.orangeMain500)
|
||||||
.cornerRadius(60)
|
.cornerRadius(60)
|
||||||
.disabled(!registerViewModel.isLinkActive)
|
.disabled(!registerViewModel.isLinkActive)
|
||||||
.padding(.bottom)
|
.padding(.bottom)
|
||||||
.simultaneousGesture(
|
.simultaneousGesture(
|
||||||
TapGesture().onEnded {
|
TapGesture().onEnded {
|
||||||
if !(registerViewModel.username.isEmpty || registerViewModel.dialPlanValueSelected == "---" || registerViewModel.phoneNumber.isEmpty || registerViewModel.passwd.isEmpty) {
|
if !(registerViewModel.username.isEmpty || registerViewModel.phoneNumber.isEmpty || registerViewModel.passwd.isEmpty) {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
self.isShowPopup = true
|
self.isShowPopup = true
|
||||||
}
|
}
|
||||||
|
|
@ -348,8 +347,11 @@ struct RegisterFragment: View {
|
||||||
.clipped()
|
.clipped()
|
||||||
}
|
}
|
||||||
.frame(minHeight: geometry.size.height)
|
.frame(minHeight: geometry.size.height)
|
||||||
.padding(.bottom, keyboard.currentHeight)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
RegisterFragment(registerViewModel: RegisterViewModel())
|
||||||
|
}
|
||||||
|
|
||||||
// swiftlint:enable line_length
|
// swiftlint:enable line_length
|
||||||
|
|
|
||||||
|
|
@ -24,61 +24,25 @@ struct ThirdPartySipAccountLoginFragment: View {
|
||||||
@ObservedObject private var coreContext = CoreContext.shared
|
@ObservedObject private var coreContext = CoreContext.shared
|
||||||
@ObservedObject var accountLoginViewModel: AccountLoginViewModel
|
@ObservedObject var accountLoginViewModel: AccountLoginViewModel
|
||||||
|
|
||||||
@StateObject private var keyboard = KeyboardResponder()
|
|
||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
@State private var isSecured: Bool = true
|
@State private var isSecured: Bool = true
|
||||||
@State private var advancedSettingsIsOpen: Bool = false
|
|
||||||
|
|
||||||
@FocusState var isNameFocused: Bool
|
@FocusState var isNameFocused: Bool
|
||||||
@FocusState var isPasswordFocused: Bool
|
@FocusState var isPasswordFocused: Bool
|
||||||
@FocusState var isDomainFocused: Bool
|
@FocusState var isDomainFocused: Bool
|
||||||
@FocusState var isDisplayNameFocused: Bool
|
@FocusState var isDisplayNameFocused: Bool
|
||||||
@FocusState var isSipProxyUrlFocused: Bool
|
|
||||||
@FocusState var isAuthIdFocused: Bool
|
|
||||||
@FocusState var isOutboundProxyFocused: Bool
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
ScrollViewReader { proxy in
|
if #available(iOS 16.4, *) {
|
||||||
if #available(iOS 16.4, *) {
|
ScrollView(.vertical) {
|
||||||
ScrollView(.vertical) {
|
innerScrollView(geometry: geometry)
|
||||||
innerScrollView(geometry: geometry)
|
}
|
||||||
}
|
.scrollBounceBehavior(.basedOnSize)
|
||||||
.scrollBounceBehavior(.basedOnSize)
|
} else {
|
||||||
.onChange(of: isAuthIdFocused) { field in
|
ScrollView(.vertical) {
|
||||||
if field {
|
innerScrollView(geometry: geometry)
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
||||||
proxy.scrollTo(2, anchor: .top)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onChange(of: isOutboundProxyFocused) { field in
|
|
||||||
if field {
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
||||||
proxy.scrollTo(2, anchor: .top)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ScrollView(.vertical) {
|
|
||||||
innerScrollView(geometry: geometry)
|
|
||||||
}
|
|
||||||
.onChange(of: isAuthIdFocused) { field in
|
|
||||||
if field {
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
||||||
proxy.scrollTo(2, anchor: .top)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onChange(of: isOutboundProxyFocused) { field in
|
|
||||||
if field {
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
||||||
proxy.scrollTo(2, anchor: .top)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -244,74 +208,6 @@ struct ThirdPartySipAccountLoginFragment: View {
|
||||||
.stroke(Color.gray200, lineWidth: 1)
|
.stroke(Color.gray200, lineWidth: 1)
|
||||||
)
|
)
|
||||||
.padding(.bottom)
|
.padding(.bottom)
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Text("settings_advanced_title")
|
|
||||||
.default_text_style_800(styleSize: 18)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Image(advancedSettingsIsOpen ? "caret-up" : "caret-down")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
}
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.bottom, 10)
|
|
||||||
.background(.white)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
advancedSettingsIsOpen.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if advancedSettingsIsOpen {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("authentication_id")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
TextField("authentication_id", text: $accountLoginViewModel.authId)
|
|
||||||
.id(1)
|
|
||||||
.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(isAuthIdFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.focused($isAuthIdFocused)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("account_settings_sip_proxy_url_title")
|
|
||||||
.default_text_style_700(styleSize: 15)
|
|
||||||
.padding(.bottom, -5)
|
|
||||||
|
|
||||||
TextField("account_settings_sip_proxy_url_title", text: $accountLoginViewModel.outboundProxy)
|
|
||||||
.id(2)
|
|
||||||
.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(isOutboundProxyFocused ? Color.orangeMain500 : Color.gray200, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.focused($isOutboundProxyFocused)
|
|
||||||
}
|
|
||||||
.padding(.bottom)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: SharedMainViewModel.shared.maxWidth)
|
.frame(maxWidth: SharedMainViewModel.shared.maxWidth)
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
|
|
@ -345,7 +241,6 @@ struct ThirdPartySipAccountLoginFragment: View {
|
||||||
.clipped()
|
.clipped()
|
||||||
}
|
}
|
||||||
.frame(minHeight: geometry.size.height)
|
.frame(minHeight: geometry.size.height)
|
||||||
.padding(.bottom, keyboard.currentHeight)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,6 @@ class AccountLoginViewModel: ObservableObject {
|
||||||
@Published var domain: String = "sip.linphone.org"
|
@Published var domain: String = "sip.linphone.org"
|
||||||
@Published var displayName: String = ""
|
@Published var displayName: String = ""
|
||||||
@Published var transportType: String = "TLS"
|
@Published var transportType: String = "TLS"
|
||||||
@Published var authId: String = ""
|
|
||||||
@Published var outboundProxy: String = ""
|
|
||||||
|
|
||||||
private var mCoreDelegate: CoreDelegate!
|
private var mCoreDelegate: CoreDelegate!
|
||||||
|
|
||||||
|
|
@ -41,7 +39,8 @@ class AccountLoginViewModel: ObservableObject {
|
||||||
guard self.coreContext.networkStatusIsConnected else {
|
guard self.coreContext.networkStatusIsConnected else {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.coreContext.loggingInProgress = false
|
self.coreContext.loggingInProgress = false
|
||||||
ToastViewModel.shared.show("Unavailable_network")
|
ToastViewModel.shared.toastMessage = "Unavailable_network"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -84,7 +83,7 @@ class AccountLoginViewModel: ObservableObject {
|
||||||
// The realm will be determined automatically from the first register, as well as the algorithm
|
// The realm will be determined automatically from the first register, as well as the algorithm
|
||||||
let authInfo = try Factory.Instance.createAuthInfo(
|
let authInfo = try Factory.Instance.createAuthInfo(
|
||||||
username: self.username,
|
username: self.username,
|
||||||
userid: self.authId,
|
userid: "",
|
||||||
passwd: self.passwd,
|
passwd: self.passwd,
|
||||||
ha1: "",
|
ha1: "",
|
||||||
realm: "",
|
realm: "",
|
||||||
|
|
@ -101,26 +100,15 @@ class AccountLoginViewModel: ObservableObject {
|
||||||
try accountParams.setIdentityaddress(newValue: identity)
|
try accountParams.setIdentityaddress(newValue: identity)
|
||||||
|
|
||||||
// We also need to configure where the proxy server is located
|
// We also need to configure where the proxy server is located
|
||||||
var serverAddress: Address
|
let address = try Factory.Instance.createAddress(addr: String("sip:" + self.domain))
|
||||||
if (!self.outboundProxy.isEmpty) {
|
|
||||||
let server = self.outboundProxy.starts(with: "sip:") ? self.outboundProxy : String("sip:" + self.outboundProxy)
|
|
||||||
serverAddress = try Factory.Instance.createAddress(addr: server)
|
|
||||||
} else {
|
|
||||||
serverAddress = try Factory.Instance.createAddress(addr: String("sip:" + self.domain))
|
|
||||||
}
|
|
||||||
|
|
||||||
let address = serverAddress
|
|
||||||
|
|
||||||
// We use the Address object to easily set the transport protocol
|
// We use the Address object to easily set the transport protocol
|
||||||
try address.setTransport(newValue: transport)
|
try address.setTransport(newValue: transport)
|
||||||
try accountParams.setServeraddress(newValue: address)
|
try accountParams.setServeraddress(newValue: address)
|
||||||
// And we ensure the account will start the registration process
|
// And we ensure the account will start the registration process
|
||||||
accountParams.registerEnabled = true
|
accountParams.registerEnabled = true
|
||||||
|
accountParams.pushNotificationAllowed = true
|
||||||
if accountParams.pushNotificationAllowed {
|
accountParams.remotePushNotificationAllowed = true
|
||||||
accountParams.pushNotificationAllowed = true
|
|
||||||
accountParams.remotePushNotificationAllowed = true
|
|
||||||
}
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
let pushEnvironment = ".dev"
|
let pushEnvironment = ".dev"
|
||||||
#else
|
#else
|
||||||
|
|
@ -128,6 +116,10 @@ class AccountLoginViewModel: ObservableObject {
|
||||||
#endif
|
#endif
|
||||||
accountParams.pushNotificationConfig?.provider = "apns" + pushEnvironment
|
accountParams.pushNotificationConfig?.provider = "apns" + pushEnvironment
|
||||||
|
|
||||||
|
accountParams.internationalPrefix = "33"
|
||||||
|
accountParams.internationalPrefixIsoCountryCode = "FRA"
|
||||||
|
accountParams.useInternationalPrefixForCallsAndChats = true
|
||||||
|
|
||||||
self.mCoreDelegate = CoreDelegateStub(onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in
|
self.mCoreDelegate = CoreDelegateStub(onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in
|
||||||
|
|
||||||
Log.info("New registration state is \(state) for user id " +
|
Log.info("New registration state is \(state) for user id " +
|
||||||
|
|
@ -161,8 +153,6 @@ class AccountLoginViewModel: ObservableObject {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.domain = "sip.linphone.org"
|
self.domain = "sip.linphone.org"
|
||||||
self.transportType = "TLS"
|
self.transportType = "TLS"
|
||||||
self.authId = ""
|
|
||||||
self.outboundProxy = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch { NSLog(error.localizedDescription) }
|
} catch { NSLog(error.localizedDescription) }
|
||||||
|
|
|
||||||
|
|
@ -75,18 +75,15 @@ class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate {
|
||||||
core.stop()
|
core.stop()
|
||||||
try? core.start()
|
try? core.start()
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
ToastViewModel.shared.toastMessage = "Success_qr_code_validated"
|
||||||
ToastViewModel.shared.show("Success_qr_code_validated")
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.async {
|
ToastViewModel.shared.toastMessage = "Invalide URI"
|
||||||
ToastViewModel.shared.show("Invalide URI")
|
ToastViewModel.shared.displayToast.toggle()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.async {
|
ToastViewModel.shared.toastMessage = "Invalide URI"
|
||||||
ToastViewModel.shared.show("Invalide URI")
|
ToastViewModel.shared.displayToast.toggle()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class RegisterViewModel: ObservableObject {
|
||||||
@Published var displayName: String = ""
|
@Published var displayName: String = ""
|
||||||
@Published var transportType: String = "TLS"
|
@Published var transportType: String = "TLS"
|
||||||
|
|
||||||
@Published var dialPlanValueSelected: String = "---"
|
@Published var dialPlanValueSelected: String = "🇫🇷 +33"
|
||||||
|
|
||||||
private let HASHALGORITHM = "SHA-256"
|
private let HASHALGORITHM = "SHA-256"
|
||||||
|
|
||||||
|
|
@ -168,7 +168,8 @@ class RegisterViewModel: ObservableObject {
|
||||||
|
|
||||||
if !errorMessage.isEmpty {
|
if !errorMessage.isEmpty {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.show("Error: \(errorMessage)")
|
ToastViewModel.shared.toastMessage = "Error: \(errorMessage)"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,7 +257,7 @@ class RegisterViewModel: ObservableObject {
|
||||||
|
|
||||||
SharedMainViewModel.shared.dialPlansList.forEach { dial in
|
SharedMainViewModel.shared.dialPlansList.forEach { dial in
|
||||||
let countryCode = dialPlanValueSelected.components(separatedBy: "+")
|
let countryCode = dialPlanValueSelected.components(separatedBy: "+")
|
||||||
if dial?.countryCallingCode == countryCode[1] {
|
if dial.countryCallingCode == countryCode[1] {
|
||||||
dialPlan = dial
|
dialPlan = dial
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -340,7 +341,8 @@ class RegisterViewModel: ObservableObject {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.createInProgress = false
|
self.createInProgress = false
|
||||||
|
|
||||||
ToastViewModel.shared.show("Failed_push_notification_not_received_error")
|
ToastViewModel.shared.toastMessage = "Failed_push_notification_not_received_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -410,7 +412,7 @@ class RegisterViewModel: ObservableObject {
|
||||||
|
|
||||||
for dial in SharedMainViewModel.shared.dialPlansList {
|
for dial in SharedMainViewModel.shared.dialPlansList {
|
||||||
let countryCode = self.dialPlanValueSelected.components(separatedBy: "+")
|
let countryCode = self.dialPlanValueSelected.components(separatedBy: "+")
|
||||||
if dial?.countryCallingCode == countryCode[1] {
|
if dial.countryCallingCode == countryCode[1] {
|
||||||
dialPlan = dial
|
dialPlan = dial
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -428,7 +430,8 @@ class RegisterViewModel: ObservableObject {
|
||||||
Log.error("\(RegisterViewModel.TAG) Account manager services hasn't been initialized!")
|
Log.error("\(RegisterViewModel.TAG) Account manager services hasn't been initialized!")
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.show("Failed_account_register_unexpected_error")
|
ToastViewModel.shared.toastMessage = "Failed_account_register_unexpected_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -347,7 +347,7 @@ struct CallView: View {
|
||||||
.padding(.leading, 50)
|
.padding(.leading, 50)
|
||||||
.padding(.top, 35)
|
.padding(.top, 35)
|
||||||
|
|
||||||
Text(callViewModel.isConference ? "call_srtp_point_to_point_encrypted" : "call_zrtp_end_to_end_encrypted")
|
Text("call_zrtp_end_to_end_encrypted")
|
||||||
.foregroundStyle(Color.blueInfo500)
|
.foregroundStyle(Color.blueInfo500)
|
||||||
.default_text_style_white(styleSize: 12)
|
.default_text_style_white(styleSize: 12)
|
||||||
.padding(.top, 35)
|
.padding(.top, 35)
|
||||||
|
|
@ -526,10 +526,8 @@ struct CallView: View {
|
||||||
.padding(.top)
|
.padding(.top)
|
||||||
.default_text_style_white(styleSize: 22)
|
.default_text_style_white(styleSize: 22)
|
||||||
|
|
||||||
if !CorePreferences.hideSipAddresses {
|
Text(callViewModel.remoteAddressCleanedString)
|
||||||
Text(callViewModel.remoteAddressCleanedString)
|
.default_text_style_white_300(styleSize: 16)
|
||||||
.default_text_style_white_300(styleSize: 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
@ -560,10 +558,6 @@ struct CallView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
coreContext.doOnCoreQueue { core in
|
|
||||||
core.nativeVideoWindow = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if callViewModel.videoDisplayed {
|
if callViewModel.videoDisplayed {
|
||||||
if !callViewModel.isPaused && TelecomManager.shared.callInProgress
|
if !callViewModel.isPaused && TelecomManager.shared.callInProgress
|
||||||
&& !(coreContext.pipViewModel.pipController?.isPictureInPictureActive ?? false) {
|
&& !(coreContext.pipViewModel.pipController?.isPictureInPictureActive ?? false) {
|
||||||
|
|
@ -591,11 +585,6 @@ struct CallView: View {
|
||||||
core.nativePreviewWindow = view
|
core.nativePreviewWindow = view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
|
||||||
coreContext.doOnCoreQueue { core in
|
|
||||||
core.nativePreviewWindow = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.aspectRatio(callViewModel.callStatsModel.sentVideoWindow.widthFactor/callViewModel.callStatsModel.sentVideoWindow.heightFactor, contentMode: .fill)
|
.aspectRatio(callViewModel.callStatsModel.sentVideoWindow.widthFactor/callViewModel.callStatsModel.sentVideoWindow.heightFactor, contentMode: .fill)
|
||||||
.frame(maxWidth: callViewModel.callStatsModel.sentVideoWindow.widthFactor * 256,
|
.frame(maxWidth: callViewModel.callStatsModel.sentVideoWindow.widthFactor * 256,
|
||||||
maxHeight: callViewModel.callStatsModel.sentVideoWindow.heightFactor * 256)
|
maxHeight: callViewModel.callStatsModel.sentVideoWindow.heightFactor * 256)
|
||||||
|
|
@ -672,7 +661,8 @@ struct CallView: View {
|
||||||
)
|
)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.show("Success_address_copied_into_clipboard")
|
ToastViewModel.shared.toastMessage = "Success_address_copied_into_clipboard"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}, label: {
|
}, label: {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
@ -713,11 +703,6 @@ struct CallView: View {
|
||||||
core.nativePreviewWindow = view
|
core.nativePreviewWindow = view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
|
||||||
coreContext.doOnCoreQueue { core in
|
|
||||||
core.nativePreviewWindow = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.aspectRatio(callViewModel.callStatsModel.sentVideoWindow.widthFactor/callViewModel.callStatsModel.sentVideoWindow.heightFactor, contentMode: .fill)
|
.aspectRatio(callViewModel.callStatsModel.sentVideoWindow.widthFactor/callViewModel.callStatsModel.sentVideoWindow.heightFactor, contentMode: .fill)
|
||||||
.frame(maxWidth: callViewModel.callStatsModel.sentVideoWindow.widthFactor * 256,
|
.frame(maxWidth: callViewModel.callStatsModel.sentVideoWindow.widthFactor * 256,
|
||||||
maxHeight: callViewModel.callStatsModel.sentVideoWindow.heightFactor * 256)
|
maxHeight: callViewModel.callStatsModel.sentVideoWindow.heightFactor * 256)
|
||||||
|
|
@ -912,9 +897,6 @@ struct CallView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
coreContext.doOnCoreQueue { core in
|
|
||||||
core.nativeVideoWindow = nil
|
|
||||||
}
|
|
||||||
if !callViewModel.isPaused && TelecomManager.shared.callInProgress
|
if !callViewModel.isPaused && TelecomManager.shared.callInProgress
|
||||||
&& !(coreContext.pipViewModel.pipController?.isPictureInPictureActive ?? false) {
|
&& !(coreContext.pipViewModel.pipController?.isPictureInPictureActive ?? false) {
|
||||||
// TODO: Enable PIP in 6.1
|
// TODO: Enable PIP in 6.1
|
||||||
|
|
@ -996,11 +978,6 @@ struct CallView: View {
|
||||||
core.nativePreviewWindow = view
|
core.nativePreviewWindow = view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
|
||||||
coreContext.doOnCoreQueue { core in
|
|
||||||
core.nativePreviewWindow = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(width: angleDegree == 0 ? 120*1.2 : 160*1.2, height: angleDegree == 0 ? 160*1.2 : 120*1.2)
|
.frame(width: angleDegree == 0 ? 120*1.2 : 160*1.2, height: angleDegree == 0 ? 160*1.2 : 120*1.2)
|
||||||
.scaledToFill()
|
.scaledToFill()
|
||||||
.clipped()
|
.clipped()
|
||||||
|
|
@ -1166,11 +1143,6 @@ struct CallView: View {
|
||||||
core.nativePreviewWindow = view
|
core.nativePreviewWindow = view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
|
||||||
coreContext.doOnCoreQueue { core in
|
|
||||||
core.nativePreviewWindow = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(width: angleDegree == 0 ? 120*1.2 : 160*1.2, height: angleDegree == 0 ? 160*1.2 : 120*1.2)
|
.frame(width: angleDegree == 0 ? 120*1.2 : 160*1.2, height: angleDegree == 0 ? 160*1.2 : 120*1.2)
|
||||||
.scaledToFill()
|
.scaledToFill()
|
||||||
.clipped()
|
.clipped()
|
||||||
|
|
@ -1390,11 +1362,6 @@ struct CallView: View {
|
||||||
core.nativePreviewWindow = view
|
core.nativePreviewWindow = view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
|
||||||
coreContext.doOnCoreQueue { core in
|
|
||||||
core.nativePreviewWindow = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(
|
.frame(
|
||||||
width: 120 * ceil(maxValue / 120),
|
width: 120 * ceil(maxValue / 120),
|
||||||
height: 160 * ceil(maxValue / 120)
|
height: 160 * ceil(maxValue / 120)
|
||||||
|
|
@ -1631,11 +1598,6 @@ struct CallView: View {
|
||||||
core.nativePreviewWindow = view
|
core.nativePreviewWindow = view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
|
||||||
coreContext.doOnCoreQueue { core in
|
|
||||||
core.nativePreviewWindow = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(
|
.frame(
|
||||||
width: 160 * ceil(maxValue / 120),
|
width: 160 * ceil(maxValue / 120),
|
||||||
height: 120 * ceil(maxValue / 120)
|
height: 120 * ceil(maxValue / 120)
|
||||||
|
|
@ -1955,38 +1917,36 @@ struct CallView: View {
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if !SharedMainViewModel.shared.disableVideoCall {
|
ZStack {
|
||||||
ZStack {
|
Button {
|
||||||
Button {
|
if optionsChangeLayout == 3 {
|
||||||
if optionsChangeLayout == 3 {
|
optionsChangeLayout = 2
|
||||||
optionsChangeLayout = 2
|
callViewModel.toggleVideoMode(isAudioOnlyMode: false)
|
||||||
callViewModel.toggleVideoMode(isAudioOnlyMode: false)
|
} else {
|
||||||
} else {
|
callViewModel.displayMyVideo()
|
||||||
callViewModel.displayMyVideo()
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image(callViewModel.videoDisplayed ? "video-camera" : "video-camera-slash")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.frame(width: 32, height: 32)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.buttonStyle(PressedButtonStyle(buttonSize: buttonSize))
|
} label: {
|
||||||
.frame(width: buttonSize, height: buttonSize)
|
HStack {
|
||||||
.background(Color.gray500)
|
Image(callViewModel.videoDisplayed ? "video-camera" : "video-camera-slash")
|
||||||
.cornerRadius(40)
|
.renderingMode(.template)
|
||||||
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote || telecomManager.outgoingCallStarted || optionsChangeLayout == 3)
|
.resizable()
|
||||||
|
.foregroundStyle(.white)
|
||||||
if callViewModel.isPaused || telecomManager.isPausedByRemote || telecomManager.outgoingCallStarted || optionsChangeLayout == 3 {
|
.frame(width: 32, height: 32)
|
||||||
Color.gray600.opacity(0.8)
|
|
||||||
.cornerRadius(40)
|
|
||||||
.allowsHitTesting(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.buttonStyle(PressedButtonStyle(buttonSize: buttonSize))
|
||||||
.frame(width: buttonSize, height: buttonSize)
|
.frame(width: buttonSize, height: buttonSize)
|
||||||
|
.background(Color.gray500)
|
||||||
|
.cornerRadius(40)
|
||||||
|
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
|
||||||
|
|
||||||
|
if callViewModel.isPaused || telecomManager.isPausedByRemote {
|
||||||
|
Color.gray600.opacity(0.8)
|
||||||
|
.cornerRadius(40)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.frame(width: buttonSize, height: buttonSize)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
callViewModel.toggleMuteMicrophone()
|
callViewModel.toggleMuteMicrophone()
|
||||||
|
|
@ -2259,7 +2219,7 @@ struct CallView: View {
|
||||||
changeLayoutSheet = true
|
changeLayoutSheet = true
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image("layout")
|
Image("notebook")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
|
|
@ -2688,7 +2648,7 @@ struct CallView: View {
|
||||||
changeLayoutSheet = true
|
changeLayoutSheet = true
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image("layout")
|
Image("notebook")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
|
|
|
||||||
|
|
@ -103,21 +103,17 @@ struct CallsListFragment: View {
|
||||||
.background(.white)
|
.background(.white)
|
||||||
|
|
||||||
if self.isShowPopup {
|
if self.isShowPopup {
|
||||||
PopupView(
|
PopupView(isShowPopup: $isShowPopup,
|
||||||
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: nil,
|
actionFirstButton: {self.isShowPopup.toggle()},
|
||||||
actionFirstButton: {},
|
titleSecondButton: Text("calls_list_dialog_merge_into_conference_label"),
|
||||||
titleSecondButton: Text("calls_list_dialog_merge_into_conference_label"),
|
actionSecondButton: {
|
||||||
actionSecondButton: {
|
callViewModel.mergeCallsIntoConference()
|
||||||
callViewModel.mergeCallsIntoConference()
|
self.isShowPopup.toggle()
|
||||||
self.isShowPopup.toggle()
|
isShowCallsListFragment.toggle()
|
||||||
isShowCallsListFragment.toggle()
|
})
|
||||||
},
|
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
|
||||||
actionThirdButton: { self.isShowPopup.toggle() },
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
self.isShowPopup.toggle()
|
self.isShowPopup.toggle()
|
||||||
|
|
|
||||||
|
|
@ -108,21 +108,17 @@ struct ParticipantsListFragment: View {
|
||||||
|
|
||||||
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(String(format: String(localized: "meeting_call_remove_participant_confirmation_message"), callViewModel.participantList[indexToRemove].name))
|
||||||
PopupView(
|
PopupView(isShowPopup: $isShowPopup,
|
||||||
isShowPopup: $isShowPopup,
|
title: Text("meeting_call_remove_participant_confirmation_title"),
|
||||||
title: Text("meeting_call_remove_participant_confirmation_title"),
|
content: contentPopup,
|
||||||
content: contentPopup,
|
titleFirstButton: Text("dialog_no"),
|
||||||
titleFirstButton: nil,
|
actionFirstButton: {self.isShowPopup.toggle()},
|
||||||
actionFirstButton: {},
|
titleSecondButton: Text("dialog_yes"),
|
||||||
titleSecondButton: Text("dialog_yes"),
|
actionSecondButton: {
|
||||||
actionSecondButton: {
|
callViewModel.removeParticipant(index: indexToRemove)
|
||||||
callViewModel.removeParticipant(index: indexToRemove)
|
self.isShowPopup.toggle()
|
||||||
self.isShowPopup.toggle()
|
indexToRemove = -1
|
||||||
indexToRemove = -1
|
})
|
||||||
},
|
|
||||||
titleThirdButton: Text("dialog_no"),
|
|
||||||
actionThirdButton: { self.isShowPopup.toggle() }
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
self.isShowPopup.toggle()
|
self.isShowPopup.toggle()
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ struct MeetingWaitingRoomFragment: View {
|
||||||
.sheet(isPresented: $audioRouteSheet, onDismiss: {
|
.sheet(isPresented: $audioRouteSheet, onDismiss: {
|
||||||
audioRouteSheet = false
|
audioRouteSheet = false
|
||||||
}, content: {
|
}, content: {
|
||||||
innerBottomSheet().presentationDetents([.fraction(0.4)])
|
innerBottomSheet().presentationDetents([.fraction(0.3)])
|
||||||
})
|
})
|
||||||
.onAppear {
|
.onAppear {
|
||||||
meetingWaitingRoomViewModel.enableAVAudioSession()
|
meetingWaitingRoomViewModel.enableAVAudioSession()
|
||||||
|
|
@ -266,25 +266,23 @@ struct MeetingWaitingRoomFragment: View {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if !SharedMainViewModel.shared.disableVideoCall {
|
Button {
|
||||||
Button {
|
!meetingWaitingRoomViewModel.videoDisplayed
|
||||||
!meetingWaitingRoomViewModel.videoDisplayed
|
? meetingWaitingRoomViewModel.enableVideoPreview() : meetingWaitingRoomViewModel.disableVideoPreview()
|
||||||
? meetingWaitingRoomViewModel.enableVideoPreview() : meetingWaitingRoomViewModel.disableVideoPreview()
|
} label: {
|
||||||
} label: {
|
HStack {
|
||||||
HStack {
|
Image(meetingWaitingRoomViewModel.videoDisplayed ? "video-camera" : "video-camera-slash")
|
||||||
Image(meetingWaitingRoomViewModel.videoDisplayed ? "video-camera" : "video-camera-slash")
|
.renderingMode(.template)
|
||||||
.renderingMode(.template)
|
.resizable()
|
||||||
.resizable()
|
.foregroundStyle(.white)
|
||||||
.foregroundStyle(.white)
|
.frame(width: 32, height: 32)
|
||||||
.frame(width: 32, height: 32)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.buttonStyle(PressedButtonStyle(buttonSize: 60))
|
|
||||||
.frame(width: 60, height: 60)
|
|
||||||
.background(Color.gray500)
|
|
||||||
.cornerRadius(40)
|
|
||||||
.padding(.horizontal, 5)
|
|
||||||
}
|
}
|
||||||
|
.buttonStyle(PressedButtonStyle(buttonSize: 60))
|
||||||
|
.frame(width: 60, height: 60)
|
||||||
|
.background(Color.gray500)
|
||||||
|
.cornerRadius(40)
|
||||||
|
.padding(.horizontal, 5)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
meetingWaitingRoomViewModel.toggleMuteMicrophone()
|
meetingWaitingRoomViewModel.toggleMuteMicrophone()
|
||||||
|
|
|
||||||
|
|
@ -178,9 +178,7 @@ class CallViewModel: ObservableObject {
|
||||||
let friend = ContactsManager.shared.getFriendWithAddress(address: self.currentCall!.remoteAddress)
|
let friend = ContactsManager.shared.getFriendWithAddress(address: self.currentCall!.remoteAddress)
|
||||||
if friend != nil && friend!.address != nil && friend!.address!.displayName != nil {
|
if friend != nil && friend!.address != nil && friend!.address!.displayName != nil {
|
||||||
displayNameTmp = friend!.address!.displayName!
|
displayNameTmp = friend!.address!.displayName!
|
||||||
} else if friend != nil && friend?.name != nil {
|
} else {
|
||||||
displayNameTmp = friend?.name ?? "No name"
|
|
||||||
} else {
|
|
||||||
if self.currentCall!.remoteAddress!.displayName != nil {
|
if self.currentCall!.remoteAddress!.displayName != nil {
|
||||||
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 {
|
||||||
|
|
@ -294,10 +292,6 @@ class CallViewModel: ObservableObject {
|
||||||
self.zrtpPopupDisplayed = true
|
self.zrtpPopupDisplayed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, onStateChanged: { (_: Call, state: Call.State, message: String) in
|
|
||||||
if let currentParamsTmp = self.currentCall?.currentParams, state == .StreamsRunning, currentParamsTmp.mediaEncryption == .None || currentParamsTmp.mediaEncryption == .SRTP {
|
|
||||||
self.updateEncryption(withToast: false)
|
|
||||||
}
|
|
||||||
}, onStatsUpdated: { (_: Call, stats: CallStats) in
|
}, onStatsUpdated: { (_: Call, stats: CallStats) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if self.currentCall != nil {
|
if self.currentCall != nil {
|
||||||
|
|
@ -719,45 +713,44 @@ class CallViewModel: ObservableObject {
|
||||||
|
|
||||||
func displayMyVideo() {
|
func displayMyVideo() {
|
||||||
coreContext.doOnCoreQueue { core in
|
coreContext.doOnCoreQueue { core in
|
||||||
guard let call = self.currentCall else { return }
|
if self.currentCall != nil {
|
||||||
|
do {
|
||||||
guard call.state == .StreamsRunning else {
|
let params = try core.createCallParams(call: self.currentCall)
|
||||||
Log.warn("\(CallViewModel.TAG) displayMyVideo called in invalid state: \(call.state), skipping update")
|
|
||||||
return
|
if (params.videoEnabled == false) {
|
||||||
}
|
Log.info("\(CallViewModel.TAG) Conference found and video disabled in params, enabling it")
|
||||||
|
params.videoEnabled = true
|
||||||
do {
|
params.videoDirection = MediaDirection.SendRecv
|
||||||
let params = try core.createCallParams(call: call)
|
|
||||||
|
|
||||||
if !params.videoEnabled {
|
|
||||||
Log.info("\(CallViewModel.TAG) Video disabled in params, enabling it")
|
|
||||||
params.videoEnabled = true
|
|
||||||
params.videoDirection = .SendRecv
|
|
||||||
} else {
|
|
||||||
if params.videoDirection == .SendRecv || params.videoDirection == .SendOnly {
|
|
||||||
Log.info("\(CallViewModel.TAG) Video already enabled, switching to recv only")
|
|
||||||
params.videoDirection = .RecvOnly
|
|
||||||
} else {
|
} else {
|
||||||
Log.info("\(CallViewModel.TAG) Video already enabled, switching to send & recv")
|
if (params.videoDirection == MediaDirection.SendRecv || params.videoDirection == MediaDirection.SendOnly) {
|
||||||
params.videoDirection = .SendRecv
|
Log.info(
|
||||||
|
"\(CallViewModel.TAG) Conference found with video already enabled, changing video media direction to receive only"
|
||||||
|
)
|
||||||
|
params.videoDirection = MediaDirection.RecvOnly
|
||||||
|
} else {
|
||||||
|
Log.info(
|
||||||
|
"\(CallViewModel.TAG) Conference found with video already enabled, changing video media direction to send & receive"
|
||||||
|
)
|
||||||
|
params.videoDirection = MediaDirection.SendRecv
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
try self.currentCall!.update(params: params)
|
||||||
try call.update(params: params)
|
|
||||||
|
let video = params.videoDirection == .SendRecv || params.videoDirection == .SendOnly
|
||||||
let video = params.videoDirection == .SendRecv || params.videoDirection == .SendOnly
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + (video ? 1 : 0)) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + (video ? 1 : 0)) {
|
||||||
if video {
|
if video {
|
||||||
self.videoDisplayed = false
|
self.videoDisplayed = false
|
||||||
|
}
|
||||||
|
self.videoDisplayed = video
|
||||||
}
|
}
|
||||||
self.videoDisplayed = video
|
} catch {
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
Log.error("\(CallViewModel.TAG) Failed to update video params: \(error)")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func toggleVideoMode(isAudioOnlyMode: Bool) {
|
func toggleVideoMode(isAudioOnlyMode: Bool) {
|
||||||
coreContext.doOnCoreQueue { core in
|
coreContext.doOnCoreQueue { core in
|
||||||
|
|
@ -976,7 +969,8 @@ class CallViewModel: ObservableObject {
|
||||||
self.isNotEncrypted = false
|
self.isNotEncrypted = false
|
||||||
|
|
||||||
if isDeviceTrusted && withToast {
|
if isDeviceTrusted && withToast {
|
||||||
ToastViewModel.shared.show("Info_call_securised")
|
ToastViewModel.shared.toastMessage = "Info_call_securised"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -991,11 +985,14 @@ class CallViewModel: ObservableObject {
|
||||||
self.isNotEncrypted = false
|
self.isNotEncrypted = false
|
||||||
}
|
}
|
||||||
case MediaEncryption.None:
|
case MediaEncryption.None:
|
||||||
let isNotEncryptedTmp = self.currentCall?.state == .StreamsRunning && !self.telecomManager.outgoingCallStarted
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.isMediaEncrypted = false
|
self.isMediaEncrypted = false
|
||||||
self.isZrtp = false
|
self.isZrtp = false
|
||||||
self.isNotEncrypted = isNotEncryptedTmp
|
if self.currentCall!.state == .StreamsRunning {
|
||||||
|
self.isNotEncrypted = true
|
||||||
|
} else {
|
||||||
|
self.isNotEncrypted = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1059,9 +1056,8 @@ class CallViewModel: ObservableObject {
|
||||||
try callToTransferTo!.transferToAnother(dest: self.currentCall!)
|
try callToTransferTo!.transferToAnother(dest: self.currentCall!)
|
||||||
Log.info("[CallViewModel] Attended transfer is successful")
|
Log.info("[CallViewModel] Attended transfer is successful")
|
||||||
} catch _ {
|
} catch _ {
|
||||||
DispatchQueue.main.async {
|
ToastViewModel.shared.toastMessage = "Failed_toast_call_transfer_failed"
|
||||||
ToastViewModel.shared.show("Failed_toast_call_transfer_failed")
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
|
||||||
|
|
||||||
Log.error("[CallViewModel] Failed to make attended transfer!")
|
Log.error("[CallViewModel] Failed to make attended transfer!")
|
||||||
}
|
}
|
||||||
|
|
@ -1080,9 +1076,9 @@ class CallViewModel: ObservableObject {
|
||||||
try self.currentCall!.transferTo(referTo: toAddress)
|
try self.currentCall!.transferTo(referTo: toAddress)
|
||||||
Log.info("[CallViewModel] Blind call transfer is successful")
|
Log.info("[CallViewModel] Blind call transfer is successful")
|
||||||
} catch _ {
|
} catch _ {
|
||||||
DispatchQueue.main.async {
|
ToastViewModel.shared.toastMessage = "Failed_toast_call_transfer_failed"
|
||||||
ToastViewModel.shared.show("Failed_toast_call_transfer_failed")
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
|
||||||
Log.error("[CallViewModel] Failed to make blind call transfer!")
|
Log.error("[CallViewModel] Failed to make blind call transfer!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1277,7 +1273,8 @@ class CallViewModel: ObservableObject {
|
||||||
)
|
)
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1367,7 +1364,8 @@ class CallViewModel: ObservableObject {
|
||||||
self.chatRoomDelegate = nil
|
self.chatRoomDelegate = nil
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
||||||
|
|
@ -1401,7 +1399,8 @@ class CallViewModel: ObservableObject {
|
||||||
self.chatRoomDelegate = nil
|
self.chatRoomDelegate = nil
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ class MeetingWaitingRoomViewModel: ObservableObject {
|
||||||
let confNameTmp = conf?.subject ?? "Conference"
|
let confNameTmp = conf?.subject ?? "Conference"
|
||||||
var userNameTmp = ""
|
var userNameTmp = ""
|
||||||
|
|
||||||
let friend = core.defaultAccount != nil && core.defaultAccount!.params?.identityAddress != nil
|
let friend = core.defaultAccount != nil && core.defaultAccount!.contactAddress != nil
|
||||||
? ContactsManager.shared.getFriendWithAddress(address: core.defaultAccount?.params?.identityAddress)
|
? ContactsManager.shared.getFriendWithAddress(address: core.defaultAccount?.contactAddress)
|
||||||
: nil
|
: nil
|
||||||
|
|
||||||
let addressTmp = friend?.address?.asStringUriOnly() ?? ""
|
let addressTmp = friend?.address?.asStringUriOnly() ?? ""
|
||||||
|
|
@ -78,13 +78,13 @@ class MeetingWaitingRoomViewModel: ObservableObject {
|
||||||
if friend != nil && friend!.address != nil && friend!.address!.displayName != nil {
|
if friend != nil && friend!.address != nil && friend!.address!.displayName != nil {
|
||||||
userNameTmp = friend!.address!.displayName!
|
userNameTmp = friend!.address!.displayName!
|
||||||
} else {
|
} else {
|
||||||
if core.defaultAccount != nil && core.defaultAccount!.params?.identityAddress != nil {
|
if core.defaultAccount != nil && core.defaultAccount!.contactAddress != nil {
|
||||||
if core.defaultAccount!.params?.identityAddress!.displayName != nil {
|
if core.defaultAccount!.contactAddress!.displayName != nil {
|
||||||
userNameTmp = core.defaultAccount!.params!.identityAddress!.displayName!
|
userNameTmp = core.defaultAccount!.contactAddress!.displayName!
|
||||||
} else if core.defaultAccount!.params?.identityAddress!.username != nil {
|
} else if core.defaultAccount!.contactAddress!.username != nil {
|
||||||
userNameTmp = core.defaultAccount!.params!.identityAddress!.username!
|
userNameTmp = core.defaultAccount!.contactAddress!.username!
|
||||||
} else {
|
} else {
|
||||||
userNameTmp = String(core.defaultAccount!.params!.identityAddress!.asStringUriOnly().dropFirst(4))
|
userNameTmp = String(core.defaultAccount!.contactAddress!.asStringUriOnly().dropFirst(4))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +92,7 @@ class MeetingWaitingRoomViewModel: ObservableObject {
|
||||||
let avatarModelTmp = friend != nil
|
let avatarModelTmp = friend != nil
|
||||||
? ContactsManager.shared.avatarListModel.first(where: {
|
? ContactsManager.shared.avatarListModel.first(where: {
|
||||||
$0.friend!.name == friend!.name
|
$0.friend!.name == friend!.name
|
||||||
&& $0.friend!.address!.asStringUriOnly() == core.defaultAccount!.params?.identityAddress!.asStringUriOnly()
|
&& $0.friend!.address!.asStringUriOnly() == core.defaultAccount!.contactAddress!.asStringUriOnly()
|
||||||
}) ?? ContactAvatarModel(friend: nil, name: userNameTmp, address: addressTmp, withPresence: false)
|
}) ?? ContactAvatarModel(friend: nil, name: userNameTmp, address: addressTmp, withPresence: false)
|
||||||
: ContactAvatarModel(friend: nil, name: userNameTmp, address: addressTmp, withPresence: false)
|
: ContactAvatarModel(friend: nil, name: userNameTmp, address: addressTmp, withPresence: false)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,24 +32,22 @@ struct ContactsView: View {
|
||||||
ZStack(alignment: .bottomTrailing) {
|
ZStack(alignment: .bottomTrailing) {
|
||||||
ContactsFragment(isShowDeletePopup: $isShowDeletePopup, text: $text)
|
ContactsFragment(isShowDeletePopup: $isShowDeletePopup, text: $text)
|
||||||
|
|
||||||
if !CorePreferences.disableAddContact {
|
Button {
|
||||||
Button {
|
withAnimation {
|
||||||
withAnimation {
|
contactsListViewModel.selectedEditFriend = nil
|
||||||
contactsListViewModel.selectedEditFriend = nil
|
isShowEditContactFragment.toggle()
|
||||||
isShowEditContactFragment.toggle()
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Image("user-plus")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.padding()
|
|
||||||
.background(Color.orangeMain500)
|
|
||||||
.clipShape(Circle())
|
|
||||||
.shadow(color: .black.opacity(0.2), radius: 4)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding()
|
} label: {
|
||||||
|
Image("user-plus")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.padding()
|
||||||
|
.background(Color.orangeMain500)
|
||||||
|
.clipShape(Circle())
|
||||||
|
.shadow(color: .black.opacity(0.2), radius: 4)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
|
|
||||||
// For testing crashlytics
|
// For testing crashlytics
|
||||||
/*Button(action: CoreContext.shared.crashForCrashlytics, label: {
|
/*Button(action: CoreContext.shared.crashForCrashlytics, label: {
|
||||||
|
|
|
||||||
|
|
@ -39,152 +39,127 @@ struct ContactInnerActionsFragment: View {
|
||||||
|
|
||||||
var actionEditButton: () -> Void
|
var actionEditButton: () -> Void
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if !CorePreferences.hideSipAddresses || (CorePreferences.hideSipAddresses && !contactAvatarModel.phoneNumbersWithLabel.isEmpty) {
|
HStack(alignment: .center) {
|
||||||
HStack(alignment: .center) {
|
Text("contact_details_numbers_and_addresses_title")
|
||||||
Text("contact_details_numbers_and_addresses_title")
|
.default_text_style_800(styleSize: 15)
|
||||||
.default_text_style_800(styleSize: 15)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Image(informationIsOpen ? "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, 16)
|
|
||||||
.background(Color.gray100)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
informationIsOpen.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
if informationIsOpen {
|
Image(informationIsOpen ? "caret-up" : "caret-down")
|
||||||
VStack(spacing: 0) {
|
.renderingMode(.template)
|
||||||
if !CorePreferences.hideSipAddresses {
|
.resizable()
|
||||||
ForEach(0..<contactAvatarModel.addresses.count, id: \.self) { index in
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
HStack {
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
HStack {
|
.padding(.all, 10)
|
||||||
VStack {
|
}
|
||||||
Text(String(localized: "sip_address") + ":")
|
.padding(.top, 30)
|
||||||
.default_text_style_700(styleSize: 14)
|
.padding(.bottom, 10)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.padding(.horizontal, 16)
|
||||||
Text(contactAvatarModel.addresses[index].dropFirst(4))
|
.background(Color.gray100)
|
||||||
.default_text_style(styleSize: 14)
|
.onTapGesture {
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
withAnimation {
|
||||||
.lineLimit(1)
|
informationIsOpen.toggle()
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
if informationIsOpen {
|
||||||
|
VStack(spacing: 0) {
|
||||||
Image("phone")
|
ForEach(0..<contactAvatarModel.addresses.count, id: \.self) { index in
|
||||||
.renderingMode(.template)
|
HStack {
|
||||||
.resizable()
|
HStack {
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
VStack {
|
||||||
.frame(width: 25, height: 25)
|
Text(String(localized: "sip_address") + ":")
|
||||||
.padding(.all, 10)
|
.default_text_style_700(styleSize: 14)
|
||||||
}
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(.vertical, 15)
|
Text(contactAvatarModel.addresses[index].dropFirst(4))
|
||||||
.padding(.horizontal, 20)
|
.default_text_style(styleSize: 14)
|
||||||
}
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.background(.white)
|
.lineLimit(1)
|
||||||
.onTapGesture {
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
do {
|
|
||||||
let address = try Factory.Instance.createAddress(addr: contactAvatarModel.addresses[index])
|
|
||||||
telecomManager.doCallOrJoinConf(address: address)
|
|
||||||
} catch {
|
|
||||||
Log.error("[ContactInnerActionsFragment] unable to create address for a new outgoing call : \(contactAvatarModel.addresses[index]) \(error) ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onLongPressGesture(minimumDuration: 0.2) {
|
|
||||||
contactsListViewModel.stringToCopy = contactAvatarModel.addresses[index]
|
|
||||||
showingSheet.toggle()
|
|
||||||
}
|
}
|
||||||
|
Spacer()
|
||||||
|
|
||||||
if !contactAvatarModel.phoneNumbersWithLabel.isEmpty
|
Image("phone")
|
||||||
|| index < contactAvatarModel.addresses.count - 1 {
|
.renderingMode(.template)
|
||||||
VStack {
|
.resizable()
|
||||||
Divider()
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
}
|
.frame(width: 25, height: 25)
|
||||||
.padding(.horizontal)
|
.padding(.all, 10)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
}
|
||||||
|
.background(.white)
|
||||||
|
.onTapGesture {
|
||||||
|
do {
|
||||||
|
let address = try Factory.Instance.createAddress(addr: contactAvatarModel.addresses[index])
|
||||||
|
withAnimation {
|
||||||
|
telecomManager.doCallOrJoinConf(address: address)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Log.error("[ContactInnerActionsFragment] unable to create address for a new outgoing call : \(contactAvatarModel.addresses[index]) \(error) ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onLongPressGesture(minimumDuration: 0.2) {
|
||||||
|
contactsListViewModel.stringToCopy = contactAvatarModel.addresses[index]
|
||||||
|
showingSheet.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach(contactAvatarModel.phoneNumbersWithLabel.indices, id: \.self) { index in
|
if !contactAvatarModel.phoneNumbersWithLabel.isEmpty
|
||||||
let entry = contactAvatarModel.phoneNumbersWithLabel[index]
|
|| index < contactAvatarModel.addresses.count - 1 {
|
||||||
HStack {
|
VStack {
|
||||||
HStack {
|
Divider()
|
||||||
VStack {
|
|
||||||
if !entry.label.isEmpty {
|
|
||||||
Text(String(localized: "phone_number") + " (\(entry.label)):")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
} else {
|
|
||||||
Text(String(localized: "phone_number") + ":")
|
|
||||||
.default_text_style_700(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
Text(entry.phoneNumber)
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.lineLimit(1)
|
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Image("phone")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
}
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
|
||||||
.background(.white)
|
|
||||||
.onTapGesture {
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
let address = core.interpretUrl(url: contactAvatarModel.phoneNumbersWithLabel[index].phoneNumber, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core))
|
|
||||||
if address != nil {
|
|
||||||
TelecomManager.shared.doCallOrJoinConf(address: address!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onLongPressGesture(minimumDuration: 0.2) {
|
|
||||||
contactsListViewModel.stringToCopy = entry.phoneNumber
|
|
||||||
showingSheet.toggle()
|
|
||||||
}
|
|
||||||
|
|
||||||
if index < contactAvatarModel.phoneNumbersWithLabel.count - 1 {
|
|
||||||
VStack {
|
|
||||||
Divider()
|
|
||||||
}
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach(contactAvatarModel.phoneNumbersWithLabel.indices, id: \.self) { index in
|
||||||
|
let entry = contactAvatarModel.phoneNumbersWithLabel[index]
|
||||||
|
HStack {
|
||||||
|
HStack {
|
||||||
|
VStack {
|
||||||
|
if !entry.label.isEmpty {
|
||||||
|
Text(String(localized: "phone_number") + " (\(entry.label)):")
|
||||||
|
.default_text_style_700(styleSize: 14)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
} else {
|
||||||
|
Text(String(localized: "phone_number") + ":")
|
||||||
|
.default_text_style_700(styleSize: 14)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
Text(entry.phoneNumber)
|
||||||
|
.default_text_style(styleSize: 14)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.lineLimit(1)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
}
|
||||||
|
.background(.white)
|
||||||
|
.onLongPressGesture(minimumDuration: 0.2) {
|
||||||
|
contactsListViewModel.stringToCopy = entry.phoneNumber
|
||||||
|
showingSheet.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < contactAvatarModel.phoneNumbersWithLabel.count - 1 {
|
||||||
|
VStack {
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.background(.white)
|
|
||||||
.cornerRadius(15)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.zIndex(-1)
|
|
||||||
.transition(.move(edge: .top))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
HStack {}
|
|
||||||
.frame(height: 20)
|
|
||||||
}
|
}
|
||||||
|
.background(.white)
|
||||||
|
.cornerRadius(15)
|
||||||
|
.padding(.horizontal)
|
||||||
|
.zIndex(-1)
|
||||||
|
.transition(.move(edge: .top))
|
||||||
|
}
|
||||||
|
|
||||||
if !contactAvatarModel.organization.isEmpty || !contactAvatarModel.jobTitle.isEmpty {
|
if !contactAvatarModel.organization.isEmpty || !contactAvatarModel.jobTitle.isEmpty {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
@ -228,11 +203,33 @@ struct ContactInnerActionsFragment: View {
|
||||||
.background(Color.gray100)
|
.background(Color.gray100)
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
if !contactAvatarModel.isReadOnly {
|
if !contactAvatarModel.nativeUri.isEmpty {
|
||||||
if !contactAvatarModel.editable {
|
Button {
|
||||||
Button {
|
actionEditButton()
|
||||||
actionEditButton()
|
} label: {
|
||||||
} label: {
|
HStack {
|
||||||
|
Image("pencil-simple")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
|
.frame(width: 25, height: 25)
|
||||||
|
.padding(.all, 10)
|
||||||
|
|
||||||
|
Text("contact_details_edit")
|
||||||
|
.default_text_style(styleSize: 14)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.lineLimit(1)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NavigationLink(destination: EditContactFragment(
|
||||||
|
contactAvatarModel: contactAvatarModel,
|
||||||
|
isShowEditContactFragment: $isShowEditContactFragmentInContactDetails,
|
||||||
|
isShowDismissPopup: $isShowDismissPopup)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image("pencil-simple")
|
Image("pencil-simple")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
|
|
@ -250,71 +247,47 @@ struct ContactInnerActionsFragment: View {
|
||||||
}
|
}
|
||||||
.padding(.vertical, 15)
|
.padding(.vertical, 15)
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NavigationLink(destination: EditContactFragment(
|
|
||||||
contactAvatarModel: contactAvatarModel,
|
|
||||||
isShowEditContactFragment: $isShowEditContactFragmentInContactDetails,
|
|
||||||
isShowDismissPopup: $isShowDismissPopup)) {
|
|
||||||
HStack {
|
|
||||||
Image("pencil-simple")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
|
|
||||||
Text("contact_details_edit")
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.lineLimit(1)
|
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
}
|
||||||
.simultaneousGesture(
|
.simultaneousGesture(
|
||||||
TapGesture().onEnded {
|
TapGesture().onEnded {
|
||||||
isShowEditContactFragmentInContactDetails = true
|
isShowEditContactFragmentInContactDetails = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
Divider()
|
|
||||||
}
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
Button {
|
|
||||||
contactsListViewModel.toggleStarredSelectedFriend()
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Image(contactAvatarModel.starred == true ? "heart-fill" : "heart")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(contactAvatarModel.starred == true ? Color.redDanger500 : Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
.padding(.all, 10)
|
|
||||||
Text(contactAvatarModel.starred == true
|
|
||||||
? "contact_details_remove_from_favourites"
|
|
||||||
: "contact_details_add_to_favourites")
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.lineLimit(1)
|
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
Divider()
|
|
||||||
}
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
contactsListViewModel.toggleStarredSelectedFriend()
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Image(contactAvatarModel.starred == true ? "heart-fill" : "heart")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(contactAvatarModel.starred == true ? Color.redDanger500 : Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25)
|
||||||
|
.padding(.all, 10)
|
||||||
|
Text(contactAvatarModel.starred == true
|
||||||
|
? "contact_details_remove_from_favourites"
|
||||||
|
: "contact_details_add_to_favourites")
|
||||||
|
.default_text_style(styleSize: 14)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.lineLimit(1)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
showShareSheet.toggle()
|
showShareSheet.toggle()
|
||||||
} label: {
|
} label: {
|
||||||
|
|
@ -337,34 +310,32 @@ struct ContactInnerActionsFragment: View {
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !contactAvatarModel.isReadOnly {
|
VStack {
|
||||||
VStack {
|
Divider()
|
||||||
Divider()
|
}
|
||||||
}
|
.padding(.horizontal)
|
||||||
.padding(.horizontal)
|
|
||||||
|
Button {
|
||||||
Button {
|
isShowDeletePopup.toggle()
|
||||||
isShowDeletePopup.toggle()
|
} label: {
|
||||||
} label: {
|
HStack {
|
||||||
HStack {
|
Image("trash-simple")
|
||||||
Image("trash-simple")
|
.renderingMode(.template)
|
||||||
.renderingMode(.template)
|
.resizable()
|
||||||
.resizable()
|
.foregroundStyle(Color.redDanger500)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.frame(width: 25, height: 25)
|
||||||
.frame(width: 25, height: 25)
|
.padding(.all, 10)
|
||||||
.padding(.all, 10)
|
|
||||||
|
Text("contact_details_delete")
|
||||||
Text("contact_details_delete")
|
.foregroundStyle(Color.redDanger500)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.default_text_style(styleSize: 14)
|
||||||
.default_text_style(styleSize: 14)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.lineLimit(1)
|
||||||
.lineLimit(1)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
Spacer()
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 15)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
}
|
||||||
|
.padding(.vertical, 15)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.background(.white)
|
.background(.white)
|
||||||
|
|
|
||||||
|
|
@ -72,11 +72,23 @@ struct ContactInnerFragment: View {
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if !contactAvatarModel.isReadOnly {
|
if !contactAvatarModel.nativeUri.isEmpty {
|
||||||
if !contactAvatarModel.editable {
|
Button(action: {
|
||||||
Button(action: {
|
editNativeContact()
|
||||||
editNativeContact()
|
}, label: {
|
||||||
}, label: {
|
Image("pencil-simple")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.orangeMain500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
.padding(.top, 2)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
NavigationLink(destination: EditContactFragment(
|
||||||
|
contactAvatarModel: contactAvatarModel,
|
||||||
|
isShowEditContactFragment: $isShowEditContactFragmentInContactDetails,
|
||||||
|
isShowDismissPopup: $isShowDismissPopup)) {
|
||||||
Image("pencil-simple")
|
Image("pencil-simple")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
|
|
@ -84,26 +96,12 @@ struct ContactInnerFragment: View {
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
.padding(.top, 2)
|
.padding(.top, 2)
|
||||||
})
|
}
|
||||||
} else {
|
.simultaneousGesture(
|
||||||
NavigationLink(destination: EditContactFragment(
|
TapGesture().onEnded {
|
||||||
contactAvatarModel: contactAvatarModel,
|
isShowEditContactFragmentInContactDetails = true
|
||||||
isShowEditContactFragment: $isShowEditContactFragmentInContactDetails,
|
|
||||||
isShowDismissPopup: $isShowDismissPopup)) {
|
|
||||||
Image("pencil-simple")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.padding(.top, 2)
|
|
||||||
}
|
}
|
||||||
.simultaneousGesture(
|
)
|
||||||
TapGesture().onEnded {
|
|
||||||
isShowEditContactFragmentInContactDetails = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
@ -144,24 +142,16 @@ struct ContactInnerFragment: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
if contactAvatarModel.addresses.count <= 1 {
|
||||||
if contactAvatarModel.addresses.count == 1 {
|
do {
|
||||||
do {
|
let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
|
||||||
let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
|
telecomManager.doCallOrJoinConf(address: address, isVideo: false)
|
||||||
telecomManager.doCallOrJoinConf(address: address, isVideo: false)
|
} catch {
|
||||||
} catch {
|
Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ")
|
||||||
Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ")
|
|
||||||
}
|
|
||||||
} else if contactAvatarModel.addresses.count < 1 && contactAvatarModel.phoneNumbersWithLabel.count == 1 {
|
|
||||||
if let firstPhoneNumbersWithLabel = contactAvatarModel.phoneNumbersWithLabel.first, let address = core.interpretUrl(url: firstPhoneNumbersWithLabel.phoneNumber, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)) {
|
|
||||||
telecomManager.doCallOrJoinConf(address: address, isVideo: false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
isShowSipAddressesPopupType = 0
|
|
||||||
isShowSipAddressesPopup = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
isShowSipAddressesPopupType = 0
|
||||||
|
isShowSipAddressesPopup = true
|
||||||
}
|
}
|
||||||
}, label: {
|
}, label: {
|
||||||
VStack {
|
VStack {
|
||||||
|
|
@ -169,103 +159,88 @@ struct ContactInnerFragment: View {
|
||||||
Image("phone")
|
Image("phone")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
.foregroundStyle(contactAvatarModel.address.isEmpty ? Color.grayMain2c400 : Color.grayMain2c600)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
}
|
}
|
||||||
.padding(16)
|
.padding(16)
|
||||||
.background(Color.grayMain2c200)
|
.background(contactAvatarModel.address.isEmpty ? Color.grayMain2c100 : 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)
|
||||||
|
|
||||||
if !CorePreferences.disableChatFeature {
|
if !CorePreferences.disableChatFeature {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
if contactAvatarModel.addresses.count <= 1 {
|
||||||
if contactAvatarModel.addresses.count == 1 {
|
do {
|
||||||
do {
|
let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
|
||||||
let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
|
contactsListViewModel.createOneToOneChatRoomWith(remote: address)
|
||||||
contactsListViewModel.createOneToOneChatRoomWith(remote: address)
|
} catch {
|
||||||
} catch {
|
Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ")
|
||||||
Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ")
|
}
|
||||||
}
|
} else {
|
||||||
} else if contactAvatarModel.addresses.count < 1 && contactAvatarModel.phoneNumbersWithLabel.count == 1 {
|
isShowSipAddressesPopupType = 1
|
||||||
if let firstPhoneNumbersWithLabel = contactAvatarModel.phoneNumbersWithLabel.first, let address = core.interpretUrl(url: firstPhoneNumbersWithLabel.phoneNumber, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)) {
|
isShowSipAddressesPopup = true
|
||||||
contactsListViewModel.createOneToOneChatRoomWith(remote: address)
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
isShowSipAddressesPopupType = 1
|
|
||||||
isShowSipAddressesPopup = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, label: {
|
}, label: {
|
||||||
VStack {
|
VStack {
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
Image("chat-teardrop-text")
|
Image("chat-teardrop-text")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
.foregroundStyle(contactAvatarModel.address.isEmpty ? Color.grayMain2c400 : Color.grayMain2c600)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
}
|
}
|
||||||
.padding(16)
|
.padding(16)
|
||||||
.background(Color.grayMain2c200)
|
.background(contactAvatarModel.address.isEmpty ? Color.grayMain2c100 : 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()
|
||||||
|
|
||||||
if !SharedMainViewModel.shared.disableVideoCall {
|
Button(action: {
|
||||||
Button(action: {
|
if contactAvatarModel.addresses.count <= 1 {
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
do {
|
||||||
if contactAvatarModel.addresses.count == 1 {
|
let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
|
||||||
do {
|
telecomManager.doCallOrJoinConf(address: address, isVideo: true)
|
||||||
let address = try Factory.Instance.createAddress(addr: contactAvatarModel.address)
|
} catch {
|
||||||
telecomManager.doCallOrJoinConf(address: address, isVideo: true)
|
Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ")
|
||||||
} catch {
|
|
||||||
Log.error("[ContactInnerFragment] unable to create address for a new outgoing call : \(contactAvatarModel.address) \(error) ")
|
|
||||||
}
|
|
||||||
} else if contactAvatarModel.addresses.count < 1 && contactAvatarModel.phoneNumbersWithLabel.count == 1 {
|
|
||||||
if let firstPhoneNumbersWithLabel = contactAvatarModel.phoneNumbersWithLabel.first, let address = core.interpretUrl(url: firstPhoneNumbersWithLabel.phoneNumber, applyInternationalPrefix: LinphoneUtils.applyInternationalPrefix(core: core)) {
|
|
||||||
telecomManager.doCallOrJoinConf(address: address, isVideo: true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
isShowSipAddressesPopupType = 2
|
|
||||||
isShowSipAddressesPopup = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, label: {
|
} else {
|
||||||
VStack {
|
isShowSipAddressesPopupType = 2
|
||||||
HStack(alignment: .center) {
|
isShowSipAddressesPopup = true
|
||||||
Image("video-camera")
|
}
|
||||||
.renderingMode(.template)
|
}, label: {
|
||||||
.resizable()
|
VStack {
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
HStack(alignment: .center) {
|
||||||
.frame(width: 25, height: 25)
|
Image("video-camera")
|
||||||
}
|
.renderingMode(.template)
|
||||||
.padding(16)
|
.resizable()
|
||||||
.background(Color.grayMain2c200)
|
.foregroundStyle(contactAvatarModel.address.isEmpty ? Color.grayMain2c400 : Color.grayMain2c600)
|
||||||
.cornerRadius(40)
|
.frame(width: 25, height: 25)
|
||||||
|
|
||||||
Text("contact_video_call_action")
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
}
|
}
|
||||||
})
|
.padding(16)
|
||||||
|
.background(contactAvatarModel.address.isEmpty ? Color.grayMain2c100 : Color.grayMain2c200)
|
||||||
Spacer()
|
.cornerRadius(40)
|
||||||
}
|
|
||||||
|
Text("contact_video_call_action")
|
||||||
|
.default_text_style(styleSize: 14)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.disabled(contactAvatarModel.address.isEmpty)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding(.top, 20)
|
.padding(.top, 20)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ struct ContactListBottomSheet: View {
|
||||||
.padding(.trailing)
|
.padding(.trailing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
Button {
|
Button {
|
||||||
UIPasteboard.general.setValue(
|
UIPasteboard.general.setValue(
|
||||||
contactsListViewModel.stringToCopy.prefix(4) == "sip:"
|
contactsListViewModel.stringToCopy.prefix(4) == "sip:"
|
||||||
|
|
@ -66,7 +67,8 @@ struct ContactListBottomSheet: View {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
ToastViewModel.shared.show("Success_address_copied_into_clipboard")
|
ToastViewModel.shared.toastMessage = "Success_address_copied_into_clipboard"
|
||||||
|
ToastViewModel.shared.displayToast.toggle()
|
||||||
|
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ struct ContactsFragment: View {
|
||||||
showingSheet: $showingSheet,
|
showingSheet: $showingSheet,
|
||||||
showShareSheet: $showShareSheet
|
showShareSheet: $showShareSheet
|
||||||
)
|
)
|
||||||
.presentationDetents(contactsListViewModel.selectedFriend?.isReadOnly == true ? [.fraction(0.1)] : [.fraction(0.4)])
|
.presentationDetents([.fraction(0.3)])
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $showShareSheet) {
|
.sheet(isPresented: $showShareSheet) {
|
||||||
ShareSheet(friendToShare: contactsListViewModel.selectedFriendToShare!)
|
ShareSheet(friendToShare: contactsListViewModel.selectedFriendToShare!)
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,7 @@ import linphonesw
|
||||||
|
|
||||||
struct ContactsInnerFragment: View {
|
struct ContactsInnerFragment: View {
|
||||||
|
|
||||||
@ObservedObject var sharedMainViewModel = SharedMainViewModel.shared
|
|
||||||
@ObservedObject var contactsManager = ContactsManager.shared
|
@ObservedObject var contactsManager = ContactsManager.shared
|
||||||
@ObservedObject var magicSearch = MagicSearchSingleton.shared
|
|
||||||
|
|
||||||
@EnvironmentObject var contactsListViewModel: ContactsListViewModel
|
@EnvironmentObject var contactsListViewModel: ContactsListViewModel
|
||||||
|
|
||||||
|
|
@ -34,84 +32,71 @@ struct ContactsInnerFragment: View {
|
||||||
@Binding var text: String
|
@Binding var text: String
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
VStack(alignment: .leading) {
|
||||||
VStack(alignment: .leading) {
|
if contactsManager.avatarListModel.contains(where: { $0.starred }) {
|
||||||
if contactsManager.avatarListModel.contains(where: { $0.starred }) {
|
HStack(alignment: .center) {
|
||||||
HStack(alignment: .center) {
|
Text("contacts_list_favourites_title")
|
||||||
Text("contacts_list_favourites_title")
|
.default_text_style_800(styleSize: 16)
|
||||||
.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(.all, 10)
|
|
||||||
}
|
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
.background(.white)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
isFavoriteOpen.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isFavoriteOpen {
|
Spacer()
|
||||||
FavoriteContactsListFragment(showingSheet: $showingSheet)
|
|
||||||
.zIndex(-1)
|
|
||||||
.transition(.move(edge: .top))
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
Image(isFavoriteOpen ? "caret-up" : "caret-down")
|
||||||
Text("contacts_list_all_contacts_title")
|
.renderingMode(.template)
|
||||||
.default_text_style_800(styleSize: 16)
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
Spacer()
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
}
|
||||||
|
.padding(.top, 10)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
.background(.white)
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation {
|
||||||
|
isFavoriteOpen.toggle()
|
||||||
}
|
}
|
||||||
.padding(.top, 10)
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VStack {
|
if isFavoriteOpen {
|
||||||
List {
|
FavoriteContactsListFragment(showingSheet: $showingSheet)
|
||||||
ContactsListFragment(showingSheet: $showingSheet, startCallFunc: {_ in })}
|
.zIndex(-1)
|
||||||
.safeAreaInset(edge: .top, content: {
|
.transition(.move(edge: .top))
|
||||||
Spacer()
|
|
||||||
.frame(height: 12)
|
|
||||||
})
|
|
||||||
.listStyle(.plain)
|
|
||||||
.if(sharedMainViewModel.cardDavFriendsListsCount > 0) { view in
|
|
||||||
view.refreshable {
|
|
||||||
contactsManager.refreshCardDavContacts()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.overlay(
|
|
||||||
VStack {
|
|
||||||
if contactsManager.avatarListModel.isEmpty {
|
|
||||||
Spacer()
|
|
||||||
Image("illus-belledonne")
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.clipped()
|
|
||||||
.padding(.all)
|
|
||||||
Text(!text.isEmpty ? "list_filter_no_result_found" : "contacts_list_empty")
|
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
Spacer()
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.all)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Text("contacts_list_all_contacts_title")
|
||||||
|
.default_text_style_800(styleSize: 16)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.top, 10)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
if magicSearch.isLoading {
|
VStack {
|
||||||
ProgressView()
|
List {
|
||||||
.controlSize(.large)
|
ContactsListFragment(showingSheet: $showingSheet, startCallFunc: {_ in })}
|
||||||
.progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
|
.safeAreaInset(edge: .top, content: {
|
||||||
|
Spacer()
|
||||||
|
.frame(height: 12)
|
||||||
|
})
|
||||||
|
.listStyle(.plain)
|
||||||
|
.overlay(
|
||||||
|
VStack {
|
||||||
|
if contactsManager.avatarListModel.isEmpty {
|
||||||
|
Spacer()
|
||||||
|
Image("illus-belledonne")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.clipped()
|
||||||
|
.padding(.all)
|
||||||
|
Text(!text.isEmpty ? "list_filter_no_result_found" : "contacts_list_empty")
|
||||||
|
.default_text_style_800(styleSize: 16)
|
||||||
|
Spacer()
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.all)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
|
|
|
||||||
|
|
@ -55,51 +55,49 @@ struct ContactsListBottomSheet: View {
|
||||||
.padding(.trailing)
|
.padding(.trailing)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !contactsListViewModel.selectedFriend!.isReadOnly {
|
Spacer()
|
||||||
Spacer()
|
|
||||||
|
Button {
|
||||||
|
self.contactsListViewModel.toggleStarredSelectedFriend()
|
||||||
|
|
||||||
Button {
|
if #available(iOS 16.0, *) {
|
||||||
self.contactsListViewModel.toggleStarredSelectedFriend()
|
if idiom != .pad {
|
||||||
|
showingSheet.toggle()
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
if idiom != .pad {
|
|
||||||
showingSheet.toggle()
|
|
||||||
} else {
|
|
||||||
showingSheet.toggle()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
showingSheet.toggle()
|
showingSheet.toggle()
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
} label: {
|
} else {
|
||||||
HStack {
|
showingSheet.toggle()
|
||||||
Image(contactsListViewModel.selectedFriend != nil && contactsListViewModel.selectedFriend!.starred == true ? "heart-fill" : "heart")
|
dismiss()
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(
|
|
||||||
contactsListViewModel.selectedFriend != nil && contactsListViewModel.selectedFriend!.starred == true
|
|
||||||
? Color.redDanger500
|
|
||||||
: Color.grayMain2c500
|
|
||||||
)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
Text(contactsListViewModel.selectedFriend != nil && contactsListViewModel.selectedFriend!.starred == true
|
|
||||||
? "contact_details_remove_from_favourites"
|
|
||||||
: "contact_details_add_to_favourites")
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.frame(maxHeight: .infinity)
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 30)
|
} label: {
|
||||||
.background(Color.gray100)
|
HStack {
|
||||||
|
Image(contactsListViewModel.selectedFriend != nil && contactsListViewModel.selectedFriend!.starred == true ? "heart-fill" : "heart")
|
||||||
VStack {
|
.renderingMode(.template)
|
||||||
Divider()
|
.resizable()
|
||||||
|
.foregroundStyle(
|
||||||
|
contactsListViewModel.selectedFriend != nil && contactsListViewModel.selectedFriend!.starred == true
|
||||||
|
? Color.redDanger500
|
||||||
|
: Color.grayMain2c500
|
||||||
|
)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
Text(contactsListViewModel.selectedFriend != nil && contactsListViewModel.selectedFriend!.starred == true
|
||||||
|
? "contact_details_remove_from_favourites"
|
||||||
|
: "contact_details_add_to_favourites")
|
||||||
|
.default_text_style(styleSize: 16)
|
||||||
|
Spacer()
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 30)
|
||||||
|
.background(Color.gray100)
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
|
|
@ -137,46 +135,45 @@ struct ContactsListBottomSheet: View {
|
||||||
.padding(.horizontal, 30)
|
.padding(.horizontal, 30)
|
||||||
.background(Color.gray100)
|
.background(Color.gray100)
|
||||||
|
|
||||||
if !contactsListViewModel.selectedFriend!.isReadOnly {
|
VStack {
|
||||||
VStack {
|
Divider()
|
||||||
Divider()
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
if contactsListViewModel.selectedFriend != nil {
|
||||||
|
isShowDeletePopup.toggle()
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
|
|
||||||
Button {
|
if #available(iOS 16.0, *) {
|
||||||
if contactsListViewModel.selectedFriend != nil {
|
if idiom != .pad {
|
||||||
isShowDeletePopup.toggle()
|
showingSheet.toggle()
|
||||||
}
|
|
||||||
|
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
if idiom != .pad {
|
|
||||||
showingSheet.toggle()
|
|
||||||
} else {
|
|
||||||
showingSheet.toggle()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
showingSheet.toggle()
|
showingSheet.toggle()
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
} label: {
|
} else {
|
||||||
HStack {
|
showingSheet.toggle()
|
||||||
Image("trash-simple")
|
dismiss()
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
Text("contact_details_delete")
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.frame(maxHeight: .infinity)
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 30)
|
} label: {
|
||||||
.background(Color.gray100)
|
HStack {
|
||||||
|
Image("trash-simple")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.redDanger500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
Text("contact_details_delete")
|
||||||
|
.foregroundStyle(Color.redDanger500)
|
||||||
|
.default_text_style(styleSize: 16)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 30)
|
||||||
|
.background(Color.gray100)
|
||||||
|
|
||||||
}
|
}
|
||||||
.background(Color.gray100)
|
.background(Color.gray100)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ struct EditContactFragment: View {
|
||||||
@State private var orientation = UIDevice.current.orientation
|
@State private var orientation = UIDevice.current.orientation
|
||||||
|
|
||||||
@StateObject private var editContactViewModel: EditContactViewModel
|
@StateObject private var editContactViewModel: EditContactViewModel
|
||||||
@StateObject private var keyboard = KeyboardResponder()
|
|
||||||
|
|
||||||
@Binding var isShowEditContactFragment: Bool
|
@Binding var isShowEditContactFragment: Bool
|
||||||
@Binding var isShowDismissPopup: Bool
|
@Binding var isShowDismissPopup: Bool
|
||||||
|
|
@ -101,8 +100,8 @@ struct EditContactFragment: View {
|
||||||
if editContactViewModel.selectedEditFriend == nil
|
if editContactViewModel.selectedEditFriend == nil
|
||||||
&& editContactViewModel.firstName.isEmpty
|
&& editContactViewModel.firstName.isEmpty
|
||||||
&& editContactViewModel.lastName.isEmpty
|
&& editContactViewModel.lastName.isEmpty
|
||||||
&& editContactViewModel.sipAddresses.first?.isEmpty ?? true
|
&& editContactViewModel.sipAddresses.first!.isEmpty
|
||||||
&& editContactViewModel.phoneNumbers.first?.isEmpty ?? true
|
&& editContactViewModel.phoneNumbers.first!.isEmpty
|
||||||
&& editContactViewModel.company.isEmpty
|
&& editContactViewModel.company.isEmpty
|
||||||
&& editContactViewModel.jobTitle.isEmpty {
|
&& editContactViewModel.jobTitle.isEmpty {
|
||||||
delayColorDismiss()
|
delayColorDismiss()
|
||||||
|
|
@ -114,8 +113,8 @@ struct EditContactFragment: View {
|
||||||
} else {
|
} else {
|
||||||
if editContactViewModel.firstName.isEmpty
|
if editContactViewModel.firstName.isEmpty
|
||||||
&& editContactViewModel.lastName.isEmpty
|
&& editContactViewModel.lastName.isEmpty
|
||||||
&& editContactViewModel.sipAddresses.first?.isEmpty ?? true
|
&& editContactViewModel.sipAddresses.first!.isEmpty
|
||||||
&& editContactViewModel.phoneNumbers.first?.isEmpty ?? true
|
&& editContactViewModel.phoneNumbers.first!.isEmpty
|
||||||
&& editContactViewModel.company.isEmpty
|
&& editContactViewModel.company.isEmpty
|
||||||
&& editContactViewModel.jobTitle.isEmpty {
|
&& editContactViewModel.jobTitle.isEmpty {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
|
|
@ -319,6 +318,7 @@ struct EditContactFragment: View {
|
||||||
.padding(.bottom, -5)
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
ForEach(editContactViewModel.sipAddresses.indices, id: \.self) { index in
|
ForEach(editContactViewModel.sipAddresses.indices, id: \.self) { index in
|
||||||
|
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
TextField("sip_address", text: $editContactViewModel.sipAddresses[index])
|
TextField("sip_address", text: $editContactViewModel.sipAddresses[index])
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
|
|
@ -336,27 +336,27 @@ struct EditContactFragment: View {
|
||||||
)
|
)
|
||||||
.focused($isSIPAddressFocused, equals: index)
|
.focused($isSIPAddressFocused, equals: index)
|
||||||
.onChange(of: editContactViewModel.sipAddresses[index]) { newValue in
|
.onChange(of: editContactViewModel.sipAddresses[index]) { newValue in
|
||||||
if !newValue.isEmpty && index == editContactViewModel.sipAddresses.count - 1 {
|
if !newValue.isEmpty && index + 1 == editContactViewModel.sipAddresses.count {
|
||||||
editContactViewModel.sipAddresses.append("")
|
editContactViewModel.sipAddresses.append("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
guard editContactViewModel.sipAddresses.indices.contains(index) else { return }
|
|
||||||
editContactViewModel.sipAddresses.remove(at: index)
|
editContactViewModel.sipAddresses.remove(at: index)
|
||||||
}) {
|
}, label: {
|
||||||
Image("x")
|
Image("x")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(
|
.foregroundStyle(
|
||||||
editContactViewModel.sipAddresses[index].isEmpty && index == editContactViewModel.sipAddresses.count - 1
|
editContactViewModel.sipAddresses[index].isEmpty && editContactViewModel.sipAddresses.count == index + 1
|
||||||
? Color.gray100
|
? Color.gray100
|
||||||
: Color.grayMain2c600
|
: Color.grayMain2c600
|
||||||
)
|
)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
}
|
})
|
||||||
.disabled(editContactViewModel.sipAddresses[index].isEmpty && index == editContactViewModel.sipAddresses.count - 1)
|
.disabled(editContactViewModel.sipAddresses[index].isEmpty && editContactViewModel.sipAddresses.count == index + 1)
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -367,12 +367,12 @@ struct EditContactFragment: View {
|
||||||
.default_text_style_700(styleSize: 15)
|
.default_text_style_700(styleSize: 15)
|
||||||
.padding(.bottom, -5)
|
.padding(.bottom, -5)
|
||||||
|
|
||||||
ForEach(editContactViewModel.phoneNumbers.indices, id: \.self) { index in
|
ForEach(0..<editContactViewModel.phoneNumbers.count, id: \.self) { index in
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
TextField("phone_number", text: $editContactViewModel.phoneNumbers[index])
|
TextField("phone_number", text: $editContactViewModel.phoneNumbers[index])
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.textContentType(.oneTimeCode)
|
.textContentType(.oneTimeCode)
|
||||||
.keyboardType(.phonePad)
|
.keyboardType(.numberPad)
|
||||||
.frame(height: 25)
|
.frame(height: 25)
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
.padding(.vertical, 15)
|
.padding(.vertical, 15)
|
||||||
|
|
@ -385,7 +385,7 @@ struct EditContactFragment: View {
|
||||||
)
|
)
|
||||||
.focused($isPhoneNumberFocused, equals: index)
|
.focused($isPhoneNumberFocused, equals: index)
|
||||||
.onChange(of: editContactViewModel.phoneNumbers[index]) { newValue in
|
.onChange(of: editContactViewModel.phoneNumbers[index]) { newValue in
|
||||||
if !newValue.isEmpty && index == editContactViewModel.phoneNumbers.count - 1 {
|
if !newValue.isEmpty && index + 1 == editContactViewModel.phoneNumbers.count {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
editContactViewModel.phoneNumbers.append("")
|
editContactViewModel.phoneNumbers.append("")
|
||||||
}
|
}
|
||||||
|
|
@ -393,21 +393,21 @@ struct EditContactFragment: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
guard editContactViewModel.phoneNumbers.indices.contains(index) else { return }
|
|
||||||
editContactViewModel.phoneNumbers.remove(at: index)
|
editContactViewModel.phoneNumbers.remove(at: index)
|
||||||
}) {
|
}, label: {
|
||||||
Image("x")
|
Image("x")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(
|
.foregroundStyle(
|
||||||
editContactViewModel.phoneNumbers[index].isEmpty && index == editContactViewModel.phoneNumbers.count - 1
|
editContactViewModel.phoneNumbers[index].isEmpty && editContactViewModel.phoneNumbers.count == index + 1
|
||||||
? Color.gray100
|
? Color.gray100
|
||||||
: Color.grayMain2c600
|
: Color.grayMain2c600
|
||||||
)
|
)
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
}
|
})
|
||||||
.disabled(editContactViewModel.phoneNumbers[index].isEmpty && index == editContactViewModel.phoneNumbers.count - 1)
|
.disabled(editContactViewModel.phoneNumbers[index].isEmpty && editContactViewModel.phoneNumbers.count == index + 1)
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
.zIndex(isPhoneNumberFocused == index ? 1 : 0)
|
.zIndex(isPhoneNumberFocused == index ? 1 : 0)
|
||||||
.transition(.move(edge: .top))
|
.transition(.move(edge: .top))
|
||||||
|
|
@ -510,90 +510,82 @@ struct EditContactFragment: View {
|
||||||
organizationName: editContactViewModel.company,
|
organizationName: editContactViewModel.company,
|
||||||
jobTitle: editContactViewModel.jobTitle,
|
jobTitle: editContactViewModel.jobTitle,
|
||||||
displayName: "",
|
displayName: "",
|
||||||
sipAddresses: editContactViewModel.sipAddresses,
|
sipAddresses: editContactViewModel.sipAddresses.map { $0 },
|
||||||
phoneNumbers: editContactViewModel.phoneNumbers.map { PhoneNumber(numLabel: "", num: $0) },
|
phoneNumbers: editContactViewModel.phoneNumbers.map { PhoneNumber(numLabel: "", num: $0)},
|
||||||
imageData: ""
|
imageData: ""
|
||||||
)
|
)
|
||||||
|
|
||||||
let existingFriend = editContactViewModel.selectedEditFriend?.friend
|
if editContactViewModel.selectedEditFriend != nil && editContactViewModel.selectedEditFriend!.friend != nil && selectedImage == nil &&
|
||||||
let friendHasCustomPhoto = existingFriend?.photo?.suffix(11) != "default.png"
|
!removedImage && editContactViewModel.selectedEditFriend!.friend!.photo!.suffix(11) != "default.png" {
|
||||||
|
ContactsManager.shared.saveFriend(
|
||||||
// Case: editing existing friend without changing the image
|
result: String(editContactViewModel.selectedEditFriend!.friend!.photo!.dropFirst(6)),
|
||||||
if let existingFriend = existingFriend,
|
|
||||||
selectedImage == nil,
|
|
||||||
!removedImage,
|
|
||||||
friendHasCustomPhoto,
|
|
||||||
let photo = existingFriend.photo {
|
|
||||||
|
|
||||||
let resultPhoto = String(photo.dropFirst(6))
|
|
||||||
ContactsManager.shared.saveFriend(result: resultPhoto, contact: newContact, existingFriend: existingFriend) { _ in
|
|
||||||
self.updateAvatar(for: existingFriend)
|
|
||||||
self.finishUIUpdate(existingFriend: existingFriend)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Case: creating new friend or updating with a new image
|
|
||||||
let imageToSave = selectedImage ?? ContactsManager.shared.textToImage(
|
|
||||||
firstName: editContactViewModel.firstName,
|
|
||||||
lastName: editContactViewModel.lastName
|
|
||||||
)
|
|
||||||
let prefix = selectedImage == nil ? "-default" : ""
|
|
||||||
|
|
||||||
saveImageThreadSafe(
|
|
||||||
image: imageToSave,
|
|
||||||
name: editContactViewModel.firstName + editContactViewModel.lastName,
|
|
||||||
prefix: prefix,
|
|
||||||
contact: newContact,
|
contact: newContact,
|
||||||
existingFriend: existingFriend,
|
existingFriend: editContactViewModel.selectedEditFriend!.friend, completion: {_ in
|
||||||
linphoneFriend: CorePreferences.friendListInWhichStoreNewlyCreatedFriends
|
if let selectedFriendTmp = editContactViewModel.selectedEditFriend?.friend {
|
||||||
|
let addressTmp = selectedFriendTmp.address?.clone()?.asStringUriOnly() ?? ""
|
||||||
|
SharedMainViewModel.shared.displayedFriend?.resetContactAvatarModel(
|
||||||
|
friend: selectedFriendTmp,
|
||||||
|
name: selectedFriendTmp.name ?? "",
|
||||||
|
address: addressTmp,
|
||||||
|
withPresence: SharedMainViewModel.shared.displayedFriend?.withPresence
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let friendIsNil = editContactViewModel.selectedEditFriend?.friend == nil
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
delayColorDismiss()
|
||||||
|
if friendIsNil {
|
||||||
|
withAnimation {
|
||||||
|
isShowEditContactFragment.toggle()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
withAnimation {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editContactViewModel.resetValues()
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func saveImageThreadSafe(image: UIImage, name: String, prefix: String, contact: Contact, existingFriend: Friend?, linphoneFriend: String) {
|
|
||||||
ContactsManager.shared.saveImage(
|
|
||||||
image: image,
|
|
||||||
name: name,
|
|
||||||
prefix: prefix,
|
|
||||||
contact: contact,
|
|
||||||
linphoneFriend: linphoneFriend,
|
|
||||||
existingFriend: existingFriend,
|
|
||||||
editingFriend: true
|
|
||||||
) {
|
|
||||||
if let existingFriend = existingFriend {
|
|
||||||
self.updateAvatar(for: existingFriend)
|
|
||||||
} else {
|
} else {
|
||||||
MagicSearchSingleton.shared.searchForContacts()
|
ContactsManager.shared.saveImage(
|
||||||
ContactsManager.shared.updateSubscriptionsLinphoneList()
|
image: selectedImage
|
||||||
}
|
?? ContactsManager.shared.textToImage(
|
||||||
self.finishUIUpdate(existingFriend: existingFriend)
|
firstName: editContactViewModel.firstName, lastName: editContactViewModel.lastName),
|
||||||
}
|
name: editContactViewModel.firstName
|
||||||
}
|
+ editContactViewModel.lastName,
|
||||||
|
prefix: ((selectedImage == nil) ? "-default" : ""),
|
||||||
private func updateAvatar(for friend: Friend) {
|
contact: newContact, linphoneFriend: "Linphone address-book", existingFriend: editContactViewModel.selectedEditFriend?.friend) {
|
||||||
let addressTmp = friend.address?.clone()?.asStringUriOnly() ?? ""
|
if let selectedFriendTmp = editContactViewModel.selectedEditFriend?.friend {
|
||||||
SharedMainViewModel.shared.displayedFriend?.resetContactAvatarModel(
|
let addressTmp = selectedFriendTmp.address?.clone()?.asStringUriOnly() ?? ""
|
||||||
friend: friend,
|
SharedMainViewModel.shared.displayedFriend?.resetContactAvatarModel(
|
||||||
name: friend.name ?? "",
|
friend: selectedFriendTmp,
|
||||||
address: addressTmp,
|
name: selectedFriendTmp.name ?? "",
|
||||||
withPresence: SharedMainViewModel.shared.displayedFriend?.withPresence
|
address: addressTmp,
|
||||||
)
|
withPresence: SharedMainViewModel.shared.displayedFriend?.withPresence
|
||||||
}
|
)
|
||||||
|
} else {
|
||||||
private func finishUIUpdate(existingFriend: Friend?) {
|
MagicSearchSingleton.shared.searchForContacts()
|
||||||
let friendIsNil = existingFriend == nil
|
ContactsManager.shared.updateSubscriptionsLinphoneList()
|
||||||
DispatchQueue.main.async {
|
}
|
||||||
delayColorDismiss()
|
|
||||||
withAnimation {
|
let friendIsNil = editContactViewModel.selectedEditFriend?.friend == nil
|
||||||
if friendIsNil {
|
|
||||||
isShowEditContactFragment.toggle()
|
DispatchQueue.main.async {
|
||||||
} else {
|
delayColorDismiss()
|
||||||
dismiss()
|
if friendIsNil {
|
||||||
}
|
withAnimation {
|
||||||
|
isShowEditContactFragment.toggle()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
withAnimation {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editContactViewModel.resetValues()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
editContactViewModel.resetValues()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,6 @@ class ContactAvatarModel: ObservableObject, Identifiable {
|
||||||
@Published var phoneNumbersWithLabel: [(label: String, phoneNumber: String)] = []
|
@Published var phoneNumbersWithLabel: [(label: String, phoneNumber: String)] = []
|
||||||
|
|
||||||
var nativeUri: String = ""
|
var nativeUri: String = ""
|
||||||
var editable: Bool = true
|
|
||||||
var isReadOnly: Bool = false
|
|
||||||
var withPresence: Bool?
|
var withPresence: Bool?
|
||||||
|
|
||||||
@Published var starred: Bool = false
|
@Published var starred: Bool = false
|
||||||
|
|
@ -72,20 +70,12 @@ class ContactAvatarModel: ObservableObject, Identifiable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let nativeUriTmp = friend?.nativeUri ?? ""
|
let nativeUriTmp = friend?.nativeUri ?? ""
|
||||||
let editableTmp = friend?.friendList?.type == .CardDAV || nativeUriTmp.isEmpty
|
|
||||||
let isReadOnlyTmp = (friend?.isReadOnly == true) || (friend?.inList() == false)
|
|
||||||
let withPresenceTmp = withPresence
|
let withPresenceTmp = withPresence
|
||||||
let starredTmp = friend?.starred ?? false
|
let starredTmp = friend?.starred ?? false
|
||||||
let vcardTmp = friend?.vcard ?? nil
|
let vcardTmp = friend?.vcard ?? nil
|
||||||
let organizationTmp = friend?.organization ?? ""
|
let organizationTmp = friend?.organization ?? ""
|
||||||
let jobTitleTmp = friend?.jobTitle ?? ""
|
let jobTitleTmp = friend?.jobTitle ?? ""
|
||||||
var photoTmp = friend?.photo ?? ""
|
let photoTmp = friend?.photo ?? ""
|
||||||
|
|
||||||
if friend?.friendList?.type == .CardDAV && friend?.photo?.isEmpty == false {
|
|
||||||
let fileName = "file:/" + name + ".png"
|
|
||||||
photoTmp = fileName.replacingOccurrences(of: " ", with: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastPresenceInfoTmp = ""
|
var lastPresenceInfoTmp = ""
|
||||||
var presenceStatusTmp: ConsolidatedPresence = .Offline
|
var presenceStatusTmp: ConsolidatedPresence = .Offline
|
||||||
|
|
||||||
|
|
@ -118,8 +108,6 @@ class ContactAvatarModel: ObservableObject, Identifiable {
|
||||||
self.addresses = addressesTmp
|
self.addresses = addressesTmp
|
||||||
self.phoneNumbersWithLabel = phoneNumbersWithLabelTmp
|
self.phoneNumbersWithLabel = phoneNumbersWithLabelTmp
|
||||||
self.nativeUri = nativeUriTmp
|
self.nativeUri = nativeUriTmp
|
||||||
self.editable = editableTmp
|
|
||||||
self.isReadOnly = isReadOnlyTmp
|
|
||||||
self.withPresence = withPresenceTmp
|
self.withPresence = withPresenceTmp
|
||||||
self.starred = starredTmp
|
self.starred = starredTmp
|
||||||
self.vcard = vcardTmp
|
self.vcard = vcardTmp
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,6 @@ class ContactsListViewModel: ObservableObject {
|
||||||
|
|
||||||
private var contactChatRoomDelegate: ChatRoomDelegate?
|
private var contactChatRoomDelegate: ChatRoomDelegate?
|
||||||
|
|
||||||
private let nativeAddressBookFriendList = "Native address-book"
|
|
||||||
let linphoneAddressBookFriendList = "Linphone address-book"
|
|
||||||
let tempRemoteAddressBookFriendList = "TempRemoteDirectoryContacts address-book"
|
|
||||||
|
|
||||||
init() {}
|
init() {}
|
||||||
|
|
||||||
func createOneToOneChatRoomWith(remote: Address) {
|
func createOneToOneChatRoomWith(remote: Address) {
|
||||||
|
|
@ -91,7 +87,8 @@ class ContactsListViewModel: ObservableObject {
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
SharedMainViewModel.shared.operationInProgress = false
|
SharedMainViewModel.shared.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -136,7 +133,8 @@ class ContactsListViewModel: ObservableObject {
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
SharedMainViewModel.shared.operationInProgress = false
|
SharedMainViewModel.shared.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -166,7 +164,8 @@ class ContactsListViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
SharedMainViewModel.shared.operationInProgress = false
|
SharedMainViewModel.shared.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
||||||
|
|
@ -204,7 +203,8 @@ class ContactsListViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
SharedMainViewModel.shared.operationInProgress = false
|
SharedMainViewModel.shared.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,11 @@ class EditContactViewModel: ObservableObject {
|
||||||
|
|
||||||
func resetValues() {
|
func resetValues() {
|
||||||
CoreContext.shared.doOnCoreQueue { _ in
|
CoreContext.shared.doOnCoreQueue { _ in
|
||||||
let nativeUriTmp = self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.nativeUri
|
let nativeUriTmp = (self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.nativeUri) ?? ""
|
||||||
let givenNameTmp = (self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.vcard?.givenName) ?? ""
|
let givenNameTmp = (self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.vcard?.givenName) ?? ""
|
||||||
let familyNameTmp = (self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.vcard?.familyName) ?? ""
|
let familyNameTmp = (self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.vcard?.familyName) ?? ""
|
||||||
let organizationTmp = self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.organization
|
let organizationTmp = (self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.organization) ?? ""
|
||||||
let jobTitleTmp = self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.jobTitle
|
let jobTitleTmp = (self.selectedEditFriend == nil ? "" : self.selectedEditFriend!.jobTitle) ?? ""
|
||||||
|
|
||||||
var sipAddressesTmp: [String] = []
|
var sipAddressesTmp: [String] = []
|
||||||
var phoneNumbersTmp: [String] = []
|
var phoneNumbersTmp: [String] = []
|
||||||
|
|
|
||||||
|
|
@ -60,13 +60,11 @@ struct ContentView: View {
|
||||||
@State var isShowDismissPopup = false
|
@State var isShowDismissPopup = false
|
||||||
@State var isShowSendCancelMeetingNotificationPopup = false
|
@State var isShowSendCancelMeetingNotificationPopup = false
|
||||||
@State var isShowStartCallGroupPopup = false
|
@State var isShowStartCallGroupPopup = false
|
||||||
@State var isShowDeleteMessagePopup = false
|
|
||||||
@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 isShowAccountProfileFragment = false
|
||||||
@State var isShowSettingsFragment = false
|
@State var isShowSettingsFragment = false
|
||||||
@State var isShowRecordingsListFragment = false
|
|
||||||
@State var isShowHelpFragment = false
|
@State var isShowHelpFragment = false
|
||||||
|
|
||||||
@State var fullscreenVideo = false
|
@State var fullscreenVideo = false
|
||||||
|
|
@ -140,54 +138,6 @@ struct ContentView: View {
|
||||||
.background(Color.redDanger500)
|
.background(Color.redDanger500)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sharedMainViewModel.waitingMessageCount > 0 && (!telecomManager.callInProgress || (telecomManager.callInProgress && !telecomManager.callDisplayed)) {
|
|
||||||
HStack {
|
|
||||||
Image("voicemail")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.frame(width: 26, height: 26)
|
|
||||||
.padding(.leading, 10)
|
|
||||||
|
|
||||||
if sharedMainViewModel.waitingMessageCount > 1 {
|
|
||||||
Text(String(format: String(localized: "mwi_messages_are_waiting_multiple"), sharedMainViewModel.waitingMessageCount.description))
|
|
||||||
.default_text_style_white(styleSize: 16)
|
|
||||||
} else {
|
|
||||||
Text(String(localized: "mwi_messages_are_waiting_single"))
|
|
||||||
.default_text_style_white(styleSize: 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(
|
|
||||||
action: {
|
|
||||||
withAnimation {
|
|
||||||
sharedMainViewModel.waitingMessageCount = 0
|
|
||||||
}
|
|
||||||
}, label: {
|
|
||||||
Image("x")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.frame(width: 26, height: 26)
|
|
||||||
.padding(.trailing, 10)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.frame(height: 40)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.background(Color.gray)
|
|
||||||
.onTapGesture {
|
|
||||||
if let index = accountProfileViewModel.defaultAccountModelIndex,
|
|
||||||
index < coreContext.accounts.count {
|
|
||||||
sharedMainViewModel.waitingMessageCount = 0
|
|
||||||
coreContext.accounts[index].callVoicemailUri()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sharedMainViewModel.fileUrlsToShare.isEmpty && (!telecomManager.callInProgress || (telecomManager.callInProgress && !telecomManager.callDisplayed)) {
|
if !sharedMainViewModel.fileUrlsToShare.isEmpty && (!telecomManager.callInProgress || (telecomManager.callInProgress && !telecomManager.callDisplayed)) {
|
||||||
HStack {
|
HStack {
|
||||||
Image("share-network")
|
Image("share-network")
|
||||||
|
|
@ -396,34 +346,32 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
.frame(height: geometry.size.height/4)
|
.frame(height: geometry.size.height/4)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sharedMainViewModel.disableMeetingFeature {
|
Button(action: {
|
||||||
Button(action: {
|
sharedMainViewModel.changeIndexView(indexViewInt: 3)
|
||||||
sharedMainViewModel.changeIndexView(indexViewInt: 3)
|
sharedMainViewModel.displayedFriend = nil
|
||||||
sharedMainViewModel.displayedFriend = nil
|
sharedMainViewModel.displayedCall = nil
|
||||||
sharedMainViewModel.displayedCall = nil
|
sharedMainViewModel.displayedConversation = nil
|
||||||
sharedMainViewModel.displayedConversation = nil
|
}, label: {
|
||||||
}, label: {
|
VStack {
|
||||||
VStack {
|
Image("video-conference")
|
||||||
Image("video-conference")
|
.renderingMode(.template)
|
||||||
.renderingMode(.template)
|
.resizable()
|
||||||
.resizable()
|
.foregroundStyle(sharedMainViewModel.indexView == 3 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||||
.foregroundStyle(sharedMainViewModel.indexView == 3 ? Color.orangeMain500 : Color.grayMain2c600)
|
.frame(width: 25, height: 25)
|
||||||
.frame(width: 25, height: 25)
|
if sharedMainViewModel.indexView == 0 {
|
||||||
if sharedMainViewModel.indexView == 0 {
|
Text("bottom_navigation_meetings_label")
|
||||||
Text("bottom_navigation_meetings_label")
|
.default_text_style_700(styleSize: 10)
|
||||||
.default_text_style_700(styleSize: 10)
|
} else {
|
||||||
} else {
|
Text("bottom_navigation_meetings_label")
|
||||||
Text("bottom_navigation_meetings_label")
|
.default_text_style(styleSize: 10)
|
||||||
.default_text_style(styleSize: 10)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.padding(.top)
|
})
|
||||||
.frame(height: geometry.size.height/4)
|
.padding(.top)
|
||||||
|
.frame(height: geometry.size.height/4)
|
||||||
Spacer()
|
|
||||||
}
|
Spacer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(width: 75, height: geometry.size.height)
|
.frame(width: 75, height: geometry.size.height)
|
||||||
|
|
@ -457,17 +405,6 @@ struct ContentView: View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
if searchIsActive == false {
|
if searchIsActive == false {
|
||||||
HStack {
|
HStack {
|
||||||
Button {
|
|
||||||
openMenu()
|
|
||||||
} label: {
|
|
||||||
Image("list")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let index = accountProfileViewModel.defaultAccountModelIndex,
|
if let index = accountProfileViewModel.defaultAccountModelIndex,
|
||||||
index < coreContext.accounts.count {
|
index < coreContext.accounts.count {
|
||||||
|
|
||||||
|
|
@ -536,7 +473,7 @@ struct ContentView: View {
|
||||||
|
|
||||||
Text(String(localized: sharedMainViewModel.indexView == 0 ? "bottom_navigation_contacts_label" : (sharedMainViewModel.indexView == 1 ? "bottom_navigation_calls_label" : (sharedMainViewModel.indexView == 2 ? "bottom_navigation_conversations_label" : "bottom_navigation_meetings_label"))))
|
Text(String(localized: sharedMainViewModel.indexView == 0 ? "bottom_navigation_contacts_label" : (sharedMainViewModel.indexView == 1 ? "bottom_navigation_calls_label" : (sharedMainViewModel.indexView == 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, 2)
|
.padding(.leading, 10)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
|
@ -572,7 +509,7 @@ struct ContentView: View {
|
||||||
Button {
|
Button {
|
||||||
sharedMainViewModel.displayedFriend = nil
|
sharedMainViewModel.displayedFriend = nil
|
||||||
isMenuOpen = false
|
isMenuOpen = false
|
||||||
magicSearch.changeAllContact(allContactBool: true)
|
magicSearch.allContact = true
|
||||||
magicSearch.searchForContacts()
|
magicSearch.searchForContacts()
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
@ -590,11 +527,11 @@ struct ContentView: View {
|
||||||
Button {
|
Button {
|
||||||
sharedMainViewModel.displayedFriend = nil
|
sharedMainViewModel.displayedFriend = nil
|
||||||
isMenuOpen = false
|
isMenuOpen = false
|
||||||
magicSearch.changeAllContact(allContactBool: false)
|
magicSearch.allContact = false
|
||||||
magicSearch.searchForContacts()
|
magicSearch.searchForContacts()
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Text(!magicSearch.linphoneDomain ? String(localized: "contacts_list_filter_popup_see_sip_only") : String(format: String(localized: "contacts_list_filter_popup_see_linphone_only"), Bundle.main.displayName))
|
Text(String(format: String(localized: "contacts_list_filter_popup_see_linphone_only"), Bundle.main.displayName))
|
||||||
Spacer()
|
Spacer()
|
||||||
if !magicSearch.allContact {
|
if !magicSearch.allContact {
|
||||||
Image("green-check")
|
Image("green-check")
|
||||||
|
|
@ -958,33 +895,31 @@ struct ContentView: View {
|
||||||
.frame(width: 66)
|
.frame(width: 66)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sharedMainViewModel.disableMeetingFeature {
|
Spacer()
|
||||||
Spacer()
|
Button(action: {
|
||||||
Button(action: {
|
sharedMainViewModel.changeIndexView(indexViewInt: 3)
|
||||||
sharedMainViewModel.changeIndexView(indexViewInt: 3)
|
sharedMainViewModel.displayedFriend = nil
|
||||||
sharedMainViewModel.displayedFriend = nil
|
sharedMainViewModel.displayedCall = nil
|
||||||
sharedMainViewModel.displayedCall = nil
|
sharedMainViewModel.displayedConversation = nil
|
||||||
sharedMainViewModel.displayedConversation = nil
|
}, label: {
|
||||||
}, label: {
|
VStack {
|
||||||
VStack {
|
Image("video-conference")
|
||||||
Image("video-conference")
|
.renderingMode(.template)
|
||||||
.renderingMode(.template)
|
.resizable()
|
||||||
.resizable()
|
.foregroundStyle(sharedMainViewModel.indexView == 3 ? Color.orangeMain500 : Color.grayMain2c600)
|
||||||
.foregroundStyle(sharedMainViewModel.indexView == 3 ? Color.orangeMain500 : Color.grayMain2c600)
|
.frame(width: 25, height: 25)
|
||||||
.frame(width: 25, height: 25)
|
if sharedMainViewModel.indexView == 3 {
|
||||||
if sharedMainViewModel.indexView == 3 {
|
Text("bottom_navigation_meetings_label")
|
||||||
Text("bottom_navigation_meetings_label")
|
.default_text_style_700(styleSize: 9)
|
||||||
.default_text_style_700(styleSize: 9)
|
} else {
|
||||||
} else {
|
Text("bottom_navigation_meetings_label")
|
||||||
Text("bottom_navigation_meetings_label")
|
.default_text_style(styleSize: 9)
|
||||||
.default_text_style(styleSize: 9)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.padding(.top)
|
})
|
||||||
.frame(width: 66)
|
.padding(.top)
|
||||||
}
|
.frame(width: 66)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
@ -1037,7 +972,6 @@ struct ContentView: View {
|
||||||
ConversationFragment(
|
ConversationFragment(
|
||||||
isShowConversationFragment: $isShowConversationFragment,
|
isShowConversationFragment: $isShowConversationFragment,
|
||||||
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
|
isShowStartCallGroupPopup: $isShowStartCallGroupPopup,
|
||||||
isShowDeleteMessagePopup: $isShowDeleteMessagePopup,
|
|
||||||
isShowEditContactFragment: $isShowEditContactFragment,
|
isShowEditContactFragment: $isShowEditContactFragment,
|
||||||
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress,
|
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress,
|
||||||
isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment,
|
isShowScheduleMeetingFragment: $isShowScheduleMeetingFragment,
|
||||||
|
|
@ -1092,7 +1026,6 @@ struct ContentView: View {
|
||||||
isShowLoginFragment: $isShowLoginFragment,
|
isShowLoginFragment: $isShowLoginFragment,
|
||||||
isShowAccountProfileFragment: $isShowAccountProfileFragment,
|
isShowAccountProfileFragment: $isShowAccountProfileFragment,
|
||||||
isShowSettingsFragment: $isShowSettingsFragment,
|
isShowSettingsFragment: $isShowSettingsFragment,
|
||||||
isShowRecordingsListFragment: $isShowRecordingsListFragment,
|
|
||||||
isShowHelpFragment: $isShowHelpFragment
|
isShowHelpFragment: $isShowHelpFragment
|
||||||
)
|
)
|
||||||
.environmentObject(accountProfileViewModel)
|
.environmentObject(accountProfileViewModel)
|
||||||
|
|
@ -1114,22 +1047,17 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if isShowEditContactFragment {
|
if isShowEditContactFragment {
|
||||||
VStack {
|
EditContactFragment(
|
||||||
EditContactFragment(
|
isShowEditContactFragment: $isShowEditContactFragment,
|
||||||
isShowEditContactFragment: $isShowEditContactFragment,
|
isShowDismissPopup: $isShowDismissPopup,
|
||||||
isShowDismissPopup: $isShowDismissPopup,
|
isShowEditContactFragmentAddress: isShowEditContactFragmentAddress
|
||||||
isShowEditContactFragmentAddress: isShowEditContactFragmentAddress
|
)
|
||||||
)
|
|
||||||
.frame(height: geometry.size.height)
|
|
||||||
.onAppear {
|
|
||||||
sharedMainViewModel.displayedFriend = nil
|
|
||||||
isShowEditContactFragmentAddress = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.zIndex(3)
|
.zIndex(3)
|
||||||
.transition(.opacity.combined(with: .move(edge: .bottom)))
|
.transition(.opacity.combined(with: .move(edge: .bottom)))
|
||||||
|
.onAppear {
|
||||||
|
sharedMainViewModel.displayedFriend = nil
|
||||||
|
isShowEditContactFragmentAddress = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isShowStartCallFragment {
|
if isShowStartCallFragment {
|
||||||
|
|
@ -1163,16 +1091,14 @@ struct ContentView: View {
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
content: Text("contact_dialog_delete_message"),
|
content: Text("contact_dialog_delete_message"),
|
||||||
titleFirstButton: nil,
|
titleFirstButton: Text("dialog_cancel"),
|
||||||
actionFirstButton: {},
|
actionFirstButton: {
|
||||||
titleSecondButton: Text("dialog_confirm"),
|
self.isShowDeleteContactPopup.toggle()},
|
||||||
|
titleSecondButton: Text("dialog_ok"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
contactsListVM.deleteSelectedContact()
|
contactsListVM.deleteSelectedContact()
|
||||||
self.isShowDeleteContactPopup.toggle()
|
self.isShowDeleteContactPopup.toggle()
|
||||||
},
|
})
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
|
||||||
actionThirdButton: { self.isShowDeleteContactPopup.toggle() }
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.zIndex(3)
|
.zIndex(3)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|
@ -1184,30 +1110,27 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if isShowDeleteAllHistoryPopup {
|
if isShowDeleteAllHistoryPopup {
|
||||||
PopupView(
|
PopupView(isShowPopup: $isShowDeleteContactPopup,
|
||||||
isShowPopup: $isShowDeleteContactPopup,
|
title: Text("history_dialog_delete_all_call_logs_title"),
|
||||||
title: Text("history_dialog_delete_all_call_logs_title"),
|
content: Text("history_dialog_delete_all_call_logs_message"),
|
||||||
content: Text("history_dialog_delete_all_call_logs_message"),
|
titleFirstButton: Text("dialog_cancel"),
|
||||||
titleFirstButton: nil,
|
actionFirstButton: {
|
||||||
actionFirstButton: {},
|
self.isShowDeleteAllHistoryPopup.toggle()
|
||||||
titleSecondButton: Text("dialog_confirm"),
|
if let historyListVM = historyListViewModel {
|
||||||
actionSecondButton: {
|
historyListVM.callLogsAddressToDelete = ""
|
||||||
if let historyListVM = historyListViewModel {
|
|
||||||
historyListVM.removeCallLogs()
|
|
||||||
}
|
|
||||||
self.isShowDeleteAllHistoryPopup.toggle()
|
|
||||||
sharedMainViewModel.displayedCall = nil
|
|
||||||
|
|
||||||
ToastViewModel.shared.show("Success_remove_call_logs")
|
|
||||||
},
|
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
|
||||||
actionThirdButton: {
|
|
||||||
self.isShowDeleteAllHistoryPopup.toggle()
|
|
||||||
if let historyListVM = historyListViewModel {
|
|
||||||
historyListVM.callLogsAddressToDelete = ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
},
|
||||||
|
titleSecondButton: Text("dialog_ok"),
|
||||||
|
actionSecondButton: {
|
||||||
|
if let historyListVM = historyListViewModel {
|
||||||
|
historyListVM.removeCallLogs()
|
||||||
|
}
|
||||||
|
self.isShowDeleteAllHistoryPopup.toggle()
|
||||||
|
sharedMainViewModel.displayedCall = nil
|
||||||
|
|
||||||
|
ToastViewModel.shared.toastMessage = "Success_remove_call_logs"
|
||||||
|
ToastViewModel.shared.displayToast.toggle()
|
||||||
|
})
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.zIndex(3)
|
.zIndex(3)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|
@ -1216,24 +1139,20 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if isShowDismissPopup {
|
if isShowDismissPopup {
|
||||||
PopupView(
|
PopupView(isShowPopup: $isShowDismissPopup,
|
||||||
isShowPopup: $isShowDismissPopup,
|
title: Text("contact_editor_dialog_abort_confirmation_title"),
|
||||||
title: Text("contact_editor_dialog_abort_confirmation_title"),
|
content: Text("contact_editor_dialog_abort_confirmation_message"),
|
||||||
content: Text("contact_editor_dialog_abort_confirmation_message"),
|
titleFirstButton: Text("dialog_cancel"),
|
||||||
titleFirstButton: nil,
|
actionFirstButton: {self.isShowDismissPopup.toggle()},
|
||||||
actionFirstButton: {},
|
titleSecondButton: Text("dialog_ok"),
|
||||||
titleSecondButton: Text("dialog_confirm"),
|
actionSecondButton: {
|
||||||
actionSecondButton: {
|
self.isShowDismissPopup.toggle()
|
||||||
self.isShowDismissPopup.toggle()
|
if isShowEditContactFragment {
|
||||||
if isShowEditContactFragment {
|
isShowEditContactFragment = false
|
||||||
isShowEditContactFragment = false
|
} else {
|
||||||
} else {
|
isShowEditContactFragmentInContactDetails = false
|
||||||
isShowEditContactFragmentInContactDetails = false
|
}
|
||||||
}
|
})
|
||||||
},
|
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
|
||||||
actionThirdButton: { self.isShowDismissPopup.toggle() }
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.zIndex(3)
|
.zIndex(3)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|
@ -1346,14 +1265,6 @@ struct ContentView: View {
|
||||||
.transition(.move(edge: .trailing))
|
.transition(.move(edge: .trailing))
|
||||||
}
|
}
|
||||||
|
|
||||||
if isShowRecordingsListFragment {
|
|
||||||
RecordingsListFragment(
|
|
||||||
isShowRecordingsListFragment: $isShowRecordingsListFragment
|
|
||||||
)
|
|
||||||
.zIndex(3)
|
|
||||||
.transition(.move(edge: .trailing))
|
|
||||||
}
|
|
||||||
|
|
||||||
if isShowHelpFragment {
|
if isShowHelpFragment {
|
||||||
HelpFragment(
|
HelpFragment(
|
||||||
isShowHelpFragment: $isShowHelpFragment
|
isShowHelpFragment: $isShowHelpFragment
|
||||||
|
|
@ -1363,25 +1274,21 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let meetingsListVM = meetingsListViewModel, isShowSendCancelMeetingNotificationPopup {
|
if let meetingsListVM = meetingsListViewModel, isShowSendCancelMeetingNotificationPopup {
|
||||||
PopupView(
|
PopupView(isShowPopup: $isShowSendCancelMeetingNotificationPopup,
|
||||||
isShowPopup: $isShowSendCancelMeetingNotificationPopup,
|
title: Text("meeting_schedule_cancel_dialog_title"),
|
||||||
title: Text("meeting_schedule_cancel_dialog_title"),
|
content: !sharedMainViewModel.disableChatFeature ? Text("meeting_schedule_cancel_dialog_message") : Text(""),
|
||||||
content: !sharedMainViewModel.disableChatFeature ? Text("meeting_schedule_cancel_dialog_message") : Text(""),
|
titleFirstButton: Text("dialog_cancel"),
|
||||||
titleFirstButton: nil,
|
actionFirstButton: {
|
||||||
actionFirstButton: {},
|
sharedMainViewModel.displayedMeeting = nil
|
||||||
titleSecondButton: Text("dialog_confirm"),
|
meetingsListVM.deleteSelectedMeeting()
|
||||||
actionSecondButton: {
|
self.isShowSendCancelMeetingNotificationPopup.toggle(
|
||||||
sharedMainViewModel.displayedMeeting = nil
|
) },
|
||||||
meetingsListVM.cancelMeetingWithNotifications()
|
titleSecondButton: Text("dialog_ok"),
|
||||||
self.isShowSendCancelMeetingNotificationPopup.toggle()
|
actionSecondButton: {
|
||||||
},
|
sharedMainViewModel.displayedMeeting = nil
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
meetingsListVM.cancelMeetingWithNotifications()
|
||||||
actionThirdButton: {
|
self.isShowSendCancelMeetingNotificationPopup.toggle()
|
||||||
sharedMainViewModel.displayedMeeting = nil
|
})
|
||||||
meetingsListVM.deleteSelectedMeeting()
|
|
||||||
self.isShowSendCancelMeetingNotificationPopup.toggle()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.zIndex(3)
|
.zIndex(3)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|
@ -1394,17 +1301,17 @@ 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: nil,
|
titleFirstButton: Text("dialog_cancel"),
|
||||||
actionFirstButton: {},
|
actionFirstButton: {
|
||||||
titleSecondButton: Text("dialog_confirm"),
|
self.isShowStartCallGroupPopup.toggle()
|
||||||
|
},
|
||||||
|
titleSecondButton: Text("dialog_ok"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
if sharedMainViewModel.displayedConversation != nil {
|
if sharedMainViewModel.displayedConversation != nil {
|
||||||
sharedMainViewModel.displayedConversation!.createGroupCall()
|
sharedMainViewModel.displayedConversation!.createGroupCall()
|
||||||
}
|
}
|
||||||
self.isShowStartCallGroupPopup.toggle()
|
self.isShowStartCallGroupPopup.toggle()
|
||||||
},
|
}
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
|
||||||
actionThirdButton: { self.isShowStartCallGroupPopup.toggle() }
|
|
||||||
)
|
)
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.zIndex(3)
|
.zIndex(3)
|
||||||
|
|
@ -1413,31 +1320,6 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isShowDeleteMessagePopup {
|
|
||||||
PopupView(
|
|
||||||
isShowPopup: $isShowDeleteMessagePopup,
|
|
||||||
title: Text("conversation_dialog_delete_chat_message_title"),
|
|
||||||
content: nil,
|
|
||||||
titleFirstButton: Text("conversation_dialog_delete_for_everyone_label"),
|
|
||||||
actionFirstButton: {
|
|
||||||
NotificationCenter.default.post(name: NSNotification.Name("DeleteMessageForEveryone"), object: nil)
|
|
||||||
self.isShowDeleteMessagePopup.toggle()
|
|
||||||
},
|
|
||||||
titleSecondButton: Text("conversation_dialog_delete_locally_label"),
|
|
||||||
actionSecondButton: {
|
|
||||||
NotificationCenter.default.post(name: NSNotification.Name("DeleteMessageForMe"), object: nil)
|
|
||||||
self.isShowDeleteMessagePopup.toggle()
|
|
||||||
},
|
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
|
||||||
actionThirdButton: { self.isShowDeleteMessagePopup.toggle() }
|
|
||||||
)
|
|
||||||
.background(.black.opacity(0.65))
|
|
||||||
.zIndex(3)
|
|
||||||
.onTapGesture {
|
|
||||||
self.isShowDeleteMessagePopup.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isShowConversationInfoPopup {
|
if isShowConversationInfoPopup {
|
||||||
PopupViewWithTextField(
|
PopupViewWithTextField(
|
||||||
isShowConversationInfoPopup: $isShowConversationInfoPopup,
|
isShowConversationInfoPopup: $isShowConversationInfoPopup,
|
||||||
|
|
@ -1506,8 +1388,6 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("ContactLoaded"))) { _ in
|
.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("ContactLoaded"))) { _ in
|
||||||
callViewModel.resetCallView()
|
|
||||||
|
|
||||||
if let conversationsListVM = conversationsListViewModel {
|
if let conversationsListVM = conversationsListViewModel {
|
||||||
conversationsListVM.updateChatRoomsList()
|
conversationsListVM.updateChatRoomsList()
|
||||||
}
|
}
|
||||||
|
|
@ -1585,7 +1465,6 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.id(coreContext.reloadID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func openMenu() {
|
func openMenu() {
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ struct ChatBubbleView: View {
|
||||||
HStack {
|
HStack {
|
||||||
if eventLogMessage.eventModel.eventLogType == .ConferenceChatMessage {
|
if eventLogMessage.eventModel.eventLogType == .ConferenceChatMessage {
|
||||||
VStack {
|
VStack {
|
||||||
if !eventLogMessage.message.text.isEmpty || !eventLogMessage.message.attachments.isEmpty || eventLogMessage.message.isIcalendar || eventLogMessage.message.isRetracted {
|
if !eventLogMessage.message.text.isEmpty || !eventLogMessage.message.attachments.isEmpty || eventLogMessage.message.isIcalendar {
|
||||||
HStack(alignment: .top, content: {
|
HStack(alignment: .top, content: {
|
||||||
if eventLogMessage.message.isOutgoing {
|
if eventLogMessage.message.isOutgoing {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
@ -137,12 +137,6 @@ struct ChatBubbleView: View {
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
.default_text_style(styleSize: 14)
|
.default_text_style(styleSize: 14)
|
||||||
.lineLimit(/*@START_MENU_TOKEN@*/2/*@END_MENU_TOKEN@*/)
|
.lineLimit(/*@START_MENU_TOKEN@*/2/*@END_MENU_TOKEN@*/)
|
||||||
} else if eventLogMessage.message.replyMessage!.isRetracted {
|
|
||||||
Text(eventLogMessage.message.replyMessage!.isOutgoing ? "conversation_message_content_deleted_by_us_label" : "conversation_message_content_deleted_label")
|
|
||||||
.italic()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.font(.system(size: 14))
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.all, 15)
|
.padding(.all, 15)
|
||||||
|
|
@ -151,8 +145,7 @@ struct ChatBubbleView: View {
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 1))
|
.clipShape(RoundedRectangle(cornerRadius: 1))
|
||||||
.roundedCorner(
|
.roundedCorner(
|
||||||
16,
|
16,
|
||||||
corners: eventLogMessage.message.isOutgoing ? [.topLeft, .topRight, .bottomLeft] : [.topLeft, .topRight, .bottomRight],
|
corners: eventLogMessage.message.isOutgoing ? [.topLeft, .topRight, .bottomLeft] : [.topLeft, .topRight, .bottomRight]
|
||||||
stroke: eventLogMessage.message.id == conversationViewModel.highlightedMessageID
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|
@ -180,18 +173,7 @@ struct ChatBubbleView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !eventLogMessage.message.text.isEmpty {
|
if !eventLogMessage.message.text.isEmpty {
|
||||||
DynamicLinkText(
|
DynamicLinkText(text: eventLogMessage.message.text)
|
||||||
text: eventLogMessage.message.text,
|
|
||||||
isMessageId: eventLogMessage.message.id == conversationViewModel.highlightedMessageID,
|
|
||||||
searchText: conversationViewModel.searchText,
|
|
||||||
participantConversationModel: conversationViewModel.participantConversationModel
|
|
||||||
)
|
|
||||||
} else if eventLogMessage.message.isRetracted {
|
|
||||||
Text(eventLogMessage.message.isOutgoing ? "conversation_message_content_deleted_by_us_label" : "conversation_message_content_deleted_label")
|
|
||||||
.italic()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.font(.system(size: 14))
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if eventLogMessage.message.isIcalendar && eventLogMessage.message.messageConferenceInfo != nil {
|
if eventLogMessage.message.isIcalendar && eventLogMessage.message.messageConferenceInfo != nil {
|
||||||
|
|
@ -343,14 +325,6 @@ struct ChatBubbleView: View {
|
||||||
.padding(.top, 1)
|
.padding(.top, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if eventLogMessage.message.isEdited && eventLogMessage.message.isOutgoing {
|
|
||||||
Text("conversation_message_edited_label")
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.default_text_style_300(styleSize: 12)
|
|
||||||
.padding(.top, 1)
|
|
||||||
.padding(.trailing, -4)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(conversationViewModel.getMessageTime(startDate: eventLogMessage.message.dateReceived))
|
Text(conversationViewModel.getMessageTime(startDate: eventLogMessage.message.dateReceived))
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
.default_text_style_300(styleSize: 12)
|
.default_text_style_300(styleSize: 12)
|
||||||
|
|
@ -375,14 +349,6 @@ struct ChatBubbleView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if eventLogMessage.message.isEdited && !eventLogMessage.message.isOutgoing {
|
|
||||||
Text("conversation_message_edited_label")
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.default_text_style_300(styleSize: 12)
|
|
||||||
.padding(.top, 1)
|
|
||||||
.padding(.trailing, -4)
|
|
||||||
}
|
|
||||||
|
|
||||||
if eventLogMessage.message.isEphemeral && !eventLogMessage.message.isOutgoing {
|
if eventLogMessage.message.isEphemeral && !eventLogMessage.message.isOutgoing {
|
||||||
Image("clock-countdown")
|
Image("clock-countdown")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
|
|
@ -421,9 +387,7 @@ struct ChatBubbleView: View {
|
||||||
.roundedCorner(
|
.roundedCorner(
|
||||||
16,
|
16,
|
||||||
corners: eventLogMessage.message.isOutgoing && eventLogMessage.message.isFirstMessage ? [.topLeft, .topRight, .bottomLeft] :
|
corners: eventLogMessage.message.isOutgoing && eventLogMessage.message.isFirstMessage ? [.topLeft, .topRight, .bottomLeft] :
|
||||||
(!eventLogMessage.message.isOutgoing && eventLogMessage.message.isFirstMessage ? [.topRight, .bottomRight, .bottomLeft] : [.allCorners]),
|
(!eventLogMessage.message.isOutgoing && eventLogMessage.message.isFirstMessage ? [.topRight, .bottomRight, .bottomLeft] : [.allCorners]))
|
||||||
stroke: eventLogMessage.message.id == conversationViewModel.highlightedMessageID
|
|
||||||
)
|
|
||||||
|
|
||||||
if !eventLogMessage.message.reactions.isEmpty {
|
if !eventLogMessage.message.reactions.isEmpty {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
@ -954,123 +918,40 @@ struct ChatBubbleView: View {
|
||||||
|
|
||||||
struct DynamicLinkText: View {
|
struct DynamicLinkText: View {
|
||||||
let text: String
|
let text: String
|
||||||
let isMessageId: Bool
|
|
||||||
let searchText: String
|
|
||||||
let participantConversationModel: [ContactAvatarModel]
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Text(makeAttributedString(from: text))
|
let components = text.components(separatedBy: " ")
|
||||||
|
|
||||||
|
Text(makeAttributedString(from: components))
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.lineLimit(nil)
|
.lineLimit(nil)
|
||||||
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
.default_text_style(styleSize: 14)
|
.default_text_style(styleSize: 14)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Builder
|
// Function to create an AttributedString with clickable links
|
||||||
|
private func makeAttributedString(from components: [String]) -> AttributedString {
|
||||||
private func makeAttributedString(from text: String) -> AttributedString {
|
var result = AttributedString("")
|
||||||
var result = AttributedString()
|
for (index, component) in components.enumerated() {
|
||||||
var currentWord = ""
|
if let url = URL(string: component.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""),
|
||||||
|
url.scheme == "http" || url.scheme == "https" {
|
||||||
for char in text {
|
var attributedText = AttributedString(component)
|
||||||
if char == " " || char == "\n" {
|
attributedText.link = url
|
||||||
appendWord(currentWord, to: &result)
|
attributedText.foregroundColor = .blue
|
||||||
result.append(AttributedString(String(char)))
|
attributedText.underlineStyle = .single
|
||||||
currentWord = ""
|
result.append(attributedText)
|
||||||
} else {
|
} else {
|
||||||
currentWord.append(char)
|
result.append(AttributedString(component))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add space between words except for the last one
|
||||||
|
if index < components.count - 1 {
|
||||||
|
result.append(AttributedString(" "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appendWord(currentWord, to: &result)
|
|
||||||
|
|
||||||
highlightSearch(in: &result, originalText: text)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Word handling
|
|
||||||
|
|
||||||
private func appendWord(_ word: String, to result: inout AttributedString) {
|
|
||||||
guard !word.isEmpty else { return }
|
|
||||||
|
|
||||||
// URL
|
|
||||||
if
|
|
||||||
let encoded = word.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
|
|
||||||
let url = URL(string: encoded),
|
|
||||||
["http", "https", "sip", "sips"].contains(url.scheme)
|
|
||||||
{
|
|
||||||
var link = AttributedString(word)
|
|
||||||
link.link = url
|
|
||||||
link.foregroundColor = .blue
|
|
||||||
link.underlineStyle = .single
|
|
||||||
result.append(link)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mention
|
|
||||||
if isMention(word),
|
|
||||||
let participant = participantConversationModel.first(
|
|
||||||
where: { ($0.address.dropFirst(4).split(separator: "@").first ?? "") == word.dropFirst() }
|
|
||||||
),
|
|
||||||
let mentionURL = URL(string: "linphone-mention://\(participant.address)")
|
|
||||||
{
|
|
||||||
var mention = AttributedString("@" + participant.name)
|
|
||||||
mention.link = mentionURL
|
|
||||||
mention.foregroundColor = Color.orangeMain500
|
|
||||||
mention.font = .system(size: 14)
|
|
||||||
result.append(mention)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text
|
|
||||||
var normal = AttributedString(word)
|
|
||||||
normal.foregroundColor = Color.grayMain2c700
|
|
||||||
result.append(normal)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Highlight global
|
|
||||||
|
|
||||||
private func highlightSearch(
|
|
||||||
in attributed: inout AttributedString,
|
|
||||||
originalText: String
|
|
||||||
) {
|
|
||||||
guard !searchText.isEmpty && isMessageId else { return }
|
|
||||||
|
|
||||||
let base = originalText.folding(
|
|
||||||
options: [.caseInsensitive, .diacriticInsensitive],
|
|
||||||
locale: .current
|
|
||||||
)
|
|
||||||
|
|
||||||
let search = searchText.folding(
|
|
||||||
options: [.caseInsensitive, .diacriticInsensitive],
|
|
||||||
locale: .current
|
|
||||||
)
|
|
||||||
|
|
||||||
var searchRange = base.startIndex..<base.endIndex
|
|
||||||
|
|
||||||
while let found = base.range(of: search, range: searchRange) {
|
|
||||||
guard
|
|
||||||
let start = AttributedString.Index(found.lowerBound, within: attributed),
|
|
||||||
let end = AttributedString.Index(found.upperBound, within: attributed)
|
|
||||||
else { break }
|
|
||||||
|
|
||||||
attributed[start..<end].font = .system(size: 14, weight: .bold)
|
|
||||||
|
|
||||||
searchRange = found.upperBound..<base.endIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Mention validation
|
|
||||||
|
|
||||||
private func isMention(_ word: String) -> Bool {
|
|
||||||
guard word.first == "@", word.count > 1 else { return false }
|
|
||||||
|
|
||||||
let username = word.dropFirst()
|
|
||||||
return username.allSatisfy {
|
|
||||||
$0.isLetter || $0.isNumber || $0 == "." || $0 == "_"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum URLType {
|
enum URLType {
|
||||||
|
|
@ -1148,12 +1029,8 @@ struct RoundedCorner: Shape {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
func roundedCorner(_ radius: CGFloat, corners: UIRectCorner, stroke: Bool? = false) -> some View {
|
func roundedCorner(_ radius: CGFloat, corners: UIRectCorner) -> some View {
|
||||||
clipShape(RoundedCorner(radius: radius, corners: corners) )
|
clipShape(RoundedCorner(radius: radius, corners: corners) )
|
||||||
.overlay(
|
|
||||||
RoundedCorner(radius: radius, corners: corners)
|
|
||||||
.stroke(Color.orangeMain500, lineWidth: (stroke ?? false) ? 1 : 0)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,58 +141,50 @@ struct ConversationForwardMessageFragment: View {
|
||||||
.padding(.vertical)
|
.padding(.vertical)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|
||||||
ZStack {
|
ScrollView {
|
||||||
ScrollView {
|
if !conversationForwardMessageViewModel.conversationsList.isEmpty {
|
||||||
if !conversationForwardMessageViewModel.conversationsList.isEmpty {
|
HStack(alignment: .center) {
|
||||||
HStack(alignment: .center) {
|
Text("bottom_navigation_conversations_label")
|
||||||
Text("bottom_navigation_conversations_label")
|
.default_text_style_800(styleSize: 16)
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
|
|
||||||
conversationsList
|
Spacer()
|
||||||
}
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
if !ContactsManager.shared.lastSearch.isEmpty {
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Text("contacts_list_all_contacts_title")
|
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
ContactsListFragment(showingSheet: .constant(false), startCallFunc: { addr in
|
|
||||||
withAnimation {
|
|
||||||
conversationForwardMessageViewModel.createOneToOneChatRoomWith(remote: addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
if !contactsManager.lastSearchSuggestions.isEmpty {
|
conversationsList
|
||||||
HStack(alignment: .center) {
|
|
||||||
Text("generic_address_picker_suggestions_list_title")
|
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
|
|
||||||
suggestionsList
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if magicSearch.isLoading {
|
if !ContactsManager.shared.lastSearch.isEmpty {
|
||||||
ProgressView()
|
HStack(alignment: .center) {
|
||||||
.controlSize(.large)
|
Text("contacts_list_all_contacts_title")
|
||||||
.progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
|
.default_text_style_800(styleSize: 16)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactsListFragment(showingSheet: .constant(false), startCallFunc: { addr in
|
||||||
|
withAnimation {
|
||||||
|
conversationForwardMessageViewModel.createOneToOneChatRoomWith(remote: addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
|
if !contactsManager.lastSearchSuggestions.isEmpty {
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Text("generic_address_picker_suggestions_list_title")
|
||||||
|
.default_text_style_800(styleSize: 16)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
|
suggestionsList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +275,6 @@ struct ConversationForwardMessageFragment: View {
|
||||||
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!.domain != CorePreferences.defaultDomain {
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
Image(uiImage: contactsManager.textToImage(
|
||||||
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
||||||
lastName: ""))
|
lastName: ""))
|
||||||
|
|
@ -293,29 +284,9 @@ struct ConversationForwardMessageFragment: View {
|
||||||
|
|
||||||
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.foregroundStyle(Color.orangeMain500)
|
||||||
} else {
|
|
||||||
if let address = contactsManager.lastSearchSuggestions[index].address {
|
|
||||||
let nameTmp = address.displayName
|
|
||||||
?? address.username
|
|
||||||
?? String(address.asStringUriOnly().dropFirst(4))
|
|
||||||
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: nameTmp,
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(nameTmp)
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Image("profil-picture-default")
|
Image("profil-picture-default")
|
||||||
.resizable()
|
.resizable()
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ struct ConversationFragment: View {
|
||||||
@State var isMenuOpen = false
|
@State var isMenuOpen = false
|
||||||
@State private var isMuted: Bool = false
|
@State private var isMuted: Bool = false
|
||||||
|
|
||||||
@FocusState var isSearchTextFocused: Bool
|
|
||||||
@FocusState var isMessageTextFocused: Bool
|
@FocusState var isMessageTextFocused: Bool
|
||||||
|
|
||||||
@State var offset: CGPoint = .zero
|
@State var offset: CGPoint = .zero
|
||||||
|
|
@ -65,7 +64,6 @@ struct ConversationFragment: View {
|
||||||
|
|
||||||
@Binding var isShowConversationFragment: Bool
|
@Binding var isShowConversationFragment: Bool
|
||||||
@Binding var isShowStartCallGroupPopup: Bool
|
@Binding var isShowStartCallGroupPopup: Bool
|
||||||
@Binding var isShowDeleteMessagePopup: Bool
|
|
||||||
|
|
||||||
@State private var selectedCategoryIndex = 0
|
@State private var selectedCategoryIndex = 0
|
||||||
|
|
||||||
|
|
@ -82,30 +80,14 @@ struct ConversationFragment: View {
|
||||||
@Binding var isShowConversationInfoPopup: Bool
|
@Binding var isShowConversationInfoPopup: Bool
|
||||||
@Binding var conversationInfoPopupText: String
|
@Binding var conversationInfoPopupText: String
|
||||||
|
|
||||||
@State var searchText: String = ""
|
|
||||||
@State var messageText: String = ""
|
@State var messageText: String = ""
|
||||||
|
|
||||||
@State private var chosen: String?
|
@State private var chosen: String?
|
||||||
@State private var showPicker = false
|
@State private var showPicker = false
|
||||||
@State private var isSheetVisible = false
|
@State private var isSheetVisible = false
|
||||||
@State private var isSearchVisible = false
|
|
||||||
|
|
||||||
@State private var isImdnOrReactionsSheetVisible = false
|
@State private var isImdnOrReactionsSheetVisible = false
|
||||||
|
|
||||||
@State var mentionIsOpen: Bool = false
|
|
||||||
@State var mentionQuery: String = ""
|
|
||||||
|
|
||||||
private let rowHeight: CGFloat = 60
|
|
||||||
private let maxVisibleRows: CGFloat = 3.5
|
|
||||||
|
|
||||||
private var filteredParticipants: [ContactAvatarModel] {
|
|
||||||
conversationViewModel.participantConversationModel.filter {
|
|
||||||
mentionQuery.isEmpty
|
|
||||||
|| $0.name.localizedCaseInsensitiveContains(mentionQuery)
|
|
||||||
|| String($0.address.dropFirst(4).split(separator: "@").first ?? "").localizedCaseInsensitiveContains(mentionQuery)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
|
|
@ -251,8 +233,6 @@ struct ConversationFragment: View {
|
||||||
if SharedMainViewModel.shared.displayedConversation != nil && (navigationManager.peerAddr == nil || navigationManager.peerAddr!.contains(SharedMainViewModel.shared.displayedConversation!.remoteSipUri)) {
|
if SharedMainViewModel.shared.displayedConversation != nil && (navigationManager.peerAddr == nil || navigationManager.peerAddr!.contains(SharedMainViewModel.shared.displayedConversation!.remoteSipUri)) {
|
||||||
conversationViewModel.resetDisplayedChatRoom()
|
conversationViewModel.resetDisplayedChatRoom()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
conversationViewModel.compose(stop: true, cachedConversation: cachedConversation)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -295,253 +275,170 @@ struct ConversationFragment: View {
|
||||||
.edgesIgnoringSafeArea(.top)
|
.edgesIgnoringSafeArea(.top)
|
||||||
.frame(height: 0)
|
.frame(height: 0)
|
||||||
|
|
||||||
if !isSearchVisible {
|
HStack {
|
||||||
HStack {
|
if (!(orientation == .landscapeLeft || orientation == .landscapeRight
|
||||||
if (!(orientation == .landscapeLeft || orientation == .landscapeRight
|
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)) || isShowConversationFragment {
|
||||||
|| UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height)) || isShowConversationFragment {
|
Image("caret-left")
|
||||||
Image("caret-left")
|
.renderingMode(.template)
|
||||||
.renderingMode(.template)
|
.resizable()
|
||||||
.resizable()
|
.foregroundStyle(Color.orangeMain500)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.padding(.all, 10)
|
||||||
.padding(.all, 10)
|
|
||||||
.padding(.top, 4)
|
|
||||||
.padding(.leading, -10)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
if isShowConversationFragment {
|
|
||||||
isShowConversationFragment = false
|
|
||||||
}
|
|
||||||
SharedMainViewModel.shared.displayedConversation = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Avatar(contactAvatarModel: SharedMainViewModel.shared.displayedConversation?.avatarModel ?? cachedConversation!.avatarModel, avatarSize: 50)
|
|
||||||
.padding(.top, 4)
|
.padding(.top, 4)
|
||||||
|
.padding(.leading, -10)
|
||||||
VStack(spacing: 1) {
|
.onTapGesture {
|
||||||
Text(SharedMainViewModel.shared.displayedConversation?.subject ?? cachedConversation!.subject)
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(.top, 4)
|
|
||||||
.lineLimit(1)
|
|
||||||
|
|
||||||
if isMuted || conversationViewModel.ephemeralTime != NSLocalizedString("conversation_ephemeral_messages_duration_disabled", comment: "") {
|
|
||||||
HStack {
|
|
||||||
if isMuted {
|
|
||||||
Image("bell-slash")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 16, height: 16, alignment: .trailing)
|
|
||||||
}
|
|
||||||
|
|
||||||
if conversationViewModel.ephemeralTime != NSLocalizedString("conversation_ephemeral_messages_duration_disabled", comment: "") {
|
|
||||||
Image("clock-countdown")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 16, height: 16, alignment: .trailing)
|
|
||||||
|
|
||||||
Text(conversationViewModel.ephemeralTime)
|
|
||||||
.default_text_style(styleSize: 12)
|
|
||||||
.padding(.leading, -2)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.background(.white)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation {
|
|
||||||
isShowInfoConversationFragment = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
if !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly) {
|
|
||||||
Button {
|
|
||||||
if SharedMainViewModel.shared.displayedConversation!.isGroup {
|
|
||||||
isShowStartCallGroupPopup.toggle()
|
|
||||||
} else {
|
|
||||||
SharedMainViewModel.shared.displayedConversation!.call()
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Image("phone")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.padding(.top, 4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
Button {
|
|
||||||
isMenuOpen = false
|
|
||||||
withAnimation {
|
withAnimation {
|
||||||
isShowInfoConversationFragment = true
|
if isShowConversationFragment {
|
||||||
|
isShowConversationFragment = false
|
||||||
|
}
|
||||||
|
SharedMainViewModel.shared.displayedConversation = nil
|
||||||
}
|
}
|
||||||
} label: {
|
}
|
||||||
HStack {
|
}
|
||||||
Text("conversation_menu_go_to_info")
|
|
||||||
Spacer()
|
Avatar(contactAvatarModel: SharedMainViewModel.shared.displayedConversation?.avatarModel ?? cachedConversation!.avatarModel, avatarSize: 50)
|
||||||
Image("info")
|
.padding(.top, 4)
|
||||||
|
|
||||||
|
VStack(spacing: 1) {
|
||||||
|
Text(SharedMainViewModel.shared.displayedConversation?.subject ?? cachedConversation!.subject)
|
||||||
|
.default_text_style(styleSize: 16)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.padding(.top, 4)
|
||||||
|
.lineLimit(1)
|
||||||
|
|
||||||
|
if isMuted || conversationViewModel.ephemeralTime != NSLocalizedString("conversation_ephemeral_messages_duration_disabled", comment: "") {
|
||||||
|
HStack {
|
||||||
|
if isMuted {
|
||||||
|
Image("bell-slash")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
.foregroundStyle(Color.orangeMain500)
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 16, height: 16, alignment: .trailing)
|
||||||
.padding(.all, 10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
isMenuOpen = false
|
|
||||||
withAnimation {
|
|
||||||
isSearchVisible = true
|
|
||||||
}
|
|
||||||
isSearchTextFocused = true
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Text("conversation_menu_search_in_messages")
|
|
||||||
Spacer()
|
|
||||||
Image("magnifying-glass")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly) {
|
|
||||||
Button {
|
|
||||||
isMenuOpen = false
|
|
||||||
SharedMainViewModel.shared.displayedConversation!.toggleMute()
|
|
||||||
isMuted = !isMuted
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Text(isMuted ? "conversation_action_unmute" : "conversation_action_mute")
|
|
||||||
Spacer()
|
|
||||||
Image(isMuted ? "bell-simple" : "bell-simple-slash")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
if conversationViewModel.ephemeralTime != NSLocalizedString("conversation_ephemeral_messages_duration_disabled", comment: "") {
|
||||||
isMenuOpen = false
|
Image("clock-countdown")
|
||||||
withAnimation {
|
.renderingMode(.template)
|
||||||
isShowEphemeralFragment = true
|
.resizable()
|
||||||
}
|
.foregroundStyle(Color.orangeMain500)
|
||||||
} label: {
|
.frame(width: 16, height: 16, alignment: .trailing)
|
||||||
HStack {
|
|
||||||
Text("conversation_menu_configure_ephemeral_messages")
|
Text(conversationViewModel.ephemeralTime)
|
||||||
Spacer()
|
.default_text_style(styleSize: 12)
|
||||||
Image("clock-countdown")
|
.padding(.leading, -2)
|
||||||
.renderingMode(.template)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.resizable()
|
.lineLimit(1)
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(.white)
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation {
|
||||||
|
isShowInfoConversationFragment = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
if !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly) {
|
||||||
|
Button {
|
||||||
|
if SharedMainViewModel.shared.displayedConversation!.isGroup {
|
||||||
|
isShowStartCallGroupPopup.toggle()
|
||||||
|
} else {
|
||||||
|
SharedMainViewModel.shared.displayedConversation!.call()
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Image("dots-three-vertical")
|
Image("phone")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.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)
|
||||||
.padding(.top, 4)
|
.padding(.top, 4)
|
||||||
.onChange(of: isMuted) { _ in }
|
|
||||||
.onAppear {
|
|
||||||
isMuted = SharedMainViewModel.shared.displayedConversation!.isMuted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onTapGesture {
|
|
||||||
isMenuOpen = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.frame(height: 50)
|
Menu {
|
||||||
.padding(.horizontal)
|
Button {
|
||||||
.padding(.bottom, 4)
|
isMenuOpen = false
|
||||||
.background(.white)
|
withAnimation {
|
||||||
} else {
|
isShowInfoConversationFragment = true
|
||||||
HStack {
|
}
|
||||||
Image("caret-left")
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text("conversation_menu_go_to_info")
|
||||||
|
Spacer()
|
||||||
|
Image("info")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly) {
|
||||||
|
Button {
|
||||||
|
isMenuOpen = false
|
||||||
|
SharedMainViewModel.shared.displayedConversation!.toggleMute()
|
||||||
|
isMuted = !isMuted
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text(isMuted ? "conversation_action_unmute" : "conversation_action_mute")
|
||||||
|
Spacer()
|
||||||
|
Image(isMuted ? "bell-simple" : "bell-simple-slash")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
isMenuOpen = false
|
||||||
|
withAnimation {
|
||||||
|
isShowEphemeralFragment = true
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text("conversation_menu_configure_ephemeral_messages")
|
||||||
|
Spacer()
|
||||||
|
Image("clock-countdown")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image("dots-three-vertical")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.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)
|
||||||
.padding(.top, 4)
|
.padding(.top, 4)
|
||||||
.padding(.leading, -10)
|
.onChange(of: isMuted) { _ in }
|
||||||
.onTapGesture {
|
.onAppear {
|
||||||
searchText = ""
|
isMuted = SharedMainViewModel.shared.displayedConversation!.isMuted
|
||||||
conversationViewModel.searchText = ""
|
|
||||||
conversationViewModel.latestMatch = nil
|
|
||||||
conversationViewModel.canSearchDown = false
|
|
||||||
conversationViewModel.highlightedMessageID = nil
|
|
||||||
withAnimation {
|
|
||||||
isSearchVisible = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField("conversation_menu_search_in_messages", text: $searchText)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.focused($isSearchTextFocused)
|
|
||||||
.padding(.vertical, 5)
|
|
||||||
.submitLabel(.search)
|
|
||||||
.onSubmit {
|
|
||||||
conversationViewModel.searchChatMessage(direction: .Up, textToSearch: searchText)
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
conversationViewModel.searchChatMessage(direction: .Up, textToSearch: searchText)
|
|
||||||
} label: {
|
|
||||||
Image("caret-up")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(searchText.isEmpty ? Color.grayMain2c300 : Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.padding(.top, 4)
|
|
||||||
}
|
|
||||||
.disabled(searchText.isEmpty)
|
|
||||||
|
|
||||||
Button {
|
|
||||||
conversationViewModel.searchChatMessage(direction: .Down, textToSearch: searchText)
|
|
||||||
} label: {
|
|
||||||
Image("caret-down")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle((searchText.isEmpty || !conversationViewModel.canSearchDown) ? Color.grayMain2c300 : Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
.padding(.top, 4)
|
|
||||||
}
|
|
||||||
.disabled(searchText.isEmpty || !conversationViewModel.canSearchDown)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
.onTapGesture {
|
||||||
.frame(height: 50)
|
isMenuOpen = true
|
||||||
.padding(.horizontal)
|
}
|
||||||
.padding(.bottom, 4)
|
|
||||||
.background(.white)
|
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.frame(height: 50)
|
||||||
|
.padding(.horizontal)
|
||||||
|
.padding(.bottom, 4)
|
||||||
|
.background(.white)
|
||||||
|
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
ZStack(alignment: .bottomTrailing) {
|
ZStack(alignment: .bottomTrailing) {
|
||||||
|
|
@ -563,7 +460,6 @@ struct ConversationFragment: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
conversationViewModel.compose(stop: true, cachedConversation: cachedConversation)
|
|
||||||
conversationViewModel.resetMessage()
|
conversationViewModel.resetMessage()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -659,7 +555,6 @@ struct ConversationFragment: View {
|
||||||
conversationViewModel.getMessages()
|
conversationViewModel.getMessages()
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
conversationViewModel.compose(stop: true, cachedConversation: cachedConversation)
|
|
||||||
conversationViewModel.resetMessage()
|
conversationViewModel.resetMessage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -679,7 +574,7 @@ struct ConversationFragment: View {
|
||||||
.transition(.move(edge: .bottom))
|
.transition(.move(edge: .bottom))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly) && !isSearchVisible {
|
if !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly) {
|
||||||
if conversationViewModel.messageToReply != nil {
|
if conversationViewModel.messageToReply != nil {
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
@ -725,43 +620,6 @@ struct ConversationFragment: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.transition(.move(edge: .bottom))
|
.transition(.move(edge: .bottom))
|
||||||
} else if conversationViewModel.messageToEdit != nil {
|
|
||||||
ZStack(alignment: .top) {
|
|
||||||
HStack {
|
|
||||||
VStack {
|
|
||||||
Text("conversation_editing_message_title")
|
|
||||||
.default_text_style_300(styleSize: 15)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.padding(.bottom, 1)
|
|
||||||
.lineLimit(1)
|
|
||||||
|
|
||||||
Text("\(conversationViewModel.messageToEdit!.message.text)")
|
|
||||||
.default_text_style_300(styleSize: 15)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.padding(.all, 20)
|
|
||||||
.background(Color.gray100)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
messageText = ""
|
|
||||||
withAnimation {
|
|
||||||
conversationViewModel.messageToEdit = nil
|
|
||||||
}
|
|
||||||
}, label: {
|
|
||||||
Image("x")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 30, height: 30, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.transition(.move(edge: .bottom))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !conversationViewModel.mediasToSend.isEmpty || mediasIsLoading {
|
if !conversationViewModel.mediasToSend.isEmpty || mediasIsLoading {
|
||||||
|
|
@ -963,72 +821,6 @@ struct ConversationFragment: View {
|
||||||
.transition(.move(edge: .bottom))
|
.transition(.move(edge: .bottom))
|
||||||
}
|
}
|
||||||
|
|
||||||
if mentionIsOpen && SharedMainViewModel.shared.displayedConversation!.isGroup {
|
|
||||||
ZStack(alignment: .top) {
|
|
||||||
ScrollView {
|
|
||||||
LazyVStack(alignment: .leading, spacing: 0) {
|
|
||||||
Text("conversation_participants_list_header")
|
|
||||||
.default_text_style_300(styleSize: 12)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(height: 14)
|
|
||||||
.padding(.vertical, 8)
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
|
|
||||||
if filteredParticipants.isEmpty {
|
|
||||||
VStack {
|
|
||||||
Text("conversation_participants_list_empty")
|
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
}
|
|
||||||
.frame(height: rowHeight)
|
|
||||||
}
|
|
||||||
ForEach(filteredParticipants, id: \.id) { participant in
|
|
||||||
Button {
|
|
||||||
messageText = String(messageText.dropLast(mentionQuery.count))
|
|
||||||
messageText.append((participant.address.dropFirst(4).split(separator: "@").first ?? "") + " ")
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Avatar(contactAvatarModel: participant, avatarSize: 40)
|
|
||||||
|
|
||||||
Text(participant.name)
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.lineLimit(1)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.background(Color.gray100)
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
|
||||||
.frame(height: rowHeight)
|
|
||||||
.buttonStyle(.plain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(
|
|
||||||
height: filteredParticipants.isEmpty ? rowHeight + 30 : min(
|
|
||||||
(CGFloat(filteredParticipants.count) * rowHeight) + 30,
|
|
||||||
(rowHeight * maxVisibleRows) + 30
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.clipped()
|
|
||||||
.background(Color.gray100)
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
Button {
|
|
||||||
withAnimation { mentionIsOpen = false }
|
|
||||||
} label: {
|
|
||||||
Image("x")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 24, height: 24)
|
|
||||||
.padding(10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.transition(.move(edge: .bottom))
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
if !voiceRecordingInProgress {
|
if !voiceRecordingInProgress {
|
||||||
Button {
|
Button {
|
||||||
|
|
@ -1055,8 +847,9 @@ struct ConversationFragment: View {
|
||||||
.focused($isMessageTextFocused)
|
.focused($isMessageTextFocused)
|
||||||
.padding(.vertical, 5)
|
.padding(.vertical, 5)
|
||||||
.onChange(of: messageText) { text in
|
.onChange(of: messageText) { text in
|
||||||
self.updateMentionState(from: text)
|
if !text.isEmpty {
|
||||||
conversationViewModel.compose(stop: text.isEmpty)
|
conversationViewModel.compose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ZStack(alignment: .leading) {
|
ZStack(alignment: .leading) {
|
||||||
|
|
@ -1067,7 +860,9 @@ struct ConversationFragment: View {
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.focused($isMessageTextFocused)
|
.focused($isMessageTextFocused)
|
||||||
.onChange(of: messageText) { text in
|
.onChange(of: messageText) { text in
|
||||||
conversationViewModel.compose(stop: text.isEmpty)
|
if !text.isEmpty {
|
||||||
|
conversationViewModel.compose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if messageText.isEmpty {
|
if messageText.isEmpty {
|
||||||
|
|
@ -1084,66 +879,43 @@ struct ConversationFragment: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if conversationViewModel.messageToEdit == nil {
|
if messageText.isEmpty && conversationViewModel.mediasToSend.isEmpty {
|
||||||
if messageText.isEmpty && conversationViewModel.mediasToSend.isEmpty {
|
|
||||||
Button {
|
|
||||||
voiceRecordingInProgress = true
|
|
||||||
} label: {
|
|
||||||
Image("microphone")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 28, height: 28, alignment: .leading)
|
|
||||||
.padding(.all, 6)
|
|
||||||
.padding(.top, 4)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Button {
|
|
||||||
if conversationViewModel.displayedConversationHistorySize > 1 {
|
|
||||||
NotificationCenter.default.post(name: .onScrollToBottom, object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
let messageTextTmp = self.messageText
|
|
||||||
messageText = " "
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
|
||||||
messageText = ""
|
|
||||||
isMessageTextFocused = true
|
|
||||||
|
|
||||||
conversationViewModel.sendMessage(messageText: messageTextTmp)
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Image("paper-plane-tilt")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
.frame(width: 28, height: 28, alignment: .leading)
|
|
||||||
.padding(.all, 6)
|
|
||||||
.padding(.top, 4)
|
|
||||||
.rotationEffect(.degrees(45))
|
|
||||||
}
|
|
||||||
.padding(.trailing, 4)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Button {
|
Button {
|
||||||
let messageTextTmp = self.messageText
|
voiceRecordingInProgress = true
|
||||||
messageText = " "
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
|
||||||
messageText = ""
|
|
||||||
isMessageTextFocused = true
|
|
||||||
|
|
||||||
conversationViewModel.sendMessage(messageText: messageTextTmp)
|
|
||||||
}
|
|
||||||
} label: {
|
} label: {
|
||||||
Image("pencil-simple")
|
Image("microphone")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.resizable()
|
.resizable()
|
||||||
.foregroundStyle(messageText.isEmpty ? Color.gray300 : Color.orangeMain500)
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
.frame(width: 28, height: 28, alignment: .leading)
|
.frame(width: 28, height: 28, alignment: .leading)
|
||||||
.padding(.all, 6)
|
.padding(.all, 6)
|
||||||
.padding(.top, 4)
|
.padding(.top, 4)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Button {
|
||||||
|
if conversationViewModel.displayedConversationHistorySize > 1 {
|
||||||
|
NotificationCenter.default.post(name: .onScrollToBottom, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
let messageTextTmp = self.messageText
|
||||||
|
messageText = " "
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||||
|
messageText = ""
|
||||||
|
isMessageTextFocused = true
|
||||||
|
|
||||||
|
conversationViewModel.sendMessage(messageText: messageTextTmp)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image("paper-plane-tilt")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.orangeMain500)
|
||||||
|
.frame(width: 28, height: 28, alignment: .leading)
|
||||||
|
.padding(.all, 6)
|
||||||
|
.padding(.top, 4)
|
||||||
|
.rotationEffect(.degrees(45))
|
||||||
|
}
|
||||||
.padding(.trailing, 4)
|
.padding(.trailing, 4)
|
||||||
.disabled(messageText.isEmpty)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.leading, 15)
|
.padding(.leading, 15)
|
||||||
|
|
@ -1324,68 +1096,29 @@ struct ConversationFragment: View {
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
|
|
||||||
if conversationViewModel.selectedMessage!.message.isOutgoing
|
|
||||||
&& !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly)
|
|
||||||
&& conversationViewModel.selectedMessage!.message.isEditable {
|
|
||||||
Button {
|
|
||||||
if let chatMessage = conversationViewModel.selectedMessage {
|
|
||||||
if voiceRecordingInProgress {
|
|
||||||
voiceRecordingInProgress = false
|
|
||||||
}
|
|
||||||
|
|
||||||
messageText = chatMessage.message.text
|
|
||||||
conversationViewModel.selectedMessage = nil
|
|
||||||
conversationViewModel.editMessage(
|
|
||||||
chatMessage: chatMessage,
|
|
||||||
isMessageTextFocused: Binding(
|
|
||||||
get: { isMessageTextFocused },
|
|
||||||
set: { isMessageTextFocused = $0 }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Text("menu_edit_chat_message")
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
Spacer()
|
|
||||||
Image("pencil-simple")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 20, height: 20, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(.vertical, 5)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
|
||||||
|
|
||||||
Divider()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !conversationViewModel.selectedMessage!.message.isRetracted {
|
Button {
|
||||||
Button {
|
let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.id == conversationViewModel.selectedMessage!.message.id})
|
||||||
let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.id == conversationViewModel.selectedMessage!.message.id})
|
conversationViewModel.selectedMessage = nil
|
||||||
conversationViewModel.selectedMessage = nil
|
conversationViewModel.replyToMessage(index: indexMessage ?? 0, isMessageTextFocused: Binding(
|
||||||
conversationViewModel.replyToMessage(index: indexMessage ?? 0, isMessageTextFocused: Binding(
|
get: { isMessageTextFocused },
|
||||||
get: { isMessageTextFocused },
|
set: { isMessageTextFocused = $0 }
|
||||||
set: { isMessageTextFocused = $0 }
|
))
|
||||||
))
|
} label: {
|
||||||
} label: {
|
HStack {
|
||||||
HStack {
|
Text("menu_reply_to_chat_message")
|
||||||
Text("menu_reply_to_chat_message")
|
.default_text_style(styleSize: 15)
|
||||||
.default_text_style(styleSize: 15)
|
Spacer()
|
||||||
Spacer()
|
Image("reply")
|
||||||
Image("reply")
|
.resizable()
|
||||||
.resizable()
|
.frame(width: 20, height: 20, alignment: .leading)
|
||||||
.frame(width: 20, height: 20, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(.vertical, 5)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
}
|
||||||
|
.padding(.vertical, 5)
|
||||||
Divider()
|
.padding(.horizontal, 20)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
if !conversationViewModel.selectedMessage!.message.text.isEmpty {
|
if !conversationViewModel.selectedMessage!.message.text.isEmpty {
|
||||||
Button {
|
Button {
|
||||||
UIPasteboard.general.setValue(
|
UIPasteboard.general.setValue(
|
||||||
|
|
@ -1393,7 +1126,8 @@ struct ConversationFragment: View {
|
||||||
forPasteboardType: UTType.plainText.identifier
|
forPasteboardType: UTType.plainText.identifier
|
||||||
)
|
)
|
||||||
|
|
||||||
ToastViewModel.shared.show("Success_message_copied_into_clipboard")
|
ToastViewModel.shared.toastMessage = "Success_message_copied_into_clipboard"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
|
|
||||||
conversationViewModel.selectedMessage = nil
|
conversationViewModel.selectedMessage = nil
|
||||||
} label: {
|
} label: {
|
||||||
|
|
@ -1412,35 +1146,27 @@ struct ConversationFragment: View {
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !conversationViewModel.selectedMessage!.message.isRetracted {
|
Button {
|
||||||
Button {
|
withAnimation {
|
||||||
withAnimation {
|
isShowConversationForwardMessageFragment = true
|
||||||
isShowConversationForwardMessageFragment = true
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Text("menu_forward_chat_message")
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
Spacer()
|
|
||||||
Image("forward")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 20, height: 20, alignment: .leading)
|
|
||||||
}
|
|
||||||
.padding(.vertical, 5)
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
Divider()
|
HStack {
|
||||||
|
Text("menu_forward_chat_message")
|
||||||
|
.default_text_style(styleSize: 15)
|
||||||
|
Spacer()
|
||||||
|
Image("forward")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 20, height: 20, alignment: .leading)
|
||||||
|
}
|
||||||
|
.padding(.vertical, 5)
|
||||||
|
.padding(.horizontal, 20)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if conversationViewModel.selectedMessage!.message.isOutgoing
|
conversationViewModel.deleteMessage()
|
||||||
&& !(SharedMainViewModel.shared.displayedConversation?.isReadOnly ?? cachedConversation!.isReadOnly)
|
|
||||||
&& conversationViewModel.selectedMessage!.message.isRetractable && !conversationViewModel.selectedMessage!.message.isRetracted {
|
|
||||||
isShowDeleteMessagePopup = true
|
|
||||||
} else {
|
|
||||||
conversationViewModel.deleteMessage()
|
|
||||||
}
|
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Text("menu_delete_selected_item")
|
Text("menu_delete_selected_item")
|
||||||
|
|
@ -1485,18 +1211,11 @@ struct ConversationFragment: View {
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
touchFeedback()
|
touchFeedback()
|
||||||
if isMessageTextFocused {
|
|
||||||
isMessageTextFocused = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
if conversationViewModel.selectedMessage != nil {
|
if conversationViewModel.selectedMessage != nil {
|
||||||
conversationViewModel.selectedMessage = nil
|
conversationViewModel.selectedMessage = nil
|
||||||
}
|
}
|
||||||
}.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("DeleteMessageForMe"))) { _ in
|
|
||||||
conversationViewModel.deleteMessage()
|
|
||||||
}.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("DeleteMessageForEveryone"))) { _ in
|
|
||||||
conversationViewModel.deleteMessageForEveryone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1542,59 +1261,8 @@ struct ConversationFragment: View {
|
||||||
.zIndex(5)
|
.zIndex(5)
|
||||||
.transition(.move(edge: .trailing))
|
.transition(.move(edge: .trailing))
|
||||||
}
|
}
|
||||||
|
|
||||||
if conversationViewModel.searchInProgress {
|
|
||||||
PopupLoadingView()
|
|
||||||
.background(.black.opacity(0.65))
|
|
||||||
.onDisappear {
|
|
||||||
if conversationViewModel.targetIndex >= 0 {
|
|
||||||
NotificationCenter.default.post(
|
|
||||||
name: NSNotification.Name("onScrollToIndex"),
|
|
||||||
object: nil,
|
|
||||||
userInfo: ["index": conversationViewModel.targetIndex, "animated": true]
|
|
||||||
)
|
|
||||||
|
|
||||||
conversationViewModel.targetIndex = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMentionState(from text: String) {
|
|
||||||
guard let atIndex = text.lastIndex(of: "@") else {
|
|
||||||
closeMention()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if atIndex > text.startIndex {
|
|
||||||
let before = text[text.index(before: atIndex)]
|
|
||||||
if before != " " && before != "\n" {
|
|
||||||
closeMention()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let query = String(text[text.index(after: atIndex)...])
|
|
||||||
|
|
||||||
if query.contains(" ") || query.contains("\n") {
|
|
||||||
closeMention()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
withAnimation {
|
|
||||||
mentionQuery = query
|
|
||||||
mentionIsOpen = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeMention() {
|
|
||||||
withAnimation {
|
|
||||||
mentionIsOpen = false
|
|
||||||
mentionQuery = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// swiftlint:enable cyclomatic_complexity
|
// swiftlint:enable cyclomatic_complexity
|
||||||
// swiftlint:enable function_body_length
|
// swiftlint:enable function_body_length
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,14 +104,12 @@ struct ConversationInfoFragment: View {
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
if !CorePreferences.hideSipAddresses {
|
Text(conversationViewModel.participantConversationModel.first?.address ?? "")
|
||||||
Text(conversationViewModel.participantConversationModel.first?.address ?? "")
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
.multilineTextAlignment(.center)
|
||||||
.multilineTextAlignment(.center)
|
.default_text_style(styleSize: 14)
|
||||||
.default_text_style(styleSize: 14)
|
.frame(maxWidth: .infinity)
|
||||||
.frame(maxWidth: .infinity)
|
.padding(.top, 5)
|
||||||
.padding(.top, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !SharedMainViewModel.shared.displayedConversation!.avatarModel.lastPresenceInfo.isEmpty {
|
if !SharedMainViewModel.shared.displayedConversation!.avatarModel.lastPresenceInfo.isEmpty {
|
||||||
Text(SharedMainViewModel.shared.displayedConversation!.avatarModel.lastPresenceInfo)
|
Text(SharedMainViewModel.shared.displayedConversation!.avatarModel.lastPresenceInfo)
|
||||||
|
|
@ -353,65 +351,59 @@ struct ConversationInfoFragment: View {
|
||||||
|
|
||||||
if conversationViewModel.myParticipantConversationModel != nil && conversationViewModel.myParticipantConversationModel!.address != participantConversationModel.address {
|
if conversationViewModel.myParticipantConversationModel != nil && conversationViewModel.myParticipantConversationModel!.address != participantConversationModel.address {
|
||||||
Menu {
|
Menu {
|
||||||
let addressConv = participantConversationModel.address
|
Button(
|
||||||
|
action: {
|
||||||
let friendIndex = contactsManager.lastSearch.firstIndex(
|
let addressConv = participantConversationModel.address
|
||||||
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressConv})})
|
|
||||||
|
let friendIndex = contactsManager.avatarListModel.first(
|
||||||
let disableAddContact = CorePreferences.disableAddContact
|
where: {$0.addresses.contains(where: {$0 == addressConv})})
|
||||||
|
|
||||||
if (!disableAddContact || (disableAddContact && friendIndex != nil)) {
|
SharedMainViewModel.shared.displayedCall = nil
|
||||||
Button(
|
SharedMainViewModel.shared.changeIndexView(indexViewInt: 0)
|
||||||
action: {
|
|
||||||
let addressConv = participantConversationModel.address
|
if friendIndex != nil {
|
||||||
|
withAnimation {
|
||||||
let friendIndex = contactsManager.avatarListModel.first(
|
SharedMainViewModel.shared.displayedFriend = friendIndex
|
||||||
where: {$0.addresses.contains(where: {$0 == addressConv})})
|
|
||||||
|
|
||||||
SharedMainViewModel.shared.changeIndexView(indexViewInt: 0)
|
|
||||||
|
|
||||||
if friendIndex != nil {
|
|
||||||
withAnimation {
|
|
||||||
SharedMainViewModel.shared.displayedFriend = friendIndex
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
withAnimation {
|
|
||||||
isShowEditContactFragment.toggle()
|
|
||||||
isShowEditContactFragmentAddress = String(participantConversationModel.address.dropFirst(4))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
SharedMainViewModel.shared.displayedConversation = nil
|
withAnimation {
|
||||||
},
|
isShowEditContactFragment.toggle()
|
||||||
label: {
|
isShowEditContactFragmentAddress = String(participantConversationModel.address.dropFirst(4))
|
||||||
HStack {
|
|
||||||
if friendIndex != nil {
|
|
||||||
Image("address-book")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
|
|
||||||
Text("conversation_info_menu_go_to_contact")
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.lineLimit(1)
|
|
||||||
} else {
|
|
||||||
Image("user-plus")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
|
|
||||||
Text("conversation_info_menu_add_to_contacts")
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
},
|
||||||
}
|
label: {
|
||||||
|
HStack {
|
||||||
|
let addressConv = participantConversationModel.address
|
||||||
|
|
||||||
|
let friendIndex = contactsManager.lastSearch.firstIndex(
|
||||||
|
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressConv})})
|
||||||
|
if friendIndex != nil {
|
||||||
|
Image("address-book")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
|
.frame(width: 25, height: 25)
|
||||||
|
|
||||||
|
Text("conversation_info_menu_go_to_contact")
|
||||||
|
.default_text_style(styleSize: 16)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.lineLimit(1)
|
||||||
|
} else {
|
||||||
|
Image("user-plus")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
|
.frame(width: 25, height: 25)
|
||||||
|
|
||||||
|
Text("conversation_info_menu_add_to_contacts")
|
||||||
|
.default_text_style(styleSize: 16)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if conversationViewModel.isUserAdmin {
|
if conversationViewModel.isUserAdmin {
|
||||||
let participantConversationModelIsAdmin = conversationViewModel.participantConversationModelAdmin.first(
|
let participantConversationModelIsAdmin = conversationViewModel.participantConversationModelAdmin.first(
|
||||||
|
|
@ -533,14 +525,7 @@ struct ConversationInfoFragment: View {
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
if !SharedMainViewModel.shared.displayedConversation!.isReadOnly {
|
if !SharedMainViewModel.shared.displayedConversation!.isReadOnly {
|
||||||
let addressConv = conversationViewModel.participantConversationModel.first?.address ?? ""
|
if !SharedMainViewModel.shared.displayedConversation!.isGroup {
|
||||||
|
|
||||||
let friendIndex = contactsManager.lastSearch.firstIndex(
|
|
||||||
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressConv})})
|
|
||||||
|
|
||||||
let disableAddContact = CorePreferences.disableAddContact
|
|
||||||
|
|
||||||
if !SharedMainViewModel.shared.displayedConversation!.isGroup && (!disableAddContact || (disableAddContact && friendIndex != nil)) {
|
|
||||||
Button(
|
Button(
|
||||||
action: {
|
action: {
|
||||||
if SharedMainViewModel.shared.displayedConversation != nil {
|
if SharedMainViewModel.shared.displayedConversation != nil {
|
||||||
|
|
@ -568,6 +553,10 @@ struct ConversationInfoFragment: View {
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
HStack {
|
HStack {
|
||||||
|
let addressConv = conversationViewModel.participantConversationModel.first?.address ?? ""
|
||||||
|
|
||||||
|
let friendIndex = contactsManager.lastSearch.firstIndex(
|
||||||
|
where: {$0.friend!.addresses.contains(where: {$0.asStringUriOnly() == addressConv})})
|
||||||
if friendIndex != nil {
|
if friendIndex != nil {
|
||||||
Image("address-book")
|
Image("address-book")
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
|
|
|
||||||
|
|
@ -105,46 +105,14 @@ struct ConversationRow: View {
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
|
||||||
HStack(spacing: 0) {
|
Text(conversation.lastMessageText)
|
||||||
Text(conversation.lastMessagePrefixText)
|
.foregroundStyle(Color.grayMain2c400)
|
||||||
.foregroundStyle(Color.grayMain2c400)
|
.if(conversation.unreadMessagesCount > 0) { view in
|
||||||
.if(conversation.unreadMessagesCount > 0) { view in
|
view.default_text_style_700(styleSize: 14)
|
||||||
view.default_text_style_700(styleSize: 14)
|
|
||||||
}
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.lineLimit(1)
|
|
||||||
.layoutPriority(1)
|
|
||||||
|
|
||||||
if !conversation.lastMessageIcon.isEmpty {
|
|
||||||
Image(conversation.lastMessageIcon)
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 16, height: 16)
|
|
||||||
.layoutPriority(0)
|
|
||||||
.padding(.trailing, 2)
|
|
||||||
}
|
}
|
||||||
|
.default_text_style(styleSize: 14)
|
||||||
if conversation.lastMessageInItalic {
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
Text(conversation.lastMessageText)
|
.lineLimit(1)
|
||||||
.italic()
|
|
||||||
.if(conversation.unreadMessagesCount > 0) { view in
|
|
||||||
view.bold()
|
|
||||||
}
|
|
||||||
.foregroundStyle(Color.grayMain2c400)
|
|
||||||
.font(.system(size: 14))
|
|
||||||
.lineLimit(1)
|
|
||||||
.layoutPriority(-1)
|
|
||||||
} else {
|
|
||||||
Text(conversation.lastMessageText)
|
|
||||||
.foregroundStyle(Color.grayMain2c400)
|
|
||||||
.if(conversation.unreadMessagesCount > 0) { view in
|
|
||||||
view.default_text_style_700(styleSize: 14)
|
|
||||||
}
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.lineLimit(1)
|
|
||||||
.layoutPriority(-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,42 +170,34 @@ struct StartConversationFragment: View {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ZStack {
|
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("contacts_list_all_contacts_title")
|
.default_text_style_800(styleSize: 16)
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
ContactsListFragment(showingSheet: .constant(false), startCallFunc: { addr in
|
|
||||||
startConversationViewModel.createOneToOneChatRoomWith(remote: addr)
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
|
|
||||||
if !contactsManager.lastSearchSuggestions.isEmpty {
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Text("generic_address_picker_suggestions_list_title")
|
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
|
|
||||||
suggestionsList
|
Spacer()
|
||||||
}
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
if magicSearch.isLoading {
|
ContactsListFragment(showingSheet: .constant(false), startCallFunc: { addr in
|
||||||
ProgressView()
|
startConversationViewModel.createOneToOneChatRoomWith(remote: addr)
|
||||||
.controlSize(.large)
|
})
|
||||||
.progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
|
if !contactsManager.lastSearchSuggestions.isEmpty {
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Text("generic_address_picker_suggestions_list_title")
|
||||||
|
.default_text_style_800(styleSize: 16)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
|
suggestionsList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -265,7 +257,6 @@ struct StartConversationFragment: View {
|
||||||
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!.domain != CorePreferences.defaultDomain {
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
Image(uiImage: contactsManager.textToImage(
|
||||||
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
||||||
lastName: ""))
|
lastName: ""))
|
||||||
|
|
@ -275,29 +266,9 @@ struct StartConversationFragment: View {
|
||||||
|
|
||||||
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.foregroundStyle(Color.orangeMain500)
|
||||||
} else {
|
|
||||||
if let address = contactsManager.lastSearchSuggestions[index].address {
|
|
||||||
let nameTmp = address.displayName
|
|
||||||
?? address.username
|
|
||||||
?? String(address.asStringUriOnly().dropFirst(4))
|
|
||||||
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: nameTmp,
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(nameTmp)
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Image("profil-picture-default")
|
Image("profil-picture-default")
|
||||||
.resizable()
|
.resizable()
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ struct StartGroupConversationFragment: View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
startConversationViewModel.createGroupChatRoom()
|
startConversationViewModel.createGroupChatRoom()
|
||||||
}, label: {
|
}, label: {
|
||||||
Text("dialog_confirm")
|
Text("dialog_ok")
|
||||||
.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)
|
||||||
|
|
|
||||||
|
|
@ -551,12 +551,6 @@ struct UIList: UIViewRepresentable {
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||||
let eventLogMessage = parent.conversationViewModel.conversationMessagesSection[0].rows[indexPath.row]
|
|
||||||
|
|
||||||
guard !eventLogMessage.message.isRetracted && eventLogMessage.eventModel.eventLogType == .ConferenceChatMessage else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let archiveAction = UIContextualAction(style: .normal, title: "") { _, _, completionHandler in
|
let archiveAction = UIContextualAction(style: .normal, title: "") { _, _, completionHandler in
|
||||||
self.parent.conversationViewModel.replyToMessage(index: indexPath.row, isMessageTextFocused: Binding(
|
self.parent.conversationViewModel.replyToMessage(index: indexPath.row, isMessageTextFocused: Binding(
|
||||||
get: { self.parent.isMessageTextFocused },
|
get: { self.parent.isMessageTextFocused },
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,9 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
@Published var isMuted: Bool
|
@Published var isMuted: Bool
|
||||||
@Published var isEphemeral: Bool
|
@Published var isEphemeral: Bool
|
||||||
@Published var encryptionEnabled: Bool
|
@Published var encryptionEnabled: Bool
|
||||||
@Published var lastMessagePrefixText: String
|
|
||||||
@Published var lastMessageText: String
|
@Published var lastMessageText: String
|
||||||
@Published var lastMessageIcon: String
|
|
||||||
@Published var lastMessageIsOutgoing: Bool
|
@Published var lastMessageIsOutgoing: Bool
|
||||||
@Published var lastMessageState: Int
|
@Published var lastMessageState: Int
|
||||||
@Published var lastMessageInItalic: Bool
|
|
||||||
@Published var unreadMessagesCount: Int
|
@Published var unreadMessagesCount: Int
|
||||||
@Published var avatarModel: ContactAvatarModel
|
@Published var avatarModel: ContactAvatarModel
|
||||||
|
|
||||||
|
|
@ -141,17 +138,11 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
|
|
||||||
self.lastMessage = nil
|
self.lastMessage = nil
|
||||||
|
|
||||||
self.lastMessagePrefixText = ""
|
|
||||||
|
|
||||||
self.lastMessageText = ""
|
self.lastMessageText = ""
|
||||||
|
|
||||||
self.lastMessageIcon = ""
|
|
||||||
|
|
||||||
self.lastMessageIsOutgoing = false
|
self.lastMessageIsOutgoing = false
|
||||||
|
|
||||||
self.lastMessageState = 0
|
self.lastMessageState = 0
|
||||||
|
|
||||||
self.lastMessageInItalic = false
|
|
||||||
|
|
||||||
self.unreadMessagesCount = chatRoom.unreadMessagesCount
|
self.unreadMessagesCount = chatRoom.unreadMessagesCount
|
||||||
|
|
||||||
|
|
@ -240,7 +231,8 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
} else if state == .CreationFailed {
|
} else if state == .CreationFailed {
|
||||||
Log.error("\(ConversationModel.TAG) Failed to create group call!")
|
Log.error("\(ConversationModel.TAG) Failed to create group call!")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.show("Failed_to_create_group_call_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_group_call_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -302,10 +294,8 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
fromAddressFriend = nil
|
fromAddressFriend = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastMessagePrefixTextTmp = (fromAddressFriend ?? "")
|
var lastMessageTextTmp = (fromAddressFriend ?? "")
|
||||||
var lastMessageTextTmp = (lastMessage!.contents.first(where: {$0.isText == true})?.utf8Text ?? (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name ?? ""))
|
+ (lastMessage!.contents.first(where: {$0.isText == true})?.utf8Text ?? (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name ?? ""))
|
||||||
var lastMessageIconTmp = ""
|
|
||||||
var lastMessageInItalicTmp = false
|
|
||||||
|
|
||||||
if lastMessage!.contents.first != nil && lastMessage!.contents.first!.isIcalendar == true {
|
if lastMessage!.contents.first != nil && lastMessage!.contents.first!.isIcalendar == true {
|
||||||
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: lastMessage!.contents.first!) {
|
if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: lastMessage!.contents.first!) {
|
||||||
|
|
@ -318,30 +308,10 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
} else if conferenceInfo.state == .Cancelled {
|
} else if conferenceInfo.state == .Cancelled {
|
||||||
lastMessageTextTmp = String(localized: "message_meeting_invitation_cancelled_notification")
|
lastMessageTextTmp = String(localized: "message_meeting_invitation_cancelled_notification")
|
||||||
}
|
}
|
||||||
|
|
||||||
lastMessageIconTmp = "calendar"
|
|
||||||
|
|
||||||
lastMessageInItalicTmp = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastMessage!.contents.first(where: {$0.isFile == true || $0.isFileTransfer == true})?.name != nil) {
|
|
||||||
lastMessageIconTmp = "file"
|
|
||||||
} else if lastMessage!.isReply {
|
|
||||||
lastMessageIconTmp = "reply"
|
|
||||||
} else if lastMessage!.isForward {
|
|
||||||
lastMessageIconTmp = "forward"
|
|
||||||
}
|
|
||||||
|
|
||||||
if lastMessage!.isRetracted {
|
|
||||||
lastMessageTextTmp += lastMessage!.isOutgoing ? String(localized: "conversation_message_content_deleted_by_us_label") : String(localized: "conversation_message_content_deleted_label")
|
|
||||||
|
|
||||||
lastMessageIconTmp = "trash"
|
|
||||||
|
|
||||||
lastMessageInItalicTmp = true
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastMessageIsOutgoingTmp = lastMessage?.isOutgoing ?? false
|
let lastMessageIsOutgoingTmp = lastMessage?.isOutgoing ?? false
|
||||||
|
|
||||||
let lastUpdateTimeTmp = lastMessage?.time ?? chatRoom.lastUpdateTime
|
let lastUpdateTimeTmp = lastMessage?.time ?? chatRoom.lastUpdateTime
|
||||||
|
|
@ -349,19 +319,13 @@ class ConversationModel: ObservableObject, Identifiable {
|
||||||
let lastMessageStateTmp = lastMessage?.state.rawValue ?? 0
|
let lastMessageStateTmp = lastMessage?.state.rawValue ?? 0
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.lastMessagePrefixText = lastMessagePrefixTextTmp
|
|
||||||
|
|
||||||
self.lastMessageText = lastMessageTextTmp
|
self.lastMessageText = lastMessageTextTmp
|
||||||
|
|
||||||
self.lastMessageIcon = lastMessageIconTmp
|
|
||||||
|
|
||||||
self.lastMessageIsOutgoing = lastMessageIsOutgoingTmp
|
self.lastMessageIsOutgoing = lastMessageIsOutgoingTmp
|
||||||
|
|
||||||
self.lastUpdateTime = lastUpdateTimeTmp
|
self.lastUpdateTime = lastUpdateTimeTmp
|
||||||
|
|
||||||
self.lastMessageState = lastMessageStateTmp
|
self.lastMessageState = lastMessageStateTmp
|
||||||
|
|
||||||
self.lastMessageInItalic = lastMessageInItalicTmp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getUnreadMessagesCount()
|
getUnreadMessagesCount()
|
||||||
|
|
|
||||||
|
|
@ -68,10 +68,6 @@ public struct Message: Identifiable, Hashable {
|
||||||
public var status: Status?
|
public var status: Status?
|
||||||
public var createdAt: Date
|
public var createdAt: Date
|
||||||
public var isOutgoing: Bool
|
public var isOutgoing: Bool
|
||||||
public var isEditable: Bool
|
|
||||||
public var isRetractable: Bool
|
|
||||||
public var isEdited: Bool
|
|
||||||
public var isRetracted: Bool
|
|
||||||
public var dateReceived: time_t
|
public var dateReceived: time_t
|
||||||
|
|
||||||
public var address: String
|
public var address: String
|
||||||
|
|
@ -98,10 +94,6 @@ public struct Message: Identifiable, Hashable {
|
||||||
status: Status? = nil,
|
status: Status? = nil,
|
||||||
createdAt: Date = Date(),
|
createdAt: Date = Date(),
|
||||||
isOutgoing: Bool,
|
isOutgoing: Bool,
|
||||||
isEditable: Bool,
|
|
||||||
isRetractable: Bool,
|
|
||||||
isEdited: Bool,
|
|
||||||
isRetracted: Bool,
|
|
||||||
dateReceived: time_t,
|
dateReceived: time_t,
|
||||||
address: String,
|
address: String,
|
||||||
isFirstMessage: Bool = false,
|
isFirstMessage: Bool = false,
|
||||||
|
|
@ -124,10 +116,6 @@ public struct Message: Identifiable, Hashable {
|
||||||
self.status = status
|
self.status = status
|
||||||
self.createdAt = createdAt
|
self.createdAt = createdAt
|
||||||
self.isOutgoing = isOutgoing
|
self.isOutgoing = isOutgoing
|
||||||
self.isEditable = isEditable
|
|
||||||
self.isRetractable = isRetractable
|
|
||||||
self.isEdited = isEdited
|
|
||||||
self.isRetracted = isRetracted
|
|
||||||
self.dateReceived = dateReceived
|
self.dateReceived = dateReceived
|
||||||
self.isFirstMessage = isFirstMessage
|
self.isFirstMessage = isFirstMessage
|
||||||
self.address = address
|
self.address = address
|
||||||
|
|
@ -175,10 +163,6 @@ public struct Message: Identifiable, Hashable {
|
||||||
status: status,
|
status: status,
|
||||||
createdAt: draft.createdAt,
|
createdAt: draft.createdAt,
|
||||||
isOutgoing: draft.isOutgoing,
|
isOutgoing: draft.isOutgoing,
|
||||||
isEditable: draft.isEditable,
|
|
||||||
isRetractable: draft.isRetractable,
|
|
||||||
isEdited: draft.isEdited,
|
|
||||||
isRetracted: draft.isRetracted,
|
|
||||||
dateReceived: draft.dateReceived,
|
dateReceived: draft.dateReceived,
|
||||||
address: draft.address,
|
address: draft.address,
|
||||||
isFirstMessage: draft.isFirstMessage,
|
isFirstMessage: draft.isFirstMessage,
|
||||||
|
|
@ -200,7 +184,7 @@ extension Message {
|
||||||
|
|
||||||
extension Message: Equatable {
|
extension Message: Equatable {
|
||||||
public static func == (lhs: Message, rhs: Message) -> Bool {
|
public static func == (lhs: Message, rhs: Message) -> Bool {
|
||||||
lhs.id == rhs.id && lhs.status == rhs.status && lhs.isEdited == rhs.isEdited && lhs.isRetracted == rhs.isRetracted && lhs.isFirstMessage == rhs.isFirstMessage && lhs.text == rhs.text && lhs.attachments == rhs.attachments && lhs.replyMessage?.text == rhs.replyMessage?.text && lhs.replyMessage?.isRetracted == rhs.replyMessage?.isRetracted && lhs.ownReaction == rhs.ownReaction && lhs.reactions == rhs.reactions && lhs.ephemeralExpireTime == rhs.ephemeralExpireTime
|
lhs.id == rhs.id && lhs.status == rhs.status && lhs.isFirstMessage == rhs.isFirstMessage && lhs.ownReaction == rhs.ownReaction && lhs.reactions == rhs.reactions && lhs.ephemeralExpireTime == rhs.ephemeralExpireTime && lhs.attachments == rhs.attachments
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,10 +211,6 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
|
||||||
public var isFirstMessage: Bool
|
public var isFirstMessage: Bool
|
||||||
public var text: String
|
public var text: String
|
||||||
public var isOutgoing: Bool
|
public var isOutgoing: Bool
|
||||||
public var isEditable: Bool
|
|
||||||
public var isRetractable: Bool
|
|
||||||
public var isEdited: Bool
|
|
||||||
public var isRetracted: Bool
|
|
||||||
public var dateReceived: time_t
|
public var dateReceived: time_t
|
||||||
public var attachmentsNames: String
|
public var attachmentsNames: String
|
||||||
public var attachments: [Attachment]
|
public var attachments: [Attachment]
|
||||||
|
|
@ -241,10 +221,6 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
|
||||||
isFirstMessage: Bool = false,
|
isFirstMessage: Bool = false,
|
||||||
text: String = "",
|
text: String = "",
|
||||||
isOutgoing: Bool,
|
isOutgoing: Bool,
|
||||||
isEditable: Bool,
|
|
||||||
isRetractable: Bool,
|
|
||||||
isEdited: Bool,
|
|
||||||
isRetracted: Bool,
|
|
||||||
dateReceived: time_t,
|
dateReceived: time_t,
|
||||||
attachmentsNames: String = "",
|
attachmentsNames: String = "",
|
||||||
attachments: [Attachment] = [],
|
attachments: [Attachment] = [],
|
||||||
|
|
@ -255,10 +231,6 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
|
||||||
self.isFirstMessage = isFirstMessage
|
self.isFirstMessage = isFirstMessage
|
||||||
self.text = text
|
self.text = text
|
||||||
self.isOutgoing = isOutgoing
|
self.isOutgoing = isOutgoing
|
||||||
self.isEditable = isEditable
|
|
||||||
self.isRetractable = isRetractable
|
|
||||||
self.isEdited = isEdited
|
|
||||||
self.isRetracted = isRetracted
|
|
||||||
self.dateReceived = dateReceived
|
self.dateReceived = dateReceived
|
||||||
self.attachmentsNames = attachmentsNames
|
self.attachmentsNames = attachmentsNames
|
||||||
self.attachments = attachments
|
self.attachments = attachments
|
||||||
|
|
@ -266,24 +238,20 @@ public struct ReplyMessage: Codable, Identifiable, Hashable {
|
||||||
}
|
}
|
||||||
|
|
||||||
func toMessage() -> Message {
|
func toMessage() -> Message {
|
||||||
Message(id: id, isOutgoing: isOutgoing, isEditable: isEditable, isRetractable: isRetractable, isEdited: isEdited, isRetracted: isRetracted, dateReceived: dateReceived, address: address, isFirstMessage: isFirstMessage, text: text, attachments: attachments, recording: recording)
|
Message(id: id, isOutgoing: isOutgoing, dateReceived: dateReceived, address: address, isFirstMessage: isFirstMessage, text: text, attachments: attachments, recording: recording)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Message {
|
public extension Message {
|
||||||
|
|
||||||
func toReplyMessage() -> ReplyMessage {
|
func toReplyMessage() -> ReplyMessage {
|
||||||
ReplyMessage(id: id, address: address, isFirstMessage: isFirstMessage, text: text, isOutgoing: isOutgoing, isEditable: isEditable, isRetractable: isRetractable, isEdited: isEdited, isRetracted: isRetracted, dateReceived: dateReceived, attachments: attachments, recording: recording)
|
ReplyMessage(id: id, address: address, isFirstMessage: isFirstMessage, text: text, isOutgoing: isOutgoing, dateReceived: dateReceived, attachments: attachments, recording: recording)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct DraftMessage {
|
public struct DraftMessage {
|
||||||
public var id: String?
|
public var id: String?
|
||||||
public let isOutgoing: Bool
|
public let isOutgoing: Bool
|
||||||
public let isEditable: Bool
|
|
||||||
public let isRetractable: Bool
|
|
||||||
public let isEdited: Bool
|
|
||||||
public let isRetracted: Bool
|
|
||||||
public var dateReceived: time_t
|
public var dateReceived: time_t
|
||||||
public let address: String
|
public let address: String
|
||||||
public let isFirstMessage: Bool
|
public let isFirstMessage: Bool
|
||||||
|
|
@ -297,10 +265,6 @@ public struct DraftMessage {
|
||||||
|
|
||||||
public init(id: String? = nil,
|
public init(id: String? = nil,
|
||||||
isOutgoing: Bool,
|
isOutgoing: Bool,
|
||||||
isEditable: Bool,
|
|
||||||
isRetractable: Bool,
|
|
||||||
isEdited: Bool,
|
|
||||||
isRetracted: Bool,
|
|
||||||
dateReceived: time_t,
|
dateReceived: time_t,
|
||||||
address: String,
|
address: String,
|
||||||
isFirstMessage: Bool,
|
isFirstMessage: Bool,
|
||||||
|
|
@ -314,10 +278,6 @@ public struct DraftMessage {
|
||||||
) {
|
) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.isOutgoing = isOutgoing
|
self.isOutgoing = isOutgoing
|
||||||
self.isEditable = isEditable
|
|
||||||
self.isRetractable = isRetractable
|
|
||||||
self.isEdited = isEdited
|
|
||||||
self.isRetracted = isRetracted
|
|
||||||
self.dateReceived = dateReceived
|
self.dateReceived = dateReceived
|
||||||
self.address = address
|
self.address = address
|
||||||
self.isFirstMessage = isFirstMessage
|
self.isFirstMessage = isFirstMessage
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,8 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +180,8 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -209,7 +211,8 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
self.chatRoomDelegate = nil
|
self.chatRoomDelegate = nil
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
||||||
|
|
@ -247,7 +250,8 @@ class ConversationForwardMessageViewModel: ObservableObject {
|
||||||
self.chatRoomDelegate = nil
|
self.chatRoomDelegate = nil
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationInProgress = false
|
self.operationInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -89,16 +89,16 @@ class ConversationsListViewModel: ObservableObject {
|
||||||
fromAddressFriend = nil
|
fromAddressFriend = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastMessagePrefixTextTmp = (fromAddressFriend ?? "")
|
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 }) {
|
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
conversationModel.lastMessagePrefixText = lastMessagePrefixTextTmp
|
conversationModel.lastMessageText = lastMessageTextTmp
|
||||||
self.conversationsList[index].lastMessagePrefixText = lastMessagePrefixTextTmp
|
self.conversationsList[index].lastMessageText = lastMessageTextTmp
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
conversationModel.lastMessagePrefixText = lastMessagePrefixTextTmp
|
conversationModel.lastMessageText = lastMessageTextTmp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -148,16 +148,16 @@ class ConversationsListViewModel: ObservableObject {
|
||||||
fromAddressFriend = nil
|
fromAddressFriend = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastMessagePrefixTextTmp = (fromAddressFriend ?? "")
|
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 }) {
|
if let index = self.conversationsList.firstIndex(where: { $0.chatRoom === conversationModel.chatRoom }) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
conversationModel.lastMessagePrefixText = lastMessagePrefixTextTmp
|
conversationModel.lastMessageText = lastMessageTextTmp
|
||||||
self.conversationsList[index].lastMessagePrefixText = lastMessagePrefixTextTmp
|
self.conversationsList[index].lastMessageText = lastMessageTextTmp
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
conversationModel.lastMessagePrefixText = lastMessagePrefixTextTmp
|
conversationModel.lastMessageText = lastMessageTextTmp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,76 +183,69 @@ class ConversationsListViewModel: ObservableObject {
|
||||||
|
|
||||||
func addConversationDelegate() {
|
func addConversationDelegate() {
|
||||||
coreContext.doOnCoreQueue { core in
|
coreContext.doOnCoreQueue { core in
|
||||||
self.coreConversationDelegate = CoreDelegateStub(
|
self.coreConversationDelegate = CoreDelegateStub(onMessagesReceived: { (core: Core, chatRoom: ChatRoom, _: [ChatMessage]) in
|
||||||
onMessagesReceived: { (core: Core, chatRoom: ChatRoom, _: [ChatMessage]) in
|
if let defaultAddress = core.defaultAccount?.contactAddress,
|
||||||
if let defaultAddress = core.defaultAccount?.params?.identityAddress,
|
let localAddress = chatRoom.localAddress,
|
||||||
let localAddress = chatRoom.localAddress,
|
defaultAddress.weakEqual(address2: localAddress) {
|
||||||
defaultAddress.weakEqual(address2: localAddress) {
|
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
|
||||||
let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
|
model.getContentTextMessage(chatRoom: chatRoom)
|
||||||
model.getContentTextMessage(chatRoom: chatRoom)
|
let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
|
||||||
let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
|
DispatchQueue.main.async {
|
||||||
DispatchQueue.main.async {
|
if index != nil {
|
||||||
if index != nil {
|
self.conversationsList.remove(at: index!)
|
||||||
self.conversationsList.remove(at: index!)
|
}
|
||||||
}
|
self.conversationsList.insert(model, at: 0)
|
||||||
self.conversationsList.insert(model, at: 0)
|
}
|
||||||
}
|
SharedMainViewModel.shared.updateUnreadMessagesCount()
|
||||||
SharedMainViewModel.shared.updateUnreadMessagesCount()
|
}
|
||||||
|
}, onMessageSent: { (_: Core, chatRoom: ChatRoom, _: ChatMessage) in
|
||||||
|
if let defaultAddress = core.defaultAccount?.contactAddress,
|
||||||
|
let localAddress = chatRoom.localAddress,
|
||||||
|
defaultAddress.weakEqual(address2: localAddress) {
|
||||||
|
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
|
let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
|
||||||
|
model.getContentTextMessage(chatRoom: chatRoom)
|
||||||
|
let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
|
||||||
|
if index != nil {
|
||||||
|
self.conversationsList[index!].chatMessageRemoveDelegate()
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if index != nil {
|
||||||
|
self.conversationsList.remove(at: index!)
|
||||||
|
}
|
||||||
|
self.conversationsList.insert(model, at: 0)
|
||||||
|
}
|
||||||
|
SharedMainViewModel.shared.updateUnreadMessagesCount()
|
||||||
|
}
|
||||||
|
}, onChatRoomRead: { (_: Core, chatRoom: ChatRoom) in
|
||||||
|
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
||||||
|
let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
|
||||||
|
model.getContentTextMessage(chatRoom: chatRoom)
|
||||||
|
if let index = self.conversationsList.firstIndex(where: { $0.id == idTmp }) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.conversationsList.remove(at: index)
|
||||||
|
self.conversationsList.insert(model, at: index)
|
||||||
}
|
}
|
||||||
}, onMessageSent: { (_: Core, chatRoom: ChatRoom, _: ChatMessage) in
|
|
||||||
if let defaultAddress = core.defaultAccount?.params?.identityAddress,
|
|
||||||
let localAddress = chatRoom.localAddress,
|
|
||||||
defaultAddress.weakEqual(address2: localAddress) {
|
|
||||||
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
|
||||||
let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
|
|
||||||
model.getContentTextMessage(chatRoom: chatRoom)
|
|
||||||
let index = self.conversationsList.firstIndex(where: { $0.id == idTmp })
|
|
||||||
if index != nil {
|
|
||||||
self.conversationsList[index!].chatMessageRemoveDelegate()
|
|
||||||
}
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if index != nil {
|
|
||||||
self.conversationsList.remove(at: index!)
|
|
||||||
}
|
|
||||||
self.conversationsList.insert(model, at: 0)
|
|
||||||
}
|
|
||||||
SharedMainViewModel.shared.updateUnreadMessagesCount()
|
|
||||||
}
|
|
||||||
}, onChatRoomRead: { (_: Core, chatRoom: ChatRoom) in
|
|
||||||
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
|
||||||
let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
|
|
||||||
model.getContentTextMessage(chatRoom: chatRoom)
|
|
||||||
if let index = self.conversationsList.firstIndex(where: { $0.id == idTmp }) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.conversationsList.remove(at: index)
|
|
||||||
self.conversationsList.insert(model, at: index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SharedMainViewModel.shared.updateUnreadMessagesCount()
|
|
||||||
}, onChatRoomStateChanged: { (core: Core, chatroom: ChatRoom, state: ChatRoom.State) in
|
|
||||||
// Log.info("[ConversationsListViewModel] Conversation [${LinphoneUtils.getChatRoomId(chatRoom)}] state changed [$state]")
|
|
||||||
if let defaultAddress = core.defaultAccount?.params?.identityAddress,
|
|
||||||
let localAddress = chatroom.localAddress,
|
|
||||||
defaultAddress.weakEqual(address2: localAddress) {
|
|
||||||
if core.globalState == .On {
|
|
||||||
switch state {
|
|
||||||
case .Created:
|
|
||||||
self.addChatRoom(chatRoom: chatroom)
|
|
||||||
case .Deleted:
|
|
||||||
self.removeChatRoom(chatRoom: chatroom)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, onMessageRetracted: { (core: Core, chatRoom: ChatRoom, message: ChatMessage) in
|
|
||||||
let idTmp = LinphoneUtils.getChatRoomId(room: chatRoom)
|
|
||||||
let model = self.conversationsList.first(where: { $0.id == idTmp }) ?? ConversationModel(chatRoom: chatRoom)
|
|
||||||
model.getContentTextMessage(chatRoom: chatRoom)
|
|
||||||
SharedMainViewModel.shared.updateUnreadMessagesCount()
|
|
||||||
}
|
}
|
||||||
)
|
SharedMainViewModel.shared.updateUnreadMessagesCount()
|
||||||
|
}, onChatRoomStateChanged: { (core: Core, chatroom: ChatRoom, state: ChatRoom.State) in
|
||||||
|
// Log.info("[ConversationsListViewModel] Conversation [${LinphoneUtils.getChatRoomId(chatRoom)}] state changed [$state]")
|
||||||
|
if let defaultAddress = core.defaultAccount?.contactAddress,
|
||||||
|
let localAddress = chatroom.localAddress,
|
||||||
|
defaultAddress.weakEqual(address2: localAddress) {
|
||||||
|
if core.globalState == .On {
|
||||||
|
switch state {
|
||||||
|
case .Created:
|
||||||
|
self.addChatRoom(chatRoom: chatroom)
|
||||||
|
case .Deleted:
|
||||||
|
self.removeChatRoom(chatRoom: chatroom)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
core.addDelegate(delegate: self.coreConversationDelegate!)
|
core.addDelegate(delegate: self.coreConversationDelegate!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -457,25 +450,25 @@ class ConversationsListViewModel: ObservableObject {
|
||||||
func changeDisplayedChatRoom(conversationModel: ConversationModel) {
|
func changeDisplayedChatRoom(conversationModel: ConversationModel) {
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
CoreContext.shared.doOnCoreQueue { core in
|
||||||
let nilParams: ConferenceParams? = nil
|
let nilParams: ConferenceParams? = nil
|
||||||
if let newChatRoom = core.searchChatRoomByIdentifier(identifier: conversationModel.id) {
|
if let newChatRoom = core.searchChatRoom(params: nilParams, localAddr: nil, remoteAddr: conversationModel.chatRoom.peerAddress, participants: nil) {
|
||||||
if self.sharedMainViewModel.displayedConversation == nil {
|
if LinphoneUtils.getChatRoomId(room: newChatRoom) == conversationModel.id {
|
||||||
DispatchQueue.main.async {
|
if self.sharedMainViewModel.displayedConversation == nil {
|
||||||
withAnimation {
|
DispatchQueue.main.async {
|
||||||
self.sharedMainViewModel.displayedConversation = conversationModel
|
withAnimation {
|
||||||
}
|
self.sharedMainViewModel.displayedConversation = conversationModel
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
DispatchQueue.main.async {
|
} else {
|
||||||
self.sharedMainViewModel.displayedConversation = nil
|
DispatchQueue.main.async {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
self.sharedMainViewModel.displayedConversation = nil
|
||||||
withAnimation {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
self.sharedMainViewModel.displayedConversation = conversationModel
|
withAnimation {
|
||||||
}
|
self.sharedMainViewModel.displayedConversation = conversationModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Log.warn("\(ConversationsListViewModel.TAG) changeDisplayedChatRoom: no chat room found for identifier \(conversationModel.id)")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,8 @@ class StartConversationViewModel: ObservableObject {
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationGroupInProgress = false
|
self.operationGroupInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +206,8 @@ class StartConversationViewModel: ObservableObject {
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
self.operationOneToOneInProgress = false
|
self.operationOneToOneInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +252,8 @@ class StartConversationViewModel: ObservableObject {
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
self.operationOneToOneInProgress = false
|
self.operationOneToOneInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -281,7 +284,8 @@ class StartConversationViewModel: ObservableObject {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationOneToOneInProgress = false
|
self.operationOneToOneInProgress = false
|
||||||
self.operationGroupInProgress = false
|
self.operationGroupInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
}, onConferenceJoined: { (chatRoom: ChatRoom, _: EventLog) in
|
||||||
|
|
@ -310,7 +314,8 @@ class StartConversationViewModel: ObservableObject {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.operationOneToOneInProgress = false
|
self.operationOneToOneInProgress = false
|
||||||
self.operationGroupInProgress = false
|
self.operationGroupInProgress = false
|
||||||
ToastViewModel.shared.show("Failed_to_create_conversation_error")
|
ToastViewModel.shared.toastMessage = "Failed_to_create_conversation_error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -86,3 +86,13 @@ final class CustomHostingController<Content: View>: UIHostingController<Content>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct LazyView<Content: View>: View {
|
||||||
|
private let build: () -> Content
|
||||||
|
public init(_ build: @autoclosure @escaping () -> Content) {
|
||||||
|
self.build = build
|
||||||
|
}
|
||||||
|
public var body: Content {
|
||||||
|
build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ 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.show("help_troubleshooting_debug_logs_cleaned_toast_message")
|
ToastViewModel.shared.toastMessage = "help_troubleshooting_debug_logs_cleaned_toast_message"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ struct PopupUpdatePassword: View {
|
||||||
updateAuthInfo()
|
updateAuthInfo()
|
||||||
isShowUpdatePasswordPopup = false
|
isShowUpdatePasswordPopup = false
|
||||||
}, label: {
|
}, label: {
|
||||||
Text("dialog_confirm")
|
Text("dialog_ok")
|
||||||
.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)
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,6 @@ struct PopupView: View {
|
||||||
var titleSecondButton: Text?
|
var titleSecondButton: Text?
|
||||||
var actionSecondButton: () -> Void
|
var actionSecondButton: () -> Void
|
||||||
|
|
||||||
var titleThirdButton: Text?
|
|
||||||
var actionThirdButton: () -> Void
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
|
|
@ -52,57 +49,40 @@ struct PopupView: View {
|
||||||
.padding(.bottom, 20)
|
.padding(.bottom, 20)
|
||||||
}
|
}
|
||||||
|
|
||||||
HStack {
|
if titleFirstButton != nil {
|
||||||
if titleFirstButton != nil {
|
Button(action: {
|
||||||
Button(action: {
|
actionFirstButton()
|
||||||
actionFirstButton()
|
}, label: {
|
||||||
}, label: {
|
titleFirstButton
|
||||||
titleFirstButton
|
.default_text_style_orange_600(styleSize: 20)
|
||||||
.default_text_style_white_600(styleSize: 14)
|
.frame(height: 35)
|
||||||
.frame(height: 30)
|
.frame(maxWidth: .infinity)
|
||||||
})
|
})
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
.padding(.vertical, 10)
|
.padding(.vertical, 10)
|
||||||
.background(Color.orangeMain500)
|
.cornerRadius(60)
|
||||||
.cornerRadius(60)
|
.overlay(
|
||||||
.padding(.horizontal, 2)
|
RoundedRectangle(cornerRadius: 60)
|
||||||
}
|
.inset(by: 0.5)
|
||||||
|
.stroke(Color.orangeMain500, lineWidth: 1)
|
||||||
if titleSecondButton != nil {
|
)
|
||||||
Button(action: {
|
.padding(.bottom, 10)
|
||||||
actionSecondButton()
|
}
|
||||||
}, label: {
|
|
||||||
titleSecondButton
|
if titleSecondButton != nil {
|
||||||
.default_text_style_white_600(styleSize: 14)
|
Button(action: {
|
||||||
.frame(height: 30)
|
actionSecondButton()
|
||||||
})
|
}, label: {
|
||||||
.padding(.horizontal, 20)
|
titleSecondButton
|
||||||
.padding(.vertical, 10)
|
.default_text_style_white_600(styleSize: 20)
|
||||||
.background(Color.orangeMain500)
|
.frame(height: 35)
|
||||||
.cornerRadius(60)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.horizontal, 2)
|
})
|
||||||
}
|
.padding(.horizontal, 20)
|
||||||
|
.padding(.vertical, 10)
|
||||||
if titleThirdButton != nil {
|
.background(Color.orangeMain500)
|
||||||
Button(action: {
|
.cornerRadius(60)
|
||||||
actionThirdButton()
|
|
||||||
}, label: {
|
|
||||||
titleThirdButton
|
|
||||||
.default_text_style_orange_600(styleSize: 14)
|
|
||||||
.frame(height: 30)
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 20)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.cornerRadius(60)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 60)
|
|
||||||
.inset(by: 0.5)
|
|
||||||
.stroke(Color.orangeMain500, lineWidth: 1)
|
|
||||||
)
|
|
||||||
.padding(.horizontal, 2)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
.padding(.vertical, 20)
|
.padding(.vertical, 20)
|
||||||
|
|
@ -121,11 +101,9 @@ struct PopupView: View {
|
||||||
PopupView(isShowPopup: .constant(true),
|
PopupView(isShowPopup: .constant(true),
|
||||||
title: Text("Title"),
|
title: Text("Title"),
|
||||||
content: Text("Content"),
|
content: Text("Content"),
|
||||||
titleFirstButton: Text("Accept all"),
|
titleFirstButton: Text("Deny all"),
|
||||||
actionFirstButton: {},
|
actionFirstButton: {},
|
||||||
titleSecondButton: Text("dialog_confirm"),
|
titleSecondButton: Text("Accept all"),
|
||||||
actionSecondButton: {},
|
actionSecondButton: {})
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
|
||||||
actionThirdButton: {})
|
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ struct PopupViewWithTextField: View {
|
||||||
setNewChatRoomSubject()
|
setNewChatRoomSubject()
|
||||||
isShowConversationInfoPopup = false
|
isShowConversationInfoPopup = false
|
||||||
}, label: {
|
}, label: {
|
||||||
Text("dialog_confirm")
|
Text("dialog_ok")
|
||||||
.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)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ struct SideMenu: View {
|
||||||
@Binding var isShowLoginFragment: Bool
|
@Binding var isShowLoginFragment: Bool
|
||||||
@Binding var isShowAccountProfileFragment: Bool
|
@Binding var isShowAccountProfileFragment: Bool
|
||||||
@Binding var isShowSettingsFragment: Bool
|
@Binding var isShowSettingsFragment: Bool
|
||||||
@Binding var isShowRecordingsListFragment: Bool
|
|
||||||
@Binding var isShowHelpFragment: Bool
|
@Binding var isShowHelpFragment: Bool
|
||||||
@State private var showHelp = false
|
@State private var showHelp = false
|
||||||
|
|
||||||
|
|
@ -138,15 +137,12 @@ struct SideMenu: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
SideMenuEntry(
|
SideMenuEntry(
|
||||||
iconName: "record-fill",
|
iconName: "record-fill",
|
||||||
title: "recordings_title"
|
title: "recordings_title"
|
||||||
).onTapGesture {
|
)
|
||||||
self.menuClose()
|
*/
|
||||||
withAnimation {
|
|
||||||
isShowRecordingsListFragment = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SideMenuEntry(
|
SideMenuEntry(
|
||||||
iconName: "question",
|
iconName: "question",
|
||||||
|
|
@ -156,6 +152,7 @@ struct SideMenu: View {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
isShowHelpFragment = true
|
isShowHelpFragment = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom, safeAreaInsets.bottom + 13)
|
.padding(.bottom, safeAreaInsets.bottom + 13)
|
||||||
|
|
@ -179,15 +176,15 @@ struct SideMenu: View {
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
|
@State var triggerNavigateToLogin: Bool = false
|
||||||
SideMenu(
|
SideMenu(
|
||||||
width: geometry.size.width / 5 * 4,
|
width: geometry.size.width / 5 * 4,
|
||||||
isOpen: .constant(true),
|
isOpen: .constant(true),
|
||||||
menuClose: {},
|
menuClose: {},
|
||||||
safeAreaInsets: geometry.safeAreaInsets,
|
safeAreaInsets: geometry.safeAreaInsets,
|
||||||
isShowLoginFragment: .constant(false),
|
isShowLoginFragment: $triggerNavigateToLogin,
|
||||||
isShowAccountProfileFragment: .constant(false),
|
isShowAccountProfileFragment: .constant(false),
|
||||||
isShowSettingsFragment: .constant(false),
|
isShowSettingsFragment: .constant(false),
|
||||||
isShowRecordingsListFragment: .constant(false),
|
|
||||||
isShowHelpFragment: .constant(false)
|
isShowHelpFragment: .constant(false)
|
||||||
)
|
)
|
||||||
.ignoresSafeArea(.all)
|
.ignoresSafeArea(.all)
|
||||||
|
|
|
||||||
|
|
@ -90,51 +90,15 @@ struct SideMenuAccountRow: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
if model.voicemailCount > 0 {
|
|
||||||
Button {
|
|
||||||
model.callVoicemailUri()
|
|
||||||
} label: {
|
|
||||||
ZStack(alignment: .top) {
|
|
||||||
VStack {
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Image("voicemail")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 22, height: 22)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(String(model.voicemailCount))
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style_600(styleSize: 12)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
|
||||||
.padding(.top, 1)
|
|
||||||
}
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
}
|
|
||||||
.highPriorityGesture(
|
|
||||||
TapGesture().onEnded {
|
|
||||||
model.callVoicemailUri()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if model.notificationsCount > 0 && !CorePreferences.disableChatFeature {
|
if model.notificationsCount > 0 && !CorePreferences.disableChatFeature {
|
||||||
VStack {
|
Text(String(model.notificationsCount))
|
||||||
Text(String(model.notificationsCount))
|
.foregroundStyle(.white)
|
||||||
.foregroundStyle(.white)
|
.default_text_style(styleSize: 12)
|
||||||
.default_text_style(styleSize: 12)
|
.lineLimit(1)
|
||||||
.lineLimit(1)
|
.frame(width: 20, height: 20)
|
||||||
.frame(width: 20, height: 20)
|
.background(Color.redDanger500)
|
||||||
.background(Color.redDanger500)
|
.cornerRadius(50)
|
||||||
.cornerRadius(50)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
.padding(.trailing, -8)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
|
|
@ -148,18 +112,15 @@ struct SideMenuAccountRow: View {
|
||||||
Label("drawer_menu_manage_account", systemImage: "arrow.right.circle")
|
Label("drawer_menu_manage_account", systemImage: "arrow.right.circle")
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
VStack {
|
Image("dots-three-vertical")
|
||||||
Image("dots-three-vertical")
|
.renderingMode(.template)
|
||||||
.renderingMode(.template)
|
.resizable()
|
||||||
.resizable()
|
.foregroundColor(Color.gray)
|
||||||
.foregroundColor(Color.grayMain2c500)
|
.scaledToFit()
|
||||||
.scaledToFit()
|
.frame(height: 30)
|
||||||
.frame(height: 25)
|
|
||||||
}
|
|
||||||
.frame(width: 30, height: 30)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(alignment: .trailing)
|
.frame(width: 64, alignment: .trailing)
|
||||||
.padding(.top, 12)
|
.padding(.top, 12)
|
||||||
.padding(.bottom, 12)
|
.padding(.bottom, 12)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,39 +25,33 @@ struct ToastView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
if let toast = toastViewModel.toast {
|
if toastViewModel.displayToast {
|
||||||
HStack {
|
HStack {
|
||||||
if toast.message.contains("Failed_search") {
|
if toastViewModel.toastMessage.contains("toast_call_transfer") {
|
||||||
Image("magnifying-glass")
|
|
||||||
.resizable()
|
|
||||||
.renderingMode(.template)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
} else if toast.message.contains("toast_call_transfer") {
|
|
||||||
Image("phone-transfer")
|
Image("phone-transfer")
|
||||||
.resizable()
|
.resizable()
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.foregroundStyle(toast.message.contains("Success") ? Color.greenSuccess500 : Color.redDanger500)
|
.foregroundStyle(toastViewModel.toastMessage.contains("Success") ? Color.greenSuccess500 : Color.redDanger500)
|
||||||
} else if toast.message.contains("is recording") {
|
} else if toastViewModel.toastMessage.contains("is recording") {
|
||||||
Image("record-fill")
|
Image("record-fill")
|
||||||
.resizable()
|
.resizable()
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
} else if toast.message.contains("Info_") {
|
} else if toastViewModel.toastMessage.contains("Info_") {
|
||||||
Image("trusted")
|
Image("trusted")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
} else {
|
} else {
|
||||||
Image(toast.message.contains("Success") ? "check" : "warning-circle")
|
Image(toastViewModel.toastMessage.contains("Success") ? "check" : "warning-circle")
|
||||||
.resizable()
|
.resizable()
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
.foregroundStyle(toast.message.contains("Success") ? Color.greenSuccess500 : Color.redDanger500)
|
.foregroundStyle(toastViewModel.toastMessage.contains("Success") ? Color.greenSuccess500 : Color.redDanger500)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch toast.message {
|
switch toastViewModel.toastMessage {
|
||||||
case "Success_qr_code_validated":
|
case "Success_qr_code_validated":
|
||||||
Text("qr_code_validated")
|
Text("qr_code_validated")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|
@ -122,7 +116,7 @@ struct ToastView: View {
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case let str where str.contains("is recording"):
|
case let str where str.contains("is recording"):
|
||||||
Text(toast.message)
|
Text(toastViewModel.toastMessage)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
|
|
@ -255,7 +249,7 @@ struct ToastView: View {
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case let str where str.contains("Error: "):
|
case let str where str.contains("Error: "):
|
||||||
Text(toast.message)
|
Text(toastViewModel.toastMessage)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
|
|
@ -321,41 +315,6 @@ struct ToastView: View {
|
||||||
.foregroundStyle(Color.redDanger500)
|
.foregroundStyle(Color.redDanger500)
|
||||||
.default_text_style(styleSize: 15)
|
.default_text_style(styleSize: 15)
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|
||||||
case "Failed_search_no_match_found":
|
|
||||||
Text("conversation_search_no_match_found")
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "Failed_search_results_limit_reached":
|
|
||||||
Text("conversation_search_results_limit_reached_label")
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "Success_settings_contacts_carddav_sync_successful_toast":
|
|
||||||
Text("settings_contacts_carddav_sync_successful_toast")
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "settings_contacts_carddav_sync_error_toast":
|
|
||||||
Text("settings_contacts_carddav_sync_error_toast")
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.redDanger500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
case "Success_settings_contacts_carddav_deleted_toast":
|
|
||||||
Text("settings_contacts_carddav_deleted_toast")
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.foregroundStyle(Color.greenSuccess500)
|
|
||||||
.default_text_style(styleSize: 15)
|
|
||||||
.padding(8)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Text("Error")
|
Text("Error")
|
||||||
|
|
@ -371,14 +330,25 @@ struct ToastView: View {
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 50)
|
RoundedRectangle(cornerRadius: 50)
|
||||||
.inset(by: 0.5)
|
.inset(by: 0.5)
|
||||||
.stroke(toast.message.contains("Success")
|
.stroke(toastViewModel.toastMessage.contains("Success")
|
||||||
? Color.greenSuccess500 : (toast.message.contains("Info_")
|
? Color.greenSuccess500 : (toastViewModel.toastMessage.contains("Info_")
|
||||||
? Color.blueInfo500 : Color.redDanger500), lineWidth: 1)
|
? Color.blueInfo500 : Color.redDanger500), lineWidth: 1)
|
||||||
)
|
)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if !toast.message.contains("is recording") {
|
if !toastViewModel.toastMessage.contains("is recording") {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
toastViewModel.hide()
|
toastViewModel.toastMessage = ""
|
||||||
|
toastViewModel.displayToast = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
if !toastViewModel.toastMessage.contains("is recording") {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||||
|
withAnimation {
|
||||||
|
toastViewModel.toastMessage = ""
|
||||||
|
toastViewModel.displayToast = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,8 @@ struct DebugFragment: View {
|
||||||
helpViewModel.version,
|
helpViewModel.version,
|
||||||
forPasteboardType: UTType.plainText.identifier
|
forPasteboardType: UTType.plainText.identifier
|
||||||
)
|
)
|
||||||
ToastViewModel.shared.show("Success_text_copied_into_clipboard")
|
ToastViewModel.shared.toastMessage = "Success_text_copied_into_clipboard"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image("app-store-logo")
|
Image("app-store-logo")
|
||||||
|
|
@ -144,7 +145,8 @@ struct DebugFragment: View {
|
||||||
helpViewModel.sdkVersion,
|
helpViewModel.sdkVersion,
|
||||||
forPasteboardType: UTType.plainText.identifier
|
forPasteboardType: UTType.plainText.identifier
|
||||||
)
|
)
|
||||||
ToastViewModel.shared.show("Success_text_copied_into_clipboard")
|
ToastViewModel.shared.toastMessage = "Success_text_copied_into_clipboard"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
Image("package")
|
Image("package")
|
||||||
|
|
|
||||||
|
|
@ -25,21 +25,18 @@ struct HelpFragment: View {
|
||||||
|
|
||||||
@Binding var isShowHelpFragment: Bool
|
@Binding var isShowHelpFragment: Bool
|
||||||
|
|
||||||
var showAssistant: Bool {
|
@State var advancedSettingsIsOpen: Bool = false
|
||||||
(CoreContext.shared.coreIsStarted && CoreContext.shared.accounts.isEmpty)
|
|
||||||
|| SharedMainViewModel.shared.displayProfileMode
|
@FocusState var isVoicemailUriFocused: Bool
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
ZStack {
|
ZStack {
|
||||||
VStack(spacing: 1) {
|
VStack(spacing: 1) {
|
||||||
if !showAssistant {
|
Rectangle()
|
||||||
Rectangle()
|
.foregroundColor(Color.orangeMain500)
|
||||||
.foregroundColor(Color.orangeMain500)
|
.edgesIgnoringSafeArea(.top)
|
||||||
.edgesIgnoringSafeArea(.top)
|
.frame(height: 0)
|
||||||
.frame(height: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Image("caret-left")
|
Image("caret-left")
|
||||||
|
|
@ -73,31 +70,11 @@ struct HelpFragment: View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 20) {
|
||||||
if let urlString = CorePreferences.themeAboutPictureUrl,
|
|
||||||
let url = URL(string: urlString) {
|
|
||||||
AsyncImage(url: url) { phase in
|
|
||||||
switch phase {
|
|
||||||
case .empty:
|
|
||||||
ProgressView()
|
|
||||||
.frame(maxWidth: .infinity, minHeight: 100, maxHeight: 100)
|
|
||||||
case .success(let image):
|
|
||||||
image
|
|
||||||
.resizable()
|
|
||||||
.scaledToFit()
|
|
||||||
.frame(maxWidth: .infinity, maxHeight: 100, alignment: .center)
|
|
||||||
case .failure:
|
|
||||||
EmptyView()
|
|
||||||
@unknown default:
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
Text("help_about_title")
|
Text("help_about_title")
|
||||||
.default_text_style_800(styleSize: 16)
|
.default_text_style_800(styleSize: 16)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, 5)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if let url = URL(string: NSLocalizedString("website_user_guide_url", comment: "")) {
|
if let url = URL(string: NSLocalizedString("website_user_guide_url", comment: "")) {
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
|
|
@ -177,7 +154,7 @@ struct HelpFragment: View {
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
|
|
||||||
Text(helpViewModel.appVersion)
|
Text(helpViewModel.version)
|
||||||
.default_text_style(styleSize: 14)
|
.default_text_style(styleSize: 14)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
|
|
@ -302,17 +279,17 @@ struct HelpFragment: View {
|
||||||
isShowPopup: $helpViewModel.checkUpdateAvailable,
|
isShowPopup: $helpViewModel.checkUpdateAvailable,
|
||||||
title: Text("help_dialog_update_available_title"),
|
title: Text("help_dialog_update_available_title"),
|
||||||
content: Text(String(format: String(localized: "help_dialog_update_available_message"), helpViewModel.versionAvailable)),
|
content: Text(String(format: String(localized: "help_dialog_update_available_message"), helpViewModel.versionAvailable)),
|
||||||
titleFirstButton: nil,
|
titleFirstButton: Text("dialog_cancel"),
|
||||||
actionFirstButton: {},
|
actionFirstButton: {
|
||||||
|
helpViewModel.checkUpdateAvailable = false
|
||||||
|
},
|
||||||
titleSecondButton: Text("dialog_install"),
|
titleSecondButton: Text("dialog_install"),
|
||||||
actionSecondButton: {
|
actionSecondButton: {
|
||||||
helpViewModel.checkUpdateAvailable = false
|
helpViewModel.checkUpdateAvailable = false
|
||||||
if let url = URL(string: helpViewModel.urlVersionAvailable) {
|
if let url = URL(string: helpViewModel.urlVersionAvailable) {
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
titleThirdButton: Text("dialog_cancel"),
|
|
||||||
actionThirdButton: { helpViewModel.checkUpdateAvailable = false }
|
|
||||||
)
|
)
|
||||||
.background(.black.opacity(0.65))
|
.background(.black.opacity(0.65))
|
||||||
.zIndex(3)
|
.zIndex(3)
|
||||||
|
|
@ -325,7 +302,5 @@ struct HelpFragment: View {
|
||||||
.navigationBarHidden(true)
|
.navigationBarHidden(true)
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
.navigationTitle("")
|
|
||||||
.navigationBarHidden(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,20 +38,13 @@ class HelpViewModel: ObservableObject {
|
||||||
private var coreDelegate: CoreDelegate?
|
private var coreDelegate: CoreDelegate?
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
let appGitVersion = AppGitInfo.commit
|
let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String
|
||||||
let appGitBranch = AppGitInfo.branch
|
let versionTmp = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||||
let appGitTag = AppGitInfo.tag
|
let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String
|
||||||
let sdkGitVersion = linphonesw.sdkVersion
|
|
||||||
var sdkGitBranch = linphonesw.sdkBranch
|
|
||||||
|
|
||||||
if sdkGitBranch.hasPrefix("remotes/origin/") {
|
self.version = (versionTmp ?? "6.0.0")
|
||||||
sdkGitBranch = String(sdkGitBranch.dropFirst("remotes/origin/".count))
|
|
||||||
}
|
|
||||||
|
|
||||||
self.appVersion = appGitTag
|
self.sdkVersion = Core.getVersion
|
||||||
self.version = appGitTag + "-" + appGitVersion + "\n(\(appGitBranch))"
|
|
||||||
|
|
||||||
self.sdkVersion = sdkGitVersion + "\n(\(sdkGitBranch))"
|
|
||||||
|
|
||||||
if let path = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist"),
|
if let path = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist"),
|
||||||
let plist = NSDictionary(contentsOfFile: path) as? [String: Any],
|
let plist = NSDictionary(contentsOfFile: path) as? [String: Any],
|
||||||
|
|
@ -92,12 +85,14 @@ class HelpViewModel: ObservableObject {
|
||||||
case .UpToDate:
|
case .UpToDate:
|
||||||
Log.info("\(self.TAG): This version is up-to-date")
|
Log.info("\(self.TAG): This version is up-to-date")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.show("Success_version_up_to_date")
|
ToastViewModel.shared.toastMessage = "Success_version_up_to_date"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
Log.info("\(self.TAG): Can't check for update, an error happened [\(result)]")
|
Log.info("\(self.TAG): Can't check for update, an error happened [\(result)]")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.show("Error")
|
ToastViewModel.shared.toastMessage = "Error"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +135,8 @@ class HelpViewModel: ObservableObject {
|
||||||
Core.resetLogCollection()
|
Core.resetLogCollection()
|
||||||
Log.info("\(self.TAG) Debug logs have been cleaned")
|
Log.info("\(self.TAG) Debug logs have been cleaned")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
ToastViewModel.shared.show("Success_clear_logs")
|
ToastViewModel.shared.toastMessage = "Success_clear_logs"
|
||||||
|
ToastViewModel.shared.displayToast = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,13 @@ struct DialerBottomSheet: View {
|
||||||
HStack {
|
HStack {
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("1".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("1".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "1"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "1"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "1"
|
startCallViewModel.searchField += "1"
|
||||||
}
|
}
|
||||||
|
|
@ -121,9 +125,13 @@ struct DialerBottomSheet: View {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("2".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("2".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "2"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "2"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "2"
|
startCallViewModel.searchField += "2"
|
||||||
}
|
}
|
||||||
|
|
@ -142,9 +150,13 @@ struct DialerBottomSheet: View {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("3".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("3".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "3"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "3"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "3"
|
startCallViewModel.searchField += "3"
|
||||||
}
|
}
|
||||||
|
|
@ -165,9 +177,13 @@ struct DialerBottomSheet: View {
|
||||||
HStack {
|
HStack {
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("4".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("4".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "4"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "4"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "4"
|
startCallViewModel.searchField += "4"
|
||||||
}
|
}
|
||||||
|
|
@ -186,9 +202,13 @@ struct DialerBottomSheet: View {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("5".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("5".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "5"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "5"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "5"
|
startCallViewModel.searchField += "5"
|
||||||
}
|
}
|
||||||
|
|
@ -207,9 +227,13 @@ struct DialerBottomSheet: View {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("6".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("6".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "6"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "6"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "6"
|
startCallViewModel.searchField += "6"
|
||||||
}
|
}
|
||||||
|
|
@ -231,9 +255,13 @@ struct DialerBottomSheet: View {
|
||||||
HStack {
|
HStack {
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("7".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("7".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "7"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "7"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "7"
|
startCallViewModel.searchField += "7"
|
||||||
}
|
}
|
||||||
|
|
@ -252,9 +280,13 @@ struct DialerBottomSheet: View {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("8".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("8".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "8"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "8"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "8"
|
startCallViewModel.searchField += "8"
|
||||||
}
|
}
|
||||||
|
|
@ -273,9 +305,13 @@ struct DialerBottomSheet: View {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("9".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("9".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "9"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "9"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "9"
|
startCallViewModel.searchField += "9"
|
||||||
}
|
}
|
||||||
|
|
@ -297,9 +333,13 @@ struct DialerBottomSheet: View {
|
||||||
HStack {
|
HStack {
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("*".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("*".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "*"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "*"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "*"
|
startCallViewModel.searchField += "*"
|
||||||
}
|
}
|
||||||
|
|
@ -353,9 +393,13 @@ struct DialerBottomSheet: View {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Button {
|
Button {
|
||||||
let digit = ("0".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("0".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "0"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "0"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Text("0")
|
Text("0")
|
||||||
.foregroundStyle(currentCall != nil ? .white : Color.grayMain2c600)
|
.foregroundStyle(currentCall != nil ? .white : Color.grayMain2c600)
|
||||||
|
|
@ -372,9 +416,13 @@ struct DialerBottomSheet: View {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if currentCall != nil {
|
if currentCall != nil {
|
||||||
let digit = ("#".cString(using: String.Encoding.utf8)?[0])!
|
do {
|
||||||
self.sendDtmf(dtmf: digit)
|
let digit = ("#".cString(using: String.Encoding.utf8)?[0])!
|
||||||
dialerField += "#"
|
try currentCall!.sendDtmf(dtmf: digit)
|
||||||
|
dialerField += "#"
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startCallViewModel.searchField += "#"
|
startCallViewModel.searchField += "#"
|
||||||
}
|
}
|
||||||
|
|
@ -486,21 +534,6 @@ struct DialerBottomSheet: View {
|
||||||
orientation = newOrientation
|
orientation = newOrientation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendDtmf(dtmf: CChar) {
|
|
||||||
CoreContext.shared.doOnCoreQueue { core in
|
|
||||||
guard let call = self.currentCall, call.state == .StreamsRunning else {
|
|
||||||
Log.warn("Cannot send DTMF: call not active")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
try call.sendDtmf(dtmf: dtmf)
|
|
||||||
} catch {
|
|
||||||
Log.error("Cannot send DTMF \(dtmf) to call \(call.callLog?.callId ?? ""): \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,7 @@ struct HistoryContactFragment: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
let disableAddContact = CorePreferences.disableAddContact
|
if !historyModel.isConf {
|
||||||
let isFriend = historyModel.isFriend == true
|
|
||||||
|
|
||||||
if !historyModel.isConf && (!disableAddContact || (disableAddContact && isFriend)) {
|
|
||||||
Button {
|
Button {
|
||||||
isMenuOpen = false
|
isMenuOpen = false
|
||||||
|
|
||||||
|
|
@ -120,7 +117,8 @@ struct HistoryContactFragment: View {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ToastViewModel.shared.show("Success_address_copied_into_clipboard")
|
ToastViewModel.shared.toastMessage = "Success_address_copied_into_clipboard"
|
||||||
|
ToastViewModel.shared.displayToast.toggle()
|
||||||
|
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
@ -189,17 +187,21 @@ struct HistoryContactFragment: View {
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
if !CorePreferences.hideSipAddresses {
|
Text(historyModel.address)
|
||||||
Text(historyModel.address)
|
.foregroundStyle(Color.grayMain2c700)
|
||||||
.foregroundStyle(Color.grayMain2c700)
|
.multilineTextAlignment(.center)
|
||||||
.multilineTextAlignment(.center)
|
.default_text_style(styleSize: 14)
|
||||||
.default_text_style(styleSize: 14)
|
.frame(maxWidth: .infinity)
|
||||||
.frame(maxWidth: .infinity)
|
.padding(.top, 5)
|
||||||
.padding(.top, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let avatar = historyModel.avatarModel {
|
if let avatarModel = historyModel.avatarModel {
|
||||||
AvatarPresenceView(avatarModel: avatar)
|
Text(avatarModel.lastPresenceInfo)
|
||||||
|
.foregroundStyle(avatarModel.lastPresenceInfo == "Online" ? Color.greenSuccess500 : Color.orangeWarning600)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.default_text_style_300(styleSize: 12)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.frame(height: 20)
|
||||||
|
.padding(.top, 5)
|
||||||
} else {
|
} else {
|
||||||
Text("")
|
Text("")
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|
@ -282,31 +284,29 @@ struct HistoryContactFragment: View {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
if !SharedMainViewModel.shared.disableVideoCall {
|
Button(action: {
|
||||||
Spacer()
|
telecomManager.doCallOrJoinConf(address: historyModel.addressLinphone, isVideo: true)
|
||||||
|
}, label: {
|
||||||
Button(action: {
|
VStack {
|
||||||
telecomManager.doCallOrJoinConf(address: historyModel.addressLinphone, isVideo: true)
|
HStack(alignment: .center) {
|
||||||
}, label: {
|
Image("video-camera")
|
||||||
VStack {
|
.renderingMode(.template)
|
||||||
HStack(alignment: .center) {
|
.resizable()
|
||||||
Image("video-camera")
|
.foregroundStyle(Color.grayMain2c600)
|
||||||
.renderingMode(.template)
|
.frame(width: 25, height: 25)
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.frame(width: 25, height: 25)
|
|
||||||
}
|
|
||||||
.padding(16)
|
|
||||||
.background(Color.grayMain2c200)
|
|
||||||
.cornerRadius(40)
|
|
||||||
|
|
||||||
Text("contact_video_call_action")
|
|
||||||
.default_text_style(styleSize: 14)
|
|
||||||
.frame(minWidth: 80)
|
|
||||||
}
|
}
|
||||||
})
|
.padding(16)
|
||||||
}
|
.background(Color.grayMain2c200)
|
||||||
|
.cornerRadius(40)
|
||||||
|
|
||||||
|
Text("contact_video_call_action")
|
||||||
|
.default_text_style(styleSize: 14)
|
||||||
|
.frame(minWidth: 80)
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
|
|
@ -421,21 +421,6 @@ struct HistoryContactFragment: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AvatarPresenceView: View {
|
|
||||||
@ObservedObject var avatarModel: ContactAvatarModel
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Text(avatarModel.lastPresenceInfo)
|
|
||||||
.foregroundStyle(avatarModel.lastPresenceInfo == "Online" ? Color.greenSuccess500 : Color.orangeWarning600)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
.default_text_style_300(styleSize: 12)
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.frame(height: 20)
|
|
||||||
.padding(.top, 5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
HistoryContactFragment(
|
HistoryContactFragment(
|
||||||
isShowDeleteAllHistoryPopup: .constant(false),
|
isShowDeleteAllHistoryPopup: .constant(false),
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ struct HistoryFragment: View {
|
||||||
isShowEditContactFragment: $isShowEditContactFragment,
|
isShowEditContactFragment: $isShowEditContactFragment,
|
||||||
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress
|
isShowEditContactFragmentAddress: $isShowEditContactFragmentAddress
|
||||||
)
|
)
|
||||||
.presentationDetents([.fraction(0.4)])
|
.presentationDetents([.fraction(0.3)])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HistoryListFragment(showingSheet: $showingSheet, text: $text)
|
HistoryListFragment(showingSheet: $showingSheet, text: $text)
|
||||||
|
|
|
||||||
|
|
@ -58,74 +58,68 @@ struct HistoryListBottomSheet: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
Button {
|
||||||
let disableAddContact = CorePreferences.disableAddContact
|
|
||||||
let isFriend = historyListViewModel.selectedCall?.isFriend == true
|
if #available(iOS 16.0, *) {
|
||||||
|
if idiom != .pad {
|
||||||
if !disableAddContact || (disableAddContact && isFriend) {
|
showingSheet.toggle()
|
||||||
Button {
|
|
||||||
|
|
||||||
if #available(iOS 16.0, *) {
|
|
||||||
if idiom != .pad {
|
|
||||||
showingSheet.toggle()
|
|
||||||
} else {
|
|
||||||
showingSheet.toggle()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
showingSheet.toggle()
|
showingSheet.toggle()
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
sharedMainViewModel.changeIndexView(indexViewInt: 0)
|
showingSheet.toggle()
|
||||||
|
dismiss()
|
||||||
if let selectedCall = historyListViewModel.selectedCall, selectedCall.isFriend {
|
|
||||||
let friendIndex = contactsManager.avatarListModel.first(where: {$0.addresses.contains(where: {$0 == selectedCall.address})})
|
|
||||||
if friendIndex != nil {
|
|
||||||
withAnimation {
|
|
||||||
SharedMainViewModel.shared.displayedFriend = friendIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if let selectedCall = historyListViewModel.selectedCall {
|
|
||||||
withAnimation {
|
|
||||||
isShowEditContactFragment.toggle()
|
|
||||||
isShowEditContactFragmentAddress = String(selectedCall.address.dropFirst(4))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
if let selectedCall = historyListViewModel.selectedCall, selectedCall.isFriend {
|
|
||||||
Image("user-circle")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
Text("menu_see_existing_contact")
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
Spacer()
|
|
||||||
} else {
|
|
||||||
Image("plus-circle")
|
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
|
||||||
.foregroundStyle(Color.grayMain2c500)
|
|
||||||
.frame(width: 25, height: 25, alignment: .leading)
|
|
||||||
.padding(.all, 10)
|
|
||||||
Text("menu_add_address_to_contacts")
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxHeight: .infinity)
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 30)
|
|
||||||
.background(Color.gray100)
|
|
||||||
|
|
||||||
VStack {
|
sharedMainViewModel.changeIndexView(indexViewInt: 0)
|
||||||
Divider()
|
|
||||||
|
if let selectedCall = historyListViewModel.selectedCall, selectedCall.isFriend {
|
||||||
|
let friendIndex = contactsManager.avatarListModel.first(where: {$0.addresses.contains(where: {$0 == selectedCall.address})})
|
||||||
|
if friendIndex != nil {
|
||||||
|
withAnimation {
|
||||||
|
SharedMainViewModel.shared.displayedFriend = friendIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let selectedCall = historyListViewModel.selectedCall {
|
||||||
|
withAnimation {
|
||||||
|
isShowEditContactFragment.toggle()
|
||||||
|
isShowEditContactFragmentAddress = String(selectedCall.address.dropFirst(4))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
} label: {
|
||||||
|
HStack {
|
||||||
|
if let selectedCall = historyListViewModel.selectedCall, selectedCall.isFriend {
|
||||||
|
Image("user-circle")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
Text("menu_see_existing_contact")
|
||||||
|
.default_text_style(styleSize: 16)
|
||||||
|
Spacer()
|
||||||
|
} else {
|
||||||
|
Image("plus-circle")
|
||||||
|
.renderingMode(.template)
|
||||||
|
.resizable()
|
||||||
|
.foregroundStyle(Color.grayMain2c500)
|
||||||
|
.frame(width: 25, height: 25, alignment: .leading)
|
||||||
|
.padding(.all, 10)
|
||||||
|
Text("menu_add_address_to_contacts")
|
||||||
|
.default_text_style(styleSize: 16)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 30)
|
||||||
|
.background(Color.gray100)
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
if historyListViewModel.selectedCall != nil && historyListViewModel.selectedCall!.isOutgoing {
|
if historyListViewModel.selectedCall != nil && historyListViewModel.selectedCall!.isOutgoing {
|
||||||
|
|
@ -152,7 +146,8 @@ struct HistoryListBottomSheet: View {
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
ToastViewModel.shared.show("Success_address_copied_into_clipboard")
|
ToastViewModel.shared.toastMessage = "Success_address_copied_into_clipboard"
|
||||||
|
ToastViewModel.shared.displayToast.toggle()
|
||||||
|
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
|
|
|
||||||
|
|
@ -152,10 +152,8 @@ struct HistoryRow: View {
|
||||||
|
|
||||||
if !historyModel.isConf {
|
if !historyModel.isConf {
|
||||||
Image("phone")
|
Image("phone")
|
||||||
.renderingMode(.template)
|
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 25, height: 25)
|
.frame(width: 25, height: 25)
|
||||||
.foregroundStyle(Color.grayMain2c600)
|
|
||||||
.padding(.all, 10)
|
.padding(.all, 10)
|
||||||
.padding(.trailing, 5)
|
.padding(.trailing, 5)
|
||||||
.highPriorityGesture(
|
.highPriorityGesture(
|
||||||
|
|
|
||||||
|
|
@ -235,83 +235,75 @@ struct StartCallFragment: View {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ZStack {
|
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("contacts_list_all_contacts_title")
|
.default_text_style_800(styleSize: 16)
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
ContactsListFragment(showingSheet: .constant(false)
|
|
||||||
, startCallFunc: { addr in
|
|
||||||
if callViewModel.isTransferInsteadCall {
|
|
||||||
showingDialer = false
|
|
||||||
|
|
||||||
startCallViewModel.searchField = ""
|
|
||||||
magicSearch.currentFilter = ""
|
|
||||||
|
|
||||||
magicSearch.searchForContacts()
|
|
||||||
|
|
||||||
if callViewModel.isTransferInsteadCall == true {
|
|
||||||
callViewModel.isTransferInsteadCall = false
|
|
||||||
}
|
|
||||||
|
|
||||||
resetCallView()
|
|
||||||
|
|
||||||
delayColorDismiss()
|
|
||||||
|
|
||||||
withAnimation {
|
|
||||||
isShowStartCallFragment.toggle()
|
|
||||||
callViewModel.blindTransferCallTo(toAddress: addr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showingDialer = false
|
|
||||||
|
|
||||||
startCallViewModel.searchField = ""
|
|
||||||
magicSearch.currentFilter = ""
|
|
||||||
|
|
||||||
magicSearch.searchForContacts()
|
|
||||||
|
|
||||||
if callViewModel.isTransferInsteadCall == true {
|
|
||||||
callViewModel.isTransferInsteadCall = false
|
|
||||||
}
|
|
||||||
|
|
||||||
resetCallView()
|
|
||||||
|
|
||||||
delayColorDismiss()
|
|
||||||
|
|
||||||
withAnimation {
|
|
||||||
isShowStartCallFragment.toggle()
|
|
||||||
telecomManager.doCallOrJoinConf(address: addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
|
|
||||||
if !contactsManager.lastSearchSuggestions.isEmpty {
|
|
||||||
HStack(alignment: .center) {
|
|
||||||
Text("generic_address_picker_suggestions_list_title")
|
|
||||||
.default_text_style_800(styleSize: 16)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.padding(.horizontal, 16)
|
|
||||||
|
|
||||||
suggestionsList
|
Spacer()
|
||||||
}
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
if magicSearch.isLoading {
|
ContactsListFragment(showingSheet: .constant(false)
|
||||||
ProgressView()
|
, startCallFunc: { addr in
|
||||||
.controlSize(.large)
|
if callViewModel.isTransferInsteadCall {
|
||||||
.progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
|
showingDialer = false
|
||||||
|
|
||||||
|
startCallViewModel.searchField = ""
|
||||||
|
magicSearch.currentFilter = ""
|
||||||
|
|
||||||
|
magicSearch.searchForContacts()
|
||||||
|
|
||||||
|
if callViewModel.isTransferInsteadCall == true {
|
||||||
|
callViewModel.isTransferInsteadCall = false
|
||||||
|
}
|
||||||
|
|
||||||
|
resetCallView()
|
||||||
|
|
||||||
|
delayColorDismiss()
|
||||||
|
|
||||||
|
withAnimation {
|
||||||
|
isShowStartCallFragment.toggle()
|
||||||
|
callViewModel.blindTransferCallTo(toAddress: addr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showingDialer = false
|
||||||
|
|
||||||
|
startCallViewModel.searchField = ""
|
||||||
|
magicSearch.currentFilter = ""
|
||||||
|
|
||||||
|
magicSearch.searchForContacts()
|
||||||
|
|
||||||
|
if callViewModel.isTransferInsteadCall == true {
|
||||||
|
callViewModel.isTransferInsteadCall = false
|
||||||
|
}
|
||||||
|
|
||||||
|
resetCallView()
|
||||||
|
|
||||||
|
delayColorDismiss()
|
||||||
|
|
||||||
|
withAnimation {
|
||||||
|
isShowStartCallFragment.toggle()
|
||||||
|
telecomManager.doCallOrJoinConf(address: addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
|
if !contactsManager.lastSearchSuggestions.isEmpty {
|
||||||
|
HStack(alignment: .center) {
|
||||||
|
Text("generic_address_picker_suggestions_list_title")
|
||||||
|
.default_text_style_800(styleSize: 16)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
|
||||||
|
suggestionsList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -393,7 +385,6 @@ struct StartCallFragment: View {
|
||||||
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!.domain != CorePreferences.defaultDomain {
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
Image(uiImage: contactsManager.textToImage(
|
||||||
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
firstName: String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)),
|
||||||
lastName: ""))
|
lastName: ""))
|
||||||
|
|
@ -403,29 +394,9 @@ struct StartCallFragment: View {
|
||||||
|
|
||||||
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
Text(String(contactsManager.lastSearchSuggestions[index].address!.asStringUriOnly().dropFirst(4)))
|
||||||
.default_text_style(styleSize: 16)
|
.default_text_style(styleSize: 16)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.foregroundStyle(Color.orangeMain500)
|
.foregroundStyle(Color.orangeMain500)
|
||||||
} else {
|
|
||||||
if let address = contactsManager.lastSearchSuggestions[index].address {
|
|
||||||
let nameTmp = address.displayName
|
|
||||||
?? address.username
|
|
||||||
?? String(address.asStringUriOnly().dropFirst(4))
|
|
||||||
|
|
||||||
Image(uiImage: contactsManager.textToImage(
|
|
||||||
firstName: nameTmp,
|
|
||||||
lastName: ""))
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 45, height: 45)
|
|
||||||
.clipShape(Circle())
|
|
||||||
|
|
||||||
Text(nameTmp)
|
|
||||||
.default_text_style(styleSize: 16)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.orangeMain500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Image("profil-picture-default")
|
Image("profil-picture-default")
|
||||||
.resizable()
|
.resizable()
|
||||||
|
|
|
||||||