Merge branch 'release/1.9.16/master'

This commit is contained in:
Doug 2023-01-24 14:13:59 +00:00
commit ae87624614
184 changed files with 2523 additions and 690 deletions

View file

@ -13,22 +13,10 @@ env:
MX_GIT_BRANCH: ${{ github.event.pull_request.head.ref }}
jobs:
check-secret:
runs-on: macos-12
outputs:
out-key: ${{ steps.out-key.outputs.defined }}
steps:
- id: out-key
env:
P12_KEY: ${{ secrets.ALPHA_CERTIFICATES_P12 }}
P12_PASSWORD_KEY: ${{ secrets.ALPHA_CERTIFICATES_P12 }}
if: "${{ env.P12_KEY != '' || env.P12_PASSWORD_KEY != '' }}"
run: echo "::set-output name=defined::true"
build:
# Run job if secrets are available (not available for forks).
needs: [check-secret]
# Don't run for forks as secrets are unavailable.
if: |
needs.check-secret.outputs.out-key == 'true' &&
github.event.pull_request.head.repo.full_name == github.repository &&
(github.event_name == 'push' ||
(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'Trigger-PR-Build')))

View file

@ -1,3 +1,43 @@
## Changes in 1.9.16 (2023-01-24)
✨ Features
- Rich Text Composer: Enable bulleted/numbered lists support ([#7238](https://github.com/vector-im/element-ios/issues/7238))
- Rich Text Composer: Enable quote & code blocks support ([#7271](https://github.com/vector-im/element-ios/issues/7271))
- Voice Broadcast: When deleting a voice broadcast, all data is now deleted on server side (MSC3912 implementation). ([#7283](https://github.com/vector-im/element-ios/issues/7283))
🙌 Improvements
- Labs: VoiceBroadcast: Handle VoIP buttons when VB is used ([#7225](https://github.com/vector-im/element-ios/pull/7225))
- Polls: add UI for active poll history. ([#7267](https://github.com/vector-im/element-ios/pull/7267))
- CryptoSDK: Add labs settings to enable Crypto SDK ([#7272](https://github.com/vector-im/element-ios/pull/7272))
- Voice Broadcast: Improved detection of voice broadcast completion during playback. ([#7273](https://github.com/vector-im/element-ios/pull/7273))
- Remove "Leave" button on Room details screen ([#7275](https://github.com/vector-im/element-ios/pull/7275))
- Polls: poll history UI for past polls. ([#7278](https://github.com/vector-im/element-ios/pull/7278))
- Polls: render replies to poll events better. ([#7284](https://github.com/vector-im/element-ios/pull/7284))
- CryptoV2: Display migration progress during startup ([#7286](https://github.com/vector-im/element-ios/pull/7286))
- Upgrade MatrixSDK version ([v0.24.8](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.24.8)).
- Voice broadcast connection error handling while recording. ([#7229](https://github.com/vector-im/element-ios/issues/7229))
- Handle a connection issue when we try to start a new voice broadcast. ([#7234](https://github.com/vector-im/element-ios/issues/7234))
- Rich Text Editor: https:// or mailto: scheme is automatically added when creating a link if no scheme is specified. ([#7279](https://github.com/vector-im/element-ios/issues/7279))
- Rich Text Editor: Adding a link over a blank selection, prompts the user to create a new link with new text to replace such selection. ([#7280](https://github.com/vector-im/element-ios/issues/7280))
- Voice Broadcast: handle the lost of connectivity with the homeserver while recording. ([#7285](https://github.com/vector-im/element-ios/issues/7285))
🐛 Bugfixes
- Voice Broadcast: The Now Playing Info Center now displays a voice broadcast instead of a voice message when a user is listening to a voice broadcast. ([#7257](https://github.com/vector-im/element-ios/pull/7257))
- Fix a crash caused by the missing Avatar Service dependency. ([#7268](https://github.com/vector-im/element-ios/pull/7268))
- The (edited) tag for messages is now light grey like on web and Android. ([#5148](https://github.com/vector-im/element-ios/issues/5148))
- Live Location Sharing does not work on first selection after granting "Allow always" location permission. ([#7222](https://github.com/vector-im/element-ios/issues/7222))
- Voice Broadcast: Fixed an issue where the voice broadcast audio player progress bar behaved unexpectedly. ([#7252](https://github.com/vector-im/element-ios/issues/7252))
- Voice Broadcast: VoiceBroadcast chunks are no longer resent as voice messages ([#7261](https://github.com/vector-im/element-ios/issues/7261))
- Timeline's links and hyperlinks match now the blue colour of Android and Web. ([#7263](https://github.com/vector-im/element-ios/issues/7263))
🧱 Build
- Fix Element Alpha workflow not being able to run. ([#7256](https://github.com/vector-im/element-ios/pull/7256))
## Changes in 1.9.15 (2023-01-10)
✨ Features

View file

@ -15,5 +15,5 @@
//
// Version
MARKETING_VERSION = 1.9.15
CURRENT_PROJECT_VERSION = 1.9.15
MARKETING_VERSION = 1.9.16
CURRENT_PROJECT_VERSION = 1.9.16

View file

@ -399,6 +399,7 @@ final class BuildSettings: NSObject {
// MARK: - Polls
static let pollsEnabled = true
static var pollsHistoryEnabled: Bool = false
// MARK: - Location Sharing

View file

@ -91,6 +91,12 @@ class CommonConfiguration: NSObject, Configurable {
MXKeyProvider.sharedInstance().delegate = EncryptionKeyManager.shared
sdkOptions.enableNewClientInformationFeature = RiotSettings.shared.enableClientInformationFeature
#if DEBUG
if sdkOptions.isCryptoSDKAvailable {
sdkOptions.enableCryptoSDK = RiotSettings.shared.enableCryptoSDK
}
#endif
}
private func makeASCIIUserAgent() -> String? {

View file

@ -48,5 +48,7 @@ public struct ColorValues: Colors {
public let ems: UIColor
public let links: UIColor
public let namesAndAvatars: [UIColor]
}

View file

@ -67,6 +67,10 @@ public protocol Colors {
/// Global color: The EMS brand's purple colour.
var ems: ColorType { get }
/// - Links
/// - Hyperlinks
var links: ColorType { get }
/// - Names in chat timeline
/// - Avatars default states that include first name letter
var namesAndAvatars: [ColorType] { get }

View file

@ -21,7 +21,7 @@ import SwiftUI
Struct for holding colors for use in SwiftUI.
*/
public struct ColorSwiftUI: Colors {
public let accent: Color
public let alert: Color
@ -48,8 +48,10 @@ public struct ColorSwiftUI: Colors {
public var ems: Color
public let namesAndAvatars: [Color]
public let links: Color
public let namesAndAvatars: [Color]
init(values: ColorValues) {
accent = Color(values.accent)
alert = Color(values.alert)
@ -64,6 +66,7 @@ public struct ColorSwiftUI: Colors {
navigation = Color(values.navigation)
background = Color(values.background)
ems = Color(values.ems)
links = Color(values.links)
namesAndAvatars = values.namesAndAvatars.map({ Color($0) })
}
}

View file

@ -45,6 +45,8 @@ import UIKit
public let navigation: UIColor
public let background: UIColor
public let links: UIColor
public let namesAndAvatars: [UIColor]
@ -61,6 +63,7 @@ import UIKit
tile = values.tile
navigation = values.navigation
background = values.background
links = values.links
namesAndAvatars = values.namesAndAvatars
}
}

View file

@ -34,6 +34,7 @@ public class DarkColors {
navigation: UIColor(rgb:0x21262C),
background: UIColor(rgb:0x15191E),
ems: UIColor(rgb: 0x7E69FF),
links: UIColor(rgb: 0x0086E6),
namesAndAvatars: [
UIColor(rgb:0x368BD6),
UIColor(rgb:0xAC3BA8),

View file

@ -35,6 +35,7 @@ public class LightColors {
navigation: UIColor(rgb:0xF4F6FA),
background: UIColor(rgb:0xFFFFFF),
ems: UIColor(rgb: 0x7E69FF),
links: UIColor(rgb: 0x0086E6),
namesAndAvatars: [
UIColor(rgb:0x368BD6),
UIColor(rgb:0xAC3BA8),

View file

@ -16,7 +16,7 @@ use_frameworks!
# - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for MatrixSDK repo. Used by Fastfile during CI
#
# Warning: our internal tooling depends on the name of this variable name, so be sure not to change it
$matrixSDKVersion = '= 0.24.7'
$matrixSDKVersion = '= 0.24.8'
# $matrixSDKVersion = :local
# $matrixSDKVersion = { :branch => 'develop'}
# $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } }

View file

@ -55,9 +55,9 @@ PODS:
- LoggerAPI (1.9.200):
- Logging (~> 1.1)
- Logging (1.4.0)
- MatrixSDK (0.24.7):
- MatrixSDK/Core (= 0.24.7)
- MatrixSDK/Core (0.24.7):
- MatrixSDK (0.24.8):
- MatrixSDK/Core (= 0.24.8)
- MatrixSDK/Core (0.24.8):
- AFNetworking (~> 4.0.0)
- GZIP (~> 1.3.0)
- libbase58 (~> 0.1.4)
@ -65,12 +65,12 @@ PODS:
- OLMKit (~> 3.2.5)
- Realm (= 10.27.0)
- SwiftyBeaver (= 1.9.5)
- MatrixSDK/CryptoSDK (0.24.7):
- MatrixSDKCrypto (= 0.1.7)
- MatrixSDK/JingleCallStack (0.24.7):
- MatrixSDK/CryptoSDK (0.24.8):
- MatrixSDKCrypto (= 0.1.8)
- MatrixSDK/JingleCallStack (0.24.8):
- JitsiMeetSDK (= 5.0.2)
- MatrixSDK/Core
- MatrixSDKCrypto (0.1.7)
- MatrixSDKCrypto (0.1.8)
- OLMKit (3.2.12):
- OLMKit/olmc (= 3.2.12)
- OLMKit/olmcpp (= 3.2.12)
@ -122,8 +122,8 @@ DEPENDENCIES:
- KeychainAccess (~> 4.2.2)
- KTCenterFlowLayout (~> 1.3.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixSDK (= 0.24.7)
- MatrixSDK/JingleCallStack (= 0.24.7)
- MatrixSDK (= 0.24.8)
- MatrixSDK/JingleCallStack (= 0.24.8)
- OLMKit
- PostHog (~> 1.4.4)
- ReadMoreTextView (~> 3.0.1)
@ -197,7 +197,7 @@ CHECKOUT OPTIONS:
:git: https://github.com/matrix-org/matrix-analytics-events.git
SPEC CHECKSUMS:
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
AnalyticsEvents: 0cc8cf52da2fd464a2f39b788a295988151116ce
BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24
BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc
@ -220,8 +220,8 @@ SPEC CHECKSUMS:
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
Logging: beeb016c9c80cf77042d62e83495816847ef108b
MatrixSDK: 895929fad10b7ec9aa96d557403b44c5e3522211
MatrixSDKCrypto: 2bd9ca41b2c644839f4e680a64897d56b3f95392
MatrixSDK: cf1c1b2a9742f7f4fad21e94bd94cd8f13c47369
MatrixSDKCrypto: 862d9b4dbb6861da030943f5a18c39258ed7345b
OLMKit: da115f16582e47626616874e20f7bb92222c7a51
PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
@ -241,6 +241,6 @@ SPEC CHECKSUMS:
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: 56782e2abd382278b3c5b23824ca74994fd0a97e
PODFILE CHECKSUM: 079b57b800c666ad864e1f059ae69e150a98a4f0
COCOAPODS: 1.11.3

View file

@ -23,7 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift",
"state" : {
"revision" : "534ee5bae5e8de69ed398937b5edb7b5f21551d2"
"revision" : "6927cb878376136c4a03d919b689af8dfbdad080",
"version" : "0.19.0"
}
},
{

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "code_block.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "code_block@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "code_block@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "pollHistory.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 2C10.2239 2 10 2.22386 10 2.5V22H14V2.5C14 2.22386 13.7761 2 13.5 2H10.5ZM3 9.5C3 9.22386 3.22386 9 3.5 9H6.5C6.77614 9 7 9.22386 7 9.5V22H3V9.5ZM17 13.5C17 13.2239 17.2239 13 17.5 13H20.5C20.7761 13 21 13.2239 21 13.5V22H17V13.5Z" fill="#8D97A5"/>
</svg>

After

Width:  |  Height:  |  Size: 407 B

View file

@ -455,7 +455,7 @@
"sign_up" = "الاِشتِراك";
"dismiss" = "إبعَاد";
"discard" = "اِستِبعاد";
"abort" = "إِجهَاض";
"abort" = "إنهاء";
"yes" = "نَعَم";
// Action
@ -1078,3 +1078,8 @@
/* The placeholder will show the email address that was entered. */
"authentication_verify_email_waiting_message" = "اتبع التعليمات المرسلة إلى %@";
"invite_to" = "الدعوة إلى %@";
"password_policy_pwd_in_dict_error" = "تم العثور على كلمة المرور هذه في القاموس لدينا، وهي كلمة مرور غير مسموح في استخدامها.";
// Others
"or" = "أو";
"accessibility_selected" = "تم تحديده";

View file

@ -2700,5 +2700,34 @@
"notice_voice_broadcast_live" = "Echtzeitübertragung";
"user_other_session_security_recommendation_title" = "Andere Sitzungen";
"voice_message_broadcast_in_progress_title" = "Kann Sprachnachricht nicht beginnen";
"poll_timeline_decryption_error" = "Aufgrund von Entschlüsselungsfehlern könnten einige Stimmen nicht gezählt werden";
"voice_message_broadcast_in_progress_message" = "Du kannst kein Gespräch beginnen, da du im Moment eine Sprachübertragung aufzeichnest. Bitte beende deine Sprachübertragung, um ein Gespräch zu beginnen";
"poll_timeline_decryption_error" = "Evtl. werden infolge von Entschlüsselungsfehlern einige Stimmen nicht gezählt";
"voice_message_broadcast_in_progress_message" = "Du kannst keine Sprachnachricht beginnen, da du im Moment eine Echtzeitübertragung aufzeichnest. Bitte beende deine Sprachübertragung, um ein Gespräch zu beginnen";
"poll_timeline_ended_text" = "Umfrage beendet";
"voice_broadcast_voip_cannot_start_description" = "Du kannst keinen Anruf beginnen, da du im Moment eine Sprachübertragung aufzeichnest. Bitte beende deine Sprachübertragung, um ein Gespräch zu beginnen.";
"voice_broadcast_voip_cannot_start_title" = "Kann keinen Anruf beginnen";
"poll_history_no_past_poll_text" = "In diesem Raum gibt es keine abgeschlossenen Umfragen";
"poll_history_no_active_poll_text" = "In diesem Raum gibt es keine aktiven Umfragen";
"poll_history_past_segment_title" = "Vergangene Umfragen";
"poll_history_active_segment_title" = "Aktive Umfragen";
// MARK: - Polls history
"poll_history_title" = "Umfrageverlauf";
"room_details_polls" = "Umfrageverlauf";
"accessibility_selected" = "ausgewählt";
"voice_broadcast_playback_lock_screen_placeholder" = "Sprachübertragung";
"voice_broadcast_connection_error_message" = "Leider ist es aktuell nicht möglich, eine Aufnahme zu beginnen. Bitte versuche es später erneut.";
"voice_broadcast_connection_error_title" = "Verbindungsfehler";
"wysiwyg_composer_format_action_code_block" = "Quelltextblock umschalten";
"wysiwyg_composer_format_action_quote" = "Zitat umschalten";
"wysiwyg_composer_format_action_ordered_list" = "Nummerierte Liste umschalten";
"wysiwyg_composer_format_action_unordered_list" = "Unsortierte Liste umschalten";
"voice_broadcast_recorder_connection_error" = "Verbindungsfehler Aufzeichnung pausiert";
"poll_timeline_reply_ended_poll" = "Beendete Umfrage";
// MARK: - Launch loading
"launch_loading_migrating_data" = "Migriere Daten\n%@ %%";
"settings_labs_disable_crypto_sdk" = "Krypto-SDK ist aktiviert. Zum Deaktivieren, bitte die App neu installieren";
"settings_labs_confirm_crypto_sdk" = "Dies kann nicht rückgängig gemacht werden";
"settings_labs_enable_crypto_sdk" = "Rust-basiertes Krypto-SDK aktivieren";

View file

@ -96,6 +96,7 @@
// Accessibility
"accessibility_checkbox_label" = "checkbox";
"accessibility_button_label" = "button";
"accessibility_selected" = "selected";
// MARK: Onboarding
"onboarding_splash_register_button_title" = "Create account";
@ -803,6 +804,9 @@ Tap the + to start adding people.";
"settings_labs_enable_new_app_layout" = "New Application Layout";
"settings_labs_enable_wysiwyg_composer" = "Try out the rich text editor";
"settings_labs_enable_voice_broadcast" = "Voice broadcast";
"settings_labs_enable_crypto_sdk" = "Enable new rust-based Crypto SDK";
"settings_labs_confirm_crypto_sdk" = "This action cannot be undone";
"settings_labs_disable_crypto_sdk" = "Crypto SDK is enabled. To disable please reinstall the app";
"settings_version" = "Version %@";
"settings_olm_version" = "Olm Version %@";
@ -982,6 +986,7 @@ Tap the + to start adding people.";
"room_details_title_for_dm" = "Details";
"room_details_people" = "Members";
"room_details_files" = "Uploads";
"room_details_polls" = "Poll history";
"room_details_search" = "Search room";
"room_details_integrations" = "Integrations";
"room_details_settings" = "Settings";
@ -1522,7 +1527,7 @@ Tap the + to start adding people.";
"device_verification_cancelled_by_me" = "The verification has been cancelled. Reason: %@";
"device_verification_error_cannot_load_device" = "Cannot load session information.";
// Mark: Incoming
// MARK: Incoming
"device_verification_incoming_title" = "Incoming Verification Request";
"device_verification_incoming_description_1" = "Verify this session to mark it as trusted. Trusting sessions of partners gives you extra peace of mind when using end-to-end encrypted messages.";
"device_verification_incoming_description_2" = "Verifying this session will mark it as trusted, and also mark your session as trusted to the partner.";
@ -1974,6 +1979,7 @@ Tap the + to start adding people.";
// MARK: - Launch loading
"launch_loading_migrating_data" = "Migrating data\n%@ %%";
"launch_loading_server_syncing" = "Syncing with the server";
"launch_loading_server_syncing_nth_attempt" = "Syncing with the server\n(%@ attempt)";
"launch_loading_processing_response" = "Processing data\n%@ %%";
@ -2011,12 +2017,12 @@ Tap the + to start adding people.";
"share_invite_link_room_text" = "Hey, join this room on %@";
"share_invite_link_space_text" = "Hey, join this space on %@";
// Mark: - Room avatar view
// MARK: - Room avatar view
"room_avatar_view_accessibility_label" = "avatar";
"room_avatar_view_accessibility_hint" = "Change room avatar";
// Mark: - Room creation introduction cell
// MARK: - Room creation introduction cell
"room_intro_cell_add_participants_action" = "Add people";
@ -2033,7 +2039,7 @@ Tap the + to start adding people.";
"room_intro_cell_information_dm_sentence2" = "Only the two of you are in this conversation, no one else can join.";
"room_intro_cell_information_multiple_dm_sentence2" = "Only you are in this conversation, unless any of you invites someone to join.";
// Mark: - Room invite
// MARK: - Room invite
"room_invite_to_space_option_title" = "To %@";
"room_invite_to_space_option_detail" = "They can explore %@, but wont be a member of %@.";
@ -2042,7 +2048,7 @@ Tap the + to start adding people.";
"room_invite_not_enough_permission" = "You do not have permission to invite people to this room";
"space_invite_not_enough_permission" = "You do not have permission to invite people to this space";
// Mark: - Spaces
// MARK: - Spaces
"space_feature_unavailable_title" = "Spaces arent here yet";
"space_feature_unavailable_subtitle" = "Spaces aren't on iOS yet, but you can use them now on Web and Desktop";
@ -2099,7 +2105,7 @@ Tap the + to start adding people.";
"spaces_feature_not_available" = "This feature isn't available here. For now, you can do this with %@ on your computer.";
// Mark: - Space Creation
// MARK: - Space Creation
"spaces_creation_hint" = "Spaces are a new way to group rooms and people.";
"spaces_creation_visibility_title" = "What type of space do you want to create?";
@ -2158,7 +2164,7 @@ Tap the + to start adding people.";
"spaces_add_room_missing_permission_message" = "You do not have permissions to add rooms to this space.";
// Mark: Leave space
// MARK: Leave space
"leave_space_action" = "Leave space";
"leave_space_and_one_room" = "Leave space and 1 room";
@ -2171,17 +2177,17 @@ Tap the + to start adding people.";
"room_event_action_reaction_more" = "%@ more";
// Mark: Avatar
// MARK: Avatar
"space_avatar_view_accessibility_label" = "avatar";
"space_avatar_view_accessibility_hint" = "Change space avatar";
// Mark: - User avatar view
// MARK: - User avatar view
"user_avatar_view_accessibility_label" = "avatar";
"user_avatar_view_accessibility_hint" = "Change user avatar";
// Mark: - Side menu
// MARK: - Side menu
"side_menu_reveal_action_accessibility_label" = "Left panel";
"side_menu_action_invite_friends" = "Invite friends";
@ -2191,7 +2197,7 @@ Tap the + to start adding people.";
"side_menu_app_version" = "Version %@";
"side_menu_coach_message" = "Swipe right or tap to see all rooms";
// Mark: - Voice Messages
// MARK: - Voice Messages
"voice_message_release_to_send" = "Hold to record, release to send";
"voice_message_remaining_recording_time" = "%@s left";
@ -2200,12 +2206,13 @@ Tap the + to start adding people.";
"voice_message_broadcast_in_progress_title" = "Can't start voice message";
"voice_message_broadcast_in_progress_message" = "You can't start a voice message as you are currently recording a live broadcast. Please end your live broadcast in order to start recording a voice message";
// Mark: - Voice broadcast
// MARK: - Voice Broadcast
"voice_broadcast_unauthorized_title" = "Can't start a new voice broadcast";
"voice_broadcast_permission_denied_message" = "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.";
"voice_broadcast_blocked_by_someone_else_message" = "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.";
"voice_broadcast_already_in_progress_message" = "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.";
"voice_broadcast_playback_loading_error" = "Unable to play this voice broadcast.";
"voice_broadcast_playback_lock_screen_placeholder" = "Voice broadcast";
"voice_broadcast_live" = "Live";
"voice_broadcast_tile" = "Voice broadcast";
"voice_broadcast_time_left" = "%@ left";
@ -2213,8 +2220,13 @@ Tap the + to start adding people.";
"voice_broadcast_stop_alert_title" = "Stop live broadcasting?";
"voice_broadcast_stop_alert_description" = "Are you sure you want to stop your live broadcast? This will end the broadcast, and the full recording will be available in the room.";
"voice_broadcast_stop_alert_agree_button" = "Yes, stop";
"voice_broadcast_voip_cannot_start_title" = "Cant start a call";
"voice_broadcast_voip_cannot_start_description" = "You cant start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call.";
"voice_broadcast_connection_error_title" = "Connection error";
"voice_broadcast_connection_error_message" = "Unfortunately were unable to start a recording right now. Please try again later.";
"voice_broadcast_recorder_connection_error" = "Connection error - Recording paused";
// Mark: - Version check
// MARK: - Version check
"version_check_banner_title_supported" = "Were ending support for iOS %@";
"version_check_banner_subtitle_supported" = "We will soon be ending support for %@ on iOS %@. To continue using %@ to its full potential, we advise you to upgrade your version of iOS.";
@ -2230,7 +2242,7 @@ Tap the + to start adding people.";
"version_check_modal_subtitle_deprecated" = "We've been working on enhancing %@ for a faster and more polished experience. Unfortunately your current version of iOS is not compatible with some of those fixes and is no longer supported.\nWe're advising you to upgrade your operating system to use %@ to its full potential.";
"version_check_modal_action_title_deprecated" = "Find out how";
// Mark: - All Chats
// MARK: - All Chats
"all_chats_title" = "All chats";
"all_chats_section_title" = "Chats";
@ -2274,12 +2286,12 @@ Tap the + to start adding people.";
"all_chats_onboarding_title" = "What's new";
"all_chats_onboarding_try_it" = "Try it out";
// Mark: - Room invites
// MARK: - Room invites
"room_invites_empty_view_title" = "Nothing new.";
"room_invites_empty_view_information" = "This is where your invites appear.";
// Mark: - Space Selector
// MARK: - Space Selector
"space_selector_title" = "My spaces";
"space_selector_empty_view_title" = "No spaces yet.";
@ -2289,7 +2301,15 @@ Tap the + to start adding people.";
"space_detail_nav_title" = "Space detail";
"space_invite_nav_title" = "Space invite";
// Mark: - Polls
// MARK: - Polls history
"poll_history_title" = "Poll history";
"poll_history_active_segment_title" = "Active polls";
"poll_history_past_segment_title" = "Past polls";
"poll_history_no_active_poll_text" = "There are no active polls in this room";
"poll_history_no_past_poll_text" = "There are no past polls in this room";
// MARK: - Polls
"poll_edit_form_create_poll" = "Create poll";
@ -2353,6 +2373,8 @@ Tap the + to start adding people.";
"poll_timeline_ended_text" = "Ended the poll";
"poll_timeline_reply_ended_poll" = "Ended poll";
// MARK: - Location sharing
"location_sharing_title" = "Location";
@ -2538,7 +2560,7 @@ To enable access, tap Settings> Location and select Always";
"user_session_overview_session_details_button_title" = "Session details";
// Mark: - WYSIWYG Composer
// MARK: - WYSIWYG Composer
// Send Media Actions
"wysiwyg_composer_start_action_media_picker" = "Photo Library";
@ -2557,6 +2579,12 @@ To enable access, tap Settings> Location and select Always";
"wysiwyg_composer_format_action_strikethrough" = "Apply underline format";
"wysiwyg_composer_format_action_link" = "Apply link format";
"wysiwyg_composer_format_action_inline_code" = "Apply inline code format";
"wysiwyg_composer_format_action_unordered_list" = "Toggle bulleted list";
"wysiwyg_composer_format_action_ordered_list" = "Toggle numbered list";
"wysiwyg_composer_format_action_code_block" = "Toggle code block";
"wysiwyg_composer_format_action_quote" = "Toggle quote";
// Links
"wysiwyg_composer_link_action_text" = "Text";

View file

@ -118,3 +118,6 @@
/* New file message from a specific person, not referencing a room. */
"LOCATION_FROM_USER" = "%@ jagas oma asukohta";
/* New voice broadcast from a specific person, not referencing a room. */
"VOICE_BROADCAST_FROM_USER" = "%@ alustas ringhäälingukõnet";

View file

@ -2640,3 +2640,32 @@
"poll_timeline_decryption_error" = "Krüptimisvigade tõttu jääb osa hääli lugemata";
"voice_message_broadcast_in_progress_title" = "Häälsõnumi salvestamine või esitamine ei õnnestu";
"voice_message_broadcast_in_progress_message" = "Kuna sa hetkel salvestad ringhäälingukõnet, siis häälsõnumi salvestamine või esitamine ei õnnestu. Selleks palun lõpeta ringhäälingukõne";
"poll_timeline_ended_text" = "Küsitlus on lõppenud";
"voice_broadcast_voip_cannot_start_description" = "Kuna sa hetkel salvestad ringhäälingukõnet, siis tavakõne algatamine ei õnnestu. Kõne alustamiseks palun lõpeta ringhäälingukõne.";
"voice_broadcast_voip_cannot_start_title" = "Kõne algatamine ei õnnestu";
"poll_history_no_past_poll_text" = "Selles jututoas pole varasemaid küsitlusi";
"poll_history_no_active_poll_text" = "Selles jututoas pole käimasolevaid küsitlusi";
"poll_history_past_segment_title" = "Varasemad küsitlused";
"poll_history_active_segment_title" = "Käimasolevad küsitlused";
// MARK: - Polls history
"poll_history_title" = "Küsitluste ajalugu";
"room_details_polls" = "Küsitluste ajalugu";
"accessibility_selected" = "valitud";
"voice_broadcast_playback_lock_screen_placeholder" = "Ringhäälingukõne";
"voice_broadcast_connection_error_message" = "Kahjuks me ei saa hetkel salvestamist alustada. Palun proovi hiljem uuesti.";
"voice_broadcast_connection_error_title" = "Ühenduse viga";
"wysiwyg_composer_format_action_quote" = "Lülita tsiteerimine sisse/välja";
"wysiwyg_composer_format_action_code_block" = "Lülita koodiblokk sisse/välja";
"wysiwyg_composer_format_action_ordered_list" = "Lülita nummerdatud loend sisse/välja";
"wysiwyg_composer_format_action_unordered_list" = "Lülita täpploend sisse/välja";
"voice_broadcast_recorder_connection_error" = "Viga võrguühenduses - salvestamine on peatatud";
"poll_timeline_reply_ended_poll" = "Lõppenud küsitlus";
// MARK: - Launch loading
"launch_loading_migrating_data" = "Tõstame andmeid ümber\n%@ %%";
"settings_labs_disable_crypto_sdk" = "Uus Crypto SDK on kasutusel. Tema väljalülitamiseks palun paigalda rakendus uuesti";
"settings_labs_confirm_crypto_sdk" = "Seda toimingut ei saa tagasi pöörata";
"settings_labs_enable_crypto_sdk" = "Võta kasutusele uus Rust-keelel põhinev Crypto SDK";

View file

@ -120,3 +120,6 @@
/* New video message from a specific person, not referencing a room. */
"VIDEO_FROM_USER" = "%@ videót küldött";
/* New voice broadcast from a specific person, not referencing a room. */
"VOICE_BROADCAST_FROM_USER" = "%@ hang közvetítést indított";

View file

@ -2688,3 +2688,25 @@
"poll_timeline_decryption_error" = "Visszafejtési hibák miatt néhány szavazat nem kerül beszámításra";
"voice_message_broadcast_in_progress_message" = "Nem lehet hang üzenetet indítani élő közvetítés felvétele közben. Az élő közvetítés bejezése szükséges a hang üzenet indításához";
"voice_message_broadcast_in_progress_title" = "Hang üzenetet nem lehet elindítani";
"poll_timeline_ended_text" = "Szavazás vége";
"voice_broadcast_voip_cannot_start_description" = "Nem lehet hívást kezdeményezni élő közvetítés felvétele közben. Az élő közvetítés bejezése szükséges a hívás indításához.";
"voice_broadcast_voip_cannot_start_title" = "Nem sikerült hívást indítani";
"poll_history_no_past_poll_text" = "Nincsenek régi szavazások ebben a szobában";
"poll_history_no_active_poll_text" = "Nincsenek aktív szavazások ebben a szobában";
"poll_history_past_segment_title" = "Régi szavazások";
"poll_history_active_segment_title" = "Aktív szavazások";
// MARK: - Polls history
"poll_history_title" = "Szavazás alakulása";
"room_details_polls" = "Szavazás alakulása";
"accessibility_selected" = "kiválasztva";
"wysiwyg_composer_format_action_quote" = "Idézet be/ki";
"wysiwyg_composer_format_action_code_block" = "Kód blokk be/ki";
"wysiwyg_composer_format_action_ordered_list" = "Számozott lista ki-,bekapcsolása";
"wysiwyg_composer_format_action_unordered_list" = "Lista ki-,bekapcsolása";
"poll_timeline_reply_ended_poll" = "Lezárt szavazások";
"voice_broadcast_recorder_connection_error" = "Kapcsolódási hiba Felvétel szüneteltetve";
"voice_broadcast_connection_error_message" = "Sajnos most nem lehet elindítani a felvételt. Próbálja meg később.";
"voice_broadcast_connection_error_title" = "Kapcsolat hiba";
"voice_broadcast_playback_lock_screen_placeholder" = "Hang közvetítés";

View file

@ -2893,5 +2893,34 @@
"notice_voice_broadcast_live" = "Siaran langsung";
"user_other_session_security_recommendation_title" = "Sesi lainnya";
"poll_timeline_decryption_error" = "Karena kesalahan enkripsi, beberapa suara mungkin tidak terhitung";
"voice_message_broadcast_in_progress_message" = "Anda tidak dapat memulai sebuah pesan suara selagi Anda merekam sebuah siaran langsung. Silakan mengakhiri siaran langsung Anda untuk memulai merekam sebuah pesan suara";
"voice_message_broadcast_in_progress_message" = "Anda tidak dapat memulai sebuah pesan suara karena Anda saat ini merekam sebuah siaran langsung. Silakan mengakhiri siaran langsung Anda untuk memulai merekam sebuah pesan suara";
"voice_message_broadcast_in_progress_title" = "Tidak dapat memulai pesan suara";
"poll_timeline_ended_text" = "Mengakhiri pemungutan suara";
"voice_broadcast_voip_cannot_start_description" = "Anda tidak dapat memulai sebuah panggilan karena Anda saat ini merekam sebuah siaran langsung. Mohon akhiri siaran langsung Anda untuk memulai sebuah panggilan.";
"voice_broadcast_voip_cannot_start_title" = "Tidak dapat memulai sebuah panggilan";
"poll_history_no_past_poll_text" = "Tidak ada pemungutan suara masa lalu di ruangan ini";
"poll_history_no_active_poll_text" = "Tidak ada pemungutan suara yang aktifk di ruangan ini";
"poll_history_past_segment_title" = "Pemungutan suara sebelumnya";
"poll_history_active_segment_title" = "Pemungutan suara aktif";
// MARK: - Polls history
"poll_history_title" = "Riwayat pemungutan suara";
"room_details_polls" = "Riwayat pemungutan suara";
"accessibility_selected" = "dipilih";
"voice_broadcast_playback_lock_screen_placeholder" = "Siaran suara";
"wysiwyg_composer_format_action_quote" = "Saklar kutipan";
"wysiwyg_composer_format_action_code_block" = "Saklar blok kode";
"wysiwyg_composer_format_action_ordered_list" = "Saklar daftar bernomor";
"wysiwyg_composer_format_action_unordered_list" = "Saklar daftar bulat";
"voice_broadcast_connection_error_message" = "Sayangnya kami tidak dapat memulai sebuah rekaman saat ini. Silakan coba lagi nanti.";
"voice_broadcast_connection_error_title" = "Kesalahan koneksi";
"voice_broadcast_recorder_connection_error" = "Kesalahan koneksi - Perekaman dijeda";
"poll_timeline_reply_ended_poll" = "Pemungutan suara berakhir";
// MARK: - Launch loading
"launch_loading_migrating_data" = "Memigrasikan data\n%@ %%";
"settings_labs_disable_crypto_sdk" = "SDK Kripto diaktifkan. Untuk menonaktifkan, mohon memasang ulang aplikasi";
"settings_labs_confirm_crypto_sdk" = "Tindakan ini tidak dapat diurungkan";
"settings_labs_enable_crypto_sdk" = "Aktifkan SDK Kripto baru berbasis Rust";

View file

@ -170,3 +170,6 @@
/* Look, stuff's happened, alright? Just open the app. */
"MSGS_IN_TWO_PLUS_ROOMS" = "%@ ný skilaboð í %@, %@ og fleirum";
/* New voice broadcast from a specific person, not referencing a room. */
"VOICE_BROADCAST_FROM_USER" = "%@ byrjaði talútsendingu";

View file

@ -1283,7 +1283,7 @@
"settings_add_3pid_invalid_password_message" = "Ógild auðkenni";
"settings_add_3pid_password_title_msidsn" = "Bæta við símanúmeri";
"settings_add_3pid_password_title_email" = "Bæta við tölvupóstfangi";
"settings_labs_enable_threads" = "Skilaboð í spjallþráðum";
"settings_labs_enable_threads" = "Spjallþræðir skilaboða";
"settings_labs_enabled_polls" = "Kannanir";
"settings_integrations_allow_button" = "Sýsla með samþættingar";
"settings_new_keyword" = "Bæta við nýju stikkorði";
@ -2317,7 +2317,7 @@
"device_name_mobile" = "%@ fyrir farsíma";
"device_name_web" = "%@ á vefnum";
"device_name_desktop" = "%@ fyrir einkatölvur";
"user_session_item_details" = "%@ · Síðasta virkni %@";
"user_session_item_details" = "%1$@ · %2$@";
"location_sharing_live_loading" = "Hleð inn rauntímastaðsetningu...";
"location_sharing_live_list_item_time_left" = "%@ fór";
"location_sharing_map_credits_title" = "© Höfundarréttur";
@ -2368,3 +2368,119 @@
// MARK: Authentication
"authentication_registration_title" = "Búðu til aðganginn þinn";
"notice_voice_broadcast_ended_by_you" = "Þú endaðir talútsendingu.";
"notice_voice_broadcast_ended" = "%@ endaði talútsendingu.";
"notice_voice_broadcast_live" = "Bein útsending";
"deselect_all" = "Afvelja allt";
"wysiwyg_composer_link_action_edit_title" = "Breyta tengli";
"wysiwyg_composer_link_action_create_title" = "Búa til tengil";
"wysiwyg_composer_link_action_link" = "Tengill";
// Links
"wysiwyg_composer_link_action_text" = "Texti";
"wysiwyg_composer_start_action_voice_broadcast" = "Útvörpun tals";
"wysiwyg_composer_start_action_text_formatting" = "Sníðing texta";
"wysiwyg_composer_start_action_camera" = "Myndavél";
"wysiwyg_composer_start_action_location" = "Staðsetning";
"wysiwyg_composer_start_action_polls" = "Kannanir";
"wysiwyg_composer_start_action_attachments" = "Viðhengi";
"wysiwyg_composer_start_action_stickers" = "Límmerki";
// MARK: - WYSIWYG Composer
// Send Media Actions
"wysiwyg_composer_start_action_media_picker" = "Ljósmyndasafn";
"user_session_overview_session_details_button_title" = "Nánar um setuna";
"user_session_overview_session_title" = "Seta";
"user_session_overview_current_session_title" = "Núverandi seta";
"user_session_details_application_url" = "Slóð (URL)";
"user_session_details_application_version" = "Útgáfa";
"user_session_details_application_name" = "Heiti";
"user_session_details_device_os" = "Stýrikerfi";
"user_session_details_device_browser" = "Vafri";
"user_session_details_device_model" = "Gerð";
"user_session_details_device_ip_location" = "Staðsetning IP-vistfangs";
"user_session_details_device_ip_address" = "IP-vistfang";
"user_session_details_last_activity" = "Síðasta virkni";
"user_session_details_session_id" = "Auðkenni setu";
"user_session_details_session_name" = "Nafn á setu";
"user_session_details_device_section_header" = "Tæki";
"user_session_details_application_section_header" = "Forrit";
"user_session_details_session_section_header" = "Seta";
"user_session_details_title" = "Nánar um setuna";
"device_type_name_unknown" = "Óþekkt";
"device_type_name_mobile" = "Farsími";
"device_type_name_web" = "Vefur";
"device_type_name_desktop" = "Borðtölva";
"user_other_session_selected_count" = "%@ valið";
"user_other_session_clear_filter" = "Hreinsa síu";
"user_other_session_no_unverified_sessions" = "Engar óstaðfestar setur fundust.";
"user_other_session_no_verified_sessions" = "Engar staðfestar setur fundust.";
"user_other_session_no_inactive_sessions" = "Engar óvirkar setur fundust.";
"user_other_session_filter_menu_inactive" = "Óvirkt";
"user_other_session_filter_menu_unverified" = "Óstaðfestar";
"user_other_session_filter_menu_verified" = "Staðfestar";
"user_other_session_filter_menu_all" = "Allar setur";
"user_other_session_filter" = "Sía";
"user_other_session_security_recommendation_title" = "Aðrar setur";
"user_session_inactive_session_title" = "Óvirkar setur";
"user_session_unverified_session_title" = "Óstaðfest seta";
"user_session_verified_session_title" = "Sannreyndar setur";
"user_session_got_it" = "Náði því";
"user_session_push_notifications" = "Ýti-tilkynningar";
"user_session_verification_unknown_short" = "Óþekkt";
"user_session_verification_unknown" = "Óþekkt staða sannvottunar";
"user_sessions_view_all_action" = "Skoða öll (%d)";
"user_sessions_overview_link_device" = "Tengja tæki";
"user_sessions_overview_current_session_section_title" = "Núverandi seta";
"user_sessions_hide_location_info" = "Fela IP-vistfang";
"user_sessions_show_location_info" = "Birta IP-vistfang";
"user_sessions_overview_other_sessions_section_title" = "Aðrar setur";
"user_sessions_overview_security_recommendations_inactive_title" = "Óvirkar setur";
"user_sessions_overview_security_recommendations_unverified_title" = "Óstaðfestar setur";
"user_sessions_overview_security_recommendations_section_title" = "Ráðleggingar varðandi öryggi";
// MARK: User sessions management
// Parameter is the application display name (e.g. "Element")
"user_sessions_default_session_display_name" = "%@ iOS";
"location_sharing_live_lab_promotion_activation" = "Virkja deilingu rauntímastaðsetninga";
"location_sharing_live_timer_incoming" = "Í beinni til %@";
"poll_timeline_reply_ended_poll" = "Lauk könnun";
"poll_timeline_ended_text" = "Lauk könnuninni";
"poll_history_past_segment_title" = "Fyrri kannanir";
"poll_history_active_segment_title" = "Virkar kannanir";
// MARK: - Polls history
"poll_history_title" = "Breytingaskrá könnunar";
"all_chats_user_menu_accessibility_label" = "Valmynd notandans";
"voice_broadcast_connection_error_title" = "Villa í tengingu";
"voice_broadcast_voip_cannot_start_title" = "Get ekki hafið símtal";
"voice_broadcast_stop_alert_agree_button" = "Já, stöðva";
"voice_broadcast_buffering" = "Hleð í biðminni...";
"voice_broadcast_time_left" = "%@ eftir";
"voice_broadcast_tile" = "Útvörpun tals";
"voice_broadcast_live" = "Beint";
"voice_broadcast_playback_lock_screen_placeholder" = "Útvörpun tals";
// Unverified sessions
"key_verification_alert_title" = "Þú ert með óstaðfestar setur";
"sign_out_confirmation_message" = "Ertu viss um að þú viljir skrá þig út?";
// MARK: Sign out warning
"sign_out" = "Skrá út";
"secure_key_backup_setup_cancel_alert_message" = "Ef þú hættir við núna, geturðu tapað dulrituðum skilaboðum og gögnum ef þú missir aðgang að innskráningum þínum.\n\nÞú getur víka sett upp örugga afritun og sýslað með dulritunarlyklana þína í stillingunum.";
"room_details_polls" = "Breytingaskrá könnunar";
"manage_session_sign_out_other_sessions" = "Skrá út úr öllum öðrum setum";
"manage_session_rename" = "Endurnefna setu";
"settings_labs_enable_voice_broadcast" = "Útvörpun tals";
"authentication_qr_login_failure_retry" = "Reyna aftur";
"authentication_qr_login_loading_connecting_device" = "Tengist við tæki";
"authentication_qr_login_scan_title" = "Skanna QR-kóða";
"authentication_qr_login_start_title" = "Skanna QR-kóða";
"authentication_login_with_qr" = "Skrá inn með QR-kóða";

View file

@ -118,3 +118,6 @@
/* New file message from a specific person, not referencing a room. */
"LOCATION_FROM_USER" = "%@ ha condiviso la sua posizione";
/* New voice broadcast from a specific person, not referencing a room. */
"VOICE_BROADCAST_FROM_USER" = "%@ ha iniziato una trasmissione vocale";

View file

@ -2665,3 +2665,28 @@
"notice_voice_broadcast_live" = "Trasmissione in diretta";
"wysiwyg_composer_format_action_inline_code" = "Applica formato codice interlinea";
"user_other_session_security_recommendation_title" = "Altre sessioni";
"poll_timeline_ended_text" = "Sondaggio terminato";
"poll_timeline_decryption_error" = "A causa di errori di decifrazione, alcuni voti potrebbero non venire contati";
"voice_broadcast_voip_cannot_start_description" = "Non puoi avviare una chiamata perché stai registrando una trasmissione in diretta. Termina la trasmissione per potere iniziare una chiamata.";
"voice_broadcast_voip_cannot_start_title" = "Impossibile avviare una chiamata";
"voice_message_broadcast_in_progress_title" = "Impossibile iniziare il messaggio vocale";
"voice_message_broadcast_in_progress_message" = "Non puoi iniziare un messaggio vocale perché stai registrando una trasmissione in diretta. Termina la trasmissione per potere iniziare un messaggio vocale";
"poll_history_no_past_poll_text" = "In questa stanza non ci sono sondaggi passati";
"poll_history_no_active_poll_text" = "In questa stanza non ci sono sondaggi attivi";
"poll_history_past_segment_title" = "Sondaggi passati";
"poll_history_active_segment_title" = "Sondaggi attivi";
// MARK: - Polls history
"poll_history_title" = "Cronologia sondaggi";
"voice_broadcast_playback_lock_screen_placeholder" = "Trasmissione vocale";
"room_details_polls" = "Cronologia sondaggi";
"accessibility_selected" = "selezionato";
"wysiwyg_composer_format_action_quote" = "Attiva/disattiva citazione";
"wysiwyg_composer_format_action_code_block" = "Attiva/disattiva blocco di codice";
"wysiwyg_composer_format_action_ordered_list" = "Attiva/disattiva elenco numerato";
"wysiwyg_composer_format_action_unordered_list" = "Attiva/disattiva elenco puntato";
"poll_timeline_reply_ended_poll" = "Sondaggio terminato";
"voice_broadcast_recorder_connection_error" = "Errore di connessione - Registrazione in pausa";
"voice_broadcast_connection_error_message" = "Sfortunatamente non riusciamo ad iniziare una registrazione al momento. Riprova più tardi.";
"voice_broadcast_connection_error_title" = "Errore di connessione";

View file

@ -1720,7 +1720,7 @@
"pin_protection_reset_alert_action_reset" = "リセット";
"authentication_recaptcha_title" = "あなたは人間ですか?";
"authentication_verify_msisdn_waiting_button" = "コードを再送信";
"authentication_choose_password_submit_button" = "パスワードをリセット";
"authentication_choose_password_submit_button" = "パスワードを再設定";
"authentication_choose_password_signout_all_devices" = "全ての端末からサインアウト";
"authentication_choose_password_text_field_placeholder" = "新しいパスワード";
"authentication_terms_title" = "プライバシーポリシー";
@ -1763,13 +1763,13 @@
"home_context_menu_favourite" = "お気に入り";
"all_chats_user_menu_settings" = "ユーザー設定";
"all_chats_edit_layout_show_filters" = "フィルターを表示";
"all_chats_edit_layout_show_recents" = "最近使用したものを表示";
"all_chats_edit_layout_show_recents" = "最近の履歴を表示";
"all_chats_edit_layout_alphabetical_order" = "アルファベット順で並び替え";
"all_chats_edit_layout_activity_order" = "アクティビティで並び替え";
"space_selector_create_space" = "スペースを作成";
"space_selector_empty_view_information" = "スペースは、ルームや連絡先をグループ化する方法です。以下からスペースを作成できます。";
"space_selector_empty_view_information" = "スペースは、ルームと連絡先をまとめる方法です。はじめに、スペースを作成しましょう。";
"all_chats_all_filter" = "全て";
"all_chats_edit_layout_recents" = "最近";
"all_chats_edit_layout_recents" = "履歴";
"all_chats_edit_layout_unreads" = "未読";
"all_chats_section_title" = "チャット";
@ -1799,3 +1799,5 @@
"service_terms_modal_information_title_identity_server" = "IDサーバー";
"location_sharing_invalid_power_level_message" = "位置情報(ライブ)の共有には適切な権限が必要です。";
"location_sharing_live_error" = "位置情報(ライブ)のエラー";
"all_chats_onboarding_page_title3" = "フィードバックを送信";
"all_chats_edit_layout" = "レイアウトの設定";

View file

@ -168,3 +168,6 @@
/* New file message from a specific person, not referencing a room. */
"LOCATION_FROM_USER" = "%@ zdieľal/a svoju polohu";
/* New voice broadcast from a specific person, not referencing a room. */
"VOICE_BROADCAST_FROM_USER" = "%@ začal/a hlasové vysielanie";

View file

@ -2891,3 +2891,32 @@
"poll_timeline_decryption_error" = "Z dôvodu chýb v dešifrovaní sa niektoré hlasy nemusia započítať";
"voice_message_broadcast_in_progress_message" = "Nemôžete spustiť hlasovú správu, pretože práve nahrávate živé vysielanie. Ukončite prosím živé vysielanie, aby ste mohli začať nahrávať hlasovú správu";
"voice_message_broadcast_in_progress_title" = "Nemožno spustiť hlasovú správu";
"poll_timeline_ended_text" = "Ukončil anketu";
"voice_broadcast_voip_cannot_start_description" = "Nemôžete spustiť hovor, pretože práve nahrávate živé vysielanie. Ukončite živé vysielanie, aby ste mohli začať hovor.";
"voice_broadcast_voip_cannot_start_title" = "Nie je možné začať hovor";
"poll_history_no_past_poll_text" = "V tejto miestnosti nie sú žiadne predchádzajúce ankety";
"poll_history_no_active_poll_text" = "V tejto miestnosti nie sú žiadne aktívne ankety";
"poll_history_past_segment_title" = "Predchádzajúce ankety";
"poll_history_active_segment_title" = "Aktívne ankety";
// MARK: - Polls history
"poll_history_title" = "História ankety";
"room_details_polls" = "História ankety";
"accessibility_selected" = "vybrané";
"voice_broadcast_connection_error_message" = "Bohužiaľ teraz nemôžeme spustiť nahrávanie. Skúste to prosím neskôr.";
"voice_broadcast_connection_error_title" = "Chyba pripojenia";
"voice_broadcast_playback_lock_screen_placeholder" = "Hlasové vysielanie";
"wysiwyg_composer_format_action_quote" = "Prepínanie citácie";
"wysiwyg_composer_format_action_code_block" = "Prepnutie bloku kódu";
"wysiwyg_composer_format_action_ordered_list" = "Prepínanie číslovaného zoznamu";
"wysiwyg_composer_format_action_unordered_list" = "Prepnúť zoznam s odrážkami";
"voice_broadcast_recorder_connection_error" = "Chyba pripojenia - nahrávanie pozastavené";
"poll_timeline_reply_ended_poll" = "Ukončená anketa";
// MARK: - Launch loading
"launch_loading_migrating_data" = "Migrácia údajov\n%@ %%";
"settings_labs_disable_crypto_sdk" = "Crypto SDK je povolené. Ak to chcete vypnúť, preinštalujte prosím aplikáciu";
"settings_labs_confirm_crypto_sdk" = "Túto akciu nemožno vrátiť späť";
"settings_labs_enable_crypto_sdk" = "Zapnúť nové Crypto SDK využívajúce Rust";

View file

@ -2676,3 +2676,14 @@
"notice_voice_broadcast_ended" = "%@ përfundoi një transmetim zanor.";
"notice_voice_broadcast_live" = "Transmetim i drejtëpërdrejtë";
"user_other_session_security_recommendation_title" = "Sesione të tjerë";
"poll_timeline_ended_text" = "Përfundoi pyetësori";
"poll_timeline_decryption_error" = "Për shkak gabimesh shfshehtëzimi, mund të mos jenë numëruar disa vota";
"poll_history_no_past_poll_text" = "Në këtë dhomë ska pyetësorë të dikurshëm";
"poll_history_no_active_poll_text" = "Në këtë dhomë ska pyetësorë aktivë";
"poll_history_past_segment_title" = "Pyetësorë të dikurshëm";
"poll_history_active_segment_title" = "Pyetësorë aktivë";
"voice_broadcast_playback_lock_screen_placeholder" = "Transmetim zanor";
"voice_broadcast_voip_cannot_start_description" = "Smund të niset thirrje, ngaqë aktualisht po regjistroni një transmetim të drejtpërdrejtë. Ju lutemi, përfundoni transmetimin e drejtpërdrejtë, që të mund të nisni një thirrje.";
"voice_broadcast_voip_cannot_start_title" = "Sniset dot një thirrje";
"voice_message_broadcast_in_progress_message" = "Smund të niset mesazh zanor, ngaqë aktualisht po regjistroni një transmetim të drejtpërdrejtë. Ju lutemi, përfundoni transmetimin e drejtpërdrejtë, që të mund të nisni regjistrimin e një mesazhi zanor";
"voice_message_broadcast_in_progress_title" = "Sniset dot mesazh zanor";

View file

@ -118,3 +118,6 @@
/* New file message from a specific person, not referencing a room. */
"LOCATION_FROM_USER" = "%@ delade sin plats";
/* New voice broadcast from a specific person, not referencing a room. */
"VOICE_BROADCAST_FROM_USER" = "%@ påbörjade en röstsändning";

View file

@ -2310,7 +2310,7 @@
"authentication_terms_policy_url_error" = "Kan inte hitta den valda policyn. Vänligen pröva igen senare.";
/* The placeholder will show the homeserver's domain */
"authentication_terms_message" = "Vänligen läs villkor och policyer för %@";
"authentication_terms_title" = "Serverpolicyer";
"authentication_terms_title" = "Sekretesspolicyer";
"authentication_verify_msisdn_invalid_phone_number" = "Ogiltigt telefonnummer";
"authentication_verify_msisdn_waiting_button" = "Skicka kod igen";
/* The placeholder will show the phone number that was entered. */
@ -2363,3 +2363,112 @@
// MARK: Authentication
"authentication_registration_title" = "Skapa ditt konto";
"voice_broadcast_time_left" = "%@ kvar";
"all_chats_empty_list_placeholder_title" = "Du är ikapp.";
"all_chats_empty_view_information" = "Den säkra allt-i-ett-chattappen för lag, vänner och organisationer. Skapa en chatt, eller gå med i ett existerande rum, för att komma igång.";
"all_chats_empty_space_information" = "Utrymmen är ett nytt sätt att gruppera rum och personer. Lägg till ett existerande rum, eller skapa ett nytt, med knappen nere till höger.";
"all_chats_empty_view_title" = "%@\nser lite tom ut.";
"all_chats_all_filter" = "Alla";
"all_chats_edit_layout_alphabetical_order" = "Sortera A-Ö";
"all_chats_edit_layout_activity_order" = "Sortera efter aktivitet";
"all_chats_edit_layout_show_filters" = "Visa filter";
"all_chats_edit_layout_show_recents" = "Visa nyliga";
"all_chats_edit_layout_sorting_options_title" = "Sortera meddelanden efter";
"all_chats_edit_layout_pin_spaces_title" = "Fäst dina utrymmen";
"all_chats_edit_layout_add_filters_message" = "Filtrera automatiskt dina meddelanden i valfria kategorier";
"all_chats_edit_layout_add_filters_title" = "Filtrera dina meddelanden";
"all_chats_edit_layout_add_section_message" = "Fäst sektioner till hem för enkel åtkomst";
"all_chats_edit_layout_add_section_title" = "Lägg till sektion i hem";
"all_chats_edit_layout_unreads" = "Olästa";
"all_chats_edit_layout_recents" = "Nyliga";
"all_chats_edit_layout" = "Layoutalternativ";
"all_chats_section_title" = "Chattar";
// MARK: - All Chats
"all_chats_title" = "Alla chattar";
"voice_broadcast_voip_cannot_start_description" = "Du kan inte starta ett samtal eftersom att du för närvarande spelar in en direktsändning. Vänligen avsluta din direktsändning för att starta ett samtal.";
"voice_broadcast_voip_cannot_start_title" = "Kan inte starta ett samtal";
"voice_broadcast_stop_alert_agree_button" = "Ja, avsluta";
"voice_broadcast_stop_alert_description" = "Är du säker på att du vill avsluta din direktsändning? Det här kommer att avsluta sändningen, och den fulla inspelningen kommer att bli tillgänglig i rummet.";
"voice_broadcast_stop_alert_title" = "Avsluta direktsändning?";
"voice_broadcast_buffering" = "Buffrar…";
"voice_broadcast_tile" = "Röstsändning";
"voice_broadcast_live" = "Live";
"voice_broadcast_playback_loading_error" = "Kunde inte spela den här röstsändningen.";
"voice_broadcast_already_in_progress_message" = "Du spelar redan in en röstsändning. Vänligen avsluta din nuvarande röstsändning för att starta en ny.";
"voice_broadcast_blocked_by_someone_else_message" = "Någon annan spelar redan in en röstsändning. Vänta på att deras röstsändning avslutas för att starta en ny.";
"voice_broadcast_permission_denied_message" = "Du har inte behörigheten som krävs för att starta en röstsändning i det här rummet. Kontakta en rumsadministratör för att uppgradera din behörighet.";
// MARK: - Voice Broadcast
"voice_broadcast_unauthorized_title" = "Du kan inte starta en ny röstsändning";
"voice_message_broadcast_in_progress_message" = "Du kan inte starta ett röstmeddelande eftersom att du för närvarande spelar in en direktsändning. Vänligen avsluta din direktsändning för att börja spela in ett röstmeddelande";
"voice_message_broadcast_in_progress_title" = "Kan inte starta röstmeddelande";
"spaces_subspace_creation_visibility_message" = "Det skapade utrymmet kommer att läggas till i %@.";
"spaces_subspace_creation_visibility_title" = "Vad för sorts utrymme vill du skapa?";
"spaces_explore_rooms_format" = "Utforska %@";
"spaces_create_subspace_title" = "Skapa ett underutrymme";
"spaces_add_subspace_title" = "Skapa utrymme inuti %@";
"launch_loading_processing_response" = "Hanterar data\n%@ %%";
"launch_loading_server_syncing_nth_attempt" = "Synkar med servern\n(%@ försök)";
// MARK: - Launch loading
"launch_loading_server_syncing" = "Synkar med servern";
"key_verification_alert_body" = "Granska för att försäkra att ditt konto är säkert.";
// Unverified sessions
"key_verification_alert_title" = "Du har overifierade sessioner";
"sign_out_confirmation_message" = "Är du säker på att du vill logga ut?";
// MARK: Sign out warning
"sign_out" = "Logga ut";
// User sessions management
"user_sessions_settings" = "Hantera sessioner";
"manage_session_sign_out_other_sessions" = "Logga ut ur alla andra sessioner";
"manage_session_rename" = "Döp om session";
"manage_session_name_info_link" = "Läs mer";
/* The placeholder will be replaces with manage_session_name_info_link */
"manage_session_name_info" = "Observera att sessionsnamn också är synliga för personer du pratar med. %@";
"manage_session_name_hint" = "Anpassade sessionsnamn kan hjälpa dig att känna igen dina enheter lättare.";
"settings_labs_enable_voice_broadcast" = "Röstsändning";
"settings_labs_enable_wysiwyg_composer" = "Pröva den nya riktextredigeraren";
"settings_labs_enable_new_app_layout" = "Ny applikationslayout";
"settings_labs_enable_new_client_info_feature" = "Spara klientens namn, version och URL för att lättare känna igen sessioner i sessionshanteraren";
"settings_labs_enable_new_session_manager" = "My sessionshanterare";
"room_first_message_placeholder" = "Skicka ditt första meddelande…";
"password_policy_pwd_in_dict_error" = "Det här lösenordet har hittats i en ordlista, och tillåts inte.";
"password_policy_weak_pwd_error" = "Det här lösenordet är för svagt. Det måste innehålla minst 8 tecken, och minst ett tecken av varje typ: stor bokstav, liten bokstav, siffra och specialtecken.";
// MARK: Password policy errors
"password_policy_too_short_pwd_error" = "För kort lösenord";
"authentication_qr_login_failure_retry" = "Pröva igen";
"authentication_qr_login_failure_request_timed_out" = "Länkningen slutfördes inte inom den krävda tiden.";
"authentication_qr_login_failure_request_denied" = "Förfrågan nekades på en andra enheten.";
"authentication_qr_login_failure_invalid_qr" = "QR-kod är ogiltig.";
"authentication_qr_login_failure_title" = "Länkning misslyckades";
"authentication_qr_login_loading_signed_in" = "Du är nu inloggad på din andra enhet.";
"authentication_qr_login_loading_waiting_signin" = "Väntar på att enheten loggar in.";
"authentication_qr_login_loading_connecting_device" = "Ansluter till enhet";
"authentication_qr_login_confirm_alert" = "Vänligen försäkra att du känner till källan till den här koden. Genom att länka enheter så ger du någon full åtkomst till ditt konto.";
"authentication_qr_login_confirm_subtitle" = "Bekräfta att koden nedan matchar den andra enheten:";
"authentication_qr_login_confirm_title" = "Säker kommunikation etablerad";
"authentication_qr_login_scan_subtitle" = "Placera QR-koden i rutan nedan";
"authentication_qr_login_scan_title" = "Skanna QR-kod";
"authentication_qr_login_display_step2" = "Välj 'Logga in med QR-kod'";
"authentication_qr_login_display_step1" = "Öppna Element på din andra enhet";
"authentication_qr_login_display_subtitle" = "Skanna QR-koden nedan med din enhet som är utloggad.";
"authentication_qr_login_display_title" = "Länka en enhet";
"authentication_qr_login_start_display_qr" = "Visa QR-kod på den här enheten";
"authentication_qr_login_start_need_alternative" = "Behöver du en alternativ metod?";
"authentication_qr_login_start_step4" = "Välj 'Visa QR-kod på den här enheten'";
"authentication_qr_login_start_step3" = "Välj 'Länka en enhet'";
"authentication_qr_login_start_step2" = "Gå till Inställningar -> Säkerhet & sekretess";
"authentication_qr_login_start_step1" = "Öppna Element på den andra enheten";
"authentication_qr_login_start_subtitle" = "Använd kameran på den här enheten för att skanna QR-koden som visas på den andra enheten:";
"authentication_qr_login_start_title" = "Skanna QR-kod";
"authentication_choose_password_not_verified_message" = "Kolla din inkorg";
"authentication_choose_password_not_verified_title" = "E-post inte verifierad";
"authentication_login_with_qr" = "Logga in med QR-kod";
"invite_to" = "Bjud in till %@";

View file

@ -118,3 +118,6 @@
/* New file message from a specific person, not referencing a room. */
"LOCATION_FROM_USER" = "%@ надсилає дані про своє місцеперебування";
/* New voice broadcast from a specific person, not referencing a room. */
"VOICE_BROADCAST_FROM_USER" = "%@ розпочинає голосову трансляцію";

View file

@ -2893,3 +2893,32 @@
"poll_timeline_decryption_error" = "Через помилки під час розшифрування деякі голоси можуть бути не враховані";
"voice_message_broadcast_in_progress_title" = "Неможливо розпочати запис голосового повідомлення";
"voice_message_broadcast_in_progress_message" = "Ви не можете розпочати запис голосового повідомлення, оскільки зараз триває запис трансляції наживо. Будь ласка, завершіть трансляцію, щоб розпочати запис голосового повідомлення";
"poll_timeline_ended_text" = "Опитування завершено";
"voice_broadcast_voip_cannot_start_description" = "Ви не можете розпочати виклик, оскільки зараз відбувається запис трансляції наживо. Завершіть трансляцію, щоб розпочати виклик.";
"voice_broadcast_voip_cannot_start_title" = "Неможливо розпочати виклик";
"poll_history_no_past_poll_text" = "У цій кімнаті немає минулих опитувань";
"poll_history_no_active_poll_text" = "У цій кімнаті немає активних опитувань";
"poll_history_past_segment_title" = "Минулі опитування";
"poll_history_active_segment_title" = "Активні опитування";
// MARK: - Polls history
"poll_history_title" = "Історія опитувань";
"room_details_polls" = "Історія опитувань";
"accessibility_selected" = "вибрано";
"voice_broadcast_playback_lock_screen_placeholder" = "Голосові трансляції";
"voice_broadcast_connection_error_message" = "На жаль, ми не можемо розпочати запис прямо зараз. Повторіть спробу пізніше.";
"voice_broadcast_connection_error_title" = "Помилка з'єднання";
"wysiwyg_composer_format_action_quote" = "Перемкнути цитування";
"wysiwyg_composer_format_action_code_block" = "Перемкнути блок коду";
"wysiwyg_composer_format_action_ordered_list" = "Перемкнути на нумерований список";
"wysiwyg_composer_format_action_unordered_list" = "Перемкнути на маркований список";
"voice_broadcast_recorder_connection_error" = "Помилка з'єднання - Запис призупинено";
"poll_timeline_reply_ended_poll" = "Завершене опитування";
// MARK: - Launch loading
"launch_loading_migrating_data" = "Перенесення даних\n%@ %%";
"settings_labs_disable_crypto_sdk" = "Crypto SDK увімкнено. Щоб вимкнути, перевстановіть застосунок";
"settings_labs_confirm_crypto_sdk" = "Дію не можна скасувати";
"settings_labs_enable_crypto_sdk" = "Увімкнути новий заснований на rust Crypto SDK";

View file

@ -123,3 +123,6 @@
/* New file message from a specific person, not referencing a room. */
"LOCATION_FROM_USER" = "%@ 分享了他们的位置";
/* New voice broadcast from a specific person, not referencing a room. */
"VOICE_BROADCAST_FROM_USER" = "%@开始语音广播";

View file

@ -196,7 +196,7 @@
"room_event_action_copy" = "复制";
"room_event_action_quote" = "引用";
"room_event_action_redact" = "移除";
"room_event_action_more" = "移动";
"room_event_action_more" = "更多";
"room_event_action_share" = "分享";
"room_event_action_permalink" = "复制消息的链接";
"room_event_action_view_source" = "查看源数据";
@ -2231,3 +2231,47 @@
"onboarding_congratulations_home_button" = "带我到主页";
"onboarding_use_case_message" = "我们将帮助你连接";
"invite_to" = "邀请到%@";
"threads_empty_title" = "保持讨论的有条理性";
"threads_action_my_threads" = "我的消息列";
"threads_action_all_threads" = "所有消息列";
"threads_title" = "消息列";
"thread_copy_link_to_thread" = "将链接复制到消息列";
// MARK: Threads
"room_thread_title" = "消息列";
"room_accessibility_record_voice_message_hint" = "双击并保持录音。";
"room_accessibility_record_voice_message" = "录制语音消息";
"room_accessibility_thread_more" = "更多";
"room_accessibility_threads" = "消息列";
"room_event_copy_link_info" = "链接已复制到剪贴板。";
"room_event_action_reply_in_thread" = "消息列";
"room_event_action_view_in_room" = "在房间浏览";
"room_first_message_placeholder" = "发送您的第一条消息……";
"room_participants_invite_prompt_to_msg" = "您确定要邀请%@ 到 %@吗?";
"room_participants_leave_success" = "离开房间";
"room_participants_leave_processing" = "离开";
"search_filter_placeholder" = "过滤";
"password_policy_pwd_in_dict_error" = "此密码已在字典中找到,不允许使用。";
"password_policy_weak_pwd_error" = "此密码太弱了。它必须包含至少8个字符每种类型至少有一个字符: 大写、小写、数字和特殊字符。";
// MARK: Password policy errors
"password_policy_too_short_pwd_error" = "密码过短";
"authentication_qr_login_failure_retry" = "再试一次";
"authentication_qr_login_failure_request_timed_out" = "连接没有在规定的时间内完成。";
"authentication_qr_login_failure_request_denied" = "请求在另一个设备上被拒绝。";
"authentication_qr_login_failure_invalid_qr" = "二维码无效。";
"authentication_qr_login_failure_title" = "连接失败";
"authentication_qr_login_loading_signed_in" = "您现在已经登录到另一个设备上。";
"authentication_qr_login_loading_waiting_signin" = "等待设备登录。";
"authentication_qr_login_loading_connecting_device" = "连接到设备";
"authentication_qr_login_confirm_alert" = "请确保您知道此代码的来源。通过连接设备,您将为某人提供对您帐户的完全访问权限。";
"authentication_qr_login_confirm_subtitle" = "确认下面的代码与您的其他设备匹配:";
"authentication_qr_login_confirm_title" = "建立安全连接";
"authentication_qr_login_scan_subtitle" = "将二维码放置在下面的方框中";
"authentication_qr_login_scan_title" = "扫描二维码";
"authentication_qr_login_display_step2" = "选择“以二维码登入”";
"authentication_qr_login_display_step1" = "在您的其它设备中打开Element";
"onboarding_splash_page_4_title_no_pun" = "为您的团队发送消息。";
"user_session_learn_more" = "了解更多";
"manage_session_name_info_link" = "了解更多";
"threads_beta_information_link" = "了解更多";

View file

@ -24,7 +24,6 @@ extension MXKTableViewCellWithTextView: Themable {
func update(theme: Theme) {
mxkTextView.backgroundColor = .clear
mxkTextView.textColor = theme.textPrimaryColor
mxkTextView.tintColor = theme.tintColor
backgroundColor = theme.backgroundColor
contentView.backgroundColor = .clear
}

View file

@ -110,6 +110,7 @@ internal class Asset: NSObject {
internal static let strikethrough = ImageAsset(name: "Strikethrough")
internal static let underlined = ImageAsset(name: "Underlined")
internal static let bulletList = ImageAsset(name: "bullet_list")
internal static let codeBlock = ImageAsset(name: "code_block")
internal static let indentDecrease = ImageAsset(name: "indent_decrease")
internal static let maximiseComposer = ImageAsset(name: "maximise_composer")
internal static let minimiseComposer = ImageAsset(name: "minimise_composer")
@ -285,6 +286,7 @@ internal class Asset: NSObject {
internal static let modIcon = ImageAsset(name: "mod_icon")
internal static let moreReactions = ImageAsset(name: "more_reactions")
internal static let notifications = ImageAsset(name: "notifications")
internal static let pollHistory = ImageAsset(name: "pollHistory")
internal static let reactionsMoreAction = ImageAsset(name: "reactions_more_action")
internal static let roomAccessInfoHeaderIcon = ImageAsset(name: "room_access_info_header_icon")
internal static let scrollup = ImageAsset(name: "scrollup")

View file

@ -27,6 +27,10 @@ public class VectorL10n: NSObject {
public static var accessibilityCheckboxLabel: String {
return VectorL10n.tr("Vector", "accessibility_checkbox_label")
}
/// selected
public static var accessibilitySelected: String {
return VectorL10n.tr("Vector", "accessibility_selected")
}
/// Unable to verify email address. Please check your email and click on the link it contains. Once this is done, click continue
public static var accountEmailValidationError: String {
return VectorL10n.tr("Vector", "account_email_validation_error")
@ -3179,6 +3183,10 @@ public class VectorL10n: NSObject {
public static var later: String {
return VectorL10n.tr("Vector", "later")
}
/// Migrating data\n%@ %%
public static func launchLoadingMigratingData(_ p1: String) -> String {
return VectorL10n.tr("Vector", "launch_loading_migrating_data", p1)
}
/// Processing data\n%@ %%
public static func launchLoadingProcessingResponse(_ p1: String) -> String {
return VectorL10n.tr("Vector", "launch_loading_processing_response", p1)
@ -4839,6 +4847,26 @@ public class VectorL10n: NSObject {
public static var pollEditFormUpdateFailureTitle: String {
return VectorL10n.tr("Vector", "poll_edit_form_update_failure_title")
}
/// Active polls
public static var pollHistoryActiveSegmentTitle: String {
return VectorL10n.tr("Vector", "poll_history_active_segment_title")
}
/// There are no active polls in this room
public static var pollHistoryNoActivePollText: String {
return VectorL10n.tr("Vector", "poll_history_no_active_poll_text")
}
/// There are no past polls in this room
public static var pollHistoryNoPastPollText: String {
return VectorL10n.tr("Vector", "poll_history_no_past_poll_text")
}
/// Past polls
public static var pollHistoryPastSegmentTitle: String {
return VectorL10n.tr("Vector", "poll_history_past_segment_title")
}
/// Poll history
public static var pollHistoryTitle: String {
return VectorL10n.tr("Vector", "poll_history_title")
}
/// Due to decryption errors, some votes may not be counted
public static var pollTimelineDecryptionError: String {
return VectorL10n.tr("Vector", "poll_timeline_decryption_error")
@ -4859,6 +4887,10 @@ public class VectorL10n: NSObject {
public static var pollTimelineOneVote: String {
return VectorL10n.tr("Vector", "poll_timeline_one_vote")
}
/// Ended poll
public static var pollTimelineReplyEndedPoll: String {
return VectorL10n.tr("Vector", "poll_timeline_reply_ended_poll")
}
/// Final results based on %lu votes
public static func pollTimelineTotalFinalResults(_ p1: Int) -> String {
return VectorL10n.tr("Vector", "poll_timeline_total_final_results", p1)
@ -5503,6 +5535,10 @@ public class VectorL10n: NSObject {
public static var roomDetailsPhotoForDm: String {
return VectorL10n.tr("Vector", "room_details_photo_for_dm")
}
/// Poll history
public static var roomDetailsPolls: String {
return VectorL10n.tr("Vector", "room_details_polls")
}
/// Suggest to space members
public static var roomDetailsPromoteRoomSuggestTitle: String {
return VectorL10n.tr("Vector", "room_details_promote_room_suggest_title")
@ -7547,10 +7583,18 @@ public class VectorL10n: NSObject {
public static var settingsLabs: String {
return VectorL10n.tr("Vector", "settings_labs")
}
/// This action cannot be undone
public static var settingsLabsConfirmCryptoSdk: String {
return VectorL10n.tr("Vector", "settings_labs_confirm_crypto_sdk")
}
/// Create conference calls with jitsi
public static var settingsLabsCreateConferenceWithJitsi: String {
return VectorL10n.tr("Vector", "settings_labs_create_conference_with_jitsi")
}
/// Crypto SDK is enabled. To disable please reinstall the app
public static var settingsLabsDisableCryptoSdk: String {
return VectorL10n.tr("Vector", "settings_labs_disable_crypto_sdk")
}
/// End-to-End Encryption
public static var settingsLabsE2eEncryption: String {
return VectorL10n.tr("Vector", "settings_labs_e2e_encryption")
@ -7563,6 +7607,10 @@ public class VectorL10n: NSObject {
public static var settingsLabsEnableAutoReportDecryptionErrors: String {
return VectorL10n.tr("Vector", "settings_labs_enable_auto_report_decryption_errors")
}
/// Enable new rust-based Crypto SDK
public static var settingsLabsEnableCryptoSdk: String {
return VectorL10n.tr("Vector", "settings_labs_enable_crypto_sdk")
}
/// Live location sharing - share current location (active development, and temporarily, locations persist in room history)
public static var settingsLabsEnableLiveLocationSharing: String {
return VectorL10n.tr("Vector", "settings_labs_enable_live_location_sharing")
@ -9179,6 +9227,14 @@ public class VectorL10n: NSObject {
public static var voiceBroadcastBuffering: String {
return VectorL10n.tr("Vector", "voice_broadcast_buffering")
}
/// Unfortunately were unable to start a recording right now. Please try again later.
public static var voiceBroadcastConnectionErrorMessage: String {
return VectorL10n.tr("Vector", "voice_broadcast_connection_error_message")
}
/// Connection error
public static var voiceBroadcastConnectionErrorTitle: String {
return VectorL10n.tr("Vector", "voice_broadcast_connection_error_title")
}
/// Live
public static var voiceBroadcastLive: String {
return VectorL10n.tr("Vector", "voice_broadcast_live")
@ -9191,6 +9247,14 @@ public class VectorL10n: NSObject {
public static var voiceBroadcastPlaybackLoadingError: String {
return VectorL10n.tr("Vector", "voice_broadcast_playback_loading_error")
}
/// Voice broadcast
public static var voiceBroadcastPlaybackLockScreenPlaceholder: String {
return VectorL10n.tr("Vector", "voice_broadcast_playback_lock_screen_placeholder")
}
/// Connection error - Recording paused
public static var voiceBroadcastRecorderConnectionError: String {
return VectorL10n.tr("Vector", "voice_broadcast_recorder_connection_error")
}
/// Yes, stop
public static var voiceBroadcastStopAlertAgreeButton: String {
return VectorL10n.tr("Vector", "voice_broadcast_stop_alert_agree_button")
@ -9215,6 +9279,14 @@ public class VectorL10n: NSObject {
public static var voiceBroadcastUnauthorizedTitle: String {
return VectorL10n.tr("Vector", "voice_broadcast_unauthorized_title")
}
/// You cant start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call.
public static var voiceBroadcastVoipCannotStartDescription: String {
return VectorL10n.tr("Vector", "voice_broadcast_voip_cannot_start_description")
}
/// Cant start a call
public static var voiceBroadcastVoipCannotStartTitle: String {
return VectorL10n.tr("Vector", "voice_broadcast_voip_cannot_start_title")
}
/// You can't start a voice message as you are currently recording a live broadcast. Please end your live broadcast in order to start recording a voice message
public static var voiceMessageBroadcastInProgressMessage: String {
return VectorL10n.tr("Vector", "voice_message_broadcast_in_progress_message")
@ -9339,6 +9411,10 @@ public class VectorL10n: NSObject {
public static var wysiwygComposerFormatActionBold: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_bold")
}
/// Toggle code block
public static var wysiwygComposerFormatActionCodeBlock: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_code_block")
}
/// Apply inline code format
public static var wysiwygComposerFormatActionInlineCode: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_inline_code")
@ -9351,6 +9427,14 @@ public class VectorL10n: NSObject {
public static var wysiwygComposerFormatActionLink: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_link")
}
/// Toggle numbered list
public static var wysiwygComposerFormatActionOrderedList: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_ordered_list")
}
/// Toggle quote
public static var wysiwygComposerFormatActionQuote: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_quote")
}
/// Apply underline format
public static var wysiwygComposerFormatActionStrikethrough: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_strikethrough")
@ -9359,6 +9443,10 @@ public class VectorL10n: NSObject {
public static var wysiwygComposerFormatActionUnderline: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_underline")
}
/// Toggle bulleted list
public static var wysiwygComposerFormatActionUnorderedList: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_unordered_list")
}
/// Create a link
public static var wysiwygComposerLinkActionCreateTitle: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_link_action_create_title")

View file

@ -191,6 +191,12 @@ final class RiotSettings: NSObject {
/// Flag indicating if the voice broadcast feature is enabled
@UserDefault(key: "enableVoiceBroadcast", defaultValue: false, storage: defaults)
var enableVoiceBroadcast
#if DEBUG
/// Flag indicating if we are using rust-based `MatrixCryptoSDK` instead of `MatrixSDK`'s internal crypto module
@UserDefault(key: "enableCryptoSDK", defaultValue: false, storage: defaults)
var enableCryptoSDK
#endif
// MARK: Calls

View file

@ -168,14 +168,8 @@ class DarkTheme: NSObject, Theme {
searchBar.backgroundImage = UIImage() // Remove top and bottom shadow
searchBar.tintColor = self.tintColor
if #available(iOS 13.0, *) {
searchBar.searchTextField.backgroundColor = self.searchBackgroundColor
searchBar.searchTextField.textColor = self.searchPlaceholderColor
} else {
if let searchBarTextField = searchBar.vc_searchTextField {
searchBarTextField.textColor = self.searchPlaceholderColor
}
}
searchBar.searchTextField.backgroundColor = self.searchBackgroundColor
searchBar.searchTextField.textColor = self.searchPlaceholderColor
}
func applyStyle(onTextField texField: UITextField) {

View file

@ -177,14 +177,8 @@ class DefaultTheme: NSObject, Theme {
return
}
if #available(iOS 13.0, *) {
searchBar.searchTextField.backgroundColor = self.searchBackgroundColor
searchBar.searchTextField.textColor = self.searchPlaceholderColor
} else {
if let searchBarTextField = searchBar.vc_searchTextField {
searchBarTextField.textColor = self.searchPlaceholderColor
}
}
searchBar.searchTextField.backgroundColor = self.searchBackgroundColor
searchBar.searchTextField.textColor = self.searchPlaceholderColor
}
func applyStyle(onTextField texField: UITextField) {

View file

@ -101,7 +101,7 @@ final class AppCoordinator: NSObject, AppCoordinatorType {
if AppDelegate.theDelegate().isOffline {
self.splitViewCoordinator?.showAppStateIndicator(with: VectorL10n.networkOfflineTitle, icon: UIImage(systemName: "wifi.slash"))
} else {
self.splitViewCoordinator?.hideAppStateIndicator()
self.splitViewCoordinator?.hideAppStateIndicator()
}
}

View file

@ -618,6 +618,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Pause Voice Broadcast recording if needed
[VoiceBroadcastRecorderProvider.shared pauseRecording];
// Pause Voice Broadcast playing if needed
[VoiceBroadcastPlaybackProvider.shared pausePlayingInProgressVoiceBroadcast];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
@ -2395,14 +2398,14 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
MXLogDebug(@"[AppDelegate] showLaunchAnimation");
LaunchLoadingView *launchLoadingView;
if (MXSDKOptions.sharedInstance.enableSyncProgress)
if (MXSDKOptions.sharedInstance.enableStartupProgress)
{
MXSession *mainSession = self.mxSessions.firstObject;
launchLoadingView = [LaunchLoadingView instantiateWithSyncProgress:mainSession.syncProgress];
launchLoadingView = [LaunchLoadingView instantiateWithStartupProgress:mainSession.startupProgress];
}
else
{
launchLoadingView = [LaunchLoadingView instantiateWithSyncProgress:nil];
launchLoadingView = [LaunchLoadingView instantiateWithStartupProgress:nil];
}
launchLoadingView.frame = window.bounds;

View file

@ -613,8 +613,8 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
/// Replace the contents of the navigation router with a loading animation.
private func showLoadingAnimation() {
let syncProgress: MXSessionSyncProgress? = MXSDKOptions.sharedInstance().enableSyncProgress ? session?.syncProgress : nil
let loadingViewController = LaunchLoadingViewController(syncProgress: syncProgress)
let startupProgress: MXSessionStartupProgress? = MXSDKOptions.sharedInstance().enableStartupProgress ? session?.startupProgress : nil
let loadingViewController = LaunchLoadingViewController(startupProgress: startupProgress)
loadingViewController.modalPresentationStyle = .fullScreen
// Replace the navigation stack with the loading animation

View file

@ -106,8 +106,8 @@ final class LegacyAuthenticationCoordinator: NSObject, AuthenticationCoordinator
// MARK: - Private
private func showLoadingAnimation() {
let syncProgress: MXSessionSyncProgress? = MXSDKOptions.sharedInstance().enableSyncProgress ? session?.syncProgress : nil
let loadingViewController = LaunchLoadingViewController(syncProgress: syncProgress)
let startupProgress: MXSessionStartupProgress? = MXSDKOptions.sharedInstance().enableStartupProgress ? session?.startupProgress : nil
let loadingViewController = LaunchLoadingViewController(startupProgress: startupProgress)
loadingViewController.modalPresentationStyle = .fullScreen
// Replace the navigation stack with the loading animation

View file

@ -28,9 +28,7 @@
[super customizeTableViewCellRendering];
self.roomNameLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
[self updateUserNameColor];
}

View file

@ -30,8 +30,6 @@
[self updateUserNameColor];
self.roomNameLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)render:(MXKCellData *)cellData

View file

@ -41,9 +41,9 @@ final class LaunchLoadingView: UIView, NibLoadable, Themable {
// MARK: - Setup
static func instantiate(syncProgress: MXSessionSyncProgress?) -> LaunchLoadingView {
static func instantiate(startupProgress: MXSessionStartupProgress?) -> LaunchLoadingView {
let view = LaunchLoadingView.loadFromNib()
syncProgress?.delegate = view
startupProgress?.delegate = view
return view
}
@ -54,7 +54,7 @@ final class LaunchLoadingView: UIView, NibLoadable, Themable {
animationTimeline.play()
self.animationTimeline = animationTimeline
self.statusLabel.isHidden = !MXSDKOptions.sharedInstance().enableSyncProgress
self.statusLabel.isHidden = !MXSDKOptions.sharedInstance().enableStartupProgress
}
// MARK: - Public
@ -65,21 +65,35 @@ final class LaunchLoadingView: UIView, NibLoadable, Themable {
}
}
extension LaunchLoadingView: MXSessionSyncProgressDelegate {
func sessionDidUpdateSyncState(_ state: MXSessionSyncState) {
guard MXSDKOptions.sharedInstance().enableSyncProgress else {
extension LaunchLoadingView: MXSessionStartupProgressDelegate {
func sessionDidUpdateStartupStage(_ stage: MXSessionStartupStage) {
guard MXSDKOptions.sharedInstance().enableStartupProgress else {
return
}
updateStatusText(for: stage)
}
private func updateStatusText(for stage: MXSessionStartupStage) {
guard Thread.isMainThread else {
DispatchQueue.main.async { [weak self] in
self?.updateStatusText(for: stage)
}
return
}
// Sync may be doing a lot of heavy work on the main thread and the status text
// does not update reliably enough without explicitly refreshing
CATransaction.begin()
statusLabel.text = statusText(for: state)
statusLabel.text = statusText(for: stage)
CATransaction.commit()
}
private func statusText(for state: MXSessionSyncState) -> String {
switch state {
private func statusText(for stage: MXSessionStartupStage) -> String {
switch stage {
case .migratingData(let progress):
let percent = Int(floor(progress * 100))
return VectorL10n.launchLoadingMigratingData("\(percent)")
case .serverSyncing(let attempts):
if attempts > 1, let nth = numberFormatter.string(from: NSNumber(value: attempts)) {
return VectorL10n.launchLoadingServerSyncingNthAttempt(nth)

View file

@ -21,10 +21,10 @@ class LaunchLoadingViewController: UIViewController, Reusable {
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
init(syncProgress: MXSessionSyncProgress?) {
init(startupProgress: MXSessionStartupProgress?) {
super.init(nibName: "LaunchLoadingViewController", bundle: nil)
let launchLoadingView = LaunchLoadingView.instantiate(syncProgress: syncProgress)
let launchLoadingView = LaunchLoadingView.instantiate(startupProgress: startupProgress)
launchLoadingView.update(theme: ThemeService.shared().theme)
view.vc_addSubViewMatchingParent(launchLoadingView)

View file

@ -43,6 +43,7 @@ class LocationManager: NSObject {
private let locationManager: CLLocationManager
private var authorizationHandler: LocationAuthorizationHandler?
private var authorizationReturnedSinceRequestingAlways = false
// MARK: Public
@ -144,14 +145,16 @@ class LocationManager: NSObject {
// See https://developer.apple.com/documentation/corelocation/cllocationmanager/1620551-requestalwaysauthorization?changes=_6_6
private func tryToRequestAlwaysAuthorization(handler: @escaping LocationAuthorizationHandler) {
self.authorizationHandler = handler
self.authorizationReturnedSinceRequestingAlways = false
self.locationManager.delegate = self
self.locationManager.requestAlwaysAuthorization()
Timer.scheduledTimer(withTimeInterval: Constants.waitForAuthorizationStatusDelay, repeats: false) { [weak self] _ in
guard let self = self else {
guard let self = self, !self.authorizationReturnedSinceRequestingAlways else {
return
}
self.authorizationRequestDidComplete(with: self.locationManager.authorizationStatus)
self.authorizationAlwaysRequestDidComplete(with: self.locationManager.authorizationStatus)
}
}
@ -174,8 +177,7 @@ class LocationManager: NSObject {
return status
}
private func authorizationRequestDidComplete(with status: CLAuthorizationStatus) {
private func authorizationAlwaysRequestDidComplete(with status: CLAuthorizationStatus) {
guard let authorizationHandler = self.authorizationHandler else {
return
}
@ -191,7 +193,14 @@ extension LocationManager: CLLocationManagerDelegate {
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
let status = self.locationManager.authorizationStatus
self.authorizationRequestDidComplete(with: status)
authorizationReturnedSinceRequestingAlways = true
if status == .authorizedAlways {
// LocationManager can call locationManagerDidChangeAuthorization multiple times.
// For example it calls it at initialisation of LocationManager manager and we are also seeing it called
// after requestAlwaysAuthorization but before the user has actually selected on option on the prompt.
// Therefore we should only call `authorizationAlwaysRequestDidComplete` once on the success of authorizedAlways being granted.
self.authorizationAlwaysRequestDidComplete(with: status)
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

View file

@ -572,6 +572,7 @@ extern NSString *const kMXKRoomDataSourceTimelineErrorErrorKey;
Once complete, this local echo will be replaced by the event saved by the homeserver.
@param audioFileLocalURL the local filesystem path of the audio file to send.
@param additionalContentParams (optional) the additional parameters to the content.
@param mimeType (optional) the mime type of the file. Defaults to `audio/ogg`
@param duration the length of the voice message in milliseconds
@param samples an array of floating point values normalized to [0, 1], boxed within NSNumbers
@ -580,6 +581,7 @@ extern NSString *const kMXKRoomDataSourceTimelineErrorErrorKey;
@param failure A block object called when the operation fails.
*/
- (void)sendVoiceMessage:(NSURL *)audioFileLocalURL
additionalContentParams:(NSDictionary*)additionalContentParams
mimeType:mimeType
duration:(NSUInteger)duration
samples:(NSArray<NSNumber *> *)samples

View file

@ -1998,6 +1998,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
}
- (void)sendVoiceMessage:(NSURL *)audioFileLocalURL
additionalContentParams:(NSDictionary *)additionalContentParams
mimeType:mimeType
duration:(NSUInteger)duration
samples:(NSArray<NSNumber *> *)samples
@ -2006,7 +2007,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
{
__block MXEvent *localEchoEvent = nil;
[_room sendVoiceMessage:audioFileLocalURL mimeType:mimeType duration:duration samples:samples threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure keepActualFilename:YES];
[_room sendVoiceMessage:audioFileLocalURL additionalContentParams:additionalContentParams mimeType:mimeType duration:duration samples:samples threadId:self.threadId localEcho:&localEchoEvent success:success failure:failure keepActualFilename:YES];
if (localEchoEvent)
{
@ -2185,10 +2186,20 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) {
[self removeEventWithEventId:eventId];
if (event.isVoiceMessage) {
// Voice message
NSNumber *duration = event.content[kMXMessageContentKeyExtensibleAudioMSC1767][kMXMessageContentKeyExtensibleAudioDuration];
NSArray<NSNumber *> *samples = event.content[kMXMessageContentKeyExtensibleAudioMSC1767][kMXMessageContentKeyExtensibleAudioWaveform];
[self sendVoiceMessage:localFileURL mimeType:mimetype duration:duration.doubleValue samples:samples success:success failure:failure];
// Additional content params in case it is a voicebroacast chunk
NSDictionary* additionalContentParams = nil;
if (event.content[kMXEventRelationRelatesToKey] != nil && event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType] != nil) {
additionalContentParams = @{
kMXEventRelationRelatesToKey: event.content[kMXEventRelationRelatesToKey],
VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType: event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType]
};
}
[self sendVoiceMessage:localFileURL additionalContentParams:additionalContentParams mimeType:mimetype duration:duration.doubleValue samples:samples success:success failure:failure];
} else {
[self sendAudioFile:localFileURL mimeType:mimetype success:success failure:failure];
}

View file

@ -18,34 +18,38 @@ import Foundation
class MXKSendReplyEventStringLocalizer: NSObject, MXSendReplyEventStringLocalizerProtocol {
func senderSentAnImage() -> String {
return VectorL10n.messageReplyToSenderSentAnImage
VectorL10n.messageReplyToSenderSentAnImage
}
func senderSentAVideo() -> String {
return VectorL10n.messageReplyToSenderSentAVideo
VectorL10n.messageReplyToSenderSentAVideo
}
func senderSentAnAudioFile() -> String {
return VectorL10n.messageReplyToSenderSentAnAudioFile
VectorL10n.messageReplyToSenderSentAnAudioFile
}
func senderSentAVoiceMessage() -> String {
return VectorL10n.messageReplyToSenderSentAVoiceMessage
VectorL10n.messageReplyToSenderSentAVoiceMessage
}
func senderSentAFile() -> String {
return VectorL10n.messageReplyToSenderSentAFile
VectorL10n.messageReplyToSenderSentAFile
}
func senderSentTheirLocation() -> String {
return VectorL10n.messageReplyToSenderSentTheirLocation
VectorL10n.messageReplyToSenderSentTheirLocation
}
func senderSentTheirLiveLocation() -> String {
return VectorL10n.messageReplyToSenderSentTheirLiveLocation
VectorL10n.messageReplyToSenderSentTheirLiveLocation
}
func messageToReplyToPrefix() -> String {
return VectorL10n.messageReplyToMessageToReplyToPrefix
VectorL10n.messageReplyToMessageToReplyToPrefix
}
func replyToEndedPoll() -> String {
VectorL10n.pollTimelineReplyEndedPoll
}
}

View file

@ -51,6 +51,7 @@ class HTMLFormatter: NSObject {
DTDefaultFontName: font.fontName,
DTDefaultFontSize: font.pointSize,
DTDefaultLinkDecoration: false,
DTDefaultLinkColor: ThemeService.shared().theme.colors.links,
DTWillFlushBlockCallBack: sanitizeCallback
]
options.merge(extraOptions) { (_, new) in new }

View file

@ -366,6 +366,12 @@ typedef enum : NSUInteger {
*/
@property (nonatomic) UIColor *sendingTextColor;
/**
Color used to display links and hyperlinks contentt.
Default is [UIColor linkColor].
*/
@property (nonatomic) UIColor *linksColor;
/**
Color used to display error text.
Default is red.

View file

@ -31,6 +31,7 @@
#import "GeneratedInterface-Swift.h"
static NSString *const kHTMLATagRegexPattern = @"<a href=(?:'|\")(.*?)(?:'|\")>([^<]*)</a>";
static NSString *const kRepliedTextPattern = @"<mx-reply>.*<blockquote>.*<br>(.*)</blockquote></mx-reply>";
@interface MXKEventFormatter ()
{
@ -89,6 +90,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=(?:'|\")(.*?)(?:'|\")>(
_encryptingTextColor = [UIColor lightGrayColor];
_sendingTextColor = [UIColor lightGrayColor];
_errorTextColor = [UIColor redColor];
_linksColor = [UIColor linkColor];
_htmlBlockquoteBorderColor = [MXKTools colorWithRGBValue:0xDDDDDD];
_defaultTextFont = [UIFont systemFontOfSize:14];
@ -1749,6 +1751,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=(?:'|\")(.*?)(?:'|\")>(
if (url.URL)
{
[str addAttribute:NSLinkAttributeName value:url.URL range:matchRange];
[str addAttribute:NSForegroundColorAttributeName value:self.linksColor range:matchRange];
}
}
}
@ -1806,6 +1809,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=(?:'|\")(.*?)(?:'|\")>(
}
html = [self renderReplyTo:html withRoomState:roomState];
html = [self renderPollEndedReplyTo:html repliedEvent:repliedEvent];
}
// Apply the css style that corresponds to the event state
@ -1878,6 +1882,12 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=(?:'|\")(.*?)(?:'|\")>(
{
MXJSONModelSetString(repliedEventContent, repliedEvent.content[kMXMessageBodyKey]);
}
if (!repliedEventContent && repliedEvent.eventType == MXEventTypePollStart) {
repliedEventContent = [MXEventContentPollStart modelFromJSON:repliedEvent.content].question;
}
if (!repliedEventContent && repliedEvent.eventType == MXEventTypePollEnd) {
repliedEventContent = MXSendReplyEventDefaultStringLocalizer.new.replyToEndedPoll;
}
}
// No message content in a non-redacted event. Formatter should use fallback.
@ -2012,6 +2022,44 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=(?:'|\")(.*?)(?:'|\")>(
return html;
}
- (NSString*)renderPollEndedReplyTo:(NSString*)htmlString repliedEvent:(MXEvent*)repliedEvent {
static NSRegularExpression *endedPollRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
endedPollRegex = [NSRegularExpression regularExpressionWithPattern:kRepliedTextPattern options:NSRegularExpressionCaseInsensitive error:nil];
});
NSString* finalString = htmlString;
if (repliedEvent.eventType != MXEventTypePollEnd) {
return finalString;
}
NSTextCheckingResult* match = [endedPollRegex firstMatchInString:htmlString options:0 range:NSMakeRange(0, htmlString.length)];
if (!(match && match.numberOfRanges > 1)) {
// no useful match found
return finalString;
}
NSRange groupRange = [match rangeAtIndex:1];
NSString* replacementText;
if (repliedEvent) {
MXEvent* pollStartedEvent = [mxSession.store eventWithEventId:repliedEvent.relatesTo.eventId inRoom:repliedEvent.roomId];
replacementText = [MXEventContentPollStart modelFromJSON:pollStartedEvent.content].question;
}
if (replacementText == nil) {
replacementText = VectorL10n.pollTimelineReplyEndedPoll;
}
finalString = [htmlString stringByReplacingCharactersInRange:groupRange withString:replacementText];
return finalString;
}
- (void)postFormatMutableAttributedString:(NSMutableAttributedString*)mutableAttributedString
forEvent:(MXEvent*)event
andRepliedEvent:(MXEvent*)repliedEvent

View file

@ -46,6 +46,7 @@ static NSRegularExpression *eventIdRegex;
static NSRegularExpression *httpLinksRegex;
// A regex to find all HTML tags
static NSRegularExpression *htmlTagsRegex;
static NSDataDetector *linkDetector;
@implementation MXKTools
@ -60,7 +61,8 @@ static NSRegularExpression *htmlTagsRegex;
eventIdRegex = [NSRegularExpression regularExpressionWithPattern:kMXToolsRegexStringForMatrixEventIdentifier options:NSRegularExpressionCaseInsensitive error:nil];
httpLinksRegex = [NSRegularExpression regularExpressionWithPattern:@"(?i)\\b(https?://\\S*)\\b" options:NSRegularExpressionCaseInsensitive error:nil];
htmlTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\w+)[^>]*>" options:NSRegularExpressionCaseInsensitive error:nil];
htmlTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\w+)[^>]*>" options:NSRegularExpressionCaseInsensitive error:nil];
linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
});
}
@ -1037,6 +1039,23 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
{
[MXKTools createLinksInMutableAttributedString:mutableAttributedString matchingRegex:eventIdRegex];
}
// This allows to check for normal url based links (like https://element.io)
// And set back the default link color
NSArray *matches = [linkDetector matchesInString: [mutableAttributedString string] options:0 range: NSMakeRange(0,mutableAttributedString.length)];
if (matches)
{
for (NSTextCheckingResult *match in matches)
{
NSRange matchRange = [match range];
NSURL *matchUrl = [match URL];
NSURLComponents *url = [[NSURLComponents new] initWithURL:matchUrl resolvingAgainstBaseURL:NO];
if (url.URL)
{
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.colors.links range:matchRange];
}
}
}
}
+ (void)createLinksInMutableAttributedString:(NSMutableAttributedString*)mutableAttributedString matchingRegex:(NSRegularExpression*)regex
@ -1083,6 +1102,8 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
// If the match is fully in the link, skip it
if (NSIntersectionRange(match.range, linkMatch.range).length == match.range.length)
{
// but before we set the right color
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.colors.links range:linkMatch.range];
hasAlreadyLink = YES;
break;
}
@ -1097,6 +1118,7 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
NSString *link = [mutableAttributedString.string substringWithRange:match.range];
link = [link stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
[mutableAttributedString addAttribute:NSLinkAttributeName value:link range:match.range];
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.colors.links range:match.range];
}
}];
}

View file

@ -67,6 +67,7 @@
- (void)setAttributedText:(NSAttributedString *)attributedText
{
self.linkTextAttributes = @{};
if (@available(iOS 15.0, *)) {
[self flushPills];
}

View file

@ -174,8 +174,11 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
case .notifications:
let coordinator = createRoomNotificationSettingsCoordinator()
coordinator.start()
self.add(childCoordinator: coordinator)
self.navigationRouter.push(coordinator, animated: true, popCompletion: nil)
push(coordinator: coordinator)
case .pollHistory:
let coordinator: PollHistoryCoordinator = .init(parameters: .init(mode: .active))
coordinator.start()
push(coordinator: coordinator)
default:
guard let tabIndex = target.tabIndex else {
fatalError("No settings tab index for this target.")
@ -189,6 +192,13 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
navigationRouter.push(segmentedViewController, animated: animated, popCompletion: nil)
}
}
private func push(coordinator: Coordinator & Presentable, animated: Bool = true) {
self.add(childCoordinator: coordinator)
navigationRouter.push(coordinator, animated: animated) {
self.remove(childCoordinator: coordinator)
}
}
}
// MARK: - RoomInfoListCoordinatorDelegate

View file

@ -25,6 +25,7 @@ enum RoomInfoListTarget: Equatable {
case integrations
case search
case notifications
case pollHistory
var tabIndex: UInt? {
switch self {

View file

@ -174,6 +174,11 @@ final class RoomInfoListViewController: UIViewController {
let rowMembers = Row(type: .default, icon: Asset.Images.userIcon.image, text: text, accessoryType: .disclosureIndicator) {
self.viewModel.process(viewAction: .navigate(target: .members))
}
let rowPollHistory = Row(type: .default, icon: Asset.Images.pollHistory.image, text: VectorL10n.roomDetailsPolls, accessoryType: .disclosureIndicator) {
self.viewModel.process(viewAction: .navigate(target: .pollHistory))
}
let rowUploads = Row(type: .default, icon: Asset.Images.scrollup.image, text: VectorL10n.roomDetailsFiles, accessoryType: .disclosureIndicator) {
self.viewModel.process(viewAction: .navigate(target: .uploads))
}
@ -193,6 +198,11 @@ final class RoomInfoListViewController: UIViewController {
rows.append(rowIntegrations)
}
rows.append(rowMembers)
if BuildSettings.pollsHistoryEnabled {
rows.append(rowPollHistory)
}
rows.append(rowUploads)
if !viewData.isEncrypted {
rows.append(rowSearch)

View file

@ -2454,13 +2454,19 @@ static CGSize kThreadListBarButtonItemImageSize;
// Prevents listening a VB when recording a new one
[VoiceBroadcastPlaybackProvider.shared pausePlaying];
// Check connectivity
if ([AppDelegate theDelegate].isOffline)
{
[self showAlertWithTitle:[VectorL10n voiceBroadcastConnectionErrorTitle] message:[VectorL10n voiceBroadcastConnectionErrorMessage]];
return;
}
// Request the voice broadcast service to start recording - No service is returned if someone else is already broadcasting in the room
[session getOrCreateVoiceBroadcastServiceFor:self.roomDataSource.room completion:^(VoiceBroadcastService *voiceBroadcastService) {
if (voiceBroadcastService) {
[voiceBroadcastService startVoiceBroadcastWithSuccess:^(NSString * _Nullable success) {
} failure:^(NSError * _Nonnull error) {
[voiceBroadcastService startVoiceBroadcastWithSuccess:^(NSString * _Nullable success) { } failure:^(NSError * _Nonnull error) {
[self showAlertWithTitle:[VectorL10n voiceBroadcastConnectionErrorTitle] message:[VectorL10n voiceBroadcastConnectionErrorMessage]];
[session tearDownVoiceBroadcastService];
}];
}
else
@ -4296,8 +4302,14 @@ static CGSize kThreadListBarButtonItemImageSize;
[self startActivityIndicator];
NSArray<NSString *>* relationTypes = nil;
// If it's a voice broadcast, delete the selected event and all related events.
if (selectedEvent.eventType == MXEventTypeCustom && [selectedEvent.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]) {
relationTypes = @[MXEventRelationTypeReference];
}
MXWeakify(self);
[self.roomDataSource.room redactEvent:selectedEvent.eventId reason:nil success:^{
[self.roomDataSource.room redactEvent:selectedEvent.eventId withRelations:relationTypes reason:nil success:^{
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
} failure:^(NSError *error) {
@ -5187,7 +5199,14 @@ static CGSize kThreadListBarButtonItemImageSize;
- (IBAction)onVoiceCallPressed:(id)sender
{
if (self.isCallActive)
// Manage case of a Voice broadcast listening -> Pause Voice broadcast playback
[VoiceBroadcastPlaybackProvider.shared pausePlaying];
if (VoiceBroadcastRecorderProvider.shared.isVoiceBroadcastRecording) {
[[AppDelegate theDelegate] showAlertWithTitle:VectorL10n.voiceBroadcastVoipCannotStartTitle
message:VectorL10n.voiceBroadcastVoipCannotStartDescription];
}
else if (self.isCallActive)
{
[self hangupCall];
}
@ -5199,7 +5218,15 @@ static CGSize kThreadListBarButtonItemImageSize;
- (IBAction)onVideoCallPressed:(id)sender
{
[self placeCallWithVideo:YES];
// Manage case of a Voice broadcast listening -> Pause Voice broadcast playback
[VoiceBroadcastPlaybackProvider.shared pausePlaying];
if (VoiceBroadcastRecorderProvider.shared.isVoiceBroadcastRecording) {
[[AppDelegate theDelegate] showAlertWithTitle:VectorL10n.voiceBroadcastVoipCannotStartTitle
message:VectorL10n.voiceBroadcastVoipCannotStartDescription];
} else {
[self placeCallWithVideo:YES];
}
}
- (IBAction)onThreadListTapped:(id)sender
@ -7911,7 +7938,7 @@ static CGSize kThreadListBarButtonItemImageSize;
samples:(NSArray<NSNumber *> *)samples
completion:(void (^)(BOOL))completion
{
[self.roomDataSource sendVoiceMessage:url mimeType:nil duration:duration samples:samples success:^(NSString *eventId) {
[self.roomDataSource sendVoiceMessage:url additionalContentParams:nil mimeType:nil duration:duration samples:samples success:^(NSString *eventId) {
MXLogDebug(@"Success with event id %@", eventId);
completion(YES);
} failure:^(NSError *error) {

View file

@ -52,8 +52,7 @@ enum
ROOM_SETTINGS_MAIN_SECTION_ROW_TOPIC,
ROOM_SETTINGS_MAIN_SECTION_ROW_TAG,
ROOM_SETTINGS_MAIN_SECTION_ROW_DIRECT_CHAT,
ROOM_SETTINGS_MAIN_SECTION_ROW_MUTE_NOTIFICATIONS,
ROOM_SETTINGS_MAIN_SECTION_ROW_LEAVE
ROOM_SETTINGS_MAIN_SECTION_ROW_MUTE_NOTIFICATIONS
};
enum
@ -515,7 +514,6 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
{
[sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_MUTE_NOTIFICATIONS];
}
[sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_LEAVE];
[tmpSections addObject:sectionMain];
if (RiotSettings.shared.roomSettingsScreenAllowChangingAccessSettings)
@ -2325,22 +2323,6 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
cell = favoriteCell;
}
}
else if (row == ROOM_SETTINGS_MAIN_SECTION_ROW_LEAVE)
{
MXKTableViewCellWithButton *leaveCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier] forIndexPath:indexPath];
NSString* title = [VectorL10n leave];
[leaveCell.mxkButton setTitle:title forState:UIControlStateNormal];
[leaveCell.mxkButton setTitle:title forState:UIControlStateHighlighted];
[leaveCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
leaveCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
[leaveCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[leaveCell.mxkButton addTarget:self action:@selector(onLeave:) forControlEvents:UIControlEventTouchUpInside];
cell = leaveCell;
}
}
else if (section == SECTION_TAG_ACCESS)
{
@ -3196,76 +3178,6 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
#pragma mark - actions
- (void)onLeave:(id)sender
{
// Prompt user before leaving the room
__weak typeof(self) weakSelf = self;
[currentAlert dismissViewControllerAnimated:NO completion:nil];
NSString *title, *message;
if ([self.mainSession roomWithRoomId:self.roomId].isDirect)
{
title = [VectorL10n roomParticipantsLeavePromptTitleForDm];
message = [VectorL10n roomParticipantsLeavePromptMsgForDm];
}
else
{
title = [VectorL10n roomParticipantsLeavePromptTitle];
message = [VectorL10n roomParticipantsLeavePromptMsg];
}
currentAlert = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n leave]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
[self startActivityIndicator];
[self->mxRoom leave:^{
if (self.delegate) {
[self.delegate roomSettingsViewControllerDidLeaveRoom:self];
} else {
[[LegacyAppDelegate theDelegate] restoreInitialDisplay:nil];
}
} failure:^(NSError *error) {
[self stopActivityIndicator];
MXLogDebug(@"[RoomSettingsViewController] Leave room failed");
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}]];
[currentAlert mxk_setAccessibilityIdentifier:@"RoomSettingsVCLeaveAlert"];
[self presentViewController:currentAlert animated:YES completion:nil];
}
- (void)onRoomAvatarTap:(UITapGestureRecognizer *)recognizer
{
SingleImagePickerPresenter *singleImagePickerPresenter = [[SingleImagePickerPresenter alloc] initWithSession:self.mainSession];

View file

@ -26,8 +26,6 @@
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
@end

View file

@ -37,13 +37,6 @@
xibPictureViewTopConstraintConstant = self.pictureViewTopConstraint.constant;
}
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)prepareForReuse
{
[super prepareForReuse];

View file

@ -26,13 +26,6 @@
@implementation RoomMembershipCollapsedBubbleCell
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)layoutSubviews
{
[super layoutSubviews];

View file

@ -51,14 +51,6 @@ class TextMessageBaseBubbleCell: SizableBaseRoomCell, RoomCellURLPreviewDisplaya
override func setupMessageTextViewLongPressGesture() {
// Do nothing, otherwise default setup prevent link tap
}
override func update(theme: Theme) {
super.update(theme: theme)
if let messageTextView = self.messageTextView {
messageTextView.tintColor = theme.tintColor
}
}
}
// MARK: - RoomCellTimestampDisplayable

View file

@ -28,8 +28,6 @@
[super customizeTableViewCellRendering];
[self updateUserNameColor];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)render:(MXKCellData *)cellData

View file

@ -30,7 +30,6 @@
[self updateUserNameColor];
self.paginationLabel.textColor = ThemeService.shared.theme.tintColor;
self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor;
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)render:(MXKCellData *)cellData

View file

@ -23,13 +23,6 @@
@implementation RoomIncomingAttachmentWithoutSenderInfoBubbleCell
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
+ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth
{
CGFloat rowHeight = [self attachmentBubbleCellHeightForCellData:cellData withMaximumWidth:maxWidth];

View file

@ -28,8 +28,6 @@
[super customizeTableViewCellRendering];
[self updateUserNameColor];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)render:(MXKCellData *)cellData

View file

@ -30,7 +30,6 @@
[self updateUserNameColor];
self.paginationLabel.textColor = ThemeService.shared.theme.tintColor;
self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor;
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)render:(MXKCellData *)cellData

View file

@ -22,11 +22,5 @@
@implementation RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
@end

View file

@ -24,13 +24,6 @@
@implementation RoomOutgoingAttachmentWithoutSenderInfoBubbleCell
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)render:(MXKCellData *)cellData
{
[super render:cellData];

View file

@ -28,8 +28,6 @@
[super customizeTableViewCellRendering];
[self updateUserNameColor];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)render:(MXKCellData *)cellData

View file

@ -30,7 +30,6 @@
[self updateUserNameColor];
self.paginationLabel.textColor = ThemeService.shared.theme.tintColor;
self.paginationSeparatorView.backgroundColor = ThemeService.shared.theme.tintColor;
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)render:(MXKCellData *)cellData

View file

@ -22,11 +22,4 @@
@implementation RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
@end

View file

@ -25,8 +25,6 @@
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
@end

View file

@ -25,8 +25,6 @@
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
@end

View file

@ -28,8 +28,6 @@
[super customizeTableViewCellRendering];
[self updateUserNameColor];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}

View file

@ -25,8 +25,6 @@
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
self.messageTextView.tintColor = ThemeService.shared.theme.tintColor;
}
@end

View file

@ -44,7 +44,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
private var hostingViewController: VectorHostingController!
private var wysiwygViewModel = WysiwygComposerViewModel(
textColor: ThemeService.shared().theme.colors.primaryContent,
linkColor: ThemeService.shared().theme.colors.accent,
linkColor: ThemeService.shared().theme.colors.links,
codeBackgroundColor: ThemeService.shared().theme.selectedBackgroundColor
)
private var viewModel: ComposerViewModelProtocol!
@ -299,7 +299,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
private func update(theme: Theme) {
hostingViewController.view.backgroundColor = theme.colors.background
wysiwygViewModel.textColor = theme.colors.primaryContent
wysiwygViewModel.linkColor = theme.colors.accent
wysiwygViewModel.linkColor = theme.colors.links
wysiwygViewModel.codeBackgroundColor = theme.selectedBackgroundColor
}

View file

@ -28,6 +28,7 @@ import MediaPlayer
private var roomAvatarLoader: MXMediaLoader?
private let audioPlayers: NSMapTable<NSString, VoiceMessageAudioPlayer>
private let audioRecorders: NSHashTable<VoiceMessageAudioRecorder>
private let nowPlayingInfoDelegates: NSMapTable<VoiceMessageAudioPlayer, VoiceMessageNowPlayingInfoDelegate>
private var displayLink: CADisplayLink!
@ -93,6 +94,7 @@ import MediaPlayer
private override init() {
audioPlayers = NSMapTable<NSString, VoiceMessageAudioPlayer>(valueOptions: .weakMemory)
audioRecorders = NSHashTable<VoiceMessageAudioRecorder>(options: .weakMemory)
nowPlayingInfoDelegates = NSMapTable<VoiceMessageAudioPlayer, VoiceMessageNowPlayingInfoDelegate>(keyOptions: .weakMemory, valueOptions: .weakMemory)
activeAudioPlayers = Set<VoiceMessageAudioPlayer>()
super.init()
@ -123,27 +125,54 @@ import MediaPlayer
pauseAllServicesExcept(nil)
}
func registerNowPlayingInfoDelegate(_ delegate: VoiceMessageNowPlayingInfoDelegate, forPlayer player: VoiceMessageAudioPlayer) {
nowPlayingInfoDelegates.setObject(delegate, forKey: player)
}
func deregisterNowPlayingInfoDelegate(forPlayer player: VoiceMessageAudioPlayer) {
nowPlayingInfoDelegates.removeObject(forKey: player)
}
// MARK: - VoiceMessageAudioPlayerDelegate
func audioPlayerDidStartPlaying(_ audioPlayer: VoiceMessageAudioPlayer) {
currentlyPlayingAudioPlayer = audioPlayer
activeAudioPlayers.insert(audioPlayer)
setUpRemoteCommandCenter()
let shouldSetupRemoteCommandCenter = nowPlayingInfoDelegates.object(forKey: audioPlayer)?.shouldSetupRemoteCommandCenter(audioPlayer: audioPlayer) ?? true
if shouldSetupRemoteCommandCenter {
setUpRemoteCommandCenter()
} else {
// clean up the remote command center
tearDownRemoteCommandCenter()
}
pauseAllServicesExcept(audioPlayer)
}
func audioPlayerDidStopPlaying(_ audioPlayer: VoiceMessageAudioPlayer) {
if currentlyPlayingAudioPlayer == audioPlayer {
currentlyPlayingAudioPlayer = nil
tearDownRemoteCommandCenter()
// If we have a NowPlayingInfoDelegate for this player
let nowPlayingInfoDelegate = nowPlayingInfoDelegates.object(forKey: audioPlayer)
// ask the delegate if we should disconnect from NowPlayingInfoCenter (if there's no delegate, we consider it safe to disconnect it)
if nowPlayingInfoDelegate?.shouldDisconnectFromNowPlayingInfoCenter(audioPlayer: audioPlayer) ?? true {
currentlyPlayingAudioPlayer = nil
tearDownRemoteCommandCenter()
}
}
activeAudioPlayers.remove(audioPlayer)
}
func audioPlayerDidFinishPlaying(_ audioPlayer: VoiceMessageAudioPlayer) {
if currentlyPlayingAudioPlayer == audioPlayer {
currentlyPlayingAudioPlayer = nil
tearDownRemoteCommandCenter()
// If we have a NowPlayingInfoDelegate for this player
let nowPlayingInfoDelegate = nowPlayingInfoDelegates.object(forKey: audioPlayer)
// ask the delegate if we should disconnect from NowPlayingInfoCenter (if there's no delegate, we consider it safe to disconnect it)
if nowPlayingInfoDelegate?.shouldDisconnectFromNowPlayingInfoCenter(audioPlayer: audioPlayer) ?? true {
currentlyPlayingAudioPlayer = nil
tearDownRemoteCommandCenter()
}
}
activeAudioPlayers.remove(audioPlayer)
}
@ -249,6 +278,17 @@ import MediaPlayer
let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
nowPlayingInfoCenter.nowPlayingInfo = nil
nowPlayingInfoCenter.playbackState = .stopped
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = false
commandCenter.playCommand.removeTarget(nil)
commandCenter.pauseCommand.isEnabled = false
commandCenter.pauseCommand.removeTarget(nil)
commandCenter.skipForwardCommand.isEnabled = false
commandCenter.skipForwardCommand.removeTarget(nil)
commandCenter.skipBackwardCommand.isEnabled = false
commandCenter.skipBackwardCommand.removeTarget(nil)
}
private func updateNowPlayingInfoCenter() {
@ -256,9 +296,14 @@ import MediaPlayer
return
}
let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
nowPlayingInfoCenter.nowPlayingInfo = [MPMediaItemPropertyTitle: VectorL10n.voiceMessageLockScreenPlaceholder,
MPMediaItemPropertyPlaybackDuration: audioPlayer.duration as Any,
MPNowPlayingInfoPropertyElapsedPlaybackTime: audioPlayer.currentTime as Any]
// Checks if we have a delegate for this player, or if we should update the NowPlayingInfoCenter ourselves
if let nowPlayingInfoDelegate = nowPlayingInfoDelegates.object(forKey: audioPlayer) {
nowPlayingInfoDelegate.updateNowPlayingInfoCenter(forPlayer: audioPlayer)
} else {
let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
nowPlayingInfoCenter.nowPlayingInfo = [MPMediaItemPropertyTitle: VectorL10n.voiceMessageLockScreenPlaceholder,
MPMediaItemPropertyPlaybackDuration: audioPlayer.duration as Any,
MPNowPlayingInfoPropertyElapsedPlaybackTime: audioPlayer.currentTime as Any]
}
}
}

View file

@ -0,0 +1,26 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
@objc protocol VoiceMessageNowPlayingInfoDelegate {
func updateNowPlayingInfoCenter(forPlayer player: VoiceMessageAudioPlayer)
func shouldSetupRemoteCommandCenter(audioPlayer player: VoiceMessageAudioPlayer) -> Bool
func shouldDisconnectFromNowPlayingInfoCenter(audioPlayer: VoiceMessageAudioPlayer) -> Bool
}

View file

@ -176,7 +176,8 @@ typedef NS_ENUM(NSUInteger, LABS_ENABLE)
LABS_ENABLE_NEW_SESSION_MANAGER,
LABS_ENABLE_NEW_CLIENT_INFO_FEATURE,
LABS_ENABLE_WYSIWYG_COMPOSER,
LABS_ENABLE_VOICE_BROADCAST
LABS_ENABLE_VOICE_BROADCAST,
LABS_ENABLE_CRYPTO_SDK
};
typedef NS_ENUM(NSUInteger, SECURITY)
@ -587,6 +588,13 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
if (BuildSettings.settingsScreenShowLabSettings)
{
Section *sectionLabs = [Section sectionWithTag:SECTION_TAG_LABS];
#if DEBUG
if (MXSDKOptions.sharedInstance.isCryptoSDKAvailable)
{
[sectionLabs addRowWithTag:LABS_ENABLE_CRYPTO_SDK];
}
#endif
[sectionLabs addRowWithTag:LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX];
[sectionLabs addRowWithTag:LABS_ENABLE_THREADS_INDEX];
[sectionLabs addRowWithTag:LABS_ENABLE_AUTO_REPORT_DECRYPTION_ERRORS];
@ -2583,6 +2591,23 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
cell = labelAndSwitchCell;
}
else
{
#if DEBUG
if (row == LABS_ENABLE_CRYPTO_SDK)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
BOOL isEnabled = MXSDKOptions.sharedInstance.enableCryptoSDK;
labelAndSwitchCell.mxkLabel.text = isEnabled ? VectorL10n.settingsLabsDisableCryptoSdk : VectorL10n.settingsLabsEnableCryptoSdk;
labelAndSwitchCell.mxkSwitch.on = isEnabled;
[labelAndSwitchCell.mxkSwitch setEnabled:!isEnabled];
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableCryptoSDKFeature:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
#endif
}
}
else if (section == SECTION_TAG_SECURITY)
{
@ -3354,6 +3379,37 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
RiotSettings.shared.enableVoiceBroadcast = sender.isOn;
}
#if DEBUG
- (void)toggleEnableCryptoSDKFeature:(UISwitch *)sender
{
BOOL isEnabled = sender.isOn;
MXWeakify(self);
[currentAlert dismissViewControllerAnimated:NO completion:nil];
UIAlertController *confirmationAlert = [UIAlertController alertControllerWithTitle:nil
message:VectorL10n.settingsLabsConfirmCryptoSdk
preferredStyle:UIAlertControllerStyleAlert];
[confirmationAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel] style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[sender setOn:NO animated:YES];
}]];
[confirmationAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n continue] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
RiotSettings.shared.enableCryptoSDK = isEnabled;
MXSDKOptions.sharedInstance.enableCryptoSDK = isEnabled;
[[AppDelegate theDelegate] reloadMatrixSessions:YES];
}]];
[self presentViewController:confirmationAlert animated:YES completion:nil];
currentAlert = confirmationAlert;
}
#endif
- (void)togglePinRoomsWithMissedNotif:(UISwitch *)sender
{
RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome = sender.isOn;

View file

@ -53,6 +53,8 @@ public class VoiceBroadcastAggregator {
private var voiceBroadcastInfoStartEventContent: VoiceBroadcastInfo!
private var voiceBroadcastSenderId: String!
public private(set) var voiceBroadcastLastChunkSequence: Int = 0
private var referenceEventsListener: Any?
private var events: [MXEvent] = []
@ -168,7 +170,10 @@ public class VoiceBroadcastAggregator {
let state = VoiceBroadcastInfoState(rawValue: voiceBroadcastInfo.state) else {
return
}
// For .pause and .stopped, update the last chunk sequence
if [.stopped, .paused].contains(state) {
self.voiceBroadcastLastChunkSequence = voiceBroadcastInfo.lastChunkSequence
}
self.delegate?.voiceBroadcastAggregator(self, didReceiveState: state)
}
}
@ -187,6 +192,7 @@ public class VoiceBroadcastAggregator {
}
self.events.removeAll()
self.voiceBroadcastLastChunkSequence = 0
let filteredChunk = response.chunk.filter { event in
event.sender == self.voiceBroadcastSenderId &&

View file

@ -186,7 +186,7 @@ public class VoiceBroadcastService: NSObject {
return
}
self.room.sendStateEvent(.custom(VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType),
let httpOperation = self.room.sendStateEvent(.custom(VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType),
content: stateEventContent, stateKey: stateKey) { [weak self] response in
guard let self = self else { return }
@ -199,6 +199,9 @@ public class VoiceBroadcastService: NSObject {
}
taskCompleted()
}
// No retry to send the request
httpOperation.maxNumberOfTries = 0
}
}
}
@ -295,7 +298,7 @@ extension MXRoom {
threadId: String? = nil,
sequence: UInt,
success: @escaping ((String?) -> Void),
failure: @escaping ((Error?) -> Void)) -> MXHTTPOperation? {
failure: @escaping ((Swift.Error?) -> Void)) -> MXHTTPOperation? {
guard let relatesTo = MXEventContentRelatesTo(relationType: MXEventRelationTypeReference,
eventId: voiceBroadcastId).jsonDictionary() as? [String: Any] else {
failure(VoiceBroadcastServiceError.unknown)

View file

@ -359,7 +359,8 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
attributes:@{
NSLinkAttributeName: linkActionString,
NSForegroundColorAttributeName: self.sendingTextColor,
NSFontAttributeName: self.encryptedMessagesTextFont
NSFontAttributeName: self.encryptedMessagesTextFont,
NSUnderlineStyleAttributeName: [NSNumber numberWithInt:NSUnderlineStyleSingle]
}]];
[attributedStringWithRerequestMessage appendAttributedString:
@ -384,8 +385,6 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@", [VectorL10n eventFormatterMessageEditedMention]]
attributes:@{
NSLinkAttributeName: linkActionString,
// NOTE: Color is curretly overidden by UIText.tintColor as we use `NSLinkAttributeName`.
// If we use UITextView.linkTextAttributes to set link color we will also have the issue that color will be the same for all kind of links.
NSForegroundColorAttributeName: self.editionMentionTextColor,
NSFontAttributeName: self.editionMentionTextFont
}]];
@ -487,6 +486,7 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
self.bingTextColor = ThemeService.shared.theme.noticeColor;
self.encryptingTextColor = ThemeService.shared.theme.textPrimaryColor;
self.sendingTextColor = ThemeService.shared.theme.textPrimaryColor;
self.linksColor = ThemeService.shared.theme.colors.links;
self.errorTextColor = ThemeService.shared.theme.textPrimaryColor;
self.showEditionMention = YES;
self.editionMentionTextColor = ThemeService.shared.theme.textSecondaryColor;
@ -546,8 +546,8 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
}
#pragma mark - MXRoomSummaryUpdating
- (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withLastEvent:(MXEvent *)event eventState:(MXRoomState *)eventState roomState:(MXRoomState *)roomState {
- (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withLastEvent:(MXEvent *)event eventState:(MXRoomState *)eventState roomState:(MXRoomState *)roomState
{
// Do not display voice broadcast chunk in last message.
if (event.eventType == MXEventTypeRoomMessage && event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType])
{
@ -555,31 +555,94 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
}
// Update last message if we have a voice broadcast in the room.
if ([event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType])
MXEvent *lastVoiceBroadcastInfoEvent = [self lastVoiceBroadcastInfoEventWithEvent:event roomState:roomState];
if (lastVoiceBroadcastInfoEvent != nil)
{
return [self session:session updateRoomSummary:summary withVoiceBroadcastInfoStateEvent:event roomState:roomState];
}
else
{
MXEvent *stateEvent = [roomState stateEventsWithType:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType].lastObject;
if (stateEvent && ![VoiceBroadcastInfo isStoppedFor:[VoiceBroadcastInfo modelFromJSON: stateEvent.content].state])
MXEvent *voiceBroadcastInfoStartedEvent = [self voiceBroadcastInfoStartedEventWithEvent:lastVoiceBroadcastInfoEvent
roomId:summary.roomId
session:session];
if (voiceBroadcastInfoStartedEvent != nil
&& !(voiceBroadcastInfoStartedEvent.isRedactedEvent || [voiceBroadcastInfoStartedEvent.eventId isEqualToString:event.redacts]))
{
return [self session:session updateRoomSummary:summary withVoiceBroadcastInfoStateEvent:stateEvent roomState:roomState];
return [self session:session
updateRoomSummary:summary
withVoiceBroadcastInfoStateEvent:lastVoiceBroadcastInfoEvent
voiceBroadcastInfoStartedEvent:voiceBroadcastInfoStartedEvent roomState:roomState];
}
}
BOOL updated = [super session:session updateRoomSummary:summary withLastEvent:event eventState:eventState roomState:roomState];
if (updated) {
if (updated)
{
// Force the default text color for the last message (cancel highlighted message color)
NSMutableAttributedString *lastEventDescription = [[NSMutableAttributedString alloc] initWithAttributedString:summary.lastMessage.attributedText];
[lastEventDescription addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.textSecondaryColor range:NSMakeRange(0, lastEventDescription.length)];
[lastEventDescription addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.textSecondaryColor
range:NSMakeRange(0, lastEventDescription.length)];
summary.lastMessage.attributedText = lastEventDescription;
}
return updated;
}
- (MXEvent *)lastVoiceBroadcastInfoEventWithEvent:(MXEvent *)event roomState:(MXRoomState *)roomState
{
MXEvent *voiceBroadcastInfoEvent = nil;
VoiceBroadcastInfo *info = nil;
if ([event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType])
{
info = [VoiceBroadcastInfo modelFromJSON: event.content];
if (info != nil)
{
voiceBroadcastInfoEvent = event;
}
}
else
{
MXEvent *stateEvent = [roomState stateEventsWithType:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType].lastObject;
if (stateEvent != nil)
{
info = [VoiceBroadcastInfo modelFromJSON: stateEvent.content];
if (info != nil && ![VoiceBroadcastInfo isStoppedFor:info.state])
{
voiceBroadcastInfoEvent = stateEvent;
}
}
}
return voiceBroadcastInfoEvent;
}
- (MXEvent *)voiceBroadcastInfoStartedEventWithEvent:(MXEvent *)voiceBroadcastInfoEvent roomId:(NSString *)roomId session:(MXSession *)session
{
VoiceBroadcastInfo *voiceBroadcastInfo = [VoiceBroadcastInfo modelFromJSON: voiceBroadcastInfoEvent.content];
if ([VoiceBroadcastInfo isStartedFor:voiceBroadcastInfo.state])
{
return voiceBroadcastInfoEvent;
}
else
{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
__block MXEvent *voiceBroadcastInfoStartedEvent;
[session eventWithEventId:voiceBroadcastInfo.voiceBroadcastId inRoom:roomId success:^(MXEvent *resultEvent) {
voiceBroadcastInfoStartedEvent = resultEvent;
dispatch_group_leave(group);
} failure:^(NSError *error) {
MXLogErrorDetails(@"[EventFormatter] Fetch eventWithEventId with error = %@", error.description);
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return voiceBroadcastInfoStartedEvent;
}
}
- (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withStateEvents:(NSArray<MXEvent *> *)stateEvents roomState:(MXRoomState *)roomState
{
BOOL updated = [super session:session updateRoomSummary:summary withStateEvents:stateEvents roomState:roomState];
@ -603,18 +666,29 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
return updated;
}
- (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withVoiceBroadcastInfoStateEvent:(MXEvent *)stateEvent roomState:(MXRoomState *)roomState
- (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withVoiceBroadcastInfoStateEvent:(MXEvent *)stateEvent voiceBroadcastInfoStartedEvent:(MXEvent *)voiceBroadcastInfoStartedEvent roomState:(MXRoomState *)roomState
{
[summary updateLastMessage:[[MXRoomLastMessage alloc] initWithEvent:stateEvent]];
if (summary.lastMessage.others == nil)
BOOL isStoppedVoiceBroadcast = [VoiceBroadcastInfo isStoppedFor:[VoiceBroadcastInfo modelFromJSON: stateEvent.content].state];
if ([summary.lastMessage.eventId isEqualToString:voiceBroadcastInfoStartedEvent.eventId])
{
summary.lastMessage.others = [NSMutableDictionary dictionary];
if (!isStoppedVoiceBroadcast)
{
return NO;
}
}
else
{
[summary updateLastMessage:[[MXRoomLastMessage alloc] initWithEvent:voiceBroadcastInfoStartedEvent]];
if (summary.lastMessage.others == nil)
{
summary.lastMessage.others = [NSMutableDictionary dictionary];
}
}
summary.lastMessage.others[@"lastEventDate"] = [self dateStringFromEvent:stateEvent withTime:YES];
NSAttributedString *attachmentString = nil;
UIColor *textColor;
if ([VoiceBroadcastInfo isStoppedFor:[VoiceBroadcastInfo modelFromJSON: stateEvent.content].state])
if (isStoppedVoiceBroadcast)
{
textColor = ThemeService.shared.theme.textSecondaryColor;
NSString *senderDisplayName;
@ -627,6 +701,7 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
senderDisplayName = [self senderDisplayNameForEvent:stateEvent withRoomState:roomState];
summary.lastMessage.text = [VectorL10n noticeVoiceBroadcastEnded:senderDisplayName];
}
summary.lastMessage.others[@"lastEventDate"] = [self dateStringFromEvent:stateEvent withTime:YES];
}
else
{
@ -638,6 +713,7 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
attachmentString = [NSAttributedString attributedStringWithAttachment:attachment];
summary.lastMessage.text = VectorL10n.noticeVoiceBroadcastLive;
summary.lastMessage.others[@"lastEventDate"] = [self dateStringFromEvent:voiceBroadcastInfoStartedEvent withTime:YES];
}
// Compute the attribute text message

View file

@ -23,4 +23,6 @@
#import "BuildInfo.h"
#import "ThemeService.h"
#endif /* RiotNSE_Bridging_Header_h */

View file

@ -78,3 +78,5 @@ targets:
- "**/*.md" # excludes all files with the .md extension
- path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift
- path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK
- path: ../Riot/Managers/Theme
- path: ../Riot/Categories/UIColor.swift

View file

@ -47,7 +47,7 @@ enum MockAnalyticsPromptScreenState: MockScreenState, CaseIterable {
return (
[promptType, viewModel],
AnyView(AnalyticsPrompt(viewModel: viewModel.context)
.addDependency(MockAvatarService.example))
.environmentObject(AvatarViewModel.withMockedServices()))
)
}
}

View file

@ -32,10 +32,6 @@ class AvatarService: AvatarServiceProtocol {
private let mediaManager: MXMediaManager
static func instantiate(mediaManager: MXMediaManager) -> AvatarServiceProtocol {
AvatarService(mediaManager: mediaManager)
}
init(mediaManager: MXMediaManager) {
self.mediaManager = mediaManager
}

View file

@ -19,8 +19,7 @@ import SwiftUI
struct AvatarImage: View {
@Environment(\.theme) var theme: ThemeSwiftUI
@Environment(\.dependencies) var dependencies: DependencyContainer
@StateObject var viewModel = AvatarViewModel()
@EnvironmentObject var viewModel: AvatarViewModel
var mxContentUri: String?
var matrixItemId: String
@ -43,7 +42,6 @@ struct AvatarImage: View {
.frame(maxWidth: CGFloat(size.rawValue), maxHeight: CGFloat(size.rawValue))
.clipShape(Circle())
.onAppear {
viewModel.inject(dependencies: dependencies)
viewModel.loadAvatar(
mxContentUri: mxContentUri,
matrixItemId: matrixItemId,
@ -95,7 +93,7 @@ struct AvatarImage_Previews: PreviewProvider {
AvatarImage(mxContentUri: nil, matrixItemId: name, displayName: name, size: .xLarge)
}
}
.addDependency(MockAvatarService.example)
.environmentObject(AvatarViewModel.withMockedServices())
}
}
}

Some files were not shown because too many files have changed in this diff Show more