From c0b4e19c9b920867f55bcb172de28f2943bbb803 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 7 Dec 2022 16:29:44 +0100 Subject: [PATCH 001/228] updated the rich text editor package to the latest revision that supports links --- Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- project.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index 912bdf970..6abad0a50 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,7 +23,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift", "state" : { - "revision" : "38ad28bedbe63b3587126158245659b6c989ec2c" + "revision" : "a4a7ad46b76cfb8de4695c5fd211a735cb438e1a" } }, { diff --git a/project.yml b/project.yml index dd978f21b..12b4bc027 100644 --- a/project.yml +++ b/project.yml @@ -53,7 +53,7 @@ packages: branch: main WysiwygComposer: url: https://github.com/matrix-org/matrix-wysiwyg-composer-swift - revision: 38ad28bedbe63b3587126158245659b6c989ec2c + revision: a4a7ad46b76cfb8de4695c5fd211a735cb438e1a DeviceKit: url: https://github.com/devicekit/DeviceKit majorVersion: 4.7.0 From 34ca74d0c2491812eb6e9d18225bed9eb16e1fd1 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 7 Dec 2022 18:17:28 +0100 Subject: [PATCH 002/228] creating the coordinator for the link action view --- Riot/Modules/Room/RoomViewController.h | 3 ++ Riot/Modules/Room/RoomViewController.swift | 9 +++++ .../Views/InputToolbar/RoomInputToolbarView.h | 3 ++ .../WysiwygInputToolbarView.swift | 4 ++- .../ComposerLinkActionBridgePresenter.swift | 34 +++++++++++++++++++ .../ComposerLinkActionPresenter.swift | 29 ++++++++++++++++ .../Room/Composer/Model/ComposerModels.swift | 24 ++++++++++++- .../Modules/Room/Composer/View/Composer.swift | 11 +++++- .../ViewModel/ComposerViewModel.swift | 2 ++ 9 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift diff --git a/Riot/Modules/Room/RoomViewController.h b/Riot/Modules/Room/RoomViewController.h index 5b162b58c..59fca17dc 100644 --- a/Riot/Modules/Room/RoomViewController.h +++ b/Riot/Modules/Room/RoomViewController.h @@ -34,6 +34,7 @@ @class ThreadsCoordinatorBridgePresenter; @class LiveLocationSharingBannerView; @class VoiceBroadcastService; +@class ComposerLinkActionBridgePresenter; NS_ASSUME_NONNULL_BEGIN @@ -122,6 +123,8 @@ extern NSTimeInterval const kResizeComposerAnimationDuration; @property (nonatomic) CGFloat wysiwygTranslation; +@property (nonatomic, strong) ComposerLinkActionBridgePresenter *composerLinkActionBridgePresenter; + /** Retrieve the live data source in cases where the timeline is not live. diff --git a/Riot/Modules/Room/RoomViewController.swift b/Riot/Modules/Room/RoomViewController.swift index 9204eeae9..e5c11d4e3 100644 --- a/Riot/Modules/Room/RoomViewController.swift +++ b/Riot/Modules/Room/RoomViewController.swift @@ -243,6 +243,11 @@ extension RoomViewController { roomInputToolbarContainer.superview?.isHidden = isHidden } } + + @objc func didSendLinkAction(_ linkAction: LinkActionWrapper) { + composerLinkActionBridgePresenter = ComposerLinkActionBridgePresenter(linkAction: linkAction) + composerLinkActionBridgePresenter.delegate = self + } } // MARK: - Private Helpers @@ -281,3 +286,7 @@ private extension RoomViewController { } } } + +extension RoomViewController: ComposerLinkActionBridgePresenterDelegate { + +} diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h index 4e351806c..af84b462d 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h @@ -20,6 +20,7 @@ @class RoomActionsBar; @class RoomInputToolbarView; +@class LinkActionWrapper; /** Destination of the message in the composer @@ -77,6 +78,8 @@ typedef NS_ENUM(NSUInteger, RoomInputToolbarViewSendMode) - (void)didChangeMaximisedState: (BOOL) isMaximised; +- (void)didSendLinkAction: (LinkActionWrapper *)linkAction; + @end /** diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index 5b9c7d4a7..3399acce4 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -258,9 +258,11 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp private func handleViewModelResult(_ result: ComposerViewModelResult) { switch result { case .cancel: - self.toolbarViewDelegate?.roomInputToolbarViewDidTapCancel(self) + toolbarViewDelegate?.roomInputToolbarViewDidTapCancel(self) case let .contentDidChange(isEmpty): setVoiceMessageToolbarIsHidden(!isEmpty) + case let .linkTapped(linkAction): + toolbarViewDelegate?.didSendLinkAction(LinkActionWrapper(linkAction)) } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift new file mode 100644 index 000000000..d79dc3965 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift @@ -0,0 +1,34 @@ +// +// Copyright 2022 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 + +protocol ComposerLinkActionBridgePresenterDelegate: AnyObject { + +} + + +final class ComposerLinkActionBridgePresenter: NSObject { + private var coordinator: ComposerLinkActionCoordinator? + private var linkAction: LinkActionWrapper + + weak var delegate: ComposerLinkActionBridgePresenterDelegate? + + init(linkAction: LinkActionWrapper) { + self.linkAction = linkAction + super.init() + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift new file mode 100644 index 000000000..191778b3f --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift @@ -0,0 +1,29 @@ +// +// Copyright 2022 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 + +final class ComposerLinkActionCoordinator: Coordinator, Presentable { + var childCoordinators: [Coordinator] = [] + + func start() { + + } + + func toPresentable() -> UIViewController { + fatalError() + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift index 84b5f8f95..ffe187abe 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift @@ -36,6 +36,7 @@ enum FormatType { case italic case underline case strikethrough + case link } extension FormatType: CaseIterable, Identifiable { @@ -58,6 +59,8 @@ extension FormatItem { return Asset.Images.strikethrough.name case .underline: return Asset.Images.underlined.name + case .link: + return Asset.Images.link.name } } @@ -71,6 +74,8 @@ extension FormatItem { return "strikethroughButton" case .underline: return "underlineButton" + case .link: + return "linkButton" } } @@ -84,6 +89,9 @@ extension FormatItem { return VectorL10n.wysiwygComposerFormatActionStrikethrough case .underline: return VectorL10n.wysiwygComposerFormatActionUnderline + case .link: + // TODO: Add link accessibility label translation + return "link" } } } @@ -100,11 +108,12 @@ extension FormatType { return .strikeThrough case .underline: return .underline + case .link: + return .link } } // TODO: We probably don't need to expose this, clean up. - /// Convenience method to map it to the external rust binging action var composerAction: ComposerAction { switch self { @@ -116,6 +125,8 @@ extension FormatType { return .strikeThrough case .underline: return .underline + case .link: + return .link } } } @@ -130,11 +141,22 @@ enum ComposerSendMode: Equatable { enum ComposerViewAction: Equatable { case cancel case contentDidChange(isEmpty: Bool) + case linkTapped(linkAction: LinkAction) } enum ComposerViewModelResult: Equatable { case cancel case contentDidChange(isEmpty: Bool) + case linkTapped(LinkAction: LinkAction) +} + +final class LinkActionWrapper: NSObject { + let linkAction: LinkAction + + init(_ linkAction: LinkAction) { + self.linkAction = linkAction + super.init() + } } diff --git a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift index 5d8ec5320..4afd6e391 100644 --- a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift +++ b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift @@ -226,7 +226,11 @@ struct Composer: View { HStack(alignment: .center, spacing: 0) { sendMediaButton FormattingToolbar(formatItems: formatItems) { type in - wysiwygViewModel.apply(type.action) + if type.action == .link { + sendLinkAction() + } else { + wysiwygViewModel.apply(type.action) + } } .frame(height: 44) Spacer() @@ -242,6 +246,11 @@ struct Composer: View { } } } + + private func sendLinkAction() { + let linkAction = wysiwygViewModel.getLinkAction() + viewModel.send(viewAction: .linkTapped(linkAction: linkAction)) + } } // MARK: Previews diff --git a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift index 4e6442303..b9275709d 100644 --- a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift @@ -84,6 +84,8 @@ final class ComposerViewModel: ComposerViewModelType, ComposerViewModelProtocol callback?(.cancel) case let .contentDidChange(isEmpty): callback?(.contentDidChange(isEmpty: isEmpty)) + case let .linkTapped(linkAction): + callback?(.linkTapped(LinkAction: linkAction)) } } From 4543aec25d7f1d52cd156d6e40d08c3b39c93b5a Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 7 Dec 2022 19:22:33 +0100 Subject: [PATCH 003/228] boilerplate setup completed --- Riot/Modules/Room/RoomViewController.swift | 1 + .../ComposerLinkActionBridgePresenter.swift | 13 +++++-- .../ComposerLinkActionPresenter.swift | 11 +++++- .../MockComposerLinkActionScreenState.swift | 31 ++++++++++++++++ .../Model/ComposerLinkActionModel.swift | 28 +++++++++++++++ .../View/ComposerLinkActionView.swift | 36 +++++++++++++++++++ .../ComposerLinkActionViewModel.swift | 25 +++++++++++++ .../ComposerLinkActionViewModelProtocol.swift | 22 ++++++++++++ 8 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModelProtocol.swift diff --git a/Riot/Modules/Room/RoomViewController.swift b/Riot/Modules/Room/RoomViewController.swift index e5c11d4e3..183e2ffd5 100644 --- a/Riot/Modules/Room/RoomViewController.swift +++ b/Riot/Modules/Room/RoomViewController.swift @@ -247,6 +247,7 @@ extension RoomViewController { @objc func didSendLinkAction(_ linkAction: LinkActionWrapper) { composerLinkActionBridgePresenter = ComposerLinkActionBridgePresenter(linkAction: linkAction) composerLinkActionBridgePresenter.delegate = self + composerLinkActionBridgePresenter.present(from: self, animated: true) } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift index d79dc3965..ea9a52aa0 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift @@ -15,6 +15,7 @@ // import Foundation +import WysiwygComposer protocol ComposerLinkActionBridgePresenterDelegate: AnyObject { @@ -23,12 +24,20 @@ protocol ComposerLinkActionBridgePresenterDelegate: AnyObject { final class ComposerLinkActionBridgePresenter: NSObject { private var coordinator: ComposerLinkActionCoordinator? - private var linkAction: LinkActionWrapper + private var linkAction: LinkAction weak var delegate: ComposerLinkActionBridgePresenterDelegate? init(linkAction: LinkActionWrapper) { - self.linkAction = linkAction + self.linkAction = linkAction.linkAction super.init() } + + func present(from viewController: UIViewController, animated: Bool) { + let composerLinkActionCoordinator = ComposerLinkActionCoordinator(linkAction: linkAction) + let presentable = composerLinkActionCoordinator.toPresentable() + viewController.present(presentable, animated: animated, completion: nil) + composerLinkActionCoordinator.start() + coordinator = composerLinkActionCoordinator + } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift index 191778b3f..c36ecbe0b 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift @@ -15,15 +15,24 @@ // import Foundation +import WysiwygComposer final class ComposerLinkActionCoordinator: Coordinator, Presentable { var childCoordinators: [Coordinator] = [] + private let hostingController: UIViewController + private let viewModel: ComposerLinkActionViewModel + + init(linkAction: LinkAction) { + viewModel = ComposerLinkActionViewModel(initialViewState: .init()) + hostingController = VectorHostingController(rootView: ComposerLinkActionView(viewModel: viewModel.context)) + } + func start() { } func toPresentable() -> UIViewController { - fatalError() + hostingController } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift new file mode 100644 index 000000000..3bf974425 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift @@ -0,0 +1,31 @@ +// +// Copyright 2022 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 SwiftUI + +enum MockComposerLinkActionScreenState: MockScreenState, CaseIterable { + var screenType: Any.Type { + ComposerLinkActionView.self + } + + var screenView: ([Any], AnyView) { + let viewModel = ComposerLinkActionViewModel(initialViewState: .init()) + return ( + [viewModel], + AnyView(ComposerLinkActionView(viewModel: viewModel.context)) + ) + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift new file mode 100644 index 000000000..a4b941e33 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift @@ -0,0 +1,28 @@ +// +// Copyright 2022 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 + +enum ComposerLinkActionViewAction { +} + +enum ComposerLinkActionViewModelResult: Equatable { +} + +// MARK: View + +struct ComposerLinkActionViewState: BindableState { +} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift new file mode 100644 index 000000000..5efe10f56 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift @@ -0,0 +1,36 @@ +// +// Copyright 2022 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 SwiftUI + +struct ComposerLinkActionView: View { + @ObservedObject private var viewModel: ComposerLinkActionViewModel.Context + + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } + + init(viewModel: ComposerLinkActionViewModel.Context) { + self.viewModel = viewModel + } +} + +struct ComposerLinkActionView_Previews: PreviewProvider { + static let stateRenderer = MockComposerLinkActionScreenState.stateRenderer + static var previews: some View { + stateRenderer.screenGroup() + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift new file mode 100644 index 000000000..8fd5724fd --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift @@ -0,0 +1,25 @@ +// +// Copyright 2022 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 + +typealias ComposerLinkActionViewModelType = StateStoreViewModel + +final class ComposerLinkActionViewModel: ComposerLinkActionViewModelType, ComposerLinkActionViewModelProtocol { + + var callback: ((ComposerLinkActionViewModelResult) -> Void)? + +} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModelProtocol.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModelProtocol.swift new file mode 100644 index 000000000..73101dc3c --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModelProtocol.swift @@ -0,0 +1,22 @@ +// +// Copyright 2022 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 + +protocol ComposerLinkActionViewModelProtocol { + var context: ComposerLinkActionViewModelType.Context { get } + var callback: ((ComposerLinkActionViewModelResult) -> Void)? { get set } +} From 3c1a10dc5bc1c2fe8b8b7d445f52fee879b175c2 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 8 Dec 2022 14:54:13 +0100 Subject: [PATCH 004/228] Add Voice Broadcast backward and forward buttons for playback --- .../Contents.json | 12 +++ .../voice_broadcast_backward_30s.svg | 4 + .../Contents.json | 12 +++ .../voice_broadcast_forward_30s.svg | 4 + .../Contents.json | 11 +-- Riot/Generated/Images.swift | 2 + .../VoiceBroadcastPlaybackViewModel.swift | 89 +++++++++++++------ .../View/VoiceBroadcastPlaybackView.swift | 26 +++++- .../VoiceBroadcastPlaybackModels.swift | 4 + .../VoiceBroadcastPlaybackScreenState.swift | 2 +- 10 files changed, 127 insertions(+), 39 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_backward_30s.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_backward_30s.imageset/voice_broadcast_backward_30s.svg create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_forward_30s.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_forward_30s.imageset/voice_broadcast_forward_30s.svg diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_backward_30s.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_backward_30s.imageset/Contents.json new file mode 100644 index 000000000..bc57405fb --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_backward_30s.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "voice_broadcast_backward_30s.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_backward_30s.imageset/voice_broadcast_backward_30s.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_backward_30s.imageset/voice_broadcast_backward_30s.svg new file mode 100644 index 000000000..cfebb0829 --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_backward_30s.imageset/voice_broadcast_backward_30s.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_forward_30s.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_forward_30s.imageset/Contents.json new file mode 100644 index 000000000..7a027074b --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_forward_30s.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "voice_broadcast_forward_30s.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_forward_30s.imageset/voice_broadcast_forward_30s.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_forward_30s.imageset/voice_broadcast_forward_30s.svg new file mode 100644 index 000000000..fc63f83ae --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_forward_30s.imageset/voice_broadcast_forward_30s.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_spinner.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_spinner.imageset/Contents.json index f00919cff..470106acf 100644 --- a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_spinner.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_spinner.imageset/Contents.json @@ -2,16 +2,7 @@ "images" : [ { "filename" : "voice_broadcast_spinner.svg", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "idiom" : "universal" } ], "info" : { diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index 11839848c..1dc910c14 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -339,6 +339,8 @@ internal class Asset: NSObject { internal static let tabHome = ImageAsset(name: "tab_home") internal static let tabPeople = ImageAsset(name: "tab_people") internal static let tabRooms = ImageAsset(name: "tab_rooms") + internal static let voiceBroadcastBackward30s = ImageAsset(name: "voice_broadcast_backward_30s") + internal static let voiceBroadcastForward30s = ImageAsset(name: "voice_broadcast_forward_30s") internal static let voiceBroadcastLive = ImageAsset(name: "voice_broadcast_live") internal static let voiceBroadcastPause = ImageAsset(name: "voice_broadcast_pause") internal static let voiceBroadcastPlay = ImageAsset(name: "voice_broadcast_play") diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift index a07ff8fed..9d806210c 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift @@ -56,6 +56,8 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic return (!isPlaybackInitialized || isPlayingLastChunk) && (state.broadcastState == .started || state.broadcastState == .resumed) } + private static let defaultBackwardForwardValue: Float = 30000.0 // 30sec in ms + // MARK: Public // MARK: - Setup @@ -71,7 +73,7 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic let viewState = VoiceBroadcastPlaybackViewState(details: details, broadcastState: voiceBroadcastAggregator.voiceBroadcastState, playbackState: .stopped, - playingState: VoiceBroadcastPlayingState(duration: Float(voiceBroadcastAggregator.voiceBroadcast.duration), isLive: false), + playingState: VoiceBroadcastPlayingState(duration: Float(voiceBroadcastAggregator.voiceBroadcast.duration), isLive: false, canMoveForward: false, canMoveBackward: false), bindings: VoiceBroadcastPlaybackViewStateBindings(progress: 0)) super.init(initialViewState: viewState) @@ -101,6 +103,10 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic pause() case .sliderChange(let didChange): didSliderChanged(didChange) + case .backward: + backward() + case .forward: + forward() } } @@ -164,6 +170,52 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic audioPlayer?.stop() } + /// Backward (30sec) a voice broadcast + private func backward() { + let newProgressValue = context.progress - VoiceBroadcastPlaybackViewModel.defaultBackwardForwardValue + seekTo(newProgressValue > 0 ? newProgressValue : 0) + + state.bindings.progress = newProgressValue + updateBackwardForwardButtons() + } + + /// Forward (30sec) a voice broadcast + private func forward() { + let newProgressValue = context.progress + VoiceBroadcastPlaybackViewModel.defaultBackwardForwardValue + seekTo(newProgressValue < state.playingState.duration ? newProgressValue : state.playingState.duration) + + state.bindings.progress = newProgressValue + updateBackwardForwardButtons() + } + + private func seekTo(_ seekTime: Float) { + // Flush the chunks queue and the current audio player playlist + voiceBroadcastChunkQueue = [] + reloadVoiceBroadcastChunkQueue = isProcessingVoiceBroadcastChunk + audioPlayer?.removeAllPlayerItems() + + let chunks = reorderVoiceBroadcastChunks(chunks: Array(voiceBroadcastAggregator.voiceBroadcast.chunks)) + + // Reinject the chunks we need and play them + let remainingTime = state.playingState.duration - seekTime + var chunksDuration: UInt = 0 + for chunk in chunks.reversed() { + chunksDuration += chunk.duration + voiceBroadcastChunkQueue.append(chunk) + if Float(chunksDuration) >= remainingTime { + break + } + } + + MXLog.debug("[VoiceBroadcastPlaybackViewModel] seekTo: restart to time: \(seekTime) milliseconds") + let time = seekTime - state.playingState.duration + Float(chunksDuration) + seekToChunkTime = TimeInterval(time / 1000) + // Check the condition to resume the playback when data will be ready (after the chunk process). + if state.playbackState != .stopped, isActuallyPaused == false { + state.playbackState = .buffering + } + processPendingVoiceBroadcastChunks() + } // MARK: - Voice broadcast chunks playback @@ -295,32 +347,8 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic audioPlayer?.pause() displayLink.isPaused = true } else { - // Flush the chunks queue and the current audio player playlist - voiceBroadcastChunkQueue = [] - reloadVoiceBroadcastChunkQueue = isProcessingVoiceBroadcastChunk - audioPlayer?.removeAllPlayerItems() - - let chunks = reorderVoiceBroadcastChunks(chunks: Array(voiceBroadcastAggregator.voiceBroadcast.chunks)) - - // Reinject the chunks we need and play them - let remainingTime = state.playingState.duration - state.bindings.progress - var chunksDuration: UInt = 0 - for chunk in chunks.reversed() { - chunksDuration += chunk.duration - voiceBroadcastChunkQueue.append(chunk) - if Float(chunksDuration) >= remainingTime { - break - } - } - - MXLog.debug("[VoiceBroadcastPlaybackViewModel] didSliderChanged: restart to time: \(state.bindings.progress) milliseconds") - let time = state.bindings.progress - state.playingState.duration + Float(chunksDuration) - seekToChunkTime = TimeInterval(time / 1000) - // Check the condition to resume the playback when data will be ready (after the chunk process). - if state.playbackState != .stopped, isActuallyPaused == false { - state.playbackState = .buffering - } - processPendingVoiceBroadcastChunks() + seekTo(state.bindings.progress) + updateBackwardForwardButtons() } } @@ -343,6 +371,13 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic }.reduce(0) { $0 + $1.duration}) + (audioPlayer?.currentTime.rounded() ?? 0) * 1000 state.bindings.progress = Float(progress) + + updateBackwardForwardButtons() + } + + private func updateBackwardForwardButtons() { + state.playingState.canMoveBackward = state.bindings.progress > 0 + state.playingState.canMoveForward = state.bindings.progress < state.playingState.duration } private func handleVoiceBroadcastChunksProcessing() { diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift index c518f7e59..ff36b0e36 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift @@ -110,7 +110,19 @@ struct VoiceBroadcastPlaybackView: View { if viewModel.viewState.playbackState == .error { VoiceBroadcastPlaybackErrorView() } else { - ZStack { + HStack (spacing: 17.0) { + if viewModel.viewState.playingState.canMoveBackward { + Button { + viewModel.send(viewAction: .backward) + } label: { + Image(uiImage: Asset.Images.voiceBroadcastBackward30s.image) + .renderingMode(.original) + } + .accessibilityIdentifier("backwardButton") + } else { + Spacer().frame(width: 25.0) + } + if viewModel.viewState.playbackState == .playing || viewModel.viewState.playbackState == .buffering { Button { viewModel.send(viewAction: .pause) } label: { Image(uiImage: Asset.Images.voiceBroadcastPause.image) @@ -125,6 +137,18 @@ struct VoiceBroadcastPlaybackView: View { .disabled(viewModel.viewState.playbackState == .buffering) .accessibilityIdentifier("playButton") } + + if viewModel.viewState.playingState.canMoveForward { + Button { + viewModel.send(viewAction: .forward) + } label: { + Image(uiImage: Asset.Images.voiceBroadcastForward30s.image) + .renderingMode(.original) + } + .accessibilityIdentifier("forwardButton") + } else { + Spacer().frame(width: 25.0) + } } } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift index 18d80d3af..5e5c6696c 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift @@ -21,6 +21,8 @@ enum VoiceBroadcastPlaybackViewAction { case play case pause case sliderChange(didChange: Bool) + case backward + case forward } enum VoiceBroadcastPlaybackState { @@ -40,6 +42,8 @@ struct VoiceBroadcastPlayingState { var duration: Float var durationLabel: String? var isLive: Bool + var canMoveForward: Bool + var canMoveBackward: Bool } struct VoiceBroadcastPlaybackViewState: BindableState { diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift index d88f7dfa8..0172597b7 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift @@ -43,7 +43,7 @@ enum MockVoiceBroadcastPlaybackScreenState: MockScreenState, CaseIterable { var screenView: ([Any], AnyView) { let details = VoiceBroadcastPlaybackDetails(senderDisplayName: "Alice", avatarData: AvatarInput(mxContentUri: "", matrixItemId: "!fakeroomid:matrix.org", displayName: "The name of the room")) - let viewModel = MockVoiceBroadcastPlaybackViewModel(initialViewState: VoiceBroadcastPlaybackViewState(details: details, broadcastState: .started, playbackState: .stopped, playingState: VoiceBroadcastPlayingState(duration: 10.0, isLive: true), bindings: VoiceBroadcastPlaybackViewStateBindings(progress: 0))) + let viewModel = MockVoiceBroadcastPlaybackViewModel(initialViewState: VoiceBroadcastPlaybackViewState(details: details, broadcastState: .started, playbackState: .stopped, playingState: VoiceBroadcastPlayingState(duration: 10.0, isLive: true, canMoveForward: false, canMoveBackward: false), bindings: VoiceBroadcastPlaybackViewStateBindings(progress: 0))) return ( [false, viewModel], From ade1dc077aa2e2668cd5e406ccd83dee5679d95d Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 8 Dec 2022 14:58:15 +0100 Subject: [PATCH 005/228] Add Towncrier file --- changelog.d/pr-7146.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7146.change diff --git a/changelog.d/pr-7146.change b/changelog.d/pr-7146.change new file mode 100644 index 000000000..0b5a8f2e6 --- /dev/null +++ b/changelog.d/pr-7146.change @@ -0,0 +1 @@ +Labs: VoiceBroadcast: Add backward and forward buttons for playback From 8f965cb6f0807067332c152c3cb9031cda425a65 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 15:39:57 +0100 Subject: [PATCH 006/228] WIP --- Riot/Modules/Room/RoomViewController.h | 2 +- Riot/Modules/Room/RoomViewController.swift | 19 +++++- .../Modules/Common/Mock/MockAppScreens.swift | 1 + ...kComposerCreateActionListScreenState.swift | 15 +++-- .../ComposerLinkActionBridgePresenter.swift | 23 +++++++- .../ComposerLinkActionCoordinator.swift | 58 +++++++++++++++++++ .../MockComposerLinkActionScreenState.swift | 16 ++++- .../Model/ComposerLinkActionModel.swift | 5 +- .../View/ComposerLinkActionView.swift | 25 +++++++- .../ComposerLinkActionViewModel.swift | 28 +++++++++ .../ComposerLinkActionViewModelProtocol.swift | 2 +- 11 files changed, 177 insertions(+), 17 deletions(-) create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift diff --git a/Riot/Modules/Room/RoomViewController.h b/Riot/Modules/Room/RoomViewController.h index 59fca17dc..14bab4c05 100644 --- a/Riot/Modules/Room/RoomViewController.h +++ b/Riot/Modules/Room/RoomViewController.h @@ -123,7 +123,7 @@ extern NSTimeInterval const kResizeComposerAnimationDuration; @property (nonatomic) CGFloat wysiwygTranslation; -@property (nonatomic, strong) ComposerLinkActionBridgePresenter *composerLinkActionBridgePresenter; +@property (nonatomic, strong, nullable) ComposerLinkActionBridgePresenter *composerLinkActionBridgePresenter; /** diff --git a/Riot/Modules/Room/RoomViewController.swift b/Riot/Modules/Room/RoomViewController.swift index 183e2ffd5..dc17e93ed 100644 --- a/Riot/Modules/Room/RoomViewController.swift +++ b/Riot/Modules/Room/RoomViewController.swift @@ -245,9 +245,10 @@ extension RoomViewController { } @objc func didSendLinkAction(_ linkAction: LinkActionWrapper) { - composerLinkActionBridgePresenter = ComposerLinkActionBridgePresenter(linkAction: linkAction) - composerLinkActionBridgePresenter.delegate = self - composerLinkActionBridgePresenter.present(from: self, animated: true) + let presenter = ComposerLinkActionBridgePresenter(linkAction: linkAction) + presenter.delegate = self + composerLinkActionBridgePresenter = presenter + presenter.present(from: self, animated: true) } } @@ -289,5 +290,17 @@ private extension RoomViewController { } extension RoomViewController: ComposerLinkActionBridgePresenterDelegate { + func didDismissInteractively() { + self.cleanup() + } + func didCancel() { + self.composerLinkActionBridgePresenter?.dismiss(animated: true) { [weak self] in + self?.cleanup() + } + } + + private func cleanup() { + self.composerLinkActionBridgePresenter = nil + } } diff --git a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift index e2b3ce30e..eaee22e73 100644 --- a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift +++ b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift @@ -71,6 +71,7 @@ enum MockAppScreens { MockSpaceSelectorScreenState.self, MockComposerScreenState.self, MockComposerCreateActionListScreenState.self, + MockComposerLinkActionScreenState.self, MockVoiceBroadcastPlaybackScreenState.self ] } diff --git a/RiotSwiftUI/Modules/Room/Composer/CreateActionList/MockComposerCreateActionListScreenState.swift b/RiotSwiftUI/Modules/Room/Composer/CreateActionList/MockComposerCreateActionListScreenState.swift index 8089ab7ae..027d23f2e 100644 --- a/RiotSwiftUI/Modules/Room/Composer/CreateActionList/MockComposerCreateActionListScreenState.swift +++ b/RiotSwiftUI/Modules/Room/Composer/CreateActionList/MockComposerCreateActionListScreenState.swift @@ -33,11 +33,16 @@ enum MockComposerCreateActionListScreenState: MockScreenState, CaseIterable { case .fullList: actions = ComposerCreateAction.allCases } - let viewModel = ComposerCreateActionListViewModel(initialViewState: ComposerCreateActionListViewState( - actions: actions, - wysiwygEnabled: true, - isScrollingEnabled: false, - bindings: ComposerCreateActionListBindings(textFormattingEnabled: true))) + let viewModel = ComposerCreateActionListViewModel( + initialViewState: ComposerCreateActionListViewState( + actions: actions, + wysiwygEnabled: true, + isScrollingEnabled: false, + bindings: ComposerCreateActionListBindings( + textFormattingEnabled: true + ) + ) + ) return ( [viewModel], diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift index ea9a52aa0..f7486d50b 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift @@ -18,10 +18,10 @@ import Foundation import WysiwygComposer protocol ComposerLinkActionBridgePresenterDelegate: AnyObject { - + func didCancel() + func didDismissInteractively() } - final class ComposerLinkActionBridgePresenter: NSObject { private var coordinator: ComposerLinkActionCoordinator? private var linkAction: LinkAction @@ -35,9 +35,28 @@ final class ComposerLinkActionBridgePresenter: NSObject { func present(from viewController: UIViewController, animated: Bool) { let composerLinkActionCoordinator = ComposerLinkActionCoordinator(linkAction: linkAction) + composerLinkActionCoordinator.callback = { [weak self] action in + switch action { + case .didTapCancel: + self?.delegate?.didCancel() + case .didDismissInteractively: + self?.delegate?.didDismissInteractively() + } + } let presentable = composerLinkActionCoordinator.toPresentable() viewController.present(presentable, animated: animated, completion: nil) composerLinkActionCoordinator.start() coordinator = composerLinkActionCoordinator } + + func dismiss(animated: Bool, completion: (() -> Void)?) { + guard let coordinator = coordinator else { + return + } + // Dismiss modal + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + completion?() + } + } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift new file mode 100644 index 000000000..61b9943e9 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift @@ -0,0 +1,58 @@ +// +// Copyright 2022 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 +import WysiwygComposer + +enum ComposerLinkActionCoordinatorAction { + case didTapCancel + case didDismissInteractively +} + +final class ComposerLinkActionCoordinator: NSObject, Coordinator, Presentable { + var childCoordinators: [Coordinator] = [] + + private let hostingController: UIViewController + private let viewModel: ComposerLinkActionViewModel + + var callback: ((ComposerLinkActionCoordinatorAction) -> Void)? + + init(linkAction: LinkAction) { + viewModel = ComposerLinkActionViewModel(from: linkAction) + hostingController = VectorHostingController(rootView: ComposerLinkActionView(viewModel: viewModel.context)) + super.init() + hostingController.presentationController?.delegate = self + } + + func start() { + viewModel.callback = { [weak self] result in + switch result { + case .cancel: + self?.callback?(.didTapCancel) + } + } + } + + func toPresentable() -> UIViewController { + hostingController + } +} + +extension ComposerLinkActionCoordinator: UIAdaptivePresentationControllerDelegate { + func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + self.callback?(.didDismissInteractively) + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift index 3bf974425..e810cc8f8 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift @@ -16,13 +16,25 @@ import SwiftUI -enum MockComposerLinkActionScreenState: MockScreenState, CaseIterable { +enum MockComposerLinkActionScreenState: MockScreenState, CaseIterable { + case edit + case createWithText + case create + var screenType: Any.Type { ComposerLinkActionView.self } var screenView: ([Any], AnyView) { - let viewModel = ComposerLinkActionViewModel(initialViewState: .init()) + let viewModel: ComposerLinkActionViewModel + switch self { + case .createWithText: + viewModel = .init(from: .createWithText) + case .create: + viewModel = .init(from: .create) + case .edit: + viewModel = .init(from: .edit(link: "https://element.io")) + } return ( [viewModel], AnyView(ComposerLinkActionView(viewModel: viewModel.context)) diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift index a4b941e33..5bce52cf7 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift @@ -16,13 +16,16 @@ import Foundation -enum ComposerLinkActionViewAction { +enum ComposerLinkActionViewAction: Equatable { + case cancel } enum ComposerLinkActionViewModelResult: Equatable { + case cancel } // MARK: View struct ComposerLinkActionViewState: BindableState { + let title: String } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift index 5efe10f56..339740cf6 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift @@ -1,4 +1,4 @@ -// +// // Copyright 2022 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,10 +17,31 @@ import SwiftUI struct ComposerLinkActionView: View { + @Environment(\.theme) private var theme: ThemeSwiftUI @ObservedObject private var viewModel: ComposerLinkActionViewModel.Context var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + NavigationView { + VStack {} + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button(VectorL10n.cancel, action: { + viewModel.send(viewAction: .cancel) + }) + } + ToolbarItem(placement: .principal) { + Text(viewModel.viewState.title) + .font(.headline) + .foregroundColor(theme.colors.primaryContent) + } + } + .navigationBarTitleDisplayMode(.inline) + .introspectNavigationController { navigationController in + ThemeService.shared().theme.applyStyle(onNavigationBar: navigationController.navigationBar) + } + } + .accentColor(theme.colors.accent) + .navigationViewStyle(StackNavigationViewStyle()) } init(viewModel: ComposerLinkActionViewModel.Context) { diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift index 8fd5724fd..60fb26aef 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift @@ -15,11 +15,39 @@ // import Foundation +import WysiwygComposer typealias ComposerLinkActionViewModelType = StateStoreViewModel final class ComposerLinkActionViewModel: ComposerLinkActionViewModelType, ComposerLinkActionViewModelProtocol { + // MARK: - Properties + + // MARK: Private + + // MARK: Public + var callback: ((ComposerLinkActionViewModelResult) -> Void)? + // MARK: - Public + + init(from linkAction: LinkAction) { + let initialViewState: ComposerLinkActionViewState + // TODO: Add translations + switch linkAction { + case .edit: + initialViewState = .init(title: "Edit Link") + case .createWithText, .create: + initialViewState = .init(title: "Create a Link") + } + + super.init(initialViewState: initialViewState) + } + + override func process(viewAction: ComposerLinkActionViewAction) { + switch viewAction { + case .cancel: + callback?(.cancel) + } + } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModelProtocol.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModelProtocol.swift index 73101dc3c..a33ddde00 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModelProtocol.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModelProtocol.swift @@ -1,4 +1,4 @@ -// +// // Copyright 2022 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); From 6aae0c4444ccc64c3812acaeb18180b608d13425 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 15:40:21 +0100 Subject: [PATCH 007/228] changed name --- .../ComposerLinkActionPresenter.swift | 38 ------------------- 1 file changed, 38 deletions(-) delete mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift deleted file mode 100644 index c36ecbe0b..000000000 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionPresenter.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright 2022 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 -import WysiwygComposer - -final class ComposerLinkActionCoordinator: Coordinator, Presentable { - var childCoordinators: [Coordinator] = [] - - private let hostingController: UIViewController - private let viewModel: ComposerLinkActionViewModel - - init(linkAction: LinkAction) { - viewModel = ComposerLinkActionViewModel(initialViewState: .init()) - hostingController = VectorHostingController(rootView: ComposerLinkActionView(viewModel: viewModel.context)) - } - - func start() { - - } - - func toPresentable() -> UIViewController { - hostingController - } -} From 31fd822f39cbbcd1b70fd024d22f6917aa87dcf8 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 17:34:06 +0100 Subject: [PATCH 008/228] view is created and responsive, I need now to implement the result processing of the actions --- .../ComposerLinkActionCoordinator.swift | 2 +- .../MockComposerLinkActionScreenState.swift | 4 +- .../Model/ComposerLinkActionModel.swift | 52 ++++++++- .../LinkAction/View/ComposerLinkAction.swift | 100 ++++++++++++++++++ .../View/ComposerLinkActionView.swift | 57 ---------- .../ComposerLinkActionViewModel.swift | 19 +++- 6 files changed, 168 insertions(+), 66 deletions(-) create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift delete mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift index 61b9943e9..ae9eb099f 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift @@ -32,7 +32,7 @@ final class ComposerLinkActionCoordinator: NSObject, Coordinator, Presentable { init(linkAction: LinkAction) { viewModel = ComposerLinkActionViewModel(from: linkAction) - hostingController = VectorHostingController(rootView: ComposerLinkActionView(viewModel: viewModel.context)) + hostingController = VectorHostingController(rootView: ComposerLinkAction(viewModel: viewModel.context)) super.init() hostingController.presentationController?.delegate = self } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift index e810cc8f8..383f5207b 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift @@ -22,7 +22,7 @@ enum MockComposerLinkActionScreenState: MockScreenState, CaseIterable { case create var screenType: Any.Type { - ComposerLinkActionView.self + ComposerLinkAction.self } var screenView: ([Any], AnyView) { @@ -37,7 +37,7 @@ enum MockComposerLinkActionScreenState: MockScreenState, CaseIterable { } return ( [viewModel], - AnyView(ComposerLinkActionView(viewModel: viewModel.context)) + AnyView(ComposerLinkAction(viewModel: viewModel.context)) ) } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift index 5bce52cf7..722b80ce6 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift @@ -1,4 +1,4 @@ -// +// // Copyright 2022 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,9 +15,12 @@ // import Foundation +import WysiwygComposer enum ComposerLinkActionViewAction: Equatable { case cancel + case save + case remove } enum ComposerLinkActionViewModelResult: Equatable { @@ -27,5 +30,50 @@ enum ComposerLinkActionViewModelResult: Equatable { // MARK: View struct ComposerLinkActionViewState: BindableState { - let title: String + let linkAction: LinkAction + + var bindings: ComposerLinkActionBindings +} + +extension ComposerLinkActionViewState { + var title: String { + // TODO: Add translations + switch linkAction { + case .createWithText, .create: return "Create a link" + case .edit: return "Edit link" + } + } + + var shouldDisplayTextField: Bool { + switch linkAction { + case .createWithText: return true + default: return false + } + } + + var shouldDisplayRemoveButton: Bool { + switch linkAction { + case .edit: return true + default: return false + } + } + + var isSaveButtonDisabled: Bool { + guard isValidLink else { return true } + switch linkAction { + case .create: return false + case .createWithText: return bindings.text.isEmpty + case let .edit(originalLink): return bindings.linkUrl == originalLink + } + } + + private var isValidLink: Bool { + guard let url = URL(string: bindings.linkUrl) else { return false } + return UIApplication.shared.canOpenURL(url) + } +} + +struct ComposerLinkActionBindings { + var text: String + var linkUrl: String } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift new file mode 100644 index 000000000..09de6c90f --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift @@ -0,0 +1,100 @@ +// +// Copyright 2022 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 SwiftUI + +struct ComposerLinkAction: View { + @Environment(\.theme) private var theme: ThemeSwiftUI + @ObservedObject private var viewModel: ComposerLinkActionViewModel.Context + + var body: some View { + NavigationView { + VStack(alignment: .leading, spacing: 0) { + VStack(alignment: .leading, spacing: 24) { + if viewModel.viewState.shouldDisplayTextField { + VStack(alignment: .leading, spacing: 8.0) { + // TODO: Add Translation + Text("Text") + .font(theme.fonts.subheadline) + .foregroundColor(theme.colors.secondaryContent) + TextField("", text: $viewModel.text) + .textFieldStyle(BorderedInputFieldStyle()) + } + } + VStack(alignment: .leading, spacing: 8.0) { + // TODO: Add Translation + Text("Link") + .font(theme.fonts.subheadline) + .foregroundColor(theme.colors.secondaryContent) + TextField("", text: $viewModel.linkUrl) + .keyboardType(.URL) + .textFieldStyle(BorderedInputFieldStyle()) + } + } + Spacer() + VStack(spacing: 16) { + Button(VectorL10n.save) { + viewModel.send(viewAction: .save) + } + .buttonStyle(PrimaryActionButtonStyle()) + .disabled(viewModel.viewState.isSaveButtonDisabled) + if viewModel.viewState.shouldDisplayRemoveButton { + Button(VectorL10n.remove) { + viewModel.send(viewAction: .remove) + } + .buttonStyle(PrimaryActionButtonStyle(customColor: theme.colors.alert)) + } + Button(VectorL10n.cancel) { + viewModel.send(viewAction: .cancel) + } + .buttonStyle(SecondaryActionButtonStyle()) + } + } + .padding(.top, 40.0) + .padding(.bottom, 12.0) + .padding(.horizontal, 16.0) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button(VectorL10n.cancel, action: { + viewModel.send(viewAction: .cancel) + }) + } + ToolbarItem(placement: .principal) { + Text(viewModel.viewState.title) + .font(.headline) + .foregroundColor(theme.colors.primaryContent) + } + } + .navigationBarTitleDisplayMode(.inline) + .introspectNavigationController { navigationController in + ThemeService.shared().theme.applyStyle(onNavigationBar: navigationController.navigationBar) + } + .accentColor(theme.colors.accent) + .navigationViewStyle(StackNavigationViewStyle()) + } + } + + init(viewModel: ComposerLinkActionViewModel.Context) { + self.viewModel = viewModel + } +} + +struct ComposerLinkActionView_Previews: PreviewProvider { + static let stateRenderer = MockComposerLinkActionScreenState.stateRenderer + static var previews: some View { + stateRenderer.screenGroup() + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift deleted file mode 100644 index 339740cf6..000000000 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkActionView.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright 2022 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 SwiftUI - -struct ComposerLinkActionView: View { - @Environment(\.theme) private var theme: ThemeSwiftUI - @ObservedObject private var viewModel: ComposerLinkActionViewModel.Context - - var body: some View { - NavigationView { - VStack {} - .toolbar { - ToolbarItem(placement: .navigationBarLeading) { - Button(VectorL10n.cancel, action: { - viewModel.send(viewAction: .cancel) - }) - } - ToolbarItem(placement: .principal) { - Text(viewModel.viewState.title) - .font(.headline) - .foregroundColor(theme.colors.primaryContent) - } - } - .navigationBarTitleDisplayMode(.inline) - .introspectNavigationController { navigationController in - ThemeService.shared().theme.applyStyle(onNavigationBar: navigationController.navigationBar) - } - } - .accentColor(theme.colors.accent) - .navigationViewStyle(StackNavigationViewStyle()) - } - - init(viewModel: ComposerLinkActionViewModel.Context) { - self.viewModel = viewModel - } -} - -struct ComposerLinkActionView_Previews: PreviewProvider { - static let stateRenderer = MockComposerLinkActionScreenState.stateRenderer - static var previews: some View { - stateRenderer.screenGroup() - } -} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift index 60fb26aef..8c07669c1 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift @@ -20,7 +20,6 @@ import WysiwygComposer typealias ComposerLinkActionViewModelType = StateStoreViewModel final class ComposerLinkActionViewModel: ComposerLinkActionViewModelType, ComposerLinkActionViewModelProtocol { - // MARK: - Properties // MARK: Private @@ -33,12 +32,22 @@ final class ComposerLinkActionViewModel: ComposerLinkActionViewModelType, Compos init(from linkAction: LinkAction) { let initialViewState: ComposerLinkActionViewState + let simpleBindings = ComposerLinkActionBindings(text: "", linkUrl: "") // TODO: Add translations switch linkAction { case .edit: - initialViewState = .init(title: "Edit Link") - case .createWithText, .create: - initialViewState = .init(title: "Create a Link") + let link = "https://element.io" + initialViewState = .init( + linkAction: .edit(link: link), + bindings: .init( + text: "", + linkUrl: link + ) + ) + case .createWithText: + initialViewState = .init(linkAction: .createWithText, bindings: simpleBindings) + case .create: + initialViewState = .init(linkAction: .create, bindings: simpleBindings) } super.init(initialViewState: initialViewState) @@ -48,6 +57,8 @@ final class ComposerLinkActionViewModel: ComposerLinkActionViewModelType, Compos switch viewAction { case .cancel: callback?(.cancel) + case .save, .remove: + break } } } From 3aad54efde89495c5465d9722b75b68a1211fe40 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 18:29:13 +0100 Subject: [PATCH 009/228] implementation done, only tests, localisation and design feedback are left --- .../xcshareddata/swiftpm/Package.resolved | 2 +- Riot/Modules/Room/RoomViewController.swift | 16 ++++++++-- .../WysiwygInputToolbarView.swift | 7 +++++ .../ComposerLinkActionBridgePresenter.swift | 3 ++ .../ComposerLinkActionCoordinator.swift | 5 +++- .../Model/ComposerLinkActionModel.swift | 1 + .../ComposerLinkActionViewModel.swift | 30 ++++++++++++++----- .../Room/Composer/Model/ComposerModels.swift | 1 + .../Modules/Room/Composer/View/Composer.swift | 5 ++++ .../ViewModel/ComposerViewModel.swift | 6 +++- .../ViewModel/ComposerViewModelProtocol.swift | 1 + project.yml | 2 +- 12 files changed, 66 insertions(+), 13 deletions(-) diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6abad0a50..cd64251b6 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,7 +23,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift", "state" : { - "revision" : "a4a7ad46b76cfb8de4695c5fd211a735cb438e1a" + "revision" : "b8d55afebfaae94b32bc3deeb1607eb150442e1f" } }, { diff --git a/Riot/Modules/Room/RoomViewController.swift b/Riot/Modules/Room/RoomViewController.swift index dc17e93ed..d1745dc6b 100644 --- a/Riot/Modules/Room/RoomViewController.swift +++ b/Riot/Modules/Room/RoomViewController.swift @@ -15,6 +15,7 @@ // import UIKit +import WysiwygComposer extension RoomViewController { // MARK: - Override @@ -290,17 +291,28 @@ private extension RoomViewController { } extension RoomViewController: ComposerLinkActionBridgePresenterDelegate { + func didRequestLinkOperation(_ linkOperation: WysiwygLinkOperation) { + dismissPresenter { [weak self] in + self?.wysiwygInputToolbar?.performLinkOperation(linkOperation) + } + } + func didDismissInteractively() { - self.cleanup() + cleanup() } func didCancel() { + dismissPresenter(completion: nil) + } + + private func dismissPresenter(completion: (() -> Void)?) { self.composerLinkActionBridgePresenter?.dismiss(animated: true) { [weak self] in + completion?() self?.cleanup() } } private func cleanup() { - self.composerLinkActionBridgePresenter = nil + composerLinkActionBridgePresenter = nil } } diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index 3399acce4..b282b608e 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -212,6 +212,13 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp wysiwygViewModel.maximised = false } + func performLinkOperation(_ linkOperation: WysiwygLinkOperation) { + if let selectionToRestore = viewModel.selectionToRestore { + wysiwygViewModel.attributedContent.selection = selectionToRestore + } + wysiwygViewModel.applyLinkOperation(linkOperation) + } + // MARK: - Private @objc private func keyboardWillShow(_ notification: Notification) { diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift index f7486d50b..b0c69c221 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift @@ -20,6 +20,7 @@ import WysiwygComposer protocol ComposerLinkActionBridgePresenterDelegate: AnyObject { func didCancel() func didDismissInteractively() + func didRequestLinkOperation(_ linkOperation: WysiwygLinkOperation) } final class ComposerLinkActionBridgePresenter: NSObject { @@ -41,6 +42,8 @@ final class ComposerLinkActionBridgePresenter: NSObject { self?.delegate?.didCancel() case .didDismissInteractively: self?.delegate?.didDismissInteractively() + case let .didRequestLinkOperation(linkOperation): + self?.delegate?.didRequestLinkOperation(linkOperation) } } let presentable = composerLinkActionCoordinator.toPresentable() diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift index ae9eb099f..c3bd7406e 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift @@ -20,6 +20,7 @@ import WysiwygComposer enum ComposerLinkActionCoordinatorAction { case didTapCancel case didDismissInteractively + case didRequestLinkOperation(_ linkOperation: WysiwygLinkOperation) } final class ComposerLinkActionCoordinator: NSObject, Coordinator, Presentable { @@ -42,10 +43,12 @@ final class ComposerLinkActionCoordinator: NSObject, Coordinator, Presentable { switch result { case .cancel: self?.callback?(.didTapCancel) + case let .performOperation(linkOperation): + self?.callback?(.didRequestLinkOperation(linkOperation)) } } } - + func toPresentable() -> UIViewController { hostingController } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift index 722b80ce6..c91462a09 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift @@ -25,6 +25,7 @@ enum ComposerLinkActionViewAction: Equatable { enum ComposerLinkActionViewModelResult: Equatable { case cancel + case performOperation(_ linkOperation: WysiwygLinkOperation) } // MARK: View diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift index 8c07669c1..9683ac621 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/ViewModel/ComposerLinkActionViewModel.swift @@ -21,9 +21,9 @@ typealias ComposerLinkActionViewModelType = StateStoreViewModel Void)? @@ -33,10 +33,8 @@ final class ComposerLinkActionViewModel: ComposerLinkActionViewModelType, Compos init(from linkAction: LinkAction) { let initialViewState: ComposerLinkActionViewState let simpleBindings = ComposerLinkActionBindings(text: "", linkUrl: "") - // TODO: Add translations switch linkAction { - case .edit: - let link = "https://element.io" + case let .edit(link): initialViewState = .init( linkAction: .edit(link: link), bindings: .init( @@ -57,8 +55,26 @@ final class ComposerLinkActionViewModel: ComposerLinkActionViewModelType, Compos switch viewAction { case .cancel: callback?(.cancel) - case .save, .remove: - break + case .remove: + callback?(.performOperation(.removeLinks)) + case .save: + switch state.linkAction { + case .createWithText: + callback?( + .performOperation( + .createLink( + urlString: state.bindings.linkUrl, + text: state.bindings.text + ) + ) + ) + case .create, .edit: + callback?( + .performOperation( + .setLink(urlString: state.bindings.linkUrl) + ) + ) + } } } } diff --git a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift index ffe187abe..46efbe878 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift @@ -142,6 +142,7 @@ enum ComposerViewAction: Equatable { case cancel case contentDidChange(isEmpty: Bool) case linkTapped(linkAction: LinkAction) + case storeSelection(selection: NSRange) } enum ComposerViewModelResult: Equatable { diff --git a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift index 4afd6e391..f5b7e179e 100644 --- a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift +++ b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift @@ -227,6 +227,7 @@ struct Composer: View { sendMediaButton FormattingToolbar(formatItems: formatItems) { type in if type.action == .link { + storeCurrentSelection() sendLinkAction() } else { wysiwygViewModel.apply(type.action) @@ -247,6 +248,10 @@ struct Composer: View { } } + private func storeCurrentSelection() { + viewModel.send(viewAction: .storeSelection(selection: wysiwygViewModel.attributedContent.selection)) + } + private func sendLinkAction() { let linkAction = wysiwygViewModel.getLinkAction() viewModel.send(viewAction: .linkTapped(linkAction: linkAction)) diff --git a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift index b9275709d..a78018f60 100644 --- a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModel.swift @@ -22,7 +22,7 @@ final class ComposerViewModel: ComposerViewModelType, ComposerViewModelProtocol // MARK: - Properties // MARK: Private - + // MARK: Public var callback: ((ComposerViewModelResult) -> Void)? @@ -76,6 +76,8 @@ final class ComposerViewModel: ComposerViewModelType, ComposerViewModelProtocol state.bindings.focused } + var selectionToRestore: NSRange? + // MARK: - Public override func process(viewAction: ComposerViewAction) { @@ -86,6 +88,8 @@ final class ComposerViewModel: ComposerViewModelType, ComposerViewModelProtocol callback?(.contentDidChange(isEmpty: isEmpty)) case let .linkTapped(linkAction): callback?(.linkTapped(LinkAction: linkAction)) + case let .storeSelection(selection): + selectionToRestore = selection } } diff --git a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModelProtocol.swift b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModelProtocol.swift index 0d23be3cc..cb600c104 100644 --- a/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModelProtocol.swift +++ b/RiotSwiftUI/Modules/Room/Composer/ViewModel/ComposerViewModelProtocol.swift @@ -25,6 +25,7 @@ protocol ComposerViewModelProtocol { var placeholder: String? { get set } var isFocused: Bool { get } var isLandscapePhone: Bool { get set } + var selectionToRestore: NSRange? { get } func dismissKeyboard() func showKeyboard() diff --git a/project.yml b/project.yml index 12b4bc027..ff7756c0a 100644 --- a/project.yml +++ b/project.yml @@ -53,7 +53,7 @@ packages: branch: main WysiwygComposer: url: https://github.com/matrix-org/matrix-wysiwyg-composer-swift - revision: a4a7ad46b76cfb8de4695c5fd211a735cb438e1a + revision: b8d55afebfaae94b32bc3deeb1607eb150442e1f DeviceKit: url: https://github.com/devicekit/DeviceKit majorVersion: 4.7.0 From 0d6c5754458f46714936cbaf2e888ad721cff4d8 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 18:50:26 +0100 Subject: [PATCH 010/228] fix --- .../Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index b282b608e..bb53f7dbe 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -214,7 +214,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp func performLinkOperation(_ linkOperation: WysiwygLinkOperation) { if let selectionToRestore = viewModel.selectionToRestore { - wysiwygViewModel.attributedContent.selection = selectionToRestore + wysiwygViewModel.select(range: selectionToRestore) } wysiwygViewModel.applyLinkOperation(linkOperation) } From aa78c4d30c0adc4c5f3879eb78a4dcd9d9d913d8 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 19:09:15 +0100 Subject: [PATCH 011/228] fix --- .../ComposerLinkActionBridgePresenter.swift | 65 +++++++++++++++++++ .../ComposerLinkActionCoordinator.swift | 61 +++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Coordinator/ComposerLinkActionBridgePresenter.swift create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Coordinator/ComposerLinkActionCoordinator.swift diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coordinator/ComposerLinkActionBridgePresenter.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coordinator/ComposerLinkActionBridgePresenter.swift new file mode 100644 index 000000000..b0c69c221 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coordinator/ComposerLinkActionBridgePresenter.swift @@ -0,0 +1,65 @@ +// +// Copyright 2022 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 +import WysiwygComposer + +protocol ComposerLinkActionBridgePresenterDelegate: AnyObject { + func didCancel() + func didDismissInteractively() + func didRequestLinkOperation(_ linkOperation: WysiwygLinkOperation) +} + +final class ComposerLinkActionBridgePresenter: NSObject { + private var coordinator: ComposerLinkActionCoordinator? + private var linkAction: LinkAction + + weak var delegate: ComposerLinkActionBridgePresenterDelegate? + + init(linkAction: LinkActionWrapper) { + self.linkAction = linkAction.linkAction + super.init() + } + + func present(from viewController: UIViewController, animated: Bool) { + let composerLinkActionCoordinator = ComposerLinkActionCoordinator(linkAction: linkAction) + composerLinkActionCoordinator.callback = { [weak self] action in + switch action { + case .didTapCancel: + self?.delegate?.didCancel() + case .didDismissInteractively: + self?.delegate?.didDismissInteractively() + case let .didRequestLinkOperation(linkOperation): + self?.delegate?.didRequestLinkOperation(linkOperation) + } + } + let presentable = composerLinkActionCoordinator.toPresentable() + viewController.present(presentable, animated: animated, completion: nil) + composerLinkActionCoordinator.start() + coordinator = composerLinkActionCoordinator + } + + func dismiss(animated: Bool, completion: (() -> Void)?) { + guard let coordinator = coordinator else { + return + } + // Dismiss modal + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + completion?() + } + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coordinator/ComposerLinkActionCoordinator.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coordinator/ComposerLinkActionCoordinator.swift new file mode 100644 index 000000000..c3bd7406e --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coordinator/ComposerLinkActionCoordinator.swift @@ -0,0 +1,61 @@ +// +// Copyright 2022 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 +import WysiwygComposer + +enum ComposerLinkActionCoordinatorAction { + case didTapCancel + case didDismissInteractively + case didRequestLinkOperation(_ linkOperation: WysiwygLinkOperation) +} + +final class ComposerLinkActionCoordinator: NSObject, Coordinator, Presentable { + var childCoordinators: [Coordinator] = [] + + private let hostingController: UIViewController + private let viewModel: ComposerLinkActionViewModel + + var callback: ((ComposerLinkActionCoordinatorAction) -> Void)? + + init(linkAction: LinkAction) { + viewModel = ComposerLinkActionViewModel(from: linkAction) + hostingController = VectorHostingController(rootView: ComposerLinkAction(viewModel: viewModel.context)) + super.init() + hostingController.presentationController?.delegate = self + } + + func start() { + viewModel.callback = { [weak self] result in + switch result { + case .cancel: + self?.callback?(.didTapCancel) + case let .performOperation(linkOperation): + self?.callback?(.didRequestLinkOperation(linkOperation)) + } + } + } + + func toPresentable() -> UIViewController { + hostingController + } +} + +extension ComposerLinkActionCoordinator: UIAdaptivePresentationControllerDelegate { + func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + self.callback?(.didDismissInteractively) + } +} From a9cc5a71e7bc2d56e33c163dfa74738e96927c24 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 19:09:22 +0100 Subject: [PATCH 012/228] fix --- .../ComposerLinkActionBridgePresenter.swift | 65 ------------------- .../ComposerLinkActionCoordinator.swift | 61 ----------------- 2 files changed, 126 deletions(-) delete mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift delete mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift deleted file mode 100644 index b0c69c221..000000000 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionBridgePresenter.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright 2022 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 -import WysiwygComposer - -protocol ComposerLinkActionBridgePresenterDelegate: AnyObject { - func didCancel() - func didDismissInteractively() - func didRequestLinkOperation(_ linkOperation: WysiwygLinkOperation) -} - -final class ComposerLinkActionBridgePresenter: NSObject { - private var coordinator: ComposerLinkActionCoordinator? - private var linkAction: LinkAction - - weak var delegate: ComposerLinkActionBridgePresenterDelegate? - - init(linkAction: LinkActionWrapper) { - self.linkAction = linkAction.linkAction - super.init() - } - - func present(from viewController: UIViewController, animated: Bool) { - let composerLinkActionCoordinator = ComposerLinkActionCoordinator(linkAction: linkAction) - composerLinkActionCoordinator.callback = { [weak self] action in - switch action { - case .didTapCancel: - self?.delegate?.didCancel() - case .didDismissInteractively: - self?.delegate?.didDismissInteractively() - case let .didRequestLinkOperation(linkOperation): - self?.delegate?.didRequestLinkOperation(linkOperation) - } - } - let presentable = composerLinkActionCoordinator.toPresentable() - viewController.present(presentable, animated: animated, completion: nil) - composerLinkActionCoordinator.start() - coordinator = composerLinkActionCoordinator - } - - func dismiss(animated: Bool, completion: (() -> Void)?) { - guard let coordinator = coordinator else { - return - } - // Dismiss modal - coordinator.toPresentable().dismiss(animated: animated) { - self.coordinator = nil - completion?() - } - } -} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift deleted file mode 100644 index c3bd7406e..000000000 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Coodinator/ComposerLinkActionCoordinator.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright 2022 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 -import WysiwygComposer - -enum ComposerLinkActionCoordinatorAction { - case didTapCancel - case didDismissInteractively - case didRequestLinkOperation(_ linkOperation: WysiwygLinkOperation) -} - -final class ComposerLinkActionCoordinator: NSObject, Coordinator, Presentable { - var childCoordinators: [Coordinator] = [] - - private let hostingController: UIViewController - private let viewModel: ComposerLinkActionViewModel - - var callback: ((ComposerLinkActionCoordinatorAction) -> Void)? - - init(linkAction: LinkAction) { - viewModel = ComposerLinkActionViewModel(from: linkAction) - hostingController = VectorHostingController(rootView: ComposerLinkAction(viewModel: viewModel.context)) - super.init() - hostingController.presentationController?.delegate = self - } - - func start() { - viewModel.callback = { [weak self] result in - switch result { - case .cancel: - self?.callback?(.didTapCancel) - case let .performOperation(linkOperation): - self?.callback?(.didRequestLinkOperation(linkOperation)) - } - } - } - - func toPresentable() -> UIViewController { - hostingController - } -} - -extension ComposerLinkActionCoordinator: UIAdaptivePresentationControllerDelegate { - func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { - self.callback?(.didDismissInteractively) - } -} From de260068f04fbcebcc1cf318238ae0c208aa7d12 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 19:26:45 +0100 Subject: [PATCH 013/228] link color support added --- .../Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index bb53f7dbe..9c89dc287 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -42,7 +42,10 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp private var heightConstraint: NSLayoutConstraint! private var voiceMessageBottomConstraint: NSLayoutConstraint? private var hostingViewController: VectorHostingController! - private var wysiwygViewModel = WysiwygComposerViewModel(textColor: ThemeService.shared().theme.colors.primaryContent) + private var wysiwygViewModel = WysiwygComposerViewModel( + textColor: ThemeService.shared().theme.colors.primaryContent, + linkColor: ThemeService.shared().theme.colors.accent + ) private var viewModel: ComposerViewModelProtocol! private var isLandscapePhone: Bool { @@ -295,6 +298,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 } private func updateTextViewHeight() { From af0026adc012cdc67baf5386a7a5ed776da4322a Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 10:30:47 +0100 Subject: [PATCH 014/228] fixed link color issue --- Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- project.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index cd64251b6..7d4a2e9c4 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,7 +23,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift", "state" : { - "revision" : "b8d55afebfaae94b32bc3deeb1607eb150442e1f" + "revision" : "55266ea8f338e9c1d5c4db375713b3f902317b00" } }, { diff --git a/project.yml b/project.yml index ff7756c0a..69a0c0b97 100644 --- a/project.yml +++ b/project.yml @@ -53,7 +53,7 @@ packages: branch: main WysiwygComposer: url: https://github.com/matrix-org/matrix-wysiwyg-composer-swift - revision: b8d55afebfaae94b32bc3deeb1607eb150442e1f + revision: 55266ea8f338e9c1d5c4db375713b3f902317b00 DeviceKit: url: https://github.com/devicekit/DeviceKit majorVersion: 4.7.0 From cf543be08a417d5d76781e00e143423cbcd2874e Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 10:37:28 +0100 Subject: [PATCH 015/228] changelog --- changelog.d/7159.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7159.feature diff --git a/changelog.d/7159.feature b/changelog.d/7159.feature new file mode 100644 index 000000000..5acb4948d --- /dev/null +++ b/changelog.d/7159.feature @@ -0,0 +1 @@ +Rich Text Composer: added link creation/editing feature. \ No newline at end of file From f23b03cbd63e72080c9e5a24db8654348cd61937 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 13:56:49 +0100 Subject: [PATCH 016/228] localisation added --- Riot/Assets/en.lproj/Vector.strings | 7 +++++++ Riot/Generated/Strings.swift | 20 +++++++++++++++++++ .../Model/ComposerLinkActionModel.swift | 5 ++--- .../LinkAction/View/ComposerLinkAction.swift | 10 ++++++---- .../Room/Composer/Model/ComposerModels.swift | 3 +-- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 049c89b89..f24442af9 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2549,6 +2549,13 @@ To enable access, tap Settings> Location and select Always"; "wysiwyg_composer_format_action_italic" = "Apply italic format"; "wysiwyg_composer_format_action_underline" = "Apply strikethrough format"; "wysiwyg_composer_format_action_strikethrough" = "Apply underline format"; +"wysiwyg_composer_format_action_link" = "Apply link format"; + +// Links +"wysiwyg_composer_link_action_text" = "Text"; +"wysiwyg_composer_link_action_link" = "Link"; +"wysiwyg_composer_link_action_create_title" = "Create a link"; +"wysiwyg_composer_link_action_edit_title" = "Edit link"; // MARK: - MatrixKit diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 056673940..8e2451e7a 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -9315,6 +9315,10 @@ public class VectorL10n: NSObject { public static var wysiwygComposerFormatActionItalic: String { return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_italic") } + /// Apply link format + public static var wysiwygComposerFormatActionLink: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_link") + } /// Apply underline format public static var wysiwygComposerFormatActionStrikethrough: String { return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_strikethrough") @@ -9323,6 +9327,22 @@ public class VectorL10n: NSObject { public static var wysiwygComposerFormatActionUnderline: String { return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_underline") } + /// Create a link + public static var wysiwygComposerLinkActionCreateTitle: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_link_action_create_title") + } + /// Edit link + public static var wysiwygComposerLinkActionEditTitle: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_link_action_edit_title") + } + /// Link + public static var wysiwygComposerLinkActionLink: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_link_action_link") + } + /// Text + public static var wysiwygComposerLinkActionText: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_link_action_text") + } /// Attachments public static var wysiwygComposerStartActionAttachments: String { return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_attachments") diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift index c91462a09..74ca3c89f 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift @@ -38,10 +38,9 @@ struct ComposerLinkActionViewState: BindableState { extension ComposerLinkActionViewState { var title: String { - // TODO: Add translations switch linkAction { - case .createWithText, .create: return "Create a link" - case .edit: return "Edit link" + case .createWithText, .create: return VectorL10n.wysiwygComposerLinkActionCreateTitle + case .edit: return VectorL10n.wysiwygComposerLinkActionEditTitle } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift index 09de6c90f..731793e75 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift @@ -26,22 +26,24 @@ struct ComposerLinkAction: View { VStack(alignment: .leading, spacing: 24) { if viewModel.viewState.shouldDisplayTextField { VStack(alignment: .leading, spacing: 8.0) { - // TODO: Add Translation - Text("Text") + Text(VectorL10n.wysiwygComposerLinkActionText) .font(theme.fonts.subheadline) .foregroundColor(theme.colors.secondaryContent) TextField("", text: $viewModel.text) .textFieldStyle(BorderedInputFieldStyle()) + .accessibilityIdentifier("textTextField") + .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionText) } } VStack(alignment: .leading, spacing: 8.0) { - // TODO: Add Translation - Text("Link") + Text(VectorL10n.wysiwygComposerLinkActionLink) .font(theme.fonts.subheadline) .foregroundColor(theme.colors.secondaryContent) TextField("", text: $viewModel.linkUrl) .keyboardType(.URL) .textFieldStyle(BorderedInputFieldStyle()) + .accessibilityIdentifier("linkTextField") + .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionLink) } } Spacer() diff --git a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift index 46efbe878..8d744ff27 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift @@ -90,8 +90,7 @@ extension FormatItem { case .underline: return VectorL10n.wysiwygComposerFormatActionUnderline case .link: - // TODO: Add link accessibility label translation - return "link" + return VectorL10n.wysiwygComposerFormatActionLink } } } From e776e6944d4e2b1b2ac94ffd2ef8d45daeb1669e Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 13 Dec 2022 14:19:55 +0100 Subject: [PATCH 017/228] Delete old devices in account data --- .../MatrixSDK/UserSessionsDataProvider.swift | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift index a5c90d65d..5f1273329 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift @@ -37,7 +37,16 @@ class UserSessionsDataProvider: UserSessionsDataProviderProtocol { } func devices(completion: @escaping (MXResponse<[MXDevice]>) -> Void) { - session.matrixRestClient.devices(completion: completion) + session.matrixRestClient.devices { [weak self] response in + switch response { + case .success(let devices): + self?.deleteAccountDataIfNeeded(deviceList: devices) + case .failure: + break + } + + completion(response) + } } func device(withDeviceId deviceId: String, ofUser userId: String) -> MXDeviceInfo? { @@ -66,3 +75,22 @@ class UserSessionsDataProvider: UserSessionsDataProviderProtocol { return try await service.isServiceAvailable() } } + +private extension UserSessionsDataProvider { + func deleteAccountDataIfNeeded(deviceList: [MXDevice]) { + let deviceAccountDataKeys = Set( + session + .accountData + .allAccountDataEvents() + .map(\.key) + .filter { $0.hasPrefix(kMXAccountDataTypeClientInformation) } + ) + + let expectedDeviceAccountDataKeys = Set(deviceList.map { "\(kMXAccountDataTypeClientInformation).\($0.deviceId)" }) + let obsoletedDeviceAccountDataKeys = deviceAccountDataKeys.subtracting(expectedDeviceAccountDataKeys) + + for accountDataKey in obsoletedDeviceAccountDataKeys { + session.deleteAccountData(withType: accountDataKey, success: {}, failure: { _ in }) + } + } +} From 8b6327ece4bc66a1d9990d41e893f3d40392e285 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 15:00:46 +0100 Subject: [PATCH 018/228] removed autocapitalization and improved the animation --- .../Room/Composer/LinkAction/View/ComposerLinkAction.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift index 731793e75..7bffb6d0a 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift @@ -31,6 +31,7 @@ struct ComposerLinkAction: View { .foregroundColor(theme.colors.secondaryContent) TextField("", text: $viewModel.text) .textFieldStyle(BorderedInputFieldStyle()) + .autocapitalization(.none) .accessibilityIdentifier("textTextField") .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionText) } @@ -41,6 +42,7 @@ struct ComposerLinkAction: View { .foregroundColor(theme.colors.secondaryContent) TextField("", text: $viewModel.linkUrl) .keyboardType(.URL) + .autocapitalization(.none) .textFieldStyle(BorderedInputFieldStyle()) .accessibilityIdentifier("linkTextField") .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionLink) @@ -53,6 +55,7 @@ struct ComposerLinkAction: View { } .buttonStyle(PrimaryActionButtonStyle()) .disabled(viewModel.viewState.isSaveButtonDisabled) + .animation(.easeInOut(duration: 0.15), value: viewModel.viewState.isSaveButtonDisabled) if viewModel.viewState.shouldDisplayRemoveButton { Button(VectorL10n.remove) { viewModel.send(viewAction: .remove) From 43b04a2c534425d5d6facdf4c3838858202f6fa5 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Tue, 13 Dec 2022 15:07:21 +0100 Subject: [PATCH 019/228] Fix PR comments --- .../VoiceBroadcastPlaybackViewModel.swift | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift index 9d806210c..61b450d77 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift @@ -173,22 +173,16 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic /// Backward (30sec) a voice broadcast private func backward() { let newProgressValue = context.progress - VoiceBroadcastPlaybackViewModel.defaultBackwardForwardValue - seekTo(newProgressValue > 0 ? newProgressValue : 0) - - state.bindings.progress = newProgressValue - updateBackwardForwardButtons() + seek(to: max(newProgressValue, 0.0)) } /// Forward (30sec) a voice broadcast private func forward() { let newProgressValue = context.progress + VoiceBroadcastPlaybackViewModel.defaultBackwardForwardValue - seekTo(newProgressValue < state.playingState.duration ? newProgressValue : state.playingState.duration) - - state.bindings.progress = newProgressValue - updateBackwardForwardButtons() + seek(to: min(newProgressValue, state.playingState.duration)) } - private func seekTo(_ seekTime: Float) { + private func seek(to seekTime: Float) { // Flush the chunks queue and the current audio player playlist voiceBroadcastChunkQueue = [] reloadVoiceBroadcastChunkQueue = isProcessingVoiceBroadcastChunk @@ -215,6 +209,9 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic state.playbackState = .buffering } processPendingVoiceBroadcastChunks() + + state.bindings.progress = seekTime + updateUI() } // MARK: - Voice broadcast chunks playback @@ -347,16 +344,11 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic audioPlayer?.pause() displayLink.isPaused = true } else { - seekTo(state.bindings.progress) - updateBackwardForwardButtons() + seek(to: state.bindings.progress) } } @objc private func handleDisplayLinkTick() { - updateUI() - } - - private func updateUI() { guard let playingEventId = voiceBroadcastAttachmentCacheManagerLoadResults.first(where: { result in result.url == audioPlayer?.currentUrl })?.eventIdentifier, @@ -372,10 +364,10 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic state.bindings.progress = Float(progress) - updateBackwardForwardButtons() + updateUI() } - private func updateBackwardForwardButtons() { + private func updateUI() { state.playingState.canMoveBackward = state.bindings.progress > 0 state.playingState.canMoveForward = state.bindings.progress < state.playingState.duration } From ad3028257a65c5335e44f2e9d4baa306186c813b Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 13 Dec 2022 15:35:18 +0100 Subject: [PATCH 020/228] Add UTs --- .../MatrixSDK/UserSessionsDataProvider.swift | 21 ++++--- RiotTests/UserSessionsDataProviderTests.swift | 57 +++++++++++++++++++ 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift index 5f1273329..60f0b2054 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift @@ -76,18 +76,25 @@ class UserSessionsDataProvider: UserSessionsDataProviderProtocol { } } -private extension UserSessionsDataProvider { - func deleteAccountDataIfNeeded(deviceList: [MXDevice]) { +extension UserSessionsDataProvider { + // internal just to facilitate tests + func obsoletedDeviceAccountData(deviceList: [MXDevice], accountDataEvents: [String: Any]) -> Set { let deviceAccountDataKeys = Set( - session - .accountData - .allAccountDataEvents() + accountDataEvents .map(\.key) .filter { $0.hasPrefix(kMXAccountDataTypeClientInformation) } ) - let expectedDeviceAccountDataKeys = Set(deviceList.map { "\(kMXAccountDataTypeClientInformation).\($0.deviceId)" }) - let obsoletedDeviceAccountDataKeys = deviceAccountDataKeys.subtracting(expectedDeviceAccountDataKeys) + let expectedDeviceAccountDataKeys = Set(deviceList.map { + "\(kMXAccountDataTypeClientInformation).\($0.deviceId)" + }) + + return deviceAccountDataKeys.subtracting(expectedDeviceAccountDataKeys) + } + + private func deleteAccountDataIfNeeded(deviceList: [MXDevice]) { + let obsoletedDeviceAccountDataKeys = obsoletedDeviceAccountData(deviceList: deviceList, + accountDataEvents: session.accountData.allAccountDataEvents()) for accountDataKey in obsoletedDeviceAccountDataKeys { session.deleteAccountData(withType: accountDataKey, success: {}, failure: { _ in }) diff --git a/RiotTests/UserSessionsDataProviderTests.swift b/RiotTests/UserSessionsDataProviderTests.swift index 4695d9fc3..1877f22c4 100644 --- a/RiotTests/UserSessionsDataProviderTests.swift +++ b/RiotTests/UserSessionsDataProviderTests.swift @@ -100,10 +100,67 @@ class UserSessionCardViewDataTests: XCTestCase { XCTAssertEqual(verificationState, .permanentlyUnverified) } + + func testObsoletedDeviceInformation_someMatch() { + let mxSession = MockSession(canCrossSign: true) + let dataProvider = UserSessionsDataProvider(session: mxSession) + + let accountDataEvents: [String: Any] = [ + "io.element.matrix_client_information.D": "" + ] + + let expectedObsoletedEvents: Set = [ + "io.element.matrix_client_information.D" + ] + + let obsoletedEvents = dataProvider.obsoletedDeviceAccountData(deviceList: .mockDevices, accountDataEvents: accountDataEvents) + + XCTAssertEqual(obsoletedEvents, expectedObsoletedEvents) + } + + func testObsoletedDeviceInformation_noMatch() { + let mxSession = MockSession(canCrossSign: true) + let dataProvider = UserSessionsDataProvider(session: mxSession) + + let accountDataEvents: [String: Any] = [ + "io.element.matrix_client_information.C": "" + ] + + let expectedObsoletedEvents: Set = [] + let obsoletedEvents = dataProvider.obsoletedDeviceAccountData(deviceList: .mockDevices, accountDataEvents: accountDataEvents) + + XCTAssertEqual(obsoletedEvents, expectedObsoletedEvents) + } + + func testObsoletedDeviceInformation_allMatch() { + let mxSession = MockSession(canCrossSign: true) + let dataProvider = UserSessionsDataProvider(session: mxSession) + + let expectedObsoletedEvents = Set(["D", "E", "F"].map { "io.element.matrix_client_information.\($0)"}) + + let accountDataEvents: [String: Any] = expectedObsoletedEvents.reduce(into: [:]) { partialResult, value in + partialResult[value] = "" + } + + let obsoletedEvents = dataProvider.obsoletedDeviceAccountData(deviceList: .mockDevices, accountDataEvents: accountDataEvents) + + XCTAssertEqual(obsoletedEvents, expectedObsoletedEvents) + } } // MARK: Mocks +private extension Array where Element == MXDevice { + static let mockDevices: [MXDevice] = { + ["A", "B", "C"] + .map { + let device = MXDevice() + device.deviceId = $0 + return device + } + }() +} + // Device ID constants. private extension String { static var otherDeviceA: String { "abcdef" } From 5e9730adf1cfcc4567efd289d37e072c8918a114 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 13 Dec 2022 15:45:37 +0100 Subject: [PATCH 021/228] Refine UTs --- .../MatrixSDK/UserSessionsDataProvider.swift | 18 +++++++++--------- RiotTests/UserSessionsDataProviderTests.swift | 8 +++++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift index 60f0b2054..d54d865ac 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/Service/MatrixSDK/UserSessionsDataProvider.swift @@ -77,6 +77,15 @@ class UserSessionsDataProvider: UserSessionsDataProviderProtocol { } extension UserSessionsDataProvider { + private func deleteAccountDataIfNeeded(deviceList: [MXDevice]) { + let obsoletedDeviceAccountDataKeys = obsoletedDeviceAccountData(deviceList: deviceList, + accountDataEvents: session.accountData.allAccountDataEvents()) + + for accountDataKey in obsoletedDeviceAccountDataKeys { + session.deleteAccountData(withType: accountDataKey, success: {}, failure: { _ in }) + } + } + // internal just to facilitate tests func obsoletedDeviceAccountData(deviceList: [MXDevice], accountDataEvents: [String: Any]) -> Set { let deviceAccountDataKeys = Set( @@ -91,13 +100,4 @@ extension UserSessionsDataProvider { return deviceAccountDataKeys.subtracting(expectedDeviceAccountDataKeys) } - - private func deleteAccountDataIfNeeded(deviceList: [MXDevice]) { - let obsoletedDeviceAccountDataKeys = obsoletedDeviceAccountData(deviceList: deviceList, - accountDataEvents: session.accountData.allAccountDataEvents()) - - for accountDataKey in obsoletedDeviceAccountDataKeys { - session.deleteAccountData(withType: accountDataKey, success: {}, failure: { _ in }) - } - } } diff --git a/RiotTests/UserSessionsDataProviderTests.swift b/RiotTests/UserSessionsDataProviderTests.swift index 1877f22c4..69b1b0229 100644 --- a/RiotTests/UserSessionsDataProviderTests.swift +++ b/RiotTests/UserSessionsDataProviderTests.swift @@ -106,7 +106,8 @@ class UserSessionCardViewDataTests: XCTestCase { let dataProvider = UserSessionsDataProvider(session: mxSession) let accountDataEvents: [String: Any] = [ - "io.element.matrix_client_information.D": "" + "io.element.matrix_client_information.D": "", + "foo": "" ] let expectedObsoletedEvents: Set = [ @@ -123,7 +124,8 @@ class UserSessionCardViewDataTests: XCTestCase { let dataProvider = UserSessionsDataProvider(session: mxSession) let accountDataEvents: [String: Any] = [ - "io.element.matrix_client_information.C": "" + "io.element.matrix_client_information.C": "", + "foo": "" ] let expectedObsoletedEvents: Set = [] @@ -138,7 +140,7 @@ class UserSessionCardViewDataTests: XCTestCase { let expectedObsoletedEvents = Set(["D", "E", "F"].map { "io.element.matrix_client_information.\($0)"}) - let accountDataEvents: [String: Any] = expectedObsoletedEvents.reduce(into: [:]) { partialResult, value in + let accountDataEvents: [String: Any] = expectedObsoletedEvents.reduce(into: ["foo": ""]) { partialResult, value in partialResult[value] = "" } From 73890d29794fbe0d39d204395dc5fd096ac40ea8 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 13 Dec 2022 15:46:53 +0100 Subject: [PATCH 022/228] Add changelog.d file --- changelog.d/pr-7164.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7164.change diff --git a/changelog.d/pr-7164.change b/changelog.d/pr-7164.change new file mode 100644 index 000000000..48a618f9d --- /dev/null +++ b/changelog.d/pr-7164.change @@ -0,0 +1 @@ +Add old device data from user's account data events. From 7ca4c9c8e99de2cadc7a3b5c39c1dfe83250d599 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 15:58:33 +0100 Subject: [PATCH 023/228] some tests --- .../Model/ComposerLinkActionModel.swift | 3 +- .../ComposerLinkActionViewModelTests.swift | 103 ++++++++++++++++++ .../Test/Unit/ComposerViewModelTests.swift | 20 ++++ 3 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift index 74ca3c89f..b47c5bcd5 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Model/ComposerLinkActionModel.swift @@ -61,9 +61,8 @@ extension ComposerLinkActionViewState { var isSaveButtonDisabled: Bool { guard isValidLink else { return true } switch linkAction { - case .create: return false case .createWithText: return bindings.text.isEmpty - case let .edit(originalLink): return bindings.linkUrl == originalLink + default: return false } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift new file mode 100644 index 000000000..dbba4cfc3 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift @@ -0,0 +1,103 @@ +// +// Copyright 2022 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. +// + +@testable import RiotSwiftUI +import WysiwygComposer +import XCTest + +final class ComposerLinkActionViewModelTests: XCTestCase { + var viewModel: ComposerLinkActionViewModel! + var context: ComposerLinkActionViewModel.Context! + + override func setUpWithError() throws { + viewModel = nil + context = nil + } + + private func setUp(with linkAction: LinkAction) { + viewModel = ComposerLinkActionViewModel(from: linkAction) + context = viewModel.context + } + + func testCreateWithTextDefaultState() { + setUp(with: .createWithText) + XCTAssertEqual(context.viewState.bindings.text, "") + XCTAssertEqual(context.viewState.bindings.linkUrl, "") + XCTAssertTrue(context.viewState.isSaveButtonDisabled) + XCTAssertFalse(context.viewState.shouldDisplayRemoveButton) + XCTAssertTrue(context.viewState.shouldDisplayTextField) + XCTAssertEqual(context.viewState.title, VectorL10n.wysiwygComposerLinkActionCreateTitle) + } + + func testCreateDefaultState() { + setUp(with: .create) + XCTAssertEqual(context.viewState.bindings.text, "") + XCTAssertEqual(context.viewState.bindings.linkUrl, "") + XCTAssertTrue(context.viewState.isSaveButtonDisabled) + XCTAssertFalse(context.viewState.shouldDisplayRemoveButton) + XCTAssertFalse(context.viewState.shouldDisplayTextField) + XCTAssertEqual(context.viewState.title, VectorL10n.wysiwygComposerLinkActionCreateTitle) + } + + func testEditDefaultState() { + let link = "https://element.io" + setUp(with: .edit(link: link)) + XCTAssertEqual(context.viewState.bindings.text, "") + XCTAssertEqual(context.viewState.bindings.linkUrl, link) + XCTAssertFalse(context.viewState.isSaveButtonDisabled) + XCTAssertTrue(context.viewState.shouldDisplayRemoveButton) + XCTAssertFalse(context.viewState.shouldDisplayTextField) + XCTAssertEqual(context.viewState.title, VectorL10n.wysiwygComposerLinkActionEditTitle) + } + + func testUrlValidityCheck() { + setUp(with: .create) + XCTAssertTrue(context.viewState.isSaveButtonDisabled) + context.linkUrl = "invalid url" + XCTAssertTrue(context.viewState.isSaveButtonDisabled) + context.linkUrl = "https://element.io" + XCTAssertFalse(context.viewState.isSaveButtonDisabled) + } + + func testTextNotEmptyCheck() { + setUp(with: .createWithText) + XCTAssertTrue(context.viewState.isSaveButtonDisabled) + context.linkUrl = "https://element.io" + XCTAssertTrue(context.viewState.isSaveButtonDisabled) + context.text = "text" + XCTAssertFalse(context.viewState.isSaveButtonDisabled) + } + + func testCancelAction() { + setUp(with: .create) + var result: ComposerLinkActionViewModelResult! + viewModel.callback = { value in + result = value + } + context.send(viewAction: .cancel) + XCTAssertEqual(result, .cancel) + } + + func testRemoveAction() { + setUp(with: .edit(link: "https://element.io")) + var result: ComposerLinkActionViewModelResult! + viewModel.callback = { value in + result = value + } + context.send(viewAction: .remove) + XCTAssertEqual(result, .performOperation(.removeLinks)) + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/Test/Unit/ComposerViewModelTests.swift b/RiotSwiftUI/Modules/Room/Composer/Test/Unit/ComposerViewModelTests.swift index a49314062..e4d5b595d 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Test/Unit/ComposerViewModelTests.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Test/Unit/ComposerViewModelTests.swift @@ -81,4 +81,24 @@ final class ComposerViewModelTests: XCTestCase { viewModel.dismissKeyboard() XCTAssert(context.viewState.bindings.focused == false) } + + func testSelectionToRestore() { + XCTAssertEqual(viewModel.selectionToRestore, nil) + let testRange = NSRange(location: 0, length: 10) + context.send(viewAction: .storeSelection(selection: testRange)) + XCTAssertEqual(viewModel.selectionToRestore, testRange) + } + + func testLinkAction() { + var result: ComposerViewModelResult! + viewModel.callback = { value in + result = value + } + context.send(viewAction: .linkTapped(linkAction: .createWithText)) + XCTAssertEqual(result, .linkTapped(LinkAction: .createWithText)) + context.send(viewAction: .linkTapped(linkAction: .create)) + XCTAssertEqual(result, .linkTapped(LinkAction: .create)) + context.send(viewAction: .linkTapped(linkAction: .edit(link: "https://element.io"))) + XCTAssertEqual(result, .linkTapped(LinkAction: .edit(link: "https://element.io"))) + } } From ea2881b6149ad502d0f3332d5ed976e237ff8830 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 16:58:26 +0100 Subject: [PATCH 024/228] editing that highlights the textfield behaviour added --- .../LinkAction/View/ComposerLinkAction.swift | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift index 7bffb6d0a..dd12eb652 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift @@ -17,9 +17,24 @@ import SwiftUI struct ComposerLinkAction: View { + enum Field { + case text + case link + } + @Environment(\.theme) private var theme: ThemeSwiftUI @ObservedObject private var viewModel: ComposerLinkActionViewModel.Context + @State private var selectedField: Field? + + private var isTextFocused: Bool { + selectedField == .text + } + + private var isLinkFocused: Bool { + selectedField == .link + } + var body: some View { NavigationView { VStack(alignment: .leading, spacing: 0) { @@ -29,8 +44,14 @@ struct ComposerLinkAction: View { Text(VectorL10n.wysiwygComposerLinkActionText) .font(theme.fonts.subheadline) .foregroundColor(theme.colors.secondaryContent) - TextField("", text: $viewModel.text) - .textFieldStyle(BorderedInputFieldStyle()) + TextField( + "", + text: $viewModel.text, + onEditingChanged: { edit in + selectedField = edit ? .text : nil + } + ) + .textFieldStyle(BorderedInputFieldStyle(isEditing: isTextFocused)) .autocapitalization(.none) .accessibilityIdentifier("textTextField") .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionText) @@ -40,10 +61,16 @@ struct ComposerLinkAction: View { Text(VectorL10n.wysiwygComposerLinkActionLink) .font(theme.fonts.subheadline) .foregroundColor(theme.colors.secondaryContent) - TextField("", text: $viewModel.linkUrl) + TextField( + "", + text: $viewModel.linkUrl, + onEditingChanged: { edit in + selectedField = edit ? .link : nil + } + ) .keyboardType(.URL) .autocapitalization(.none) - .textFieldStyle(BorderedInputFieldStyle()) + .textFieldStyle(BorderedInputFieldStyle(isEditing: isLinkFocused)) .accessibilityIdentifier("linkTextField") .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionLink) } From bbf4bf3047f8451ed89a4f5e978a529702dd8899 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 17:06:12 +0100 Subject: [PATCH 025/228] view model fully tested --- .../ComposerLinkActionViewModelTests.swift | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift index dbba4cfc3..c30df7e79 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift @@ -100,4 +100,42 @@ final class ComposerLinkActionViewModelTests: XCTestCase { context.send(viewAction: .remove) XCTAssertEqual(result, .performOperation(.removeLinks)) } + + func testSaveActionForCreate() { + setUp(with: .create) + var result: ComposerLinkActionViewModelResult! + viewModel.callback = { value in + result = value + } + let link = "https://element.io" + context.linkUrl = link + context.send(viewAction: .save) + XCTAssertEqual(result, .performOperation(.setLink(urlString: link))) + } + + func testSaveActionForCreateWithText() { + setUp(with: .createWithText) + var result: ComposerLinkActionViewModelResult! + viewModel.callback = { value in + result = value + } + let link = "https://element.io" + context.linkUrl = link + let text = "test" + context.text = text + context.send(viewAction: .save) + XCTAssertEqual(result, .performOperation(.createLink(urlString: link, text: text))) + } + + func testSaveActionForEdit() { + setUp(with: .edit(link: "https://element.io")) + var result: ComposerLinkActionViewModelResult! + viewModel.callback = { value in + result = value + } + let link = "https://matrix.org" + context.linkUrl = link + context.send(viewAction: .save) + XCTAssertEqual(result, .performOperation(.setLink(urlString: link))) + } } From d6791761377803cadc77209afac91c02926e5ba0 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 18:12:47 +0100 Subject: [PATCH 026/228] tests completed --- .../Common/Test/UI/XCUIApplication+Riot.swift | 24 +++++-- .../MockComposerLinkActionScreenState.swift | 2 +- .../Test/UI/ComposerLinkActionUITests.swift | 72 +++++++++++++++++++ .../ComposerLinkActionViewModelTests.swift | 2 +- .../LinkAction/View/ComposerLinkAction.swift | 18 ++--- 5 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/UI/ComposerLinkActionUITests.swift diff --git a/RiotSwiftUI/Modules/Common/Test/UI/XCUIApplication+Riot.swift b/RiotSwiftUI/Modules/Common/Test/UI/XCUIApplication+Riot.swift index f0912c5bc..ecca19b66 100644 --- a/RiotSwiftUI/Modules/Common/Test/UI/XCUIApplication+Riot.swift +++ b/RiotSwiftUI/Modules/Common/Test/UI/XCUIApplication+Riot.swift @@ -18,16 +18,21 @@ import Foundation import XCTest extension XCUIApplication { - func goToScreenWithIdentifier(_ identifier: String) { + func goToScreenWithIdentifier(_ identifier: String, shouldUseSlowTyping: Bool = false) { // Search for the screen identifier let textField = textFields["searchQueryTextField"] let button = buttons[identifier] - // Sometimes the search gets stuck without showing any results. Try to nudge it along - for _ in 0...10 { - textField.clearAndTypeText(identifier) - if button.exists { - break + // This always fixes the stuck search issue, but makes the typing slower + if shouldUseSlowTyping { + textField.typeSlowly(identifier) + } else { + // Sometimes the search gets stuck without showing any results. Try to nudge it along + for _ in 0...10 { + textField.clearAndTypeText(identifier) + if button.exists { + break + } } } @@ -35,7 +40,7 @@ extension XCUIApplication { } } -private extension XCUIElement { +extension XCUIElement { func clearAndTypeText(_ text: String) { guard let stringValue = value as? String else { XCTFail("Tried to clear and type text into a non string value") @@ -49,4 +54,9 @@ private extension XCUIElement { typeText(deleteString) typeText(text) } + + func typeSlowly(_ text: String) { + tap() + text.forEach{ typeText(String($0)) } + } } diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift index 383f5207b..6bdc5ebc5 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/MockComposerLinkActionScreenState.swift @@ -1,4 +1,4 @@ -// +// // Copyright 2022 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/UI/ComposerLinkActionUITests.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/UI/ComposerLinkActionUITests.swift new file mode 100644 index 000000000..f30dacf30 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/UI/ComposerLinkActionUITests.swift @@ -0,0 +1,72 @@ +// +// Copyright 2022 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 RiotSwiftUI +import XCTest + +final class ComposerLinkActionUITests: MockScreenTestCase { + func testCreate() { + app.goToScreenWithIdentifier(MockComposerLinkActionScreenState.create.title, shouldUseSlowTyping: true) + XCTAssertFalse(app.buttons[VectorL10n.remove].exists) + XCTAssertTrue(app.buttons[VectorL10n.cancel].exists) + let saveButton = app.buttons[VectorL10n.save] + XCTAssertTrue(saveButton.exists) + XCTAssertFalse(saveButton.isEnabled) + XCTAssertFalse(app.textFields["textTextField"].exists) + let linkTextField = app.textFields["linkTextField"] + XCTAssertTrue(linkTextField.exists) + linkTextField.tap() + linkTextField.typeText("invalid url") + XCTAssertFalse(saveButton.isEnabled) + linkTextField.clearAndTypeText("https://element.io") + XCTAssertTrue(saveButton.isEnabled) + } + + func testCreateWithText() { + app.goToScreenWithIdentifier(MockComposerLinkActionScreenState.createWithText.title, shouldUseSlowTyping: true) + XCTAssertFalse(app.buttons[VectorL10n.remove].exists) + XCTAssertTrue(app.buttons[VectorL10n.cancel].exists) + let saveButton = app.buttons[VectorL10n.save] + XCTAssertTrue(saveButton.exists) + XCTAssertFalse(saveButton.isEnabled) + let textTextField = app.textFields["textTextField"] + XCTAssertTrue(textTextField.exists) + let linkTextField = app.textFields["linkTextField"] + XCTAssertTrue(linkTextField.exists) + linkTextField.tap() + linkTextField.typeText("https://element.io") + XCTAssertFalse(saveButton.isEnabled) + textTextField.tap() + textTextField.typeText("test") + XCTAssertTrue(saveButton.isEnabled) + } + + func testEdit() { + app.goToScreenWithIdentifier(MockComposerLinkActionScreenState.edit.title, shouldUseSlowTyping: true) + XCTAssertTrue(app.buttons[VectorL10n.remove].exists) + XCTAssertTrue(app.buttons[VectorL10n.cancel].exists) + let saveButton = app.buttons[VectorL10n.save] + XCTAssertTrue(saveButton.exists) + XCTAssertTrue(saveButton.isEnabled) + XCTAssertFalse(app.textFields["textTextField"].exists) + let linkTextField = app.textFields["linkTextField"] + XCTAssertTrue(linkTextField.exists) + let value = linkTextField.value as? String + XCTAssertEqual(value, "https://element.io") + linkTextField.clearAndTypeText("invalid url") + XCTAssertFalse(saveButton.isEnabled) + } +} diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift index c30df7e79..40ad27358 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/Test/Unit/ComposerLinkActionViewModelTests.swift @@ -1,4 +1,4 @@ -// +// // Copyright 2022 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift index dd12eb652..1dbdee3ae 100644 --- a/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift +++ b/RiotSwiftUI/Modules/Room/Composer/LinkAction/View/ComposerLinkAction.swift @@ -51,10 +51,10 @@ struct ComposerLinkAction: View { selectedField = edit ? .text : nil } ) - .textFieldStyle(BorderedInputFieldStyle(isEditing: isTextFocused)) - .autocapitalization(.none) - .accessibilityIdentifier("textTextField") - .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionText) + .textFieldStyle(BorderedInputFieldStyle(isEditing: isTextFocused)) + .autocapitalization(.none) + .accessibilityIdentifier("textTextField") + .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionText) } } VStack(alignment: .leading, spacing: 8.0) { @@ -68,11 +68,11 @@ struct ComposerLinkAction: View { selectedField = edit ? .link : nil } ) - .keyboardType(.URL) - .autocapitalization(.none) - .textFieldStyle(BorderedInputFieldStyle(isEditing: isLinkFocused)) - .accessibilityIdentifier("linkTextField") - .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionLink) + .keyboardType(.URL) + .autocapitalization(.none) + .textFieldStyle(BorderedInputFieldStyle(isEditing: isLinkFocused)) + .accessibilityIdentifier("linkTextField") + .accessibilityLabel(VectorL10n.wysiwygComposerLinkActionLink) } } Spacer() From 02449544ce9a82f52d0d3c1379dd40335c63211a Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 14 Dec 2022 17:12:06 +0100 Subject: [PATCH 027/228] inline code added --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 ++++ .../Modules/Room/Composer/Model/ComposerModels.swift | 11 +++++++++++ 3 files changed, 16 insertions(+) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 4cca29fb9..80ae8311b 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2550,6 +2550,7 @@ To enable access, tap Settings> Location and select Always"; "wysiwyg_composer_format_action_underline" = "Apply strikethrough format"; "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"; // Links "wysiwyg_composer_link_action_text" = "Text"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 57eb34850..6d5b24981 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -9311,6 +9311,10 @@ public class VectorL10n: NSObject { public static var wysiwygComposerFormatActionBold: String { return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_bold") } + /// Apply inline code format + public static var wysiwygComposerFormatActionInlineCode: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_inline_code") + } /// Apply italic format public static var wysiwygComposerFormatActionItalic: String { return VectorL10n.tr("Vector", "wysiwyg_composer_format_action_italic") diff --git a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift index 8d744ff27..20c2c7c33 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift @@ -36,6 +36,7 @@ enum FormatType { case italic case underline case strikethrough + case inlineCode case link } @@ -61,6 +62,8 @@ extension FormatItem { return Asset.Images.underlined.name case .link: return Asset.Images.link.name + case .inlineCode: + return Asset.Images.code.name } } @@ -76,6 +79,8 @@ extension FormatItem { return "underlineButton" case .link: return "linkButton" + case .inlineCode: + return "inlineCodeButton" } } @@ -91,6 +96,8 @@ extension FormatItem { return VectorL10n.wysiwygComposerFormatActionUnderline case .link: return VectorL10n.wysiwygComposerFormatActionLink + case .inlineCode: + return VectorL10n.wysiwygComposerFormatActionInlineCode } } } @@ -109,6 +116,8 @@ extension FormatType { return .underline case .link: return .link + case .inlineCode: + return .inlineCode } } @@ -126,6 +135,8 @@ extension FormatType { return .underline case .link: return .link + case .inlineCode: + return .inlineCode } } } From e012c9202e2dac5113056667e331cb6f974eeda9 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 10:34:43 +0100 Subject: [PATCH 028/228] Replace the player timeline --- .../Contents.json | 12 ++++ .../voice_broadcast_slider_max_track.svg | 3 + .../Contents.json | 12 ++++ .../voice_broadcast_slider_min_track.svg | 3 + .../Contents.json | 12 ++++ .../voice_broadcast_slider_thumb.svg | 3 + Riot/Generated/Images.swift | 3 + .../VoiceBroadcastPlaybackViewModel.swift | 44 ++++++++++-- .../View/VoiceBroadcastPlaybackView.swift | 22 ++++-- .../View/VoiceBroadcastSlider.swift | 69 +++++++++++++++++++ .../VoiceBroadcastPlaybackModels.swift | 3 +- 11 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_max_track.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_max_track.imageset/voice_broadcast_slider_max_track.svg create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_min_track.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_min_track.imageset/voice_broadcast_slider_min_track.svg create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.svg create mode 100644 RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastSlider.swift diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_max_track.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_max_track.imageset/Contents.json new file mode 100644 index 000000000..03c7aa158 --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_max_track.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "voice_broadcast_slider_max_track.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_max_track.imageset/voice_broadcast_slider_max_track.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_max_track.imageset/voice_broadcast_slider_max_track.svg new file mode 100644 index 000000000..1730c4783 --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_max_track.imageset/voice_broadcast_slider_max_track.svg @@ -0,0 +1,3 @@ + + + diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_min_track.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_min_track.imageset/Contents.json new file mode 100644 index 000000000..42ea4f6e3 --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_min_track.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "voice_broadcast_slider_min_track.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_min_track.imageset/voice_broadcast_slider_min_track.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_min_track.imageset/voice_broadcast_slider_min_track.svg new file mode 100644 index 000000000..5cb3d3427 --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_min_track.imageset/voice_broadcast_slider_min_track.svg @@ -0,0 +1,3 @@ + + + diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/Contents.json new file mode 100644 index 000000000..9904db687 --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "voice_broadcast_slider_thumb.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.svg new file mode 100644 index 000000000..507831e8b --- /dev/null +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.svg @@ -0,0 +1,3 @@ + + + diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index 1dc910c14..ed763a171 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -346,6 +346,9 @@ internal class Asset: NSObject { internal static let voiceBroadcastPlay = ImageAsset(name: "voice_broadcast_play") internal static let voiceBroadcastRecord = ImageAsset(name: "voice_broadcast_record") internal static let voiceBroadcastRecordPause = ImageAsset(name: "voice_broadcast_record_pause") + internal static let voiceBroadcastSliderMaxTrack = ImageAsset(name: "voice_broadcast_slider_max_track") + internal static let voiceBroadcastSliderMinTrack = ImageAsset(name: "voice_broadcast_slider_min_track") + internal static let voiceBroadcastSliderThumb = ImageAsset(name: "voice_broadcast_slider_thumb") internal static let voiceBroadcastSpinner = ImageAsset(name: "voice_broadcast_spinner") internal static let voiceBroadcastStop = ImageAsset(name: "voice_broadcast_stop") internal static let voiceBroadcastTileLive = ImageAsset(name: "voice_broadcast_tile_live") diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift index 61b450d77..000e3f450 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift @@ -58,6 +58,21 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic private static let defaultBackwardForwardValue: Float = 30000.0 // 30sec in ms + private var fullDateFormatter: DateComponentsFormatter { + let formatter = DateComponentsFormatter() + formatter.unitsStyle = .positional + formatter.allowedUnits = [.hour, .minute, .second] + return formatter + } + + private var shortDateFormatter: DateComponentsFormatter { + let formatter = DateComponentsFormatter() + formatter.unitsStyle = .positional + formatter.zeroFormattingBehavior = .pad + formatter.allowedUnits = [.minute, .second] + return formatter + } + // MARK: Public // MARK: - Setup @@ -330,12 +345,16 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic private func updateDuration() { let duration = voiceBroadcastAggregator.voiceBroadcast.duration - let time = TimeInterval(duration / 1000) - let formatter = DateComponentsFormatter() - formatter.unitsStyle = .abbreviated - state.playingState.duration = Float(duration) - state.playingState.durationLabel = formatter.string(from: time) + updateUI() + } + + private func dateFormatter(for time: TimeInterval) -> DateComponentsFormatter { + if time >= 3600 { + return self.fullDateFormatter + } else { + return self.shortDateFormatter + } } private func didSliderChanged(_ didChange: Bool) { @@ -368,6 +387,21 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic } private func updateUI() { + let time = TimeInterval(state.playingState.duration / 1000) + let formatter = dateFormatter(for: time) + + let currentProgress = TimeInterval(state.bindings.progress / 1000) + state.playingState.elapsedTimeLabel = formatter.string(from: currentProgress) + if let remainingTimeString = formatter.string(from: time-currentProgress) { + if time-currentProgress < 1.0 { + state.playingState.remainingTimeLabel = remainingTimeString + } else { + state.playingState.remainingTimeLabel = "-" + remainingTimeString + } + } else { + state.playingState.remainingTimeLabel = "" + } + state.playingState.canMoveBackward = state.bindings.progress > 0 state.playingState.canMoveForward = state.bindings.progress < state.playingState.duration } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift index ff36b0e36..c25e1c995 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift @@ -152,15 +152,23 @@ struct VoiceBroadcastPlaybackView: View { } } - Slider(value: $viewModel.progress, in: 0...viewModel.viewState.playingState.duration) { - Text("Slider") - } minimumValueLabel: { - Text("") - } maximumValueLabel: { - Text(viewModel.viewState.playingState.durationLabel ?? "").font(.body) - } onEditingChanged: { didChange in + VoiceBroadcastSlider(value: $viewModel.progress, + minValue: 0.0, + maxValue: viewModel.viewState.playingState.duration) { didChange in viewModel.send(viewAction: .sliderChange(didChange: didChange)) } + + HStack { + Text(viewModel.viewState.playingState.elapsedTimeLabel ?? "") + .foregroundColor(theme.colors.secondaryContent) + .font(theme.fonts.caption1) + .padding(EdgeInsets(top: -4.0, leading: 4.0, bottom: 0.0, trailing: 0.0)) + Spacer() + Text(viewModel.viewState.playingState.remainingTimeLabel ?? "") + .foregroundColor(theme.colors.secondaryContent) + .font(theme.fonts.caption1) + .padding(EdgeInsets(top: -4.0, leading: 0.0, bottom: 0.0, trailing: 4.0)) + } } .padding([.horizontal, .top], 2.0) .padding([.bottom]) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastSlider.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastSlider.swift new file mode 100644 index 000000000..50845d5c8 --- /dev/null +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastSlider.swift @@ -0,0 +1,69 @@ +// +// Copyright 2022 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 SwiftUI + +/// Customized UISlider for SwiftUI. + +struct VoiceBroadcastSlider: UIViewRepresentable { + @Binding var value: Float + + var minValue: Float = 0.0 + var maxValue: Float = 1.0 + var onEditingChanged : ((Bool) -> Void)? + + func makeUIView(context: Context) -> UISlider { + let slider = UISlider(frame: .zero) + slider.setThumbImage(Asset.Images.voiceBroadcastSliderThumb.image, for: .normal) + slider.setMinimumTrackImage(Asset.Images.voiceBroadcastSliderMinTrack.image, for: .normal) + slider.setMaximumTrackImage(Asset.Images.voiceBroadcastSliderMaxTrack.image, for: .normal) + slider.minimumValue = Float(minValue) + slider.maximumValue = Float(maxValue) + slider.value = Float(value) + slider.addTarget(context.coordinator, action: #selector(Coordinator.valueChanged(_:)), for: .valueChanged) + slider.addTarget(context.coordinator, action: #selector(Coordinator.sliderEditingChanged(_:)), for: .touchUpInside) + slider.addTarget(context.coordinator, action: #selector(Coordinator.sliderEditingChanged(_:)), for: .touchUpOutside) + slider.addTarget(context.coordinator, action: #selector(Coordinator.sliderEditingChanged(_:)), for: .touchDown) + + return slider + } + + func updateUIView(_ uiView: UISlider, context: Context) { + uiView.value = Float(value) + } + + func makeCoordinator() -> VoiceBroadcastSlider.Coordinator { + Coordinator(parent: self, value: $value) + } + + class Coordinator: NSObject { + var parent: VoiceBroadcastSlider + var value: Binding + + init(parent: VoiceBroadcastSlider, value: Binding) { + self.value = value + self.parent = parent + } + + @objc func valueChanged(_ sender: UISlider) { + self.value.wrappedValue = sender.value + } + + @objc func sliderEditingChanged(_ sender: UISlider) { + parent.onEditingChanged?(sender.isTracking) + } + } +} diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift index 5e5c6696c..c2ca1ad5f 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift @@ -40,7 +40,8 @@ struct VoiceBroadcastPlaybackDetails { struct VoiceBroadcastPlayingState { var duration: Float - var durationLabel: String? + var elapsedTimeLabel: String? + var remainingTimeLabel: String? var isLive: Bool var canMoveForward: Bool var canMoveBackward: Bool From 65e7c16de88696fca71db1189195dfb8f4355dc4 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 10:38:30 +0100 Subject: [PATCH 029/228] Add Towncrier file --- changelog.d/pr-7165.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7165.change diff --git a/changelog.d/pr-7165.change b/changelog.d/pr-7165.change new file mode 100644 index 000000000..0a4d54aee --- /dev/null +++ b/changelog.d/pr-7165.change @@ -0,0 +1 @@ +Labs: VoiceBroadcast: Replace the player timeline From 3d0229e60e7e5507e1d35426c4dae7d0c53806a3 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 16:34:57 +0100 Subject: [PATCH 030/228] Update slider thumb image --- .../Contents.json | 15 +++++++++++++-- .../voice_broadcast_slider_thumb.png | Bin 0 -> 813 bytes .../voice_broadcast_slider_thumb.svg | 3 --- .../voice_broadcast_slider_thumb@2x.png | Bin 0 -> 2232 bytes .../voice_broadcast_slider_thumb@3x.png | Bin 0 -> 4077 bytes 5 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.png delete mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.svg create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb@2x.png create mode 100644 Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb@3x.png diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/Contents.json b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/Contents.json index 9904db687..d50700f87 100644 --- a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/Contents.json @@ -1,8 +1,19 @@ { "images" : [ { - "filename" : "voice_broadcast_slider_thumb.svg", - "idiom" : "universal" + "filename" : "voice_broadcast_slider_thumb.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "voice_broadcast_slider_thumb@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "voice_broadcast_slider_thumb@3x.png", + "idiom" : "universal", + "scale" : "3x" } ], "info" : { diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.png b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..15a878c654591d0a36e1175431226b440d70676f GIT binary patch literal 813 zcmV+|1JeA7P)Vi|K~#7F#a7R6 z54j^PrpBa4>p}fTI2e(bhLEN< zDOy0;E=pSyrG+FMY|ES$5xi@6DTe-@w>pY@N`w?i&pZyTz-T-Vbeq5cb7@xwa12O=beiqujB&l)3X}i z_((pV4~hUN3z|rxml7RCy!35^LXX*+7LUcBVJP0JVg(qiL?Xe4!y&t?QGeJx%+b-#0Ib{X+7369$#=GQZIi=c_x0|9X_~rMa`=Aw@Uf=r`XrOd zm?VsrTP`5WGL9D)HM@(w9#P)Sam)n=my3DhFa5mTRY=8g>tqK_w5q|-#X^sSRIn9V zBy&~V&@_!os%mcz4-1{2y?6bA(GbJ~5P2x-S;dY~A7r^F0%DK(}0e48O0GQ(r<`=)k?#uoGT?X@$#Gn`T00000NkvXXu0mjfPo{cT literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.svg b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.svg deleted file mode 100644 index 507831e8b..000000000 --- a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb@2x.png b/Riot/Assets/Images.xcassets/VoiceBroadcast/voice_broadcast_slider_thumb.imageset/voice_broadcast_slider_thumb@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..eb8ac9760f9fbe973dd842a361ed0e3916859915 GIT binary patch literal 2232 zcmV;p2uJscP)uK~#7F)mhzd zR8yKMIY~H%Ayp!flCfw( zCORaXiK^VVapM|Xs~Q>_5_GMvudkx3zyyCYOvMc&!~_w}#M?s4D2>SYB?+04HO#!$9?i`RI4fd+f7T2bi7@d};#%5u zb#*Bw+z?y~d;wUON~P-P%Fl$g)z#H&d2EWA){L;}8Ex1Cmt>@11mM7-ObmzFgmy2O z(Ks|Tq!|OThPhZGk z*yf3lN;PjTB*j{iGKj0keZ!?I?e9JO^Y1gmqZ3`(vFXwA$(ewV?uUl6H|QIU!T1dr zPydj1v_@Ey#y_+GX#GfpdN#-q$v?-~J~i>RwY6y`o=jpr5#HI^b98iU>JLNkvyjGi zcHKQn|I!=~0?kF{ml951!M^4&J<_z7mcFoimx-@n>7;lel3aaT+v!~+kan?z7h>ep zsV{e8t_?gV&7Ec@qCR1%`Aj}fY?OGlFtch2AaXd5A3t{a*@Xl@*t{uo!glPmS^@aJ zpY7?ndvy2C*M7jG2h8!+gJXMedcU!;(Z6`{B5A;9{|xP^kP`>cfP&r2#6`p#$8~Sr z`t{(J#>SH?C0-L?+v)Awo6r8%bN_uc9~^At5NmpR+P-t=j@8%KXB(oUzOa}Er1Wjr zy*oTStfLK7p84qT;mf5UUIJ<2{MmB{jvV>4ACEb#nX9O%$PEk(_-algzR-Z?C658J zEmVzGcXzi1b>%UDq~wl_j8J%t4GjMKljArqsa1fP+1c)m4eJg}PEO9MIaAW+1V8jp z0Q$^CAk8SxgvfLlEZCm~XVT^a;vIxOr{mV`L#stRK|B^~>FBt9NQv4=aRsZ`GDyhs zpO68g8rZrXn9JoXsMUh>wKgUuChW|n&BxR#L#9!$5zWGj?jiC_h--J5aZSM%yWD>5 z%{{wzf3L~_H*Z{T-M@d|HQdf3!R0C|D|3$?J@Sy)JeW6`j&H`=OuW_rmkb4QYtz-6k$R|C?yS0ZZd@DYk)!%7zkQg zT4;dX-`}qW2M5D}aA1oPjw%zFwT$o#U|(&C>U3_;&EMn(Tb8tOsL{nlG1M-9sWL(` z5q~k6OwNvvkIyL12Pp15WM)t1f=~nepUl(RoIpIi3n){nG6AiHq!}1nhd*lCK)mi| zaI~CHWFkFFJ5y?&p}_NU4^^39W@e@v*;pCj!JGr~0mVX9g^D?|>tVYK1nQWZ&5n1d zGQrgJRF{eniYOFBgsVX^psP@*i5>%N)i$P!S{U24C;O@P9m#`+4(gBJ(#k7zU zYkFkAvQRlq5)Y6(0_*|kIe;Vi`}Vzc!}GmWHzQi(0M^2q!RY9yk1E(FTId_XJY9?r zXpmtl8i`CaGMkuKXIIaMTN;~As#S#F`+7fnb$j!-YA*6nRqFG-FOXnCH7f1P=V#J_ zx%V;gg_+rzZna84(#IO#(ONY%H9n$`kEBX_0b~CQqGiDynYae1P7T){+2z;Yod0As zLMqL9?)+D+SRB|#Ez8d?S0D%27g9WTBg6vdJ3L|5DC)4SAM ze@XX-M1xEBg-jcst)d{YOTD)-i9IznWg#?xzk!uzvk|pR#VDHcHV9@HVqh zr!MN?7Sg?qj436RCp9=e)mVzcV>X-Bc?JIj=y2#z8H>dz+xkSjX`cw91GD8_4&+H+ zo`b2#If05Ca1cVm4MVCp%!>;`%xR50 zR!T;m*YMc9lNlx2ioyp=24OcqIJc~o!jl`5`~*{ZC{AMXBpwk|(Zst&j+^U4Mmoc4 z{#2eC0=g*%=hH)JUrkqNDS!`{a+peEjZ_+#j&rozriu-hD*<_Osl<7&I9td&iV~+S z(NoBReg9&iBz7j42~Xff&AwO^DT$Oz>L^95vHGU*yup8!*{!4eO#Y+*0000ulB39jIY(-WR8)Z(gs*ARg`S==XGql}!qwrp9g zFrZf3k|j&(u*Esv*2z|zXQD7zo0BSGpqD7BFefton-qPTo87&@fGCRWF*E=JREoN~ zy7Z(2UjA@8c<`WQs-vr~zM2jnKCJiVh#8n3I&{dMOZ8fQPr7{h^2~kAxK#S*`oeTc z!72y9GEz=rjC$zEtAx}uBkFlOliRF<)*#IvTextcYq-}mFI}pVc%Q|t&{`Utnh@2{ z#3}O6(!|?lZgW7`j4#rlj7d>oLamj;XsOgFM$BY2YCz+Cj5?&CgHqOVE8@3Bfo6qNPex6+0*4*6OBG2)XB~qml1!=7lyi->p;}WO_d|K<4 zVGiZPf+Dr56LI&95jC14A2k;rX&BKyYt}3P%R(C7N}!7TuC%we7r>uTMP*ajPxtKE~F#!FyY z|3LrlxBmRb)2r{g>uq$4pPdX03?wX7X{l5y^E(|L;c~{ml<_YS|4EM*;nL*rD2%I& z$68~&V2?Mvoy`EMSmV@*B?hXraQCaPtvS`*_r}1P^XXJHJZsCAr*0Az#5Y^B2S!x)Plzx4Hb%*1G9{dlHkmS4I7re|G|e(PZeXrcI3!^ zw{6()&{BR^ySx+l)FK0=;BA?tj$~{y3X@_`7+S77y21DH%Nl;dYGh>Hf1v zE*5jN;T?DvzjNUzO!6K$1~`zQcr=R^jdDDtkrP`PXOWpxI(_4rPL zapx~4iZhcX_O_4xe)n(IuU)%#e;mhU?n9aTlyILCR=4ETsZ(Wq593jSa{@ zyk_<4)%*D;7?PMRKsy0w%kPw}aK^e7Y)7U5dz>F(afgP*7UDansi_Ih9~`~*&d#5G z`BJFWNUd*R@HEs-{3i5iu7oRI)vSL+1wn1diy{fK_~RQhC}JN3zD~>*B4)8J@u?sHkfl7zn{L2ygM5(a{r zrA6j`GzNiU2DKOwGmf*_jE@~Vrl4)XyTsP&$8%d+AD}9ro{zh?EneLIBk_~UEJW%f zIkf{8)z;Rg^XJb8;}HEjRYPV%@<^4KEqHcRu+0E6q=!eM#3SRShgIuy4}R~vZ&DS} zU*6igX64FTU+3K+8fJ{!S}fwhPvK-T7HN=+Z6-w$M$%*hUNVeg=|LVRV&*u8LA|hL zXDwd4?*7eGWpu+Azw`*nz%mp305deWezThb&2Tvg(%orYa-!i9PsR7_ejRD_jxxT}Zw$A#vxn{M#J|gNmk*G7Q26 z!bxQ2+yiKs+diYGl0zarw|(W-^;D&_l!IZ80TB;I3J`biI$vm1KS-im zuT}!k%8(LEPl?l_Hq}wXru85@XP4CH885*%+W7Fso4Q_nv4BO+&TB{ zECk_g8bJ&{V}xh>FoQYmeIb?}LK;Kxhn8G(P1RvfNLJ1Gr&BXvsU-gi(Sf~wOpq4x zHwlvJQ)UUgI|Rj+y})z#K7=e`n45TF5v5I?$eaD4ST1!|!vw?p|Ehj4nwIfX1i z$QsUICYL@u+5Jv6V-N^H-PBlr9q$e?gOH+iU@gsN94%OrZi_tWFgfq77-i?$OB^76 zK+}L4XVorZ3A%u=gM>NkwvjVPd zBJ*{Q*pk`Qa>iHk46;+Q2-xJNls#zoaF|Ci@x&wHu7mhel@t0nbLPxQ`s@c5NqyZ% zJSOK5u%>AcZ-x6KshL+XOu6ujXBD~^Py`UX%ioOA!2HzUxw8kU%Bfr)mU8ZM24ggIVnVS+Dpo899iBm9#e)QK z8PLMKI1SH7xoZZ`obHrb=)+JOuof~%vTCT*fRCVuQ=~eE zQ@lj%zNatj`uW?{4Akh`s;{l|3}WeVRUxb$t{MbcMGB^-Ln=y`0m&T(nLy6zEDD@3 z2QCjwc5L0++tbs#i>j2^DcsFrh67)AaG?RP#*P^#gmR40gjx8E9D^(tS@UNe<{T?I zNB^i|HT&bA_G}Y%NjOBxd=Jp(>$| zd%CwhvT@@PGBFn8kKdO}BH8n%7&Y^(5zRa-namdv85|N9Jz=m;Ig)qvIXL&|qmOMa zC*{lT7oiV7-SiVkwDXi@F5WW&){BOxp50WBBs znm7^e_8~>jKfk@_*=^f9E~nWCbDrI?y<_Xvr+U#R#!07I0t>FskRHs3@pn;}tWPJn zFviW#n{^VH&$gw#nCsRWM6C&G-}TZoxk4vFP&0U%f<P}hV1R~k)BRTIA!B%g(%{^)qmf=}YG%NvOw~N)qkT6O%^fXak`BkVY}sV@?k;avQwmEPemz#Vf%>c^}IBQ0{_vN{O(oE?7t@yypka1+`iBJ;5=G5nqFzt)#*3Qn(tJbdj&gQ9dCgB9``|G~V9A}|8 z0VTl+D$4-0kQ_2;R35}cD)3c$h=R20XqB#`a!;A0)n?}UP}PcZ*m2kxM6bVX#aGui zG&bBZ(PTexrnjfN>)(eC{g(Z%H<UyXUK3%GnnAk;HD~@6lXOK7RosA9 zVU)ussh@Vtk|hUNe(+h-{Bi^-r*ll9N)_u|l>X^zpkNh{#T;L1NBIKOaWQ9H7ipQS zE=SON)zM;Cz`c~g8|%WH#k+9fg4?pFYvr_Is2V$Wz z;g9aKf0mjcl!&M(8=-0y(PXR_6*EXX?rJUr>^Vyc%`v)7(%c#`ZY@#( zI#-)n1*8jBTBYeKpJr)RVw}|Inpa(cXjUKE)jg^KyIE)#RnNK_W=^EeTwzwZ4j8Z!LTx18eq>d`FR?Z33 zwX7GDBE_Zf9|4#Z$qu|BQR*{Evujgz^ZI3)O-S5?QVH&Ik|p(iUZRlG#>k@VmWH!%&9IIW9m zVN}Qa5Wz@Gf-r}1M#BkY1>JE3)v{JK4Vps4MVFi$>8MyYfbtWK8L6U*Ti_*2tY3Zj zTek+=a%<@AVmZqDVxQiOPXR?ChzI|h!~WL<|IY=e0_wKa1;6bzng7N>*?(&htIC@t zmo(Az#YLe&w|yrIoXaAq8c!?ww-h-^NvFv_Jjwhkn}Ri>9?)gqOMzMFmXi+CN-CX2 z|Er^#88adk`A}5xN5Z%*3fH+)UJ66po4(9Z3Ylb1h5sqW9uM#N|6#~DX(S6{aU>eY f^;hT$RWbb!kHxJ;0j6Cg00000NkvXXu0mjf!0phL literal 0 HcmV?d00001 From b077dec50bbd6e5a75ab5ec593166bf3aa4b466a Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 16:35:31 +0100 Subject: [PATCH 031/228] Update global layout by adjusting some padding values --- .../View/VoiceBroadcastPlaybackView.swift | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift index c25e1c995..4294f0b7d 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift @@ -51,7 +51,7 @@ struct VoiceBroadcastPlaybackView: View { HStack (alignment: .top) { AvatarImage(avatarData: viewModel.viewState.details.avatarData, size: .xSmall) - VStack(alignment: .leading, spacing: 0) { + VStack(alignment: .leading, spacing: 3) { Text(details.avatarData.displayName ?? details.avatarData.matrixItemId) .font(theme.fonts.bodySB) .foregroundColor(theme.colors.primaryContent) @@ -106,11 +106,12 @@ struct VoiceBroadcastPlaybackView: View { } } .frame(maxWidth: .infinity, alignment: .leading) + .padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 4.0, trailing: 0.0)) if viewModel.viewState.playbackState == .error { VoiceBroadcastPlaybackErrorView() } else { - HStack (spacing: 17.0) { + HStack (spacing: 34.0) { if viewModel.viewState.playingState.canMoveBackward { Button { viewModel.send(viewAction: .backward) @@ -150,6 +151,7 @@ struct VoiceBroadcastPlaybackView: View { Spacer().frame(width: 25.0) } } + .padding(EdgeInsets(top: 10.0, leading: 0.0, bottom: 10.0, trailing: 0.0)) } VoiceBroadcastSlider(value: $viewModel.progress, @@ -162,16 +164,15 @@ struct VoiceBroadcastPlaybackView: View { Text(viewModel.viewState.playingState.elapsedTimeLabel ?? "") .foregroundColor(theme.colors.secondaryContent) .font(theme.fonts.caption1) - .padding(EdgeInsets(top: -4.0, leading: 4.0, bottom: 0.0, trailing: 0.0)) + .padding(EdgeInsets(top: -8.0, leading: 4.0, bottom: 0.0, trailing: 0.0)) Spacer() Text(viewModel.viewState.playingState.remainingTimeLabel ?? "") .foregroundColor(theme.colors.secondaryContent) .font(theme.fonts.caption1) - .padding(EdgeInsets(top: -4.0, leading: 0.0, bottom: 0.0, trailing: 4.0)) + .padding(EdgeInsets(top: -8.0, leading: 0.0, bottom: 0.0, trailing: 4.0)) } } - .padding([.horizontal, .top], 2.0) - .padding([.bottom]) + .padding(EdgeInsets(top: 12.0, leading: 4.0, bottom: 12.0, trailing: 4.0)) } } From 206be8eabb2ad8488ca5745fa51f2bb11e16c034 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 16:47:54 +0100 Subject: [PATCH 032/228] Update Avatar size on Voice Broadcast playback cell --- .../View/VoiceBroadcastPlaybackView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift index 4294f0b7d..053affca7 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift @@ -49,7 +49,7 @@ struct VoiceBroadcastPlaybackView: View { VStack(alignment: .center) { HStack (alignment: .top) { - AvatarImage(avatarData: viewModel.viewState.details.avatarData, size: .xSmall) + AvatarImage(avatarData: viewModel.viewState.details.avatarData, size: .small) VStack(alignment: .leading, spacing: 3) { Text(details.avatarData.displayName ?? details.avatarData.matrixItemId) From 9022362c7b81622057367c7fe2f789b41108e32f Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Wed, 14 Dec 2022 16:30:53 +0000 Subject: [PATCH 033/228] Fix Sentry errors --- Riot/Modules/MatrixKit/Models/Account/MXKAccount.m | 9 ++++++++- .../VoiceMessageAttachmentCacheManager.swift | 10 ++++++---- .../Room/VoiceMessages/VoiceMessageController.swift | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m index e75526a13..36e4989f9 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m @@ -1909,8 +1909,15 @@ static NSArray *initialSyncSilentErrorsHTTPStatusCodes; MXRoomSummary *summary = room.summary; if (summary) { + NSString *eventId = summary.lastMessage.eventId; + if (!eventId) + { + MXLogFailure(@"[MXKAccount] onDateTimeFormatUpdate: Missing event id"); + continue; + } + dispatch_group_enter(dispatchGroup); - [summary.mxSession eventWithEventId:summary.lastMessage.eventId + [summary.mxSession eventWithEventId:eventId inRoom:summary.roomId success:^(MXEvent *event) { diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift index dc46839e3..cf97dac4e 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift @@ -137,10 +137,12 @@ class VoiceMessageAttachmentCacheManager { durations.removeAll() finalURLs.removeAll() - do { - try FileManager.default.removeItem(at: temporaryFilesFolderURL) - } catch { - MXLog.error("[VoiceMessageAttachmentCacheManager] Failed clearing cached disk files", context: error) + if FileManager.default.fileExists(atPath: temporaryFilesFolderURL.absoluteString) { + do { + try FileManager.default.removeItem(at: temporaryFilesFolderURL) + } catch { + MXLog.error("[VoiceMessageAttachmentCacheManager] Failed clearing cached disk files", context: error) + } } } diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift index 85cae9941..14b4fa2b9 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift @@ -364,7 +364,7 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, } private func deleteRecordingAtURL(_ url: URL?) { - guard let url = url else { + guard let url = url, FileManager.default.fileExists(atPath: url.absoluteString) else { return } From fe6503aaa134415a21aa6991099459295e8b3431 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 00:33:28 +0100 Subject: [PATCH 034/228] showing disabled state on format items --- .../Room/Composer/Model/ComposerModels.swift | 6 ++-- .../Modules/Room/Composer/View/Composer.swift | 3 +- .../Composer/View/FormattingToolbar.swift | 30 +++++++++++++------ 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift index 20c2c7c33..bc2e8771d 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Model/ComposerModels.swift @@ -24,10 +24,8 @@ import WysiwygComposer struct FormatItem { /// The type of the item let type: FormatType - /// Whether it is active(highlighted) - let active: Bool - /// Whether it is disabled or enabled - let disabled: Bool + /// The state of the item + let state: ActionState } /// The types of formatting actions diff --git a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift index f5b7e179e..6163f384a 100644 --- a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift +++ b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift @@ -74,8 +74,7 @@ struct Composer: View { FormatType.allCases.map { type in FormatItem( type: type, - active: wysiwygViewModel.actionStates[type.composerAction] == .reversed, - disabled: wysiwygViewModel.actionStates[type.composerAction] == .disabled + state: wysiwygViewModel.actionStates[type.composerAction] ?? .disabled ) } } diff --git a/RiotSwiftUI/Modules/Room/Composer/View/FormattingToolbar.swift b/RiotSwiftUI/Modules/Room/Composer/View/FormattingToolbar.swift index c721832bb..d8670ee0c 100644 --- a/RiotSwiftUI/Modules/Room/Composer/View/FormattingToolbar.swift +++ b/RiotSwiftUI/Modules/Room/Composer/View/FormattingToolbar.swift @@ -39,28 +39,40 @@ struct FormattingToolbar: View { } label: { Image(item.icon) .renderingMode(.template) - .foregroundColor(item.active ? theme.colors.accent : theme.colors.tertiaryContent) + .foregroundColor(getForegroundColor(for: item)) } - .disabled(item.disabled) + .disabled(item.state == .disabled) .frame(width: 44, height: 44) - .background(item.active ? theme.colors.accent.opacity(0.1) : theme.colors.background) + .background(getBackgroundColor(for: item)) .cornerRadius(8) .accessibilityIdentifier(item.accessibilityIdentifier) .accessibilityLabel(item.accessibilityLabel) } } } + + private func getForegroundColor(for item: FormatItem) -> Color { + switch item.state { + case .reversed: return theme.colors.accent + case .enabled: return theme.colors.tertiaryContent + case .disabled: return theme.colors.tertiaryContent.opacity(0.3) + } + } + + private func getBackgroundColor(for item: FormatItem) -> Color { + switch item.state { + case .reversed: return theme.colors.accent.opacity(0.1) + default: return theme.colors.background + } + } } // MARK: - Previews struct FormattingToolbar_Previews: PreviewProvider { static var previews: some View { - FormattingToolbar(formatItems: [ - FormatItem(type: .bold, active: true, disabled: false), - FormatItem(type: .italic, active: false, disabled: false), - FormatItem(type: .strikethrough, active: true, disabled: false), - FormatItem(type: .underline, active: false, disabled: true) - ], formatAction: { _ in }) + FormattingToolbar( + formatItems: FormatType.allCases.map { FormatItem(type: $0, state: .enabled) } + , formatAction: { _ in }) } } From da9b181fed0e50abd85018cdb263a09a6480d0ee Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 01:41:45 +0100 Subject: [PATCH 035/228] fix --- .../Utils/EventFormatter/HTMLFormatter.swift | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift index 982e2bc6c..087f93a50 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift @@ -51,6 +51,10 @@ class HTMLFormatter: NSObject { DTDefaultFontName: font.fontName, DTDefaultFontSize: font.pointSize, DTDefaultLinkDecoration: false, + // This fixes the issue where links are displayed in black + // However doesn't matter the value provided the color doesn't change + // From the default accent one + DTDefaultLinkColor: "", DTWillFlushBlockCallBack: sanitizeCallback ] options.merge(extraOptions) { (_, new) in new } @@ -63,6 +67,7 @@ class HTMLFormatter: NSObject { MXKTools.removeDTCoreTextArtifacts(mutableString) postFormatOperations?(mutableString) + return mutableString } @@ -109,3 +114,27 @@ extension HTMLFormatter { return stringBuilder?.generatedAttributedString() } } + +extension UIColor { + func toHexString() -> String { + var r:CGFloat = 0 + var g:CGFloat = 0 + var b:CGFloat = 0 + var a:CGFloat = 0 + + getRed(&r, green: &g, blue: &b, alpha: &a) + + let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0 + + return NSString(format:"#%06x", rgb) as String + } + + convenience init(red: Int, green: Int, blue: Int) { + assert(red >= 0 && red <= 255, "Invalid red component") + assert(green >= 0 && green <= 255, "Invalid green component") + assert(blue >= 0 && blue <= 255, "Invalid blue component") + + self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0) + } + +} From 0d569a28ff1e2002acaa512b56af028763bd981e Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 01:44:26 +0100 Subject: [PATCH 036/228] removed unused code --- .../Utils/EventFormatter/HTMLFormatter.swift | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift index 087f93a50..c8a1f0887 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift @@ -114,27 +114,3 @@ extension HTMLFormatter { return stringBuilder?.generatedAttributedString() } } - -extension UIColor { - func toHexString() -> String { - var r:CGFloat = 0 - var g:CGFloat = 0 - var b:CGFloat = 0 - var a:CGFloat = 0 - - getRed(&r, green: &g, blue: &b, alpha: &a) - - let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0 - - return NSString(format:"#%06x", rgb) as String - } - - convenience init(red: Int, green: Int, blue: Int) { - assert(red >= 0 && red <= 255, "Invalid red component") - assert(green >= 0 && green <= 255, "Invalid green component") - assert(blue >= 0 && blue <= 255, "Invalid blue component") - - self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0) - } - -} From c56bb4abde7897839bfcffe987786ee5aa307493 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 01:51:33 +0100 Subject: [PATCH 037/228] changelog --- changelog.d/7109.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7109.bugfix diff --git a/changelog.d/7109.bugfix b/changelog.d/7109.bugfix new file mode 100644 index 000000000..964fb01fa --- /dev/null +++ b/changelog.d/7109.bugfix @@ -0,0 +1 @@ +Timeline: fixed an issue where formatted links appeared in black. \ No newline at end of file From 6dca14ac2e562cc3621f449aa0318ea2f60c374e Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 03:37:15 +0100 Subject: [PATCH 038/228] better comment --- .../MatrixKit/Utils/EventFormatter/HTMLFormatter.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift index c8a1f0887..4aac22cbd 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift @@ -51,9 +51,9 @@ class HTMLFormatter: NSObject { DTDefaultFontName: font.fontName, DTDefaultFontSize: font.pointSize, DTDefaultLinkDecoration: false, - // This fixes the issue where links are displayed in black - // However doesn't matter the value provided the color doesn't change - // From the default accent one + /* This fixes the issue where links are displayed in black + but no matter the value provided, the color never changes + from the default green one */ DTDefaultLinkColor: "", DTWillFlushBlockCallBack: sanitizeCallback ] From aea1be4512cce95db4d74dc94279f7728d84bd57 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 03:40:15 +0100 Subject: [PATCH 039/228] code improvement --- Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift index 4aac22cbd..b075e8892 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift @@ -67,7 +67,6 @@ class HTMLFormatter: NSObject { MXKTools.removeDTCoreTextArtifacts(mutableString) postFormatOperations?(mutableString) - return mutableString } From ce9a64a1dc5dcf0bd02635356fd36fce5b3569ee Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 04:28:01 +0100 Subject: [PATCH 040/228] removed underline from links --- .../WYSIWYGInputToolbar/WysiwygInputToolbarView.swift | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index 9c89dc287..da0cb052c 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -115,6 +115,8 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp wysiwygViewModel.plainTextMode = !RiotSettings.shared.enableWysiwygTextFormatting inputAccessoryViewForKeyboard = UIView(frame: .zero) + wysiwygViewModel.textView.inputAccessoryView = inputAccessoryView + wysiwygViewModel.textView.linkTextAttributes[.underlineStyle] = 0 let composer = Composer( viewModel: viewModel.context, @@ -126,10 +128,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp }, showSendMediaActions: { [weak self] in guard let self = self else { return } self.showSendMediaActions() - }).introspectTextView { [weak self] textView in - guard let self = self else { return } - textView.inputAccessoryView = self.inputAccessoryViewForKeyboard - } + }) hostingViewController = VectorHostingController(rootView: composer) hostingViewController.publishHeightChanges = true From 29b591532374e634b8ec11aea4d05420e8f5b83d Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 04:29:49 +0100 Subject: [PATCH 041/228] Revert "removed underline from links" This reverts commit ce9a64a1dc5dcf0bd02635356fd36fce5b3569ee. --- .../WYSIWYGInputToolbar/WysiwygInputToolbarView.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index da0cb052c..9c89dc287 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -115,8 +115,6 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp wysiwygViewModel.plainTextMode = !RiotSettings.shared.enableWysiwygTextFormatting inputAccessoryViewForKeyboard = UIView(frame: .zero) - wysiwygViewModel.textView.inputAccessoryView = inputAccessoryView - wysiwygViewModel.textView.linkTextAttributes[.underlineStyle] = 0 let composer = Composer( viewModel: viewModel.context, @@ -128,7 +126,10 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp }, showSendMediaActions: { [weak self] in guard let self = self else { return } self.showSendMediaActions() - }) + }).introspectTextView { [weak self] textView in + guard let self = self else { return } + textView.inputAccessoryView = self.inputAccessoryViewForKeyboard + } hostingViewController = VectorHostingController(rootView: composer) hostingViewController.publishHeightChanges = true From 43729bd3a4a8207075103fd6af3391ee88d28300 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 12:36:44 +0100 Subject: [PATCH 042/228] better comment --- .../MatrixKit/Utils/EventFormatter/HTMLFormatter.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift index b075e8892..4e36fa6c5 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift @@ -52,8 +52,8 @@ class HTMLFormatter: NSObject { DTDefaultFontSize: font.pointSize, DTDefaultLinkDecoration: false, /* This fixes the issue where links are displayed in black - but no matter the value provided, the color never changes - from the default green one */ + on DTCoreText 1.6.26, the provided value does not matter + the tintColor of the UI element will be used for links */ DTDefaultLinkColor: "", DTWillFlushBlockCallBack: sanitizeCallback ] From fe44448efcb01adc2f898299fed0af252217dee7 Mon Sep 17 00:00:00 2001 From: Phl-Pro Date: Thu, 15 Dec 2022 15:49:57 +0100 Subject: [PATCH 043/228] Update RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift Co-authored-by: Yoan Pintas --- .../VoiceBroadcastPlaybackViewModel.swift | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift index 000e3f450..feeb1c183 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift @@ -391,16 +391,13 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic let formatter = dateFormatter(for: time) let currentProgress = TimeInterval(state.bindings.progress / 1000) - state.playingState.elapsedTimeLabel = formatter.string(from: currentProgress) - if let remainingTimeString = formatter.string(from: time-currentProgress) { - if time-currentProgress < 1.0 { - state.playingState.remainingTimeLabel = remainingTimeString - } else { - state.playingState.remainingTimeLabel = "-" + remainingTimeString - } - } else { - state.playingState.remainingTimeLabel = "" + let remainingTime = time-currentProgress + var label = "" + if let remainingTimeString = formatter.string(from: remainingTime) { + label = Int(remainingTime) == 0 ? remainingTimeString : "-" + remainingTimeString } + state.playingState.elapsedTimeLabel = formatter.string(from: currentProgress) + state.playingState.remainingTimeLabel = label state.playingState.canMoveBackward = state.bindings.progress > 0 state.playingState.canMoveForward = state.bindings.progress < state.playingState.duration From 71ceb2fc40d18c80073a6bffe588a9d3e73dc46a Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 16:33:25 +0100 Subject: [PATCH 044/228] fix that works both iOS 15 and iOS 16 --- .../MatrixKit/Utils/EventFormatter/HTMLFormatter.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift index 4e36fa6c5..de1b19821 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/HTMLFormatter.swift @@ -51,10 +51,6 @@ class HTMLFormatter: NSObject { DTDefaultFontName: font.fontName, DTDefaultFontSize: font.pointSize, DTDefaultLinkDecoration: false, - /* This fixes the issue where links are displayed in black - on DTCoreText 1.6.26, the provided value does not matter - the tintColor of the UI element will be used for links */ - DTDefaultLinkColor: "", DTWillFlushBlockCallBack: sanitizeCallback ] options.merge(extraOptions) { (_, new) in new } @@ -66,7 +62,11 @@ class HTMLFormatter: NSObject { let mutableString = NSMutableAttributedString(attributedString: string) MXKTools.removeDTCoreTextArtifacts(mutableString) postFormatOperations?(mutableString) - + + // Remove CTForegroundColorFromContext attribute to fix the iOS 16 black link color issue + // REF: https://github.com/Cocoanetics/DTCoreText/issues/792 + mutableString.removeAttribute(NSAttributedString.Key("CTForegroundColorFromContext"), range: NSRange(location: 0, length: mutableString.length)) + return mutableString } From dbbb2d8dc23d314a25ce01adbdbd6ac50e48396f Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 15 Dec 2022 15:42:58 +0100 Subject: [PATCH 045/228] Voice Broadcast - Link the live icon color to the recording state --- .../View/VoiceBroadcastPlaybackView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift index ff36b0e36..d7e58f778 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift @@ -33,7 +33,7 @@ struct VoiceBroadcastPlaybackView: View { @State private var bufferingSpinnerRotationValue = 0.0 private var backgroundColor: Color { - if viewModel.viewState.playingState.isLive { + if viewModel.viewState.broadcastState != .paused { return theme.colors.alert } return theme.colors.quarterlyContent From cea85ee1221b817a59e872029abd37a75926a3a9 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 15 Dec 2022 15:44:28 +0100 Subject: [PATCH 046/228] Add Towncrier file --- changelog.d/pr-7163.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7163.change diff --git a/changelog.d/pr-7163.change b/changelog.d/pr-7163.change new file mode 100644 index 000000000..eb5289c8f --- /dev/null +++ b/changelog.d/pr-7163.change @@ -0,0 +1 @@ +Labs: VoiceBroadcast: Link the live icon color to the recording state From 30fc2697b3806289f7dd96d960730ac470205d82 Mon Sep 17 00:00:00 2001 From: giomfo Date: Thu, 15 Dec 2022 16:46:09 +0100 Subject: [PATCH 047/228] Bug Fix : Crash on new voice broadcast if the room has avatar (#7173) * Bug Fix : Crash if the room has avatar and voice broadcast tiles * add logfile * update logs --- .../Coordinator/VoiceBroadcastRecorderCoordinator.swift | 1 + changelog.d/pr-7173.bugfix | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog.d/pr-7173.bugfix diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift index d0205a9fb..e5e0afe3c 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift @@ -57,6 +57,7 @@ final class VoiceBroadcastRecorderCoordinator: Coordinator, Presentable { func toPresentable() -> UIViewController { let view = VoiceBroadcastRecorderView(viewModel: voiceBroadcastRecorderViewModel.context) + .addDependency(AvatarService.instantiate(mediaManager: parameters.session.mediaManager)) return VectorHostingController(rootView: view) } diff --git a/changelog.d/pr-7173.bugfix b/changelog.d/pr-7173.bugfix new file mode 100644 index 000000000..64fb469a5 --- /dev/null +++ b/changelog.d/pr-7173.bugfix @@ -0,0 +1 @@ +Labs: Crash on new voice broadcast if the room has avatar From b88f423013b654ba1d88da1e10c21895aa8cc0e9 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 15 Dec 2022 17:01:52 +0100 Subject: [PATCH 048/228] Update Voice Broadcast recorder cell by adjusting some padding values --- .../View/VoiceBroadcastRecorderView.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift index 13df1f5ac..55f9ad878 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift @@ -42,9 +42,9 @@ struct VoiceBroadcastRecorderView: View { VStack(alignment: .center) { HStack(alignment: .top) { - AvatarImage(avatarData: viewModel.viewState.details.avatarData, size: .xSmall) + AvatarImage(avatarData: viewModel.viewState.details.avatarData, size: .small) - VStack(alignment: .leading, spacing: 0) { + VStack(alignment: .leading, spacing: 3) { Text(details.avatarData.displayName ?? details.avatarData.matrixItemId) .font(theme.fonts.bodySB) .foregroundColor(theme.colors.primaryContent) @@ -77,7 +77,7 @@ struct VoiceBroadcastRecorderView: View { .accessibilityIdentifier("liveButton") } - HStack(alignment: .top, spacing: 16.0) { + HStack(alignment: .top, spacing: 34.0) { Button { switch viewModel.viewState.recordingState { case .started, .resumed: @@ -117,9 +117,9 @@ struct VoiceBroadcastRecorderView: View { .disabled(viewModel.viewState.recordingState == .stopped) .mask(Color.black.opacity(viewModel.viewState.recordingState == .stopped ? 0.3 : 1.0)) } + .padding(EdgeInsets(top: 10.0, leading: 0.0, bottom: 10.0, trailing: 0.0)) } - .padding([.horizontal, .top], 2.0) - .padding([.bottom]) + .padding(EdgeInsets(top: 12.0, leading: 4.0, bottom: 12.0, trailing: 4.0)) } } From b83a39572cffef5320119881e6715ffaf3e95792 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 15 Dec 2022 17:04:10 +0100 Subject: [PATCH 049/228] Add Towncrier file --- changelog.d/pr-7175.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7175.change diff --git a/changelog.d/pr-7175.change b/changelog.d/pr-7175.change new file mode 100644 index 000000000..e29c02b44 --- /dev/null +++ b/changelog.d/pr-7175.change @@ -0,0 +1 @@ +Labs: VoiceBroadcast: Update Voice Broadcast recorder cell by adjusting some padding values From 3f3b7db4b68c00fc7e5f921b84ec0c68f43d1e59 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 18:45:33 +0100 Subject: [PATCH 050/228] updated package and added background color --- Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- .../Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift | 4 +++- project.yml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7d4a2e9c4..405f9427b 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,7 +23,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift", "state" : { - "revision" : "55266ea8f338e9c1d5c4db375713b3f902317b00" + "revision" : "2a6dccc6849ffdbc4f8b00bd22a696077f726fd3" } }, { diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index 9c89dc287..7956ad107 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -44,7 +44,8 @@ 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.accent, + codeBackgroundColor: ThemeService.shared().theme.selectedBackgroundColor ) private var viewModel: ComposerViewModelProtocol! @@ -299,6 +300,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp hostingViewController.view.backgroundColor = theme.colors.background wysiwygViewModel.textColor = theme.colors.primaryContent wysiwygViewModel.linkColor = theme.colors.accent + wysiwygViewModel.codeBackgroundColor = theme.selectedBackgroundColor } private func updateTextViewHeight() { diff --git a/project.yml b/project.yml index 69a0c0b97..134ed93ac 100644 --- a/project.yml +++ b/project.yml @@ -53,7 +53,7 @@ packages: branch: main WysiwygComposer: url: https://github.com/matrix-org/matrix-wysiwyg-composer-swift - revision: 55266ea8f338e9c1d5c4db375713b3f902317b00 + revision: 2a6dccc6849ffdbc4f8b00bd22a696077f726fd3 DeviceKit: url: https://github.com/devicekit/DeviceKit majorVersion: 4.7.0 From eaecccd40e9a458d15f8f2fc54cf2a267dbe7495 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 18:49:43 +0100 Subject: [PATCH 051/228] changelog --- changelog.d/7177.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7177.feature diff --git a/changelog.d/7177.feature b/changelog.d/7177.feature new file mode 100644 index 000000000..7b9eef21d --- /dev/null +++ b/changelog.d/7177.feature @@ -0,0 +1 @@ +Rich Text Composer: added inline code formatting feature. \ No newline at end of file From 8df53fb492ed237793a42202ddb68bc96ca2ad20 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Fri, 16 Dec 2022 16:40:46 +0100 Subject: [PATCH 052/228] Update live badge layout for recorder and player VoiceBroadcast cells --- .../View/VoiceBroadcastPlaybackView.swift | 5 +++-- .../View/VoiceBroadcastRecorderView.swift | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift index db3fdefcc..09ed1ff44 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/View/VoiceBroadcastPlaybackView.swift @@ -97,11 +97,12 @@ struct VoiceBroadcastPlaybackView: View { Text(VectorL10n.voiceBroadcastLive) .font(theme.fonts.caption1SB) .foregroundColor(Color.white) + .padding(.leading, -4) } icon: { Image(uiImage: Asset.Images.voiceBroadcastLive.image) } - .padding(.horizontal, 5) - .background(RoundedRectangle(cornerRadius: 4, style: .continuous).fill(backgroundColor)) + .padding(EdgeInsets(top: 2.0, leading: 4.0, bottom: 2.0, trailing: 4.0)) + .background(RoundedRectangle(cornerRadius: 2, style: .continuous).fill(backgroundColor)) .accessibilityIdentifier("liveLabel") } } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift index 55f9ad878..c0cafed9b 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/View/VoiceBroadcastRecorderView.swift @@ -69,11 +69,12 @@ struct VoiceBroadcastRecorderView: View { Text(VectorL10n.voiceBroadcastLive) .font(theme.fonts.caption1SB) .foregroundColor(Color.white) + .padding(.leading, -4) } icon: { Image(uiImage: Asset.Images.voiceBroadcastLive.image) } - .padding(.horizontal, 5) - .background(RoundedRectangle(cornerRadius: 4, style: .continuous).fill(backgroundColor)) + .padding(EdgeInsets(top: 2.0, leading: 4.0, bottom: 2.0, trailing: 4.0)) + .background(RoundedRectangle(cornerRadius: 2, style: .continuous).fill(backgroundColor)) .accessibilityIdentifier("liveButton") } From 74b5a28c655f342d9fd1b28b90d69f9b72857600 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Fri, 16 Dec 2022 16:43:14 +0100 Subject: [PATCH 053/228] Add Towncrier file --- changelog.d/pr-7178.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7178.change diff --git a/changelog.d/pr-7178.change b/changelog.d/pr-7178.change new file mode 100644 index 000000000..a5dd6e17c --- /dev/null +++ b/changelog.d/pr-7178.change @@ -0,0 +1 @@ +Labs: VoiceBroadcast: Update live badge layout for recorder and player cells From a39d503f94eb099e0fa50419b42e71526708b283 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 16:00:37 +0100 Subject: [PATCH 054/228] Fix security card tappability --- .../View/SecurityRecommendationCard.swift | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift index 090051154..399f73f51 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift @@ -43,13 +43,9 @@ struct SecurityRecommendationCard: View { .foregroundColor(theme.colors.secondaryContent) } - Button { - action() - } label: { - Text(buttonTitle) - .font(theme.fonts.body) - } - .foregroundColor(theme.colors.accent) + Text(buttonTitle) + .font(theme.fonts.body) + .foregroundColor(theme.colors.accent) } .frame(maxWidth: .infinity, alignment: .leading) } @@ -57,6 +53,9 @@ struct SecurityRecommendationCard: View { .background(theme.colors.background) .clipShape(backgroundShape) .shapedBorder(color: theme.colors.quinaryContent, borderWidth: 1.0, shape: backgroundShape) + .onTapGesture { + action() + } } private var backgroundShape: RoundedRectangle { From 11af3ce8099382ed41e615b95808725ff081cdec Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 16:06:17 +0100 Subject: [PATCH 055/228] Remove long press interaction from sessions list --- .../UserSessions/UserOtherSessions/View/UserOtherSessions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessions.swift b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessions.swift index e0f71675c..138de1a11 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessions.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessions.swift @@ -100,7 +100,7 @@ struct UserOtherSessions: View { isSeparatorHidden: viewData == viewModel.viewState.sessionItems.last, isEditModeEnabled: viewModel.isEditModeEnabled, onBackgroundTap: { sessionId in viewModel.send(viewAction: .userOtherSessionSelected(sessionId: sessionId)) }, - onBackgroundLongPress: { _ in viewModel.isEditModeEnabled = true }) + onBackgroundLongPress: { _ in }) } } .background(theme.colors.background) From aff42bb2777485aaf25598ea54f69102d73181ff Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:11:44 +0100 Subject: [PATCH 056/228] Fix info in UserSessionCardView --- .../Common/View/UserSessionCardView.swift | 20 ++++++++++++++----- .../View/UserSessionOverview.swift | 13 ++++++------ .../View/UserSessionsOverview.swift | 2 +- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift index 9f6f6460f..999f4bc5a 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift @@ -30,9 +30,16 @@ struct UserSessionCardView: View { RoundedRectangle(cornerRadius: 8) } + enum DisplayMode { + case compact + case extended + } + let showLocationInformations: Bool + let displayMode: DisplayMode + private var showExtraInformations: Bool { - viewData.isCurrentSessionDisplayMode == false && (viewData.lastActivityDateString.isEmptyOrNil == false || ipText.isEmptyOrNil == false) + displayMode == .extended && (viewData.lastActivityDateString.isEmptyOrNil == false || ipText.isEmptyOrNil == false) } var body: some View { @@ -93,7 +100,7 @@ struct UserSessionCardView: View { .accessibilityIdentifier("userSessionCardVerifyButton") } - if viewData.isCurrentSessionDisplayMode { + if viewData.isCurrentSessionDisplayMode && displayMode == .compact { Text(VectorL10n.userSessionViewDetails) .font(theme.fonts.body) .foregroundColor(theme.colors.accent) @@ -124,8 +131,9 @@ struct UserSessionCardViewPreview: View { @Environment(\.theme) var theme: ThemeSwiftUI let viewData: UserSessionCardViewData + let displayMode: UserSessionCardView.DisplayMode - init(isCurrent: Bool = false, verificationState: UserSessionInfo.VerificationState = .unverified) { + init(isCurrent: Bool = false, verificationState: UserSessionInfo.VerificationState = .unverified, displayMode: UserSessionCardView.DisplayMode = .extended) { let sessionInfo = UserSessionInfo(id: "alice", name: "iOS", deviceType: .mobile, @@ -143,11 +151,12 @@ struct UserSessionCardViewPreview: View { isActive: true, isCurrent: isCurrent) viewData = UserSessionCardViewData(sessionInfo: sessionInfo) + self.displayMode = displayMode } var body: some View { VStack { - UserSessionCardView(viewData: viewData, showLocationInformations: true) + UserSessionCardView(viewData: viewData, showLocationInformations: true, displayMode: displayMode) } .frame(maxWidth: .infinity) .background(theme.colors.system) @@ -158,7 +167,8 @@ struct UserSessionCardViewPreview: View { struct UserSessionCardView_Previews: PreviewProvider { static var previews: some View { Group { - UserSessionCardViewPreview(isCurrent: true).theme(.light).preferredColorScheme(.light) + UserSessionCardViewPreview(isCurrent: true, displayMode: .compact).theme(.light).preferredColorScheme(.light) + UserSessionCardViewPreview(isCurrent: true, displayMode: .extended).theme(.light).preferredColorScheme(.light) UserSessionCardViewPreview(isCurrent: true).theme(.dark).preferredColorScheme(.dark) UserSessionCardViewPreview().theme(.light).preferredColorScheme(.light) UserSessionCardViewPreview().theme(.dark).preferredColorScheme(.dark) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift index 023952845..84c9b31e3 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift @@ -34,7 +34,8 @@ struct UserSessionOverview: View { onLearnMoreAction: { viewModel.send(viewAction: .viewSessionInfo) }, - showLocationInformations: viewModel.viewState.showLocationInfo + showLocationInformations: viewModel.viewState.showLocationInfo, + displayMode: .extended ) .padding(16) @@ -77,12 +78,10 @@ struct UserSessionOverview: View { } .accessibilityIdentifier(VectorL10n.manageSessionRename) - if viewModel.viewState.isCurrentSession == false { - Button { - viewModel.send(viewAction: .showLocationInfo) - } label: { - Label(showLocationInfo: viewModel.viewState.showLocationInfo) - } + Button { + viewModel.send(viewAction: .showLocationInfo) + } label: { + Label(showLocationInfo: viewModel.viewState.showLocationInfo) } } DestructiveButton { diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift index c5ddce11d..12069ce3c 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift @@ -96,7 +96,7 @@ struct UserSessionsOverview: View { viewModel.send(viewAction: .verifyCurrentSession) }, onViewDetailsAction: { _ in viewModel.send(viewAction: .viewCurrentSessionDetails) - }, showLocationInformations: viewModel.viewState.showLocationInfo) + }, showLocationInformations: viewModel.viewState.showLocationInfo, displayMode: .compact) } header: { HStack(alignment: .firstTextBaseline) { Text(VectorL10n.userSessionsOverviewCurrentSessionSectionTitle) From bd58a47d8df2c8c8183250638a0700d32d3da9bf Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:12:59 +0100 Subject: [PATCH 057/228] Fix landscape layout in UserSessionsOverview --- .../UserSessionsOverview/View/UserSessionsOverview.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift index 12069ce3c..aba53778d 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift @@ -36,7 +36,7 @@ struct UserSessionsOverview: View { otherSessionsSection } } - .readableFrame() + .frame(maxWidth: .infinity) } .background(theme.colors.system.ignoresSafeArea()) .frame(maxHeight: .infinity) From ffeced9dfd5d2775b11614e8292cb5c6c372c3b2 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:34:54 +0100 Subject: [PATCH 058/228] Fix border color --- .../Modules/UserSessions/Common/View/DeviceAvatarView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarView.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarView.swift index 0e894961d..090d95861 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarView.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarView.swift @@ -43,7 +43,7 @@ struct DeviceAvatarView: View { // Verification badge Image(viewData.verificationImageName) .frame(maxWidth: CGFloat(badgeSize), maxHeight: CGFloat(badgeSize)) - .shapedBorder(color: theme.colors.system, borderWidth: 1, shape: Circle()) + .shapedBorder(color: theme.colors.quinaryContent, borderWidth: 1, shape: Circle()) .background(theme.colors.background) .clipShape(Circle()) .offset(x: 10, y: 8) From c172e13e12624f1a9205dac692be59877d0b9a20 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:38:05 +0100 Subject: [PATCH 059/228] Fix nav bar item color --- .../UserSessionOverview/View/UserSessionOverview.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift index 84c9b31e3..3c8d107f9 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift @@ -92,7 +92,7 @@ struct UserSessionOverview: View { .accessibilityIdentifier(VectorL10n.signOut) } label: { Image(systemName: "ellipsis") - .foregroundColor(theme.colors.secondaryContent) + .foregroundColor(theme.colors.accent) .padding(.horizontal, 4) .padding(.vertical, 12) } From 1a1b28918088218971499f67c20f649897ed098c Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:47:18 +0100 Subject: [PATCH 060/228] Fix background color in session details --- .../UserSessionDetails/View/UserSessionDetails.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionDetails/View/UserSessionDetails.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionDetails/View/UserSessionDetails.swift index 8801c9ef0..215441892 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionDetails/View/UserSessionDetails.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionDetails/View/UserSessionDetails.swift @@ -54,6 +54,7 @@ struct UserSessionDetails: View { } } .listStyle(.grouped) + .listBackgroundColor(theme.colors.system) .navigationBarTitle(VectorL10n.userSessionDetailsTitle) } } From a8565d95879d8b942f49066a4e2361990f5ab692 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 21:28:49 +0100 Subject: [PATCH 061/228] Hide verification badge in session card --- .../Common/View/DeviceAvatarView.swift | 16 +++++++++------- .../Common/View/UserSessionCardView.swift | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarView.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarView.swift index 090d95861..0352f35a4 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarView.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/DeviceAvatarView.swift @@ -23,6 +23,7 @@ struct DeviceAvatarView: View { var viewData: DeviceAvatarViewData var isSelected: Bool + var showVerificationBadge: Bool = true var avatarSize: CGFloat = 40 var badgeSize: CGFloat = 24 @@ -40,13 +41,14 @@ struct DeviceAvatarView: View { .background(isSelected ? theme.colors.primaryContent : theme.colors.system) .clipShape(Circle()) - // Verification badge - Image(viewData.verificationImageName) - .frame(maxWidth: CGFloat(badgeSize), maxHeight: CGFloat(badgeSize)) - .shapedBorder(color: theme.colors.quinaryContent, borderWidth: 1, shape: Circle()) - .background(theme.colors.background) - .clipShape(Circle()) - .offset(x: 10, y: 8) + if showVerificationBadge { + Image(viewData.verificationImageName) + .frame(maxWidth: CGFloat(badgeSize), maxHeight: CGFloat(badgeSize)) + .shapedBorder(color: theme.colors.quinaryContent, borderWidth: 1, shape: Circle()) + .background(theme.colors.background) + .clipShape(Circle()) + .offset(x: 10, y: 8) + } } .frame(maxWidth: CGFloat(avatarSize), maxHeight: CGFloat(avatarSize)) } diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift index 999f4bc5a..f7b7e3015 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift @@ -44,7 +44,7 @@ struct UserSessionCardView: View { var body: some View { VStack(alignment: .center, spacing: 12) { - DeviceAvatarView(viewData: viewData.deviceAvatarViewData, isSelected: false) + DeviceAvatarView(viewData: viewData.deviceAvatarViewData, isSelected: false, showVerificationBadge: false) .accessibilityHidden(true) Text(viewData.sessionName) From a42ee97be0f3ca22af58502fc6abe3d72730b2f3 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 21:47:36 +0100 Subject: [PATCH 062/228] Fix security recommendations layout --- .../View/UserSessionsOverview.swift | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift index aba53778d..cd63cafde 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift @@ -24,22 +24,19 @@ struct UserSessionsOverview: View { private let maxOtherSessionsToDisplay = 5 var body: some View { - VStack(alignment: .leading, spacing: 0) { - ScrollView { - if hasSecurityRecommendations { - securityRecommendationsSection - } - - currentSessionsSection - - if !viewModel.viewState.otherSessionsViewData.isEmpty { - otherSessionsSection - } + ScrollView { + if hasSecurityRecommendations { + securityRecommendationsSection + } + + currentSessionsSection + + if !viewModel.viewState.otherSessionsViewData.isEmpty { + otherSessionsSection } - .frame(maxWidth: .infinity) } .background(theme.colors.system.ignoresSafeArea()) - .frame(maxHeight: .infinity) + .frame(maxWidth: .infinity, maxHeight: .infinity) .navigationTitle(VectorL10n.userSessionsOverviewTitle) .navigationBarTitleDisplayMode(.inline) .activityIndicator(show: viewModel.viewState.showLoadingIndicator) @@ -51,17 +48,19 @@ struct UserSessionsOverview: View { private var securityRecommendationsSection: some View { SwiftUI.Section { - if !viewModel.viewState.unverifiedSessionsViewData.isEmpty { - SecurityRecommendationCard(style: .unverified, - sessionCount: viewModel.viewState.unverifiedSessionsViewData.count) { - viewModel.send(viewAction: .viewAllUnverifiedSessions) + VStack(spacing: 16) { + if !viewModel.viewState.unverifiedSessionsViewData.isEmpty { + SecurityRecommendationCard(style: .unverified, + sessionCount: viewModel.viewState.unverifiedSessionsViewData.count) { + viewModel.send(viewAction: .viewAllUnverifiedSessions) + } } - } - - if !viewModel.viewState.inactiveSessionsViewData.isEmpty { - SecurityRecommendationCard(style: .inactive, - sessionCount: viewModel.viewState.inactiveSessionsViewData.count) { - viewModel.send(viewAction: .viewAllInactiveSessions) + + if !viewModel.viewState.inactiveSessionsViewData.isEmpty { + SecurityRecommendationCard(style: .inactive, + sessionCount: viewModel.viewState.inactiveSessionsViewData.count) { + viewModel.send(viewAction: .viewAllInactiveSessions) + } } } } header: { From 4d5c8b3fd58a3aaff9bbc6a03cff3b95c67bcce3 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 10:57:12 +0100 Subject: [PATCH 063/228] SecurityRecommendationCard H spacing --- .../UserSessionsOverview/View/SecurityRecommendationCard.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift index 399f73f51..050140133 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift @@ -30,7 +30,7 @@ struct SecurityRecommendationCard: View { let action: () -> Void var body: some View { - HStack(alignment: .top) { + HStack(alignment: .top, spacing: 16.0) { Image(iconName) VStack(alignment: .leading, spacing: 16.0) { VStack(alignment: .leading, spacing: 8.0) { From 44d471f45a70f4617b1dc982a4026183e771eb97 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 11:07:11 +0100 Subject: [PATCH 064/228] Refine UserOtherSessionsHeaderView layout --- .../UserOtherSessions/View/UserOtherSessions.swift | 6 +++--- .../View/UserOtherSessionsHeaderView.swift | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessions.swift b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessions.swift index 138de1a11..41d79fe54 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessions.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessions.swift @@ -22,8 +22,8 @@ struct UserOtherSessions: View { @ObservedObject var viewModel: UserOtherSessionsViewModel.Context var body: some View { - VStack(spacing: 0) { - ScrollView { + ScrollView { + VStack(spacing: 0) { SwiftUI.Section { if viewModel.viewState.sessionItems.isEmpty { noItemsView() @@ -37,7 +37,7 @@ struct UserOtherSessions: View { viewModel.send(viewAction: .viewSessionInfo) } ) - .padding(.top) + .padding(.vertical, 24) } } if viewModel.isEditModeEnabled { diff --git a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsHeaderView.swift b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsHeaderView.swift index a815d7875..5555cc827 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsHeaderView.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsHeaderView.swift @@ -54,7 +54,6 @@ struct UserOtherSessionsHeaderView: View { } .font(theme.fonts.footnote) .foregroundColor(theme.colors.secondaryContent) - .padding(.bottom, 20.0) }) } .frame(maxWidth: .infinity, alignment: .leading) From 3e5a9dad405f50b18b2d674566d30c73bd0e284b Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 11:27:55 +0100 Subject: [PATCH 065/228] Fix paddings in UserSessionsOverview --- .../UserSessionsOverview/View/UserSessionsOverview.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift index cd63cafde..ad068d616 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift @@ -74,10 +74,10 @@ struct UserSessionsOverview: View { Text(VectorL10n.userSessionsOverviewSecurityRecommendationsSectionInfo) .font(theme.fonts.footnote) .foregroundColor(theme.colors.secondaryContent) - .padding(.bottom, 12.0) } .frame(maxWidth: .infinity, alignment: .leading) .padding(.top, 24) + .padding(.bottom, 8.0) } .padding(.horizontal, 16) .accessibilityIdentifier("userSessionsOverviewSecurityRecommendationsSection") @@ -103,11 +103,11 @@ struct UserSessionsOverview: View { .font(theme.fonts.footnote) .foregroundColor(theme.colors.secondaryContent) .frame(maxWidth: .infinity, alignment: .leading) - .padding(.bottom, 12.0) .padding(.top, 24.0) currentSessionMenu } + .padding(.bottom, 8.0) } .padding(.horizontal, 16) } @@ -197,11 +197,11 @@ struct UserSessionsOverview: View { Text(VectorL10n.userSessionsOverviewOtherSessionsSectionInfo) .font(theme.fonts.footnote) .foregroundColor(theme.colors.secondaryContent) - .padding(.bottom, 12.0) } .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 16.0) .padding(.top, 24.0) + .padding(.bottom, 8.0) } .accessibilityIdentifier("userSessionsOverviewOtherSection") } From cc2c1344b9a00f16d5c0c75e10856e515eab2caf Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 11:34:14 +0100 Subject: [PATCH 066/228] Fix UserSessionCardView cta top padding --- .../Modules/UserSessions/Common/View/UserSessionCardView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift index f7b7e3015..ede242807 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift @@ -100,11 +100,12 @@ struct UserSessionCardView: View { .accessibilityIdentifier("userSessionCardVerifyButton") } - if viewData.isCurrentSessionDisplayMode && displayMode == .compact { + if viewData.isCurrentSessionDisplayMode, displayMode == .compact { Text(VectorL10n.userSessionViewDetails) .font(theme.fonts.body) .foregroundColor(theme.colors.accent) .accessibilityIdentifier("userSessionCardViewDetails") + .padding(.top, 8) } } .padding(24) From 5c19963ab517b4490df5a363b543a34819950700 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 11:48:39 +0100 Subject: [PATCH 067/228] Fix borders --- .../Modules/UserSessions/Common/View/SeparatorLine.swift | 4 +++- .../UserSessions/Common/View/UserSessionCardView.swift | 2 +- .../View/SecurityRecommendationCard.swift | 2 +- .../UserSessionsOverview/View/UserSessionListItem.swift | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/SeparatorLine.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/SeparatorLine.swift index 2f56761ef..22c24aa94 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/SeparatorLine.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/SeparatorLine.swift @@ -19,10 +19,12 @@ import SwiftUI struct SeparatorLine: View { @Environment(\.theme) private var theme: ThemeSwiftUI + var height: CGFloat = 1.0 + var body: some View { Rectangle() .fill(theme.colors.quinaryContent) .frame(maxWidth: .infinity) - .frame(height: 1.0) + .frame(height: height) } } diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift index ede242807..f6193029e 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift @@ -112,7 +112,7 @@ struct UserSessionCardView: View { .frame(maxWidth: .infinity) .background(theme.colors.background) .clipShape(backgroundShape) - .shapedBorder(color: theme.colors.quinaryContent, borderWidth: 1.0, shape: backgroundShape) + .shapedBorder(color: theme.colors.quinaryContent, borderWidth: 0.5, shape: backgroundShape) .onTapGesture { if viewData.isCurrentSessionDisplayMode { onViewDetailsAction?(viewData.sessionId) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift index 050140133..4e7f8078f 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift @@ -52,7 +52,7 @@ struct SecurityRecommendationCard: View { .padding(16) .background(theme.colors.background) .clipShape(backgroundShape) - .shapedBorder(color: theme.colors.quinaryContent, borderWidth: 1.0, shape: backgroundShape) + .shapedBorder(color: theme.colors.quinaryContent, borderWidth: 0.5, shape: backgroundShape) .onTapGesture { action() } diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift index f5d24c555..ef706648f 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift @@ -67,7 +67,7 @@ struct UserSessionListItem: View { } .padding(.bottom, 16) .padding(.trailing, 16) - SeparatorLine() + SeparatorLine(height: 0.5) .isHidden(isSeparatorHidden) } .padding(.leading, 7) From d9a319e0d21ea9947842fef3de4fc6e2eab8a9b0 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 14:52:46 +0100 Subject: [PATCH 068/228] Add missing separators in UserSessionsOverview --- .../UserSessionsOverview/View/UserSessionsListViewAllView.swift | 1 - .../UserSessionsOverview/View/UserSessionsOverview.swift | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift index e3816314c..3a4612c60 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift @@ -38,7 +38,6 @@ struct UserSessionsListViewAllView: View { } .padding(.vertical, 15) .padding(.trailing, 20) - SeparatorLine() } .background(theme.colors.background) .padding(.leading, 72) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift index ad068d616..9cfc89ca4 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsOverview.swift @@ -169,6 +169,7 @@ struct UserSessionsOverview: View { private var otherSessionsSection: some View { SwiftUI.Section { LazyVStack(spacing: 0) { + SeparatorLine(height: 0.5) ForEach(viewModel.viewState.otherSessionsViewData.prefix(maxOtherSessionsToDisplay)) { viewData in UserSessionListItem(viewData: viewData, showsLocationInfo: viewModel.viewState.showLocationInfo, @@ -180,6 +181,7 @@ struct UserSessionsOverview: View { viewModel.send(viewAction: .viewAllOtherSessions) } } + SeparatorLine(height: 0.5) } .background(theme.colors.background) } header: { From cc89ee9f47324a03cefb3e09a2709b6dc820c3a4 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 14:58:30 +0100 Subject: [PATCH 069/228] Fix shield size in UserSessionCardView --- .../Common/View/UserSessionCardView.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift index f6193029e..a8a4aa351 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardView.swift @@ -52,10 +52,16 @@ struct UserSessionCardView: View { .foregroundColor(theme.colors.primaryContent) .multilineTextAlignment(.center) - Label(viewData.verificationStatusText, image: viewData.verificationStatusImageName) - .font(theme.fonts.subheadline) - .foregroundColor(theme.colors[keyPath: viewData.verificationStatusColor]) - .multilineTextAlignment(.center) + Label { + Text(viewData.verificationStatusText) + .font(theme.fonts.subheadline) + .foregroundColor(theme.colors[keyPath: viewData.verificationStatusColor]) + .multilineTextAlignment(.center) + } icon: { + Image(viewData.verificationStatusImageName) + .resizable() + .frame(width: 16, height: 16) + } InlineTextButton(viewData.verificationStatusAdditionalInfoText, tappableText: VectorL10n.userSessionLearnMore, alwaysCallAction: false) { onLearnMoreAction?() From 5443ca28bc9be7f2f596dad1372137166b7d84e5 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 15:09:28 +0100 Subject: [PATCH 070/228] Fix icon alignment in UserSessionListItem --- .../UserSessionsOverview/View/UserSessionListItem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift index ef706648f..0ac07d915 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift @@ -49,7 +49,7 @@ struct UserSessionListItem: View { .padding(.top, 16) .padding(.bottom, 2) .padding(.trailing, 16) - HStack { + HStack(alignment: .top) { if let sessionDetailsIcon = viewData.sessionDetailsIcon { Image(sessionDetailsIcon) .padding(.leading, 2) From b01ef74fc2b20a9abbbeff7b53380913b80b5d0d Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 15:27:46 +0100 Subject: [PATCH 071/228] Fix UserSessionOverview layout --- .../View/UserSessionOverview.swift | 22 ++++++++++--------- .../View/UserSessionOverviewItem.swift | 2 +- .../View/UserSessionOverviewToggleCell.swift | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift index 3c8d107f9..e188b3668 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift @@ -40,16 +40,18 @@ struct UserSessionOverview: View { .padding(16) SwiftUI.Section { - UserSessionOverviewItem(title: VectorL10n.userSessionOverviewSessionDetailsButtonTitle, - showsChevron: true) { - viewModel.send(viewAction: .viewSessionDetails) - } - - if let enabled = viewModel.viewState.isPusherEnabled { - UserSessionOverviewToggleCell(title: VectorL10n.userSessionPushNotifications, - message: VectorL10n.userSessionPushNotificationsMessage, - isOn: enabled, isEnabled: viewModel.viewState.remotelyTogglingPushersAvailable) { - viewModel.send(viewAction: .togglePushNotifications) + VStack(spacing: 24) { + UserSessionOverviewItem(title: VectorL10n.userSessionOverviewSessionDetailsButtonTitle, + showsChevron: true) { + viewModel.send(viewAction: .viewSessionDetails) + } + + if let enabled = viewModel.viewState.isPusherEnabled{ + UserSessionOverviewToggleCell(title: VectorL10n.userSessionPushNotifications, + message: VectorL10n.userSessionPushNotificationsMessage, + isOn: enabled, isEnabled: viewModel.viewState.remotelyTogglingPushersAvailable) { + viewModel.send(viewAction: .togglePushNotifications) + } } } } diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift index b54d23a99..a3a633fc0 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift @@ -39,7 +39,7 @@ struct UserSessionOverviewItem: View { Image(Asset.Images.chevron.name) } } - .padding(.vertical, 15) + .padding(.vertical, 11) .padding(.horizontal, 16) SeparatorLine() } diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewToggleCell.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewToggleCell.swift index b6f79b58f..c162e930e 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewToggleCell.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewToggleCell.swift @@ -41,7 +41,7 @@ struct UserSessionOverviewToggleCell: View { } .disabled(!isEnabled) .allowsHitTesting(false) - .padding(.vertical, 10) + .padding(.vertical, 5.5) .padding(.horizontal, 16) .accessibilityIdentifier("UserSessionOverviewToggleCell") SeparatorLine() From d7bbc0d2abd7932013cc5bea537f9f71e13a2023 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:02:20 +0100 Subject: [PATCH 072/228] Fix font --- .../UserSessionOverview/View/UserSessionOverview.swift | 2 +- .../UserSessionsOverview/View/UserSessionListItem.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift index e188b3668..8f155814f 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverview.swift @@ -46,7 +46,7 @@ struct UserSessionOverview: View { viewModel.send(viewAction: .viewSessionDetails) } - if let enabled = viewModel.viewState.isPusherEnabled{ + if let enabled = viewModel.viewState.isPusherEnabled { UserSessionOverviewToggleCell(title: VectorL10n.userSessionPushNotifications, message: VectorL10n.userSessionPushNotificationsMessage, isOn: enabled, isEnabled: viewModel.viewState.remotelyTogglingPushersAvailable) { diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift index 0ac07d915..3118f65cb 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift @@ -43,7 +43,7 @@ struct UserSessionListItem: View { DeviceAvatarView(viewData: viewData.deviceAvatarViewData, isSelected: viewData.isSelected) VStack(alignment: .leading, spacing: 0) { Text(viewData.sessionName) - .font(theme.fonts.bodySB) + .font(theme.fonts.calloutSB) .foregroundColor(theme.colors.primaryContent) .multilineTextAlignment(.leading) .padding(.top, 16) From 581477ce3fc8c3047e24359a5117a02837e13d7d Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:05:55 +0100 Subject: [PATCH 073/228] =?UTF-8?q?Add=20line=20limit=20for=20sessions?= =?UTF-8?q?=E2=80=99=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserSessionsOverview/View/UserSessionListItem.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift index 3118f65cb..4574f2913 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift @@ -43,6 +43,7 @@ struct UserSessionListItem: View { DeviceAvatarView(viewData: viewData.deviceAvatarViewData, isSelected: viewData.isSelected) VStack(alignment: .leading, spacing: 0) { Text(viewData.sessionName) + .lineLimit(1) .font(theme.fonts.calloutSB) .foregroundColor(theme.colors.primaryContent) .multilineTextAlignment(.leading) From 23958190f1762fb4ce4d40cc5155385a74a28c95 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:25:56 +0100 Subject: [PATCH 074/228] Fix chevron icon --- .../UserSessionOverview/View/UserSessionOverviewItem.swift | 3 ++- .../View/UserSessionsListViewAllView.swift | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift index a3a633fc0..e1e98ad17 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift @@ -36,7 +36,8 @@ struct UserSessionOverviewItem: View { .frame(maxWidth: .infinity, alignment: alignment) if showsChevron { - Image(Asset.Images.chevron.name) + Image(systemName: "chevron.right") + .foregroundColor(theme.colors.tertiaryContent) } } .padding(.vertical, 11) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift index 3a4612c60..cb4a635a1 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift @@ -34,7 +34,8 @@ struct UserSessionsListViewAllView: View { .font(theme.fonts.body) .foregroundColor(theme.colors.accent) .frame(maxWidth: .infinity, alignment: .leading) - Image(Asset.Images.chevron.name) + Image(systemName: "chevron.right") + .foregroundColor(theme.colors.tertiaryContent) } .padding(.vertical, 15) .padding(.trailing, 20) From da532695a842a3db0cfcd0402b44aab3eb362528 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:36:58 +0100 Subject: [PATCH 075/228] Update copy --- Riot/Assets/de.lproj/Vector.strings | 1 - Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Assets/et.lproj/Vector.strings | 1 - Riot/Assets/fr.lproj/Vector.strings | 1 - Riot/Assets/hu.lproj/Vector.strings | 1 - Riot/Assets/id.lproj/Vector.strings | 1 - Riot/Assets/it.lproj/Vector.strings | 1 - Riot/Assets/nl.lproj/Vector.strings | 1 - Riot/Assets/pt_BR.lproj/Vector.strings | 1 - Riot/Assets/sk.lproj/Vector.strings | 1 - Riot/Assets/sq.lproj/Vector.strings | 1 - Riot/Assets/uk.lproj/Vector.strings | 1 - Riot/Generated/Strings.swift | 2 +- 13 files changed, 2 insertions(+), 13 deletions(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index db34abd41..766ff9904 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2571,7 +2571,6 @@ "user_inactive_session_item_with_date" = "Inaktiv seit 90+ Tagen (%@)"; "user_inactive_session_item" = "Inaktiv seit 90+ Tagen"; "user_other_session_unverified_sessions_header_subtitle" = "Für besonders sichere Kommunikation verifiziere deine Sitzungen oder melde dich von ihnen ab, falls du sie nicht mehr identifizieren kannst."; -"user_other_session_security_recommendation_title" = "Sicherheitsempfehlung"; "user_sessions_overview_link_device" = "Verbinde ein Gerät"; // MARK: User sessions management diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 4cca29fb9..c1263cf9d 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2474,7 +2474,7 @@ To enable access, tap Settings> Location and select Always"; "user_session_rename_session_title" = "Renaming sessions"; "user_session_rename_session_description" = "Other users in direct messages and rooms that you join are able to view a full list of your sessions.\n\nThis provides them with confidence that they are really speaking to you, but it also means they can see the session name you enter here."; -"user_other_session_security_recommendation_title" = "Security recommendation"; +"user_other_session_security_recommendation_title" = "Other sessions"; "user_other_session_unverified_sessions_header_subtitle" = "Verify your sessions for enhanced secure messaging or sign out from those you don’t recognize or use anymore."; "user_other_session_current_session_details" = "Your current session"; "user_other_session_verified_sessions_header_subtitle" = "For best security, sign out from any session that you don’t recognize or use anymore."; diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index fd122e847..3797c222b 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2545,7 +2545,6 @@ "user_other_session_verified_sessions_header_subtitle" = "Parima turvalisuse nimel logi välja neist sessioonidest, mida sa enam ei kasuta või ei tunne ära."; "user_other_session_current_session_details" = "Sinu praegune sessioon"; "user_other_session_unverified_sessions_header_subtitle" = "Turvalise sõnumvahetuse nimel verifitseeri kõik oma sessioonid ning logi neist välja, mida sa enam ei kasuta või ei tunne enam ära."; -"user_other_session_security_recommendation_title" = "Turvalisusega seotud soovitused"; "user_other_session_verified_additional_info" = "See sessioon on valmis turvaliseks sõnumivahetuseks."; "user_other_session_unverified_additional_info" = "Parima turvalisuse ja töökindluse nimel verifitseeri see sessioon või logi ta võrgust välja."; "user_session_verification_unknown_additional_info" = "Selle sessiooni olekut ei saa tuvastada enne kui oled ta verifitseerinud."; diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index 4f371613b..ed4a3820f 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -2542,7 +2542,6 @@ // First item is client name and second item is session display name "user_session_name" = "%@ : %@"; "user_other_session_unverified_sessions_header_subtitle" = "Vérifiez vos sessions pour renforcer la sécurité de votre messagerie, ou déconnectez celles que vous ne reconnaissez ou utilisez plus."; -"user_other_session_security_recommendation_title" = "Recommandations de sécurité"; "user_session_push_notifications_message" = "Lorsqu’activé, cette session recevra des notifications push."; "user_session_push_notifications" = "Notifications push"; "user_sessions_overview_current_session_section_title" = "Session courante"; diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 78e52a6fd..f1f4e16ab 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -2565,7 +2565,6 @@ "user_other_session_verified_sessions_header_subtitle" = "A legjobb biztonság érdekében jelentkezz ki minden olyan munkamenetből, melyet már nem ismersz fel vagy nem használsz."; "user_other_session_current_session_details" = "Jelenlegi munkamenet"; "user_other_session_unverified_sessions_header_subtitle" = "Erősítse meg a munkameneteit a még biztonságosabb csevegéshez vagy jelentkezzen ki ezekből, ha nem ismeri fel vagy már nem használja őket."; -"user_other_session_security_recommendation_title" = "Biztonsági javaslat"; "user_session_push_notifications_message" = "Ha be van kapcsolva az eszközre Push értesítések lesznek küldve."; "user_session_push_notifications" = "Push értesítések"; "user_other_session_verified_additional_info" = "Ez a munkamenet beállítva a biztonságos üzenetküldéshez."; diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 8894d91b4..a4ae14538 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2752,7 +2752,6 @@ "user_inactive_session_item_with_date" = "Tidak aktif selama 90+ hari (%@)"; "user_inactive_session_item" = "Tidak aktif selama 90+ hari"; "user_other_session_unverified_sessions_header_subtitle" = "Verifikasi sesi Anda untuk perpesanan aman yang terbaik atau keluarkan sesi yang Anda tidak kenal atau gunakan lagi."; -"user_other_session_security_recommendation_title" = "Saran keamanan"; "user_sessions_overview_link_device" = "Tautkan sebuah perangkat"; // MARK: User sessions management diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index bc2e62e7c..b0ee26dc2 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -2525,7 +2525,6 @@ "user_inactive_session_item" = "Inattiva da 90+ giorni"; "user_inactive_session_item_with_date" = "Inattiva da 90+ giorni (%@)"; "user_other_session_unverified_sessions_header_subtitle" = "Verifica le tue sessioni per avere conversazioni più sicure o disconnetti quelle che non riconosci o che non usi più."; -"user_other_session_security_recommendation_title" = "Consiglio di sicurezza"; "user_sessions_overview_link_device" = "Collega un dispositivo"; // MARK: User sessions management diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index 0b3fd5868..5c02db6a0 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -2724,7 +2724,6 @@ "user_other_session_verified_sessions_header_subtitle" = "Voor de beste beveiliging log je uit bij elke sessie die je niet meer herkent of gebruikt."; "user_other_session_current_session_details" = "Jouw huidige sessie"; "user_other_session_unverified_sessions_header_subtitle" = "Verifieer je sessies voor verbeterde beveiligde berichtenuitwisseling of meld je af bij sessies die je niet meer herkent of gebruikt."; -"user_other_session_security_recommendation_title" = "Beveiligingsaanbeveling"; "user_session_push_notifications_message" = "Indien ingeschakeld, ontvangt deze sessie pushmeldingen."; "user_session_push_notifications" = "Pushmeldingen"; "user_other_session_verified_additional_info" = "Deze sessie is klaar voor beveiligde berichtenuitwisseling."; diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 7e2cc90f2..3bf630208 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -2526,7 +2526,6 @@ "user_inactive_session_item_with_date" = "Inativa por 90+ dias (%@)"; "user_inactive_session_item" = "Inativa por 90+ dias"; "user_other_session_unverified_sessions_header_subtitle" = "Verifique suas sessões para mensageria de segurança melhorada ou faça signout daquelas que você não reconhece ou usa mais."; -"user_other_session_security_recommendation_title" = "Recomendação de segurança"; "user_sessions_overview_link_device" = "Linkar um dispositivo"; // MARK: User sessions management diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 9f1cc6b65..8290307fa 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2748,7 +2748,6 @@ "user_inactive_session_item_with_date" = "Neaktívna viac ako 90 dní (%@)"; "user_inactive_session_item" = "Neaktívna viac ako 90 dní"; "user_other_session_unverified_sessions_header_subtitle" = "Overte si relácie pre vylepšené bezpečné zasielanie správ alebo sa odhláste z tých, ktoré už nepoznáte alebo nepoužívate."; -"user_other_session_security_recommendation_title" = "Bezpečnostné odporúčania"; "user_sessions_overview_link_device" = "Prepojiť zariadenie"; // MARK: User sessions management diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 8d6e3cbfd..24517ae23 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2486,7 +2486,6 @@ "user_other_session_verified_sessions_header_subtitle" = "Për sigurinë më të mirë, dilni nga çfarëdo sesioni që nuk e njihni apo përdorni më."; "user_other_session_current_session_details" = "Sesioni juaj i tanishëm"; "user_other_session_unverified_sessions_header_subtitle" = "Verifikoni sesionet tuaj, për shkëmbim më të sigurt mesazhesh, ose dilni prej atyre që nuk i njihni, apo përdorni më."; -"user_other_session_security_recommendation_title" = "Rekomandim sigurie"; "user_session_push_notifications_message" = "Kur aktivizohet, ky sesion do të marrë njoftime push."; "user_session_push_notifications" = "Njoftime Push"; "user_other_session_verified_additional_info" = "Ky sesion është gati për shkëmbim të sigurt mesazhesh."; diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index a38d76c6f..f274a0dbe 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2750,7 +2750,6 @@ "user_inactive_session_item_with_date" = "Неактивний понад 90 днів (%@)"; "user_inactive_session_item" = "Неактивний понад 90 днів"; "user_other_session_unverified_sessions_header_subtitle" = "Перевірте свої сеанси для посилення безпеки обміну повідомленнями або вийдіть з тих, які ви більше не розпізнаєте або не використовуєте."; -"user_other_session_security_recommendation_title" = "Поради з безпеки"; "user_sessions_overview_link_device" = "Пов'язати пристрій"; // MARK: User sessions management diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 57eb34850..f2257f1cc 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -8727,7 +8727,7 @@ public class VectorL10n: NSObject { public static var userOtherSessionPermanentlyUnverifiedAdditionalInfo: String { return VectorL10n.tr("Vector", "user_other_session_permanently_unverified_additional_info") } - /// Security recommendation + /// Other sessions public static var userOtherSessionSecurityRecommendationTitle: String { return VectorL10n.tr("Vector", "user_other_session_security_recommendation_title") } From c22a0b0fff0d2ee33618488b8d0d63ccd75f7abb Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:51:12 +0100 Subject: [PATCH 076/228] Add changelog.d file --- changelog.d/pr-7180.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7180.change diff --git a/changelog.d/pr-7180.change b/changelog.d/pr-7180.change new file mode 100644 index 000000000..80cb1db19 --- /dev/null +++ b/changelog.d/pr-7180.change @@ -0,0 +1 @@ +Updates on the UI/UX to conform the device manager to the design. From 394e12e8e5708bc10d378c51cd4988380ce03d41 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 18:23:40 +0100 Subject: [PATCH 077/228] Fix UI tests --- .../UserOtherSessions/View/UserOtherSessionsToolbar.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsToolbar.swift b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsToolbar.swift index b74606910..331ede0c3 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsToolbar.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserOtherSessions/View/UserOtherSessionsToolbar.swift @@ -107,5 +107,6 @@ struct UserOtherSessionsToolbar: ToolbarContent { .padding(.vertical, 12) } } + .accessibilityIdentifier("More") } } From a4d0c215c1aa0eb8dc15767f4ce70e2e25cb1b31 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Fri, 16 Dec 2022 18:28:54 +0100 Subject: [PATCH 078/228] latest swiftpm version --- Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- project.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index 405f9427b..102f87715 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,7 +23,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift", "state" : { - "revision" : "2a6dccc6849ffdbc4f8b00bd22a696077f726fd3" + "revision" : "534ee5bae5e8de69ed398937b5edb7b5f21551d2" } }, { diff --git a/project.yml b/project.yml index 134ed93ac..f2c4dbc23 100644 --- a/project.yml +++ b/project.yml @@ -53,7 +53,7 @@ packages: branch: main WysiwygComposer: url: https://github.com/matrix-org/matrix-wysiwyg-composer-swift - revision: 2a6dccc6849ffdbc4f8b00bd22a696077f726fd3 + revision: 534ee5bae5e8de69ed398937b5edb7b5f21551d2 DeviceKit: url: https://github.com/devicekit/DeviceKit majorVersion: 4.7.0 From 8934be147b95a6ca3fcf159835a85daf3e3d7fc9 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 19 Dec 2022 14:56:06 +0100 Subject: [PATCH 079/228] Load the thread list using server-side sorting and pagination --- .../ThreadList/ThreadListViewController.swift | 6 +++++- .../ThreadList/ThreadListViewModel.swift | 19 ++++++++++++++----- changelog.d/6059.feature | 1 + 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 changelog.d/6059.feature diff --git a/Riot/Modules/Threads/ThreadList/ThreadListViewController.swift b/Riot/Modules/Threads/ThreadList/ThreadListViewController.swift index f4075a0cf..7c532be5a 100644 --- a/Riot/Modules/Threads/ThreadList/ThreadListViewController.swift +++ b/Riot/Modules/Threads/ThreadList/ThreadListViewController.swift @@ -162,7 +162,7 @@ final class ThreadListViewController: UIViewController { private func renderLoading() { emptyView.isHidden = true - threadsTableView.isHidden = true + threadsTableView.isHidden = viewModel.numberOfThreads == 0 self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) } @@ -352,6 +352,10 @@ extension ThreadListViewController: UITableViewDelegate { cell.backgroundColor = theme.backgroundColor cell.selectedBackgroundView = UIView() cell.selectedBackgroundView?.backgroundColor = theme.selectedBackgroundColor + + if indexPath.row == viewModel.numberOfThreads - 1 { + viewModel.process(viewAction: .loadData) + } } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { diff --git a/Riot/Modules/Threads/ThreadList/ThreadListViewModel.swift b/Riot/Modules/Threads/ThreadList/ThreadListViewModel.swift index 29ae93b02..16620a621 100644 --- a/Riot/Modules/Threads/ThreadList/ThreadListViewModel.swift +++ b/Riot/Modules/Threads/ThreadList/ThreadListViewModel.swift @@ -29,7 +29,7 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { private var threads: [MXThreadProtocol] = [] private var eventFormatter: MXKEventFormatter? private var roomState: MXRoomState? - + private var nextBatch: String? private var currentOperation: MXHTTPOperation? private var longPressedThread: MXThreadProtocol? @@ -71,6 +71,7 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { viewState = .showingFilterTypes case .selectFilterType(let type): selectedFilterType = type + resetData() loadData() case .selectThread(let index): selectThread(index) @@ -230,7 +231,15 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { ) } + private func resetData() { + nextBatch = nil + threads = [] + } + private func loadData(showLoading: Bool = true) { + guard threads.isEmpty || nextBatch != nil else { + return + } if showLoading { viewState = .loading @@ -245,12 +254,12 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { onlyParticipated = true } - session.threadingService.allThreads(inRoom: roomId, - onlyParticipated: onlyParticipated) { [weak self] response in + session.threadingService.allThreads(inRoom: roomId, from: nextBatch, onlyParticipated: onlyParticipated) { [weak self] response in guard let self = self else { return } switch response { - case .success(let threads): - self.threads = threads + case .success(let value): + self.threads = self.threads + value.threads + self.nextBatch = value.nextBatch self.threadsLoaded() case .failure(let error): MXLog.error("[ThreadListViewModel] loadData", context: error) diff --git a/changelog.d/6059.feature b/changelog.d/6059.feature new file mode 100644 index 000000000..c4a9e7221 --- /dev/null +++ b/changelog.d/6059.feature @@ -0,0 +1 @@ +Threads: Load the thread list using server-side sorting and pagination \ No newline at end of file From dfe55587d41601e443d8393fe256397ffcd40342 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 19 Dec 2022 15:12:52 +0100 Subject: [PATCH 080/228] Update chevron image --- .../UserSessionOverview/View/UserSessionOverviewItem.swift | 2 +- .../UserSessionsOverview/View/UserSessionsListViewAllView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift index e1e98ad17..580c9181f 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionOverview/View/UserSessionOverviewItem.swift @@ -36,7 +36,7 @@ struct UserSessionOverviewItem: View { .frame(maxWidth: .infinity, alignment: alignment) if showsChevron { - Image(systemName: "chevron.right") + Image(Asset.Images.disclosureIcon.name) .foregroundColor(theme.colors.tertiaryContent) } } diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift index cb4a635a1..d472703b5 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionsListViewAllView.swift @@ -34,7 +34,7 @@ struct UserSessionsListViewAllView: View { .font(theme.fonts.body) .foregroundColor(theme.colors.accent) .frame(maxWidth: .infinity, alignment: .leading) - Image(systemName: "chevron.right") + Image(Asset.Images.disclosureIcon.name) .foregroundColor(theme.colors.tertiaryContent) } .padding(.vertical, 15) From 3fcdb9fe9968f9b6599263b8b499819b7baa390f Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 19 Dec 2022 17:59:38 +0100 Subject: [PATCH 081/228] Update fonts --- .../UserSessionsOverview/View/SecurityRecommendationCard.swift | 2 +- .../UserSessionsOverview/View/UserSessionListItem.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift index 4e7f8078f..e272c0563 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/SecurityRecommendationCard.swift @@ -35,7 +35,7 @@ struct SecurityRecommendationCard: View { VStack(alignment: .leading, spacing: 16.0) { VStack(alignment: .leading, spacing: 8.0) { Text(title) - .font(theme.fonts.calloutSB) + .font(theme.fonts.headline) .foregroundColor(theme.colors.primaryContent) Text(subtitle) diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift index 4574f2913..287b64003 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItem.swift @@ -44,7 +44,7 @@ struct UserSessionListItem: View { VStack(alignment: .leading, spacing: 0) { Text(viewData.sessionName) .lineLimit(1) - .font(theme.fonts.calloutSB) + .font(theme.fonts.headline) .foregroundColor(theme.colors.primaryContent) .multilineTextAlignment(.leading) .padding(.top, 16) From f6670ec5d08e28f7cafc54ddcbfe0dfe6737d39e Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Mon, 19 Dec 2022 19:26:55 +0100 Subject: [PATCH 082/228] Update the room description in the rooms list in case of live broadcast (incoming or outgoing) (#7160) --- Riot/Assets/en.lproj/Vector.strings | 3 + Riot/Generated/Strings.swift | 12 +++ .../Recents/Views/RecentTableViewCell.m | 5 +- .../MatrixKit/MatrixKit-Bridging-Header.h | 1 + .../{ => MatrixSDK}/VoiceBroadcastInfo.h | 0 .../{ => MatrixSDK}/VoiceBroadcastInfo.m | 0 .../{ => MatrixSDK}/VoiceBroadcastInfo.swift | 0 .../VoiceBroadcastInfoState.swift | 0 .../VoiceBroadcastSettings.swift | 0 Riot/Utils/EventFormatter.m | 95 +++++++++++++++++++ RiotNSE/target.yml | 2 +- RiotShareExtension/target.yml | 2 +- RiotSwiftUI/target.yml | 2 +- RiotSwiftUI/targetUITests.yml | 2 +- RiotTests/target.yml | 2 +- SiriIntents/target.yml | 2 +- changelog.d/pr-7160.change | 1 + 17 files changed, 119 insertions(+), 10 deletions(-) rename Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/{ => MatrixSDK}/VoiceBroadcastInfo.h (100%) rename Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/{ => MatrixSDK}/VoiceBroadcastInfo.m (100%) rename Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/{ => MatrixSDK}/VoiceBroadcastInfo.swift (100%) rename Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/{ => MatrixSDK}/VoiceBroadcastInfoState.swift (100%) rename Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/{ => MatrixSDK}/VoiceBroadcastSettings.swift (100%) create mode 100644 changelog.d/pr-7160.change diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 80ae8311b..37bb74994 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2698,6 +2698,9 @@ To enable access, tap Settings> Location and select Always"; "notice_crypto_error_unknown_inbound_session_id" = "The sender's session has not sent us the keys for this message."; "notice_sticker" = "sticker"; "notice_in_reply_to" = "In reply to"; +"notice_voice_broadcast_live" = "Live broadcast"; +"notice_voice_broadcast_ended" = "%@ ended a voice broadcast."; +"notice_voice_broadcast_ended_by_you" = "You ended a voice broadcast."; // room display name "room_displayname_empty_room" = "Empty room"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 6d5b24981..97c1ad7e6 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -4323,6 +4323,18 @@ public class VectorL10n: NSObject { public static var noticeVideoAttachment: String { return VectorL10n.tr("Vector", "notice_video_attachment") } + /// %@ ended a voice broadcast. + public static func noticeVoiceBroadcastEnded(_ p1: String) -> String { + return VectorL10n.tr("Vector", "notice_voice_broadcast_ended", p1) + } + /// You ended a voice broadcast. + public static var noticeVoiceBroadcastEndedByYou: String { + return VectorL10n.tr("Vector", "notice_voice_broadcast_ended_by_you") + } + /// Live broadcast + public static var noticeVoiceBroadcastLive: String { + return VectorL10n.tr("Vector", "notice_voice_broadcast_live") + } /// Always notify public static var notificationSettingsAlwaysNotify: String { return VectorL10n.tr("Vector", "notification_settings_always_notify") diff --git a/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m b/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m index 4f0c94dd9..750594020 100644 --- a/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m +++ b/Riot/Modules/Common/Recents/Views/RecentTableViewCell.m @@ -80,10 +80,7 @@ // Manage lastEventAttributedTextMessage optional property if (!roomCellData.roomSummary.spaceChildInfo && [roomCellData respondsToSelector:@selector(lastEventAttributedTextMessage)]) { - // Force the default text color for the last message (cancel highlighted message color) - NSMutableAttributedString *lastEventDescription = [[NSMutableAttributedString alloc] initWithAttributedString:roomCellData.lastEventAttributedTextMessage]; - [lastEventDescription addAttribute:NSForegroundColorAttributeName value:ThemeService.shared.theme.textSecondaryColor range:NSMakeRange(0, lastEventDescription.length)]; - self.lastEventDescription.attributedText = lastEventDescription; + self.lastEventDescription.attributedText = roomCellData.lastEventAttributedTextMessage; } else { diff --git a/Riot/Modules/MatrixKit/MatrixKit-Bridging-Header.h b/Riot/Modules/MatrixKit/MatrixKit-Bridging-Header.h index 909854dc6..635a2037c 100644 --- a/Riot/Modules/MatrixKit/MatrixKit-Bridging-Header.h +++ b/Riot/Modules/MatrixKit/MatrixKit-Bridging-Header.h @@ -16,3 +16,4 @@ #import "MXKImageView.h" #import "MXKRoomBubbleCellData.h" #import "UserIndicatorCancel.h" +#import "VoiceBroadcastInfo.h" diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.h b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfo.h similarity index 100% rename from Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.h rename to Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfo.h diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.m b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfo.m similarity index 100% rename from Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.m rename to Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfo.m diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfo.swift similarity index 100% rename from Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfo.swift rename to Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfo.swift diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfoState.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfoState.swift similarity index 100% rename from Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfoState.swift rename to Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfoState.swift diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastSettings.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastSettings.swift similarity index 100% rename from Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastSettings.swift rename to Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastSettings.swift diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index 2a1fe4879..654fab329 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -546,6 +546,40 @@ 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 { + + // Do not display voice broadcast chunk in last message. + if (event.eventType == MXEventTypeRoomMessage && event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType]) + { + return NO; + } + + // Update last message if we have a voice broadcast in the room. + if ([event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]) + { + 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]) + { + return [self session:session updateRoomSummary:summary withVoiceBroadcastInfoStateEvent:stateEvent roomState:roomState]; + } + } + + BOOL updated = [super session:session updateRoomSummary:summary withLastEvent:event eventState:eventState roomState:roomState]; + + 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)]; + summary.lastMessage.attributedText = lastEventDescription; + } + + return updated; +} + - (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withStateEvents:(NSArray *)stateEvents roomState:(MXRoomState *)roomState { BOOL updated = [super session:session updateRoomSummary:summary withStateEvents:stateEvents roomState:roomState]; @@ -569,6 +603,67 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; return updated; } +- (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary withVoiceBroadcastInfoStateEvent:(MXEvent *)stateEvent roomState:(MXRoomState *)roomState +{ + [summary updateLastMessage:[[MXRoomLastMessage alloc] initWithEvent:stateEvent]]; + 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]) + { + textColor = ThemeService.shared.theme.textSecondaryColor; + NSString *senderDisplayName; + if ([stateEvent.stateKey isEqualToString:session.myUser.userId]) + { + summary.lastMessage.text = VectorL10n.noticeVoiceBroadcastEndedByYou; + } + else + { + senderDisplayName = [self senderDisplayNameForEvent:stateEvent withRoomState:roomState]; + summary.lastMessage.text = [VectorL10n noticeVoiceBroadcastEnded:senderDisplayName]; + } + } + else + { + textColor = ThemeService.shared.theme.colors.alert; + UIImage *liveImage = AssetImages.voiceBroadcastLive.image; + + NSTextAttachment *attachment = [[NSTextAttachment alloc] init]; + attachment.image = [liveImage imageWithTintColor:textColor renderingMode:UIImageRenderingModeAlwaysTemplate]; + attachmentString = [NSAttributedString attributedStringWithAttachment:attachment]; + + summary.lastMessage.text = VectorL10n.noticeVoiceBroadcastLive; + } + + // Compute the attribute text message + NSMutableAttributedString *lastMessage; + if (attachmentString) + { + lastMessage = [[NSMutableAttributedString alloc] initWithAttributedString:attachmentString]; + // Change base line + [lastMessage addAttribute:NSBaselineOffsetAttributeName value:@(-3.0f) range:NSMakeRange(0, attachmentString.length)]; + + NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@", summary.lastMessage.text]]; + [lastMessage appendAttributedString:attributedText]; + [lastMessage addAttribute:NSFontAttributeName value:self.defaultTextFont range:NSMakeRange(0, lastMessage.length)]; + } + else + { + NSAttributedString *attributedText = [self renderString:summary.lastMessage.text forEvent:stateEvent]; + lastMessage = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText]; + } + + [lastMessage addAttribute:NSForegroundColorAttributeName value:textColor range:NSMakeRange(0, lastMessage.length)]; + summary.lastMessage.attributedText = lastMessage; + + return YES; +} + - (NSAttributedString *)redactedMessageReplacementAttributedString { UIFont *font = self.defaultTextFont; diff --git a/RiotNSE/target.yml b/RiotNSE/target.yml index c7578dffe..08515998b 100644 --- a/RiotNSE/target.yml +++ b/RiotNSE/target.yml @@ -76,4 +76,4 @@ targets: excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift - - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastSettings.swift + - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK diff --git a/RiotShareExtension/target.yml b/RiotShareExtension/target.yml index 666a6e80a..d89575738 100644 --- a/RiotShareExtension/target.yml +++ b/RiotShareExtension/target.yml @@ -82,4 +82,4 @@ targets: excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift - - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastSettings.swift + - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK diff --git a/RiotSwiftUI/target.yml b/RiotSwiftUI/target.yml index dbcf4854a..22987f7b8 100644 --- a/RiotSwiftUI/target.yml +++ b/RiotSwiftUI/target.yml @@ -63,7 +63,7 @@ targets: - path: ../Riot/Modules/Analytics/AnalyticsScreen.swift - path: ../Riot/Modules/LocationSharing/LocationAuthorizationStatus.swift - path: ../Riot/Modules/QRCode/QRCodeGenerator.swift - - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfoState.swift + - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfoState.swift - path: ../Riot/Assets/en.lproj/Untranslated.strings buildPhase: resources - path: ../Riot/Assets/Images.xcassets diff --git a/RiotSwiftUI/targetUITests.yml b/RiotSwiftUI/targetUITests.yml index 533efab5f..a25f394ae 100644 --- a/RiotSwiftUI/targetUITests.yml +++ b/RiotSwiftUI/targetUITests.yml @@ -72,7 +72,7 @@ targets: - path: ../Riot/Modules/Analytics/AnalyticsScreen.swift - path: ../Riot/Modules/LocationSharing/LocationAuthorizationStatus.swift - path: ../Riot/Modules/QRCode/QRCodeGenerator.swift - - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastInfoState.swift + - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastInfoState.swift - path: ../Riot/Assets/en.lproj/Untranslated.strings buildPhase: resources - path: ../Riot/Assets/Images.xcassets diff --git a/RiotTests/target.yml b/RiotTests/target.yml index 5e31f53d7..dd7736b6f 100644 --- a/RiotTests/target.yml +++ b/RiotTests/target.yml @@ -72,4 +72,4 @@ targets: - path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift - path: ../Riot/Modules/Room/EventMenu/EventMenuBuilder.swift - path: ../Riot/Modules/Room/EventMenu/EventMenuItemType.swift - - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastSettings.swift + - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK/VoiceBroadcastSettings.swift diff --git a/SiriIntents/target.yml b/SiriIntents/target.yml index 8bb8034f5..72bbf91d4 100644 --- a/SiriIntents/target.yml +++ b/SiriIntents/target.yml @@ -64,4 +64,4 @@ targets: excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift - - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastSettings.swift + - path: ../Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/MatrixSDK diff --git a/changelog.d/pr-7160.change b/changelog.d/pr-7160.change new file mode 100644 index 000000000..d105a1dc8 --- /dev/null +++ b/changelog.d/pr-7160.change @@ -0,0 +1 @@ +Update the room description in the rooms list in case of live broadcast (incoming or outgoing) \ No newline at end of file From 4779c4b5fb7f6878b15ffd7760c7dcb21a1919b1 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Mon, 19 Dec 2022 19:32:01 +0100 Subject: [PATCH 083/228] [Voice Broadcast] Add chunk after decrypting the event. (#7181) --- .../Room/CellData/RoomBubbleCellData.m | 8 ++ .../VoiceBroadcastAggregator.swift | 86 ++++++++++++------- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 712604203..138d9c566 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -295,6 +295,14 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat { [self updateBeaconInfoSummaryWithId:eventId andEvent:event]; } + + // Handle here the case where an audio chunk of a voice broadcast have been decrypted with delay + // We take the opportunity of this update to disable the display of this chunk in the room timeline + if (event.eventType == MXEventTypeRoomMessage && event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType]) { + self.tag = RoomBubbleCellDataTagVoiceBroadcastNoDisplay; + self.collapsable = NO; + self.collapsed = NO; + } return retVal; } diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift index 31dd0045b..a649a621d 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift @@ -71,6 +71,9 @@ public class VoiceBroadcastAggregator { if let referenceEventsListener = referenceEventsListener { room.removeListener(referenceEventsListener) } + + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.mxEventDidDecrypt, object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.mxRoomDidFlushData, object: nil) } public init(session: MXSession, room: MXRoom, voiceBroadcastStartEventId: String, voiceBroadcastState: VoiceBroadcastInfoState) throws { @@ -103,6 +106,10 @@ public class VoiceBroadcastAggregator { currentUserIdentifier: session.myUserId) } + private func registerEventDidDecryptNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(eventDidDecrypt), name: NSNotification.Name.mxEventDidDecrypt, object: nil) + } + @objc private func handleRoomDataFlush(sender: Notification) { guard let room = sender.object as? MXRoom, room == self.room else { return @@ -112,6 +119,49 @@ public class VoiceBroadcastAggregator { MXLog.warning("[VoiceBroadcastAggregator] handleRoomDataFlush is not supported yet") } + @objc private func eventDidDecrypt(sender: Notification) { + guard let event = sender.object as? MXEvent else { return } + + self.handleEvent(event: event) + } + + private func handleEvent(event: MXEvent, direction: MXTimelineDirection? = nil, roomState: MXRoomState? = nil) { + switch event.eventType { + case .roomMessage: + self.updateVoiceBroadcast(event: event) + case .custom: + if event.type == VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType { + self.updateState() + } + default: + break + } + } + + private func updateVoiceBroadcast(event: MXEvent) { + guard event.sender == self.voiceBroadcastSenderId, + let relatedEventId = event.relatesTo?.eventId, + relatedEventId == self.voiceBroadcastStartEventId, + event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType] != nil else { + return + } + + if !self.events.contains(where: { $0.eventId == event.eventId }) { + self.events.append(event) + MXLog.debug("[VoiceBroadcastAggregator] Got a new chunk for broadcast \(relatedEventId). Total: \(self.events.count)") + + if let chunk = self.voiceBroadcastBuilder.buildChunk(event: event, mediaManager: self.session.mediaManager, voiceBroadcastStartEventId: self.voiceBroadcastStartEventId) { + self.delegate?.voiceBroadcastAggregator(self, didReceiveChunk: chunk) + } + + self.voiceBroadcast = self.voiceBroadcastBuilder.build(mediaManager: self.session.mediaManager, + voiceBroadcastStartEventId: self.voiceBroadcastStartEventId, + voiceBroadcastInvoiceBroadcastStartEventContent: self.voiceBroadcastInfoStartEventContent, + events: self.events, + currentUserIdentifier: self.session.myUserId) + } + } + private func updateState() { self.room.state { roomState in guard let event = roomState?.stateEvents(with: .custom(VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType))?.last, @@ -125,7 +175,7 @@ public class VoiceBroadcastAggregator { self.delegate?.voiceBroadcastAggregator(self, didReceiveState: state) } } - + func start() { guard launchState == .idle else { return @@ -149,38 +199,8 @@ public class VoiceBroadcastAggregator { self.events.append(contentsOf: filteredChunk) let eventTypes = [VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType, kMXEventTypeStringRoomMessage] - self.referenceEventsListener = self.room.listen(toEventsOfTypes: eventTypes) { [weak self] event, direction, state in - - guard let self = self else { - return - } - - if event.eventType == .roomMessage { - guard event.sender == self.voiceBroadcastSenderId, - let relatedEventId = event.relatesTo?.eventId, - relatedEventId == self.voiceBroadcastStartEventId, - event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType] != nil else { - return - } - - if !self.events.contains(where: { $0.eventId == event.eventId }) { - self.events.append(event) - MXLog.debug("[VoiceBroadcastAggregator] Got a new chunk for broadcast \(relatedEventId). Total: \(self.events.count)") - - if let chunk = self.voiceBroadcastBuilder.buildChunk(event: event, mediaManager: self.session.mediaManager, voiceBroadcastStartEventId: self.voiceBroadcastStartEventId) { - self.delegate?.voiceBroadcastAggregator(self, didReceiveChunk: chunk) - } - - self.voiceBroadcast = self.voiceBroadcastBuilder.build(mediaManager: self.session.mediaManager, - voiceBroadcastStartEventId: self.voiceBroadcastStartEventId, - voiceBroadcastInvoiceBroadcastStartEventContent: self.voiceBroadcastInfoStartEventContent, - events: self.events, - currentUserIdentifier: self.session.myUserId) - } - } else { - self.updateState() - } - } as Any + self.referenceEventsListener = self.room.listen(toEventsOfTypes: eventTypes, onEvent: self.handleEvent) as Any + self.registerEventDidDecryptNotification() self.events.forEach { event in guard let chunk = self.voiceBroadcastBuilder.buildChunk(event: event, mediaManager: self.session.mediaManager, voiceBroadcastStartEventId: self.voiceBroadcastStartEventId) else { From 3005a53a7376692ddab78a1eeb82242a364cbf85 Mon Sep 17 00:00:00 2001 From: Aleksandrs Proskurins Date: Tue, 20 Dec 2022 12:08:29 +0200 Subject: [PATCH 084/228] Update xcversion to 14.2 (#7187) * Update xcversion to 14.2 * Changelog --- changelog.d/7182.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7182.change diff --git a/changelog.d/7182.change b/changelog.d/7182.change new file mode 100644 index 000000000..353d878ae --- /dev/null +++ b/changelog.d/7182.change @@ -0,0 +1 @@ +Updated fastlane script to use Xcode v 14.2. From d66bebfe8f906d2980a30659f2657a6e29c0fe18 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 23 Dec 2022 12:06:33 +0100 Subject: [PATCH 085/228] Handle decryption errors in TimelinePollProvider --- Riot/Categories/Sequence.swift | 28 +++++++ .../Coordinator/TimelinePollCoordinator.swift | 7 +- .../Coordinator/TimelinePollProvider.swift | 76 +++++++++++++++---- 3 files changed, 97 insertions(+), 14 deletions(-) create mode 100644 Riot/Categories/Sequence.swift diff --git a/Riot/Categories/Sequence.swift b/Riot/Categories/Sequence.swift new file mode 100644 index 000000000..f3e1dfb15 --- /dev/null +++ b/Riot/Categories/Sequence.swift @@ -0,0 +1,28 @@ +// +// Copyright 2022 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. +// + +extension Sequence { + func group(by keyPath: KeyPath) -> [GroupID: [Element]] { + var result: [GroupID: [Element]] = .init() + + for item in self { + let groupId = item[keyPath: keyPath] + result[groupId] = (result[groupId] ?? []) + [item] + } + + return result + } +} diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift index 74cfaf65b..0f4d8a7d9 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift @@ -83,6 +83,10 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel func start() { } + func handleErroredRelatedEventsIds(_ ids: Set?, to parentEvent: String) { + pollAggregator.handleErroredRelatedEventsIds(ids, to: parentEvent) + } + func toPresentable() -> UIViewController { VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context)) } @@ -132,7 +136,8 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel totalAnswerCount: poll.totalAnswerCount, type: pollKindToTimelinePollType(poll.kind), maxAllowedSelections: poll.maxAllowedSelections, - hasBeenEdited: poll.hasBeenEdited) + hasBeenEdited: poll.hasBeenEdited, + hasDecryptionError: poll.hasDecryptionError) } private func pollKindToTimelinePollType(_ kind: PollKind) -> TimelinePollType { diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift index a11cb3a2e..322c05451 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift @@ -20,17 +20,20 @@ import Foundation class TimelinePollProvider: NSObject { static let shared = TimelinePollProvider() + private var decryptionErrorsObserver: NSObjectProtocol? + var session: MXSession? { willSet { - guard let currentSession = self.session else { return } - - if currentSession != newValue { - // Clear all stored coordinators on new session - coordinatorsForEventIdentifiers.removeAll() + guard newValue != session else { + return } + + reset() + updateDecryptionErrorsObserver(newSession: newValue) } } var coordinatorsForEventIdentifiers = [String: TimelinePollCoordinator]() + var erroredEventIdsByRelatedEvent = [String: Set]() /// Create or retrieve the poll timeline coordinator for this event and return /// a view to be displayed in the timeline @@ -39,16 +42,20 @@ class TimelinePollProvider: NSObject { return nil } - if let coordinator = coordinatorsForEventIdentifiers[event.eventId] { - return coordinator.toPresentable() + let coordinator: TimelinePollCoordinator + + if let cachedCoordinator = coordinatorsForEventIdentifiers[event.eventId] { + coordinator = cachedCoordinator + } else { + let parameters = TimelinePollCoordinatorParameters(session: session, room: room, pollStartEvent: event) + guard let newCoordinator = try? TimelinePollCoordinator(parameters: parameters) else { + return nil + } + coordinator = newCoordinator + coordinatorsForEventIdentifiers[event.eventId] = newCoordinator } - let parameters = TimelinePollCoordinatorParameters(session: session, room: room, pollStartEvent: event) - guard let coordinator = try? TimelinePollCoordinator(parameters: parameters) else { - return nil - } - - coordinatorsForEventIdentifiers[event.eventId] = coordinator + coordinator.handleErroredRelatedEventsIds(erroredEventIdsByRelatedEvent[event.eventId], to: event.eventId) return coordinator.toPresentable() } @@ -60,5 +67,48 @@ class TimelinePollProvider: NSObject { func reset() { coordinatorsForEventIdentifiers.removeAll() + erroredEventIdsByRelatedEvent.removeAll() + removeObserverIfNeeded() + } +} + +private extension TimelinePollProvider { + func updateDecryptionErrorsObserver(newSession: MXSession?) { + removeObserverIfNeeded() + + guard let session = newSession else { + return + } + + decryptionErrorsObserver = NotificationCenter.default.addObserver(forName: .mxSessionDidFailToDecryptEvents, object: session, queue: .main) { [weak self] notification in + guard + let self = self, + notification.object as? MXSession == session, + let failedEvents = notification.userInfo?[kMXSessionNotificationEventsArrayKey] as? [MXEvent] + else { + return + } + + self.storeErroredEvents(failedEvents) + } + } + + func removeObserverIfNeeded() { + decryptionErrorsObserver.map(NotificationCenter.default.removeObserver(_:)) + } + + func storeErroredEvents(_ events: [MXEvent]) { + let groupedByParentEvents = events.group(by: \.relatesTo?.eventId) + + for (parentEvent, childrenEvents) in groupedByParentEvents { + guard let parentEvent = parentEvent else { + continue + } + + let currentEvents = erroredEventIdsByRelatedEvent[parentEvent] ?? [] + let updatedEvents = currentEvents.union(childrenEvents.map(\.eventId)) + self.erroredEventIdsByRelatedEvent[parentEvent] = updatedEvents + coordinatorsForEventIdentifiers[parentEvent]?.handleErroredRelatedEventsIds(updatedEvents, to: parentEvent) + } } } From ef2ad157d1443606bdbce8918fa5e962149151fc Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 23 Dec 2022 12:08:25 +0100 Subject: [PATCH 086/228] Show decryption error on TimelinePollView --- Riot/Assets/en.lproj/Vector.strings | 2 ++ Riot/Generated/Strings.swift | 4 ++++ .../Modules/Room/TimelinePoll/TimelinePollModels.swift | 5 ++++- .../Modules/Room/TimelinePoll/TimelinePollScreenState.swift | 3 ++- .../TimelinePoll/View/TimelinePollAnswerOptionButton.swift | 3 ++- .../Modules/Room/TimelinePoll/View/TimelinePollView.swift | 5 +++++ 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index f1b90008b..265cd61d1 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2347,6 +2347,8 @@ Tap the + to start adding people."; "poll_timeline_not_closed_subtitle" = "Please try again"; +"poll_timeline_decryption_error" = "Due to decryption errors, some votes may not be counted"; + // MARK: - Location sharing "location_sharing_title" = "Location"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 2008451ea..376b539cb 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -4839,6 +4839,10 @@ public class VectorL10n: NSObject { public static var pollEditFormUpdateFailureTitle: String { return VectorL10n.tr("Vector", "poll_edit_form_update_failure_title") } + /// Due to decryption errors, some votes may not be counted + public static var pollTimelineDecryptionError: String { + return VectorL10n.tr("Vector", "poll_timeline_decryption_error") + } /// Please try again public static var pollTimelineNotClosedSubtitle: String { return VectorL10n.tr("Vector", "poll_timeline_not_closed_subtitle") diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollModels.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollModels.swift index 528ad7c17..fa0d2ac49 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollModels.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollModels.swift @@ -64,13 +64,15 @@ struct TimelinePollDetails { var type: TimelinePollType var maxAllowedSelections: UInt var hasBeenEdited = true + var hasDecryptionError: Bool init(question: String, answerOptions: [TimelinePollAnswerOption], closed: Bool, totalAnswerCount: UInt, type: TimelinePollType, maxAllowedSelections: UInt, - hasBeenEdited: Bool) { + hasBeenEdited: Bool, + hasDecryptionError: Bool) { self.question = question self.answerOptions = answerOptions self.closed = closed @@ -78,6 +80,7 @@ struct TimelinePollDetails { self.type = type self.maxAllowedSelections = maxAllowedSelections self.hasBeenEdited = hasBeenEdited + self.hasDecryptionError = hasDecryptionError } var hasCurrentUserVoted: Bool { diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollScreenState.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollScreenState.swift index 01fb82c4a..5a9a9e0a1 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollScreenState.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollScreenState.swift @@ -38,7 +38,8 @@ enum MockTimelinePollScreenState: MockScreenState, CaseIterable { totalAnswerCount: 20, type: self == .closedDisclosed || self == .openDisclosed ? .disclosed : .undisclosed, maxAllowedSelections: 1, - hasBeenEdited: false) + hasBeenEdited: false, + hasDecryptionError: false) let viewModel = TimelinePollViewModel(timelinePollDetails: poll) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollAnswerOptionButton.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollAnswerOptionButton.swift index 2ffa68be9..04451bd92 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollAnswerOptionButton.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollAnswerOptionButton.swift @@ -152,7 +152,8 @@ struct TimelinePollAnswerOptionButton_Previews: PreviewProvider { totalAnswerCount: 100, type: type, maxAllowedSelections: 1, - hasBeenEdited: false) + hasBeenEdited: false, + hasDecryptionError: false) } static func buildAnswerOption(text: String = "Test", selected: Bool, winner: Bool = false) -> TimelinePollAnswerOption { diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift index ff2ce2541..cc00f017a 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift @@ -49,6 +49,7 @@ struct TimelinePollView: View { .fixedSize(horizontal: false, vertical: true) Text(totalVotesString) + .lineLimit(2) .font(theme.fonts.footnote) .foregroundColor(theme.colors.tertiaryContent) } @@ -62,6 +63,10 @@ struct TimelinePollView: View { private var totalVotesString: String { let poll = viewModel.viewState.poll + if poll.hasDecryptionError, poll.totalAnswerCount > 0 { + return VectorL10n.pollTimelineDecryptionError + } + if poll.closed { if poll.totalAnswerCount == 1 { return VectorL10n.pollTimelineTotalFinalResultsOneVote From 22aceb6040b4e269a480be12455b79a2850b5242 Mon Sep 17 00:00:00 2001 From: Phl-Pro Date: Fri, 23 Dec 2022 15:25:52 +0100 Subject: [PATCH 087/228] VoiceBroadcast: Manage app crash cases when recording (#7188) * Cancel automatically a Voice Broadcast from a room after an app crash * We limit for the moment the uncompleted voice broadcast cleaning to the breadcrumbs rooms list By considering potential performance issues, we decided to limit the uncompleted VB cleaning in the recents list service: - we run the process only once when the application is resumed - we run the process only on the breadcrumbs rooms list This prevent us from checking several times in parallel the same room (because a room may be listed in several recents sections) This prevent us from checking several times the same room (each room should be checked only once after the app resume) If a room is not checked from the recents list, a sanity check is still done in RoomViewController (to clean the room when the user opens it) --- Riot/Categories/MXRoom+VoiceBroadcast.swift | 54 +++++++++++++++++++ .../MatrixSDK/RecentsListService.swift | 27 ++++++++++ .../Service/Mock/MockRecentsListService.swift | 3 ++ Riot/Modules/Room/RoomViewController.m | 2 + Riot/Modules/Room/RoomViewController.swift | 7 +++ .../VoiceBroadcastAggregator.swift | 8 +-- .../VoiceBroadcastService.swift | 10 +++- changelog.d/pr-7188.change | 1 + 8 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 Riot/Categories/MXRoom+VoiceBroadcast.swift create mode 100644 changelog.d/pr-7188.change diff --git a/Riot/Categories/MXRoom+VoiceBroadcast.swift b/Riot/Categories/MXRoom+VoiceBroadcast.swift new file mode 100644 index 000000000..6d74cfaaf --- /dev/null +++ b/Riot/Categories/MXRoom+VoiceBroadcast.swift @@ -0,0 +1,54 @@ +// +// Copyright 2022 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 MatrixSDK + +extension MXRoom { + + func stopUncompletedVoiceBroadcastIfNeeded() { + // Detection of a potential uncompleted VoiceBroadcast + // Check whether a VoiceBroadcast is in progress on the current session for this room whereas no VoiceBroadcast Service is available. + self.lastVoiceBroadcastStateEvent { event in + guard let event = event, + event.stateKey == self.mxSession.myUserId, + let eventDeviceId = event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyDeviceId] as? String, + eventDeviceId == self.mxSession.myDeviceId, + let voiceBroadcastInfo = VoiceBroadcastInfo(fromJSON: event.content), + voiceBroadcastInfo.state != VoiceBroadcastInfoState.stopped.rawValue, + self.mxSession.voiceBroadcastService == nil else { + return + } + + self.mxSession.getOrCreateVoiceBroadcastService(for: self) { service in + guard let service = service else { + return + } + + service.stopVoiceBroadcast(lastChunkSequence: 0, + voiceBroadcastId: voiceBroadcastInfo.voiceBroadcastId ?? event.eventId) { response in + MXLog.debug("[MXRoom] stopUncompletedVoiceBroadcastIfNeeded stopVoiceBroadcast with response : \(response)") + self.mxSession.tearDownVoiceBroadcastService() + } + } + } + } + + func lastVoiceBroadcastStateEvent(completion: @escaping (MXEvent?) -> Void) { + self.state { roomState in + completion(roomState?.stateEvents(with: .custom(VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType))?.last) + } + } +} diff --git a/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift b/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift index 938361d00..069aff9a8 100644 --- a/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift +++ b/Riot/Modules/Common/Recents/Service/MatrixSDK/RecentsListService.swift @@ -30,6 +30,7 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { public private(set) var query: String? public private(set) var space: MXSpace? private var fetchersCreated: Bool = false + private var uncompletedVoiceBroadcastCleaningDone: Bool = false // MARK: - Fetchers @@ -757,6 +758,7 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol { forSection: section, totalCountsChanged: totalCountsChanged) } } else { + stopUncompletedVoiceBroadcastIfNeeded() multicastDelegate.invoke { $0.recentsListServiceDidChangeData?(self, totalCountsChanged: totalCountsChanged) } } @@ -784,6 +786,31 @@ extension RecentsListService: MXRoomListDataFetcherDelegate { } +// MARK: - VoiceBroadcast +extension RecentsListService { + + private func stopUncompletedVoiceBroadcastIfNeeded() { + guard uncompletedVoiceBroadcastCleaningDone == false, + let breadcrumbsFetcher = breadcrumbsRoomListDataFetcher else { + return + } + // We limit for the moment the uncompleted voice broadcast cleaning to the breadcrumbs rooms list + stopUncompletedVoiceBroadcastIfNeeded(for: breadcrumbsFetcher) + uncompletedVoiceBroadcastCleaningDone = true + } + + private func stopUncompletedVoiceBroadcastIfNeeded(for fetcher: MXRoomListDataFetcher) { + fetcher.data?.rooms.forEach({ roomSummary in + guard let roomSummary = roomSummary as? MXRoomSummary, + let room = roomSummary.room else { + return + } + + room.stopUncompletedVoiceBroadcastIfNeeded() + }) + } +} + // MARK: - FetcherTypes private struct FetcherTypes: OptionSet { diff --git a/Riot/Modules/Common/Recents/Service/Mock/MockRecentsListService.swift b/Riot/Modules/Common/Recents/Service/Mock/MockRecentsListService.swift index 77cecac6f..bc95f1f94 100644 --- a/Riot/Modules/Common/Recents/Service/Mock/MockRecentsListService.swift +++ b/Riot/Modules/Common/Recents/Service/Mock/MockRecentsListService.swift @@ -229,4 +229,7 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol { multicastDelegate.invoke({ $0.recentsListServiceDidChangeData?(self, totalCountsChanged: true) }) } + public func stopUncompletedVoiceBroadcastIfNeeded(for listData: MatrixSDK.MXRoomListData?) { + // nothing here + } } diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 25170d023..b244e101e 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -1082,6 +1082,8 @@ static CGSize kThreadListBarButtonItemImageSize; [self setupUserSuggestionViewIfNeeded]; [self updateTopBanners]; + + [self stopUncompletedVoiceBroadcastIfNeeded]; } - (void)onRoomDataSourceReady diff --git a/Riot/Modules/Room/RoomViewController.swift b/Riot/Modules/Room/RoomViewController.swift index d1745dc6b..ad6a1866c 100644 --- a/Riot/Modules/Room/RoomViewController.swift +++ b/Riot/Modules/Room/RoomViewController.swift @@ -316,3 +316,10 @@ extension RoomViewController: ComposerLinkActionBridgePresenterDelegate { composerLinkActionBridgePresenter = nil } } + +// MARK: - VoiceBroadcast +extension RoomViewController { + @objc func stopUncompletedVoiceBroadcastIfNeeded() { + self.roomDataSource.room.stopUncompletedVoiceBroadcastIfNeeded() + } +} diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift index a649a621d..f85945dce 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift @@ -163,15 +163,17 @@ public class VoiceBroadcastAggregator { } private func updateState() { - self.room.state { roomState in - guard let event = roomState?.stateEvents(with: .custom(VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType))?.last, + // This update is useful only in case of a live broadcast (The aggregator considers the broadcast stopped by default) + // We will consider here only the most recent voice broadcast state event + self.room.lastVoiceBroadcastStateEvent { event in + guard let event = event, event.stateKey == self.voiceBroadcastSenderId, let voiceBroadcastInfo = VoiceBroadcastInfo(fromJSON: event.content), (event.eventId == self.voiceBroadcastStartEventId || voiceBroadcastInfo.voiceBroadcastId == self.voiceBroadcastStartEventId), let state = VoiceBroadcastInfoState(rawValue: voiceBroadcastInfo.state) else { return } - + self.delegate?.voiceBroadcastAggregator(self, didReceiveState: state) } } diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift index b386f0d4d..8c707eab2 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastService.swift @@ -78,8 +78,14 @@ public class VoiceBroadcastService: NSObject { /// stop a voice broadcast info. /// - Parameters: /// - lastChunkSequence: The last sent chunk number. + /// - voiceBroadcastId: The VoiceBroadcast identifier to stop. Use it only to force stop a specific VoiceBroadcast. /// - completion: A closure called when the operation completes. Provides the event id of the event generated on the home server on success. - func stopVoiceBroadcast(lastChunkSequence: Int, completion: @escaping (MXResponse) -> Void) { + func stopVoiceBroadcast(lastChunkSequence: Int, + voiceBroadcastId: String? = nil, + completion: @escaping (MXResponse) -> Void) { + if let voiceBroadcastId = voiceBroadcastId { + self.voiceBroadcastId = voiceBroadcastId + } sendVoiceBroadcastInfo(lastChunkSequence: lastChunkSequence, state: VoiceBroadcastInfoState.stopped, completion: completion) } @@ -132,7 +138,7 @@ public class VoiceBroadcastService: NSObject { case .resumed: return [.paused, .stopped] case .stopped: - return [.started] + return [.started, .stopped] } } diff --git a/changelog.d/pr-7188.change b/changelog.d/pr-7188.change new file mode 100644 index 000000000..bd6f7056d --- /dev/null +++ b/changelog.d/pr-7188.change @@ -0,0 +1 @@ +Labs: VoiceBroadcast: Handle potential crash whereas a voice broadcast is in progress From d11ca3e25d30077305c1e861b31be86bb6d235c7 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 23 Dec 2022 15:48:53 +0100 Subject: [PATCH 088/228] Add changelog.d file --- changelog.d/pr-7206.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7206.change diff --git a/changelog.d/pr-7206.change b/changelog.d/pr-7206.change new file mode 100644 index 000000000..89d4c8e36 --- /dev/null +++ b/changelog.d/pr-7206.change @@ -0,0 +1 @@ +Polls: show decryption errors in timeline during aggregations. From 0b37a581287f88c71fa90f9d05add8db28cb58b5 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 23 Dec 2022 16:41:25 +0100 Subject: [PATCH 089/228] Cleanup code --- .../TimelinePoll/Coordinator/TimelinePollProvider.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift index 322c05451..bf5aa4adc 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift @@ -76,20 +76,19 @@ private extension TimelinePollProvider { func updateDecryptionErrorsObserver(newSession: MXSession?) { removeObserverIfNeeded() - guard let session = newSession else { + guard let newSession = newSession else { return } - decryptionErrorsObserver = NotificationCenter.default.addObserver(forName: .mxSessionDidFailToDecryptEvents, object: session, queue: .main) { [weak self] notification in + decryptionErrorsObserver = NotificationCenter.default.addObserver(forName: .mxSessionDidFailToDecryptEvents, object: newSession, queue: .main) { [weak self] notification in guard - let self = self, - notification.object as? MXSession == session, + notification.object as? MXSession == newSession, let failedEvents = notification.userInfo?[kMXSessionNotificationEventsArrayKey] as? [MXEvent] else { return } - self.storeErroredEvents(failedEvents) + self?.storeErroredEvents(failedEvents) } } From dc461ee57b1d1d749733d9af7df256e357bcfc4c Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 28 Dec 2022 11:03:15 +0100 Subject: [PATCH 090/228] App Layout: Space names in the bottom sheet should wrap to 1 line only --- .../SpaceSelector/View/SpaceSelectorListRow.swift | 1 + changelog.d/6579.bugfix | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog.d/6579.bugfix diff --git a/RiotSwiftUI/Modules/Spaces/SpaceSelectorBottomSheet/SpaceSelector/View/SpaceSelectorListRow.swift b/RiotSwiftUI/Modules/Spaces/SpaceSelectorBottomSheet/SpaceSelector/View/SpaceSelectorListRow.swift index d9e67c9b9..03b86ab45 100644 --- a/RiotSwiftUI/Modules/Spaces/SpaceSelectorBottomSheet/SpaceSelector/View/SpaceSelectorListRow.swift +++ b/RiotSwiftUI/Modules/Spaces/SpaceSelectorBottomSheet/SpaceSelector/View/SpaceSelectorListRow.swift @@ -57,6 +57,7 @@ struct SpaceSelectorListRow: View { .clipShape(RoundedRectangle(cornerRadius: 8)) } Text(displayName ?? "") + .lineLimit(1) .foregroundColor(theme.colors.primaryContent) .font(theme.fonts.bodySB) .accessibility(identifier: "itemName") diff --git a/changelog.d/6579.bugfix b/changelog.d/6579.bugfix new file mode 100644 index 000000000..d1a3181c1 --- /dev/null +++ b/changelog.d/6579.bugfix @@ -0,0 +1 @@ +App Layout: wrap Space names to 1 line only in the bottom sheet \ No newline at end of file From 830b6d0cd28afe55a0d2a1566949f41f7c49586b Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 11:37:06 +0100 Subject: [PATCH 091/228] Fix display session name --- .../UserSessions/Common/UserSessionNameFormatter.swift | 4 ++-- .../UserSessions/Common/View/UserSessionCardViewData.swift | 2 +- .../View/UserSessionListItemViewDataFactory.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/Common/UserSessionNameFormatter.swift b/RiotSwiftUI/Modules/UserSessions/Common/UserSessionNameFormatter.swift index db7f2e1e6..c39c64aba 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/UserSessionNameFormatter.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/UserSessionNameFormatter.swift @@ -19,7 +19,7 @@ import Foundation /// Enables to build user session name enum UserSessionNameFormatter { /// Session name with client name and session display name - static func sessionName(deviceType: DeviceType, sessionDisplayName: String?) -> String { - sessionDisplayName?.vc_nilIfEmpty() ?? deviceType.name + static func sessionName(sessionId: String, sessionDisplayName: String?) -> String { + sessionDisplayName?.vc_nilIfEmpty() ?? sessionId } } diff --git a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardViewData.swift b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardViewData.swift index 79cd2d812..7ccf63bff 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardViewData.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/View/UserSessionCardViewData.swift @@ -102,7 +102,7 @@ struct UserSessionCardViewData { isCurrentSessionDisplayMode: Bool = false, isActive: Bool) { self.sessionId = sessionId - sessionName = UserSessionNameFormatter.sessionName(deviceType: deviceType, sessionDisplayName: sessionDisplayName) + sessionName = UserSessionNameFormatter.sessionName(sessionId: sessionId, sessionDisplayName: sessionDisplayName) self.verificationState = verificationState var lastActivityDateString: String? diff --git a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItemViewDataFactory.swift b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItemViewDataFactory.swift index 227ed5d01..79317be71 100644 --- a/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItemViewDataFactory.swift +++ b/RiotSwiftUI/Modules/UserSessions/UserSessionsOverview/View/UserSessionListItemViewDataFactory.swift @@ -19,7 +19,7 @@ import Foundation struct UserSessionListItemViewDataFactory { func create(from sessionInfo: UserSessionInfo, isSelected: Bool = false) -> UserSessionListItemViewData { - let sessionName = UserSessionNameFormatter.sessionName(deviceType: sessionInfo.deviceType, + let sessionName = UserSessionNameFormatter.sessionName(sessionId: sessionInfo.id, sessionDisplayName: sessionInfo.name) let sessionDetails = buildSessionDetails(sessionInfo: sessionInfo) let deviceAvatarViewData = DeviceAvatarViewData(deviceType: sessionInfo.deviceType, From b80e65ab4b9c82c29f553a2d41458fb794c7e2b9 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 11:46:15 +0100 Subject: [PATCH 092/228] Add changelog.d file --- changelog.d/pr-7214.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7214.change diff --git a/changelog.d/pr-7214.change b/changelog.d/pr-7214.change new file mode 100644 index 000000000..e0b5e1f44 --- /dev/null +++ b/changelog.d/pr-7214.change @@ -0,0 +1 @@ +Device Manager: change fallback display name for sessions. From 2b9a6b69c1fa7933e5586838429ebeab21fde56e Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 12:13:56 +0100 Subject: [PATCH 093/228] Fix UTs --- .../Common/Test/Unit/UserSessionNameFormatterTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RiotSwiftUI/Modules/UserSessions/Common/Test/Unit/UserSessionNameFormatterTests.swift b/RiotSwiftUI/Modules/UserSessions/Common/Test/Unit/UserSessionNameFormatterTests.swift index c40bb2fa3..a3703ef54 100644 --- a/RiotSwiftUI/Modules/UserSessions/Common/Test/Unit/UserSessionNameFormatterTests.swift +++ b/RiotSwiftUI/Modules/UserSessions/Common/Test/Unit/UserSessionNameFormatterTests.swift @@ -20,14 +20,14 @@ import XCTest class UserSessionNameFormatterTests: XCTestCase { func testSessionDisplayNameTrumpsDeviceTypeName() { - XCTAssertEqual("Johnny's iPhone", UserSessionNameFormatter.sessionName(deviceType: .mobile, sessionDisplayName: "Johnny's iPhone")) + XCTAssertEqual("Johnny's iPhone", UserSessionNameFormatter.sessionName(sessionId: "sessionId", sessionDisplayName: "Johnny's iPhone")) } func testEmptySessionDisplayNameFallsBackToDeviceTypeName() { - XCTAssertEqual(DeviceType.mobile.name, UserSessionNameFormatter.sessionName(deviceType: .mobile, sessionDisplayName: "")) + XCTAssertEqual("sessionId", UserSessionNameFormatter.sessionName(sessionId: "sessionId", sessionDisplayName: "")) } func testNilSessionDisplayNameFallsBackToDeviceTypeName() { - XCTAssertEqual(DeviceType.mobile.name, UserSessionNameFormatter.sessionName(deviceType: .mobile, sessionDisplayName: nil)) + XCTAssertEqual("sessionId", UserSessionNameFormatter.sessionName(sessionId: "sessionId", sessionDisplayName: nil)) } } From 27c43d38c7ca73c8ab23b15939410da8808e1a4d Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 2 Jan 2023 19:49:52 +0100 Subject: [PATCH 094/228] Fix navigation to replied message --- .../MatrixKit/Utils/EventFormatter/MXKEventFormatter.m | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m index 63f86617a..7a1a06215 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m @@ -1992,11 +1992,6 @@ static NSString *const kHTMLATagRegexPattern = @"( html = [html stringByReplacingCharactersInRange:inReplyToTextRange withString:[VectorL10n noticeInReplyTo]]; } - if (inReplyToLinkRange.location != NSNotFound) - { - html = [html stringByReplacingCharactersInRange:inReplyToLinkRange withString:@"#"]; - } - return html; } From 7b25ef20eeebbd076b31e3536c8dfd161bb64548 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 3 Jan 2023 13:25:56 +0100 Subject: [PATCH 095/228] Amend comment --- .../MatrixKit/Utils/EventFormatter/MXKEventFormatter.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m index 7a1a06215..eaeaa269a 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m @@ -1983,8 +1983,8 @@ static NSString *const kHTMLATagRegexPattern = @"( } // Replace
In reply to - // By
['In reply to' from resources] - // To disable the link and to localize the "In reply to" string + // By
['In reply to' from resources] + // To localize the "In reply to" string // This link is the first HTML node of the html string if (inReplyToTextRange.location != NSNotFound) From dc4b8350ceb7b742f04b0f1fa79d7c90279122a8 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Tue, 3 Jan 2023 13:57:23 +0100 Subject: [PATCH 096/228] Fix hidden live location timeline tiles after text messages This prevents `MXEventTypeBeaconInfo` events from being added into other bubble cell data objects. Previously, when starting a live location share right after sending a text message, the beacon info event would get appended into the text message's bubble cell data which prevented it from rendering. Similarly, when restarting the app in such a situation the events would be getting reprocessed _in backwards order_ which meant the final beacon info landed in its own bubble cell data (causing it to render) but a beacon info right before the text message would now get combined with the text message causing it to go hidden. --- Riot/Modules/Room/CellData/RoomBubbleCellData.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 138d9c566..91ca6e415 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -1193,6 +1193,9 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat case MXEventTypePollStart: shouldAddEvent = NO; break; + case MXEventTypeBeaconInfo: + shouldAddEvent = NO; + break; case MXEventTypeCustom: { if ([event.type isEqualToString:kWidgetMatrixEventTypeString] From 06095ada9fe4f0ca9895f3f273ee5c4ac1e759bb Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Tue, 3 Jan 2023 14:03:45 +0100 Subject: [PATCH 097/228] Add changelog --- changelog.d/pr-7220.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7220.bugfix diff --git a/changelog.d/pr-7220.bugfix b/changelog.d/pr-7220.bugfix new file mode 100644 index 000000000..2b8477eed --- /dev/null +++ b/changelog.d/pr-7220.bugfix @@ -0,0 +1 @@ +Fix hidden live location timeline tiles after text messages From 8cb0d39df4a3fc53932e77496c8746712e95c263 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 3 Jan 2023 15:39:08 +0100 Subject: [PATCH 098/228] Fix module navigation --- Riot/Modules/Room/RoomCoordinator.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/RoomCoordinator.swift b/Riot/Modules/Room/RoomCoordinator.swift index 35caf9084..f249b863c 100644 --- a/Riot/Modules/Room/RoomCoordinator.swift +++ b/Riot/Modules/Room/RoomCoordinator.swift @@ -138,9 +138,9 @@ final class RoomCoordinator: NSObject, RoomCoordinatorProtocol { // Add `roomViewController` to the NavigationRouter, only if it has been explicitly set as parameter if let navigationRouter = self.parameters.navigationRouter { if navigationRouter.modules.isEmpty == false { - navigationRouter.push(self.roomViewController, animated: true, popCompletion: nil) + navigationRouter.push(self, animated: true, popCompletion: nil) } else { - navigationRouter.setRootModule(self.roomViewController, popCompletion: nil) + navigationRouter.setRootModule(self, popCompletion: nil) } } } From 754dbbda569ce27ddaf8b2ce42a4dd2b1861b127 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 3 Jan 2023 15:54:05 +0100 Subject: [PATCH 099/228] Add changelog.d file --- changelog.d/7003.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7003.bugfix diff --git a/changelog.d/7003.bugfix b/changelog.d/7003.bugfix new file mode 100644 index 000000000..871f28ffe --- /dev/null +++ b/changelog.d/7003.bugfix @@ -0,0 +1 @@ +Timeline: fixed navigation back from replies. From 93f53e21206d07c4d6c0e61f7ba4dfb51eddaeff Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Thu, 5 Jan 2023 10:47:01 +0100 Subject: [PATCH 100/228] fixed a crash when a new DM room is created (#7232) Signed-off-by: Nicolas Mauri --- Riot/Modules/Room/RoomViewController.m | 5 +++-- Riot/Modules/Room/RoomViewController.swift | 2 +- changelog.d/7232.bugfix | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 changelog.d/7232.bugfix diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index b244e101e..fcecdbef0 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -1064,6 +1064,9 @@ static CGSize kThreadListBarButtonItemImageSize; // Set room title view [self refreshRoomTitle]; + + // Stop any pending voice broadcast if needed + [self stopUncompletedVoiceBroadcastIfNeeded]; } else { @@ -1082,8 +1085,6 @@ static CGSize kThreadListBarButtonItemImageSize; [self setupUserSuggestionViewIfNeeded]; [self updateTopBanners]; - - [self stopUncompletedVoiceBroadcastIfNeeded]; } - (void)onRoomDataSourceReady diff --git a/Riot/Modules/Room/RoomViewController.swift b/Riot/Modules/Room/RoomViewController.swift index ad6a1866c..561cc382a 100644 --- a/Riot/Modules/Room/RoomViewController.swift +++ b/Riot/Modules/Room/RoomViewController.swift @@ -320,6 +320,6 @@ extension RoomViewController: ComposerLinkActionBridgePresenterDelegate { // MARK: - VoiceBroadcast extension RoomViewController { @objc func stopUncompletedVoiceBroadcastIfNeeded() { - self.roomDataSource.room.stopUncompletedVoiceBroadcastIfNeeded() + self.roomDataSource?.room.stopUncompletedVoiceBroadcastIfNeeded() } } diff --git a/changelog.d/7232.bugfix b/changelog.d/7232.bugfix new file mode 100644 index 000000000..7f8e254b2 --- /dev/null +++ b/changelog.d/7232.bugfix @@ -0,0 +1 @@ + Direct Message: fixed a crash when a new DM room is created From 7032ff831ee7df329624711b5e64f2c649cd6f49 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Thu, 5 Jan 2023 13:59:28 +0100 Subject: [PATCH 101/228] Update sub label (#7228) --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index f1b90008b..e11dbaee4 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -802,7 +802,7 @@ Tap the + to start adding people."; "settings_labs_enable_new_client_info_feature" = "Record the client name, version, and url to recognise sessions more easily in session manager"; "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 (under active development)"; +"settings_labs_enable_voice_broadcast" = "Voice broadcast"; "settings_version" = "Version %@"; "settings_olm_version" = "Olm Version %@"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 2008451ea..5802b17fa 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -7579,7 +7579,7 @@ public class VectorL10n: NSObject { public static var settingsLabsEnableThreads: String { return VectorL10n.tr("Vector", "settings_labs_enable_threads") } - /// Voice broadcast (under active development) + /// Voice broadcast public static var settingsLabsEnableVoiceBroadcast: String { return VectorL10n.tr("Vector", "settings_labs_enable_voice_broadcast") } From 6c1a5434ad806e320d550b10a72ec9cd8cfa04c1 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Thu, 5 Jan 2023 16:43:04 +0100 Subject: [PATCH 102/228] Fix temporary file deletion for a voice message (#7240) Signed-off-by: Nicolas Mauri --- Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift index 14b4fa2b9..b24b8cd2d 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift @@ -364,7 +364,8 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, } private func deleteRecordingAtURL(_ url: URL?) { - guard let url = url, FileManager.default.fileExists(atPath: url.absoluteString) else { + // Fix: use url.path instead of url.absoluteString when using FileManager otherwise the url seems to be percent encoded and the file is not found. + guard let url = url, FileManager.default.fileExists(atPath: url.path) else { return } From 94d14c581c8b35f9c0861a910a9d93b15bbcf2a3 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Fri, 6 Jan 2023 12:14:08 +0100 Subject: [PATCH 103/228] add support for reactions on VB (#7179) --- Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m | 5 +++++ Riot/Modules/Room/CellData/RoomBubbleCellData.m | 6 ++++-- changelog.d/7179.feature | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 changelog.d/7179.feature diff --git a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m index ed649ebf7..7e9522bfc 100644 --- a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m +++ b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m @@ -2885,6 +2885,11 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) { return YES; } + // Specific case for voice broadcast event + if (event.eventType == MXEventTypeCustom && [event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]) { + return YES; + } + BOOL isRoomMessage = (event.eventType == MXEventTypeRoomMessage); if (!isRoomMessage) { diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 91ca6e415..f3c7db526 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -634,9 +634,11 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat { __block NSInteger firstVisibleComponentIndex = NSNotFound; - BOOL isPoll = (self.events.firstObject.eventType == MXEventTypePollStart); + MXEvent *firstEvent = self.events.firstObject; + BOOL isPoll = (firstEvent.eventType == MXEventTypePollStart); + BOOL isVoiceBroadcast = (firstEvent.eventType == MXEventTypeCustom && [firstEvent.type isEqualToString: VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]); - if ((isPoll || self.attachment) && self.bubbleComponents.count) + if ((isPoll || self.attachment || isVoiceBroadcast) && self.bubbleComponents.count) { firstVisibleComponentIndex = 0; } diff --git a/changelog.d/7179.feature b/changelog.d/7179.feature new file mode 100644 index 000000000..d11f310a5 --- /dev/null +++ b/changelog.d/7179.feature @@ -0,0 +1 @@ +Voice Broadcast: allow to react on Voice Broadcast. From cf28034dad019065e16ea9ca09d1df04d43293e7 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Fri, 6 Jan 2023 16:14:43 +0100 Subject: [PATCH 104/228] Fix clearCache method on VoiceMessageAttachmentCacheManager --- .../Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift index cf97dac4e..8e31a229c 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageAttachmentCacheManager.swift @@ -137,7 +137,7 @@ class VoiceMessageAttachmentCacheManager { durations.removeAll() finalURLs.removeAll() - if FileManager.default.fileExists(atPath: temporaryFilesFolderURL.absoluteString) { + if FileManager.default.fileExists(atPath: temporaryFilesFolderURL.path) { do { try FileManager.default.removeItem(at: temporaryFilesFolderURL) } catch { From a1817be37f15ffc91ac57700bcf047ff36da7f12 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Fri, 6 Jan 2023 16:22:19 +0100 Subject: [PATCH 105/228] Add Towncrier file --- changelog.d/pr-7244.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7244.bugfix diff --git a/changelog.d/pr-7244.bugfix b/changelog.d/pr-7244.bugfix new file mode 100644 index 000000000..324e4e86a --- /dev/null +++ b/changelog.d/pr-7244.bugfix @@ -0,0 +1 @@ +Fix an issue preventing temporary audio files to be deleted. From bbb13516bf52b7886ccb98413d3ad34eb12476bc Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Fri, 6 Jan 2023 17:24:07 +0100 Subject: [PATCH 106/228] Send voice message should not be allowed during a voice broadcast recording (#7235) --- Riot/Assets/en.lproj/Vector.strings | 2 ++ Riot/Generated/Strings.swift | 8 ++++++++ Riot/Modules/Room/RoomViewController.m | 14 ++++++++++++++ .../VoiceMessages/VoiceMessageController.swift | 8 ++++++++ changelog.d/7235.bugfix | 1 + 5 files changed, 33 insertions(+) create mode 100644 changelog.d/7235.bugfix diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index e11dbaee4..4ab2ee3e8 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2197,6 +2197,8 @@ Tap the + to start adding people."; "voice_message_remaining_recording_time" = "%@s left"; "voice_message_stop_locked_mode_recording" = "Tap on your recording to stop or listen"; "voice_message_lock_screen_placeholder" = "Voice message"; +"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 "voice_broadcast_unauthorized_title" = "Can't start a new voice broadcast"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 5802b17fa..fbae6b345 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -9207,6 +9207,14 @@ public class VectorL10n: NSObject { public static var voiceBroadcastUnauthorizedTitle: String { return VectorL10n.tr("Vector", "voice_broadcast_unauthorized_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") + } + /// Can't start voice message + public static var voiceMessageBroadcastInProgressTitle: String { + return VectorL10n.tr("Vector", "voice_message_broadcast_in_progress_title") + } /// Voice message public static var voiceMessageLockScreenPlaceholder: String { return VectorL10n.tr("Vector", "voice_message_lock_screen_placeholder") diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index fcecdbef0..b26a29a86 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -7887,6 +7887,20 @@ static CGSize kThreadListBarButtonItemImageSize; }]; } +- (BOOL)voiceMessageControllerDidRequestRecording:(VoiceMessageController *)voiceMessageController +{ + MXSession* session = self.roomDataSource.mxSession; + // Check whether the user is not already broadcasting here or in another room + if (session.voiceBroadcastService) + { + [self showAlertWithTitle:[VectorL10n voiceMessageBroadcastInProgressTitle] message:[VectorL10n voiceMessageBroadcastInProgressMessage]]; + + return NO; + } + + return YES; +} + - (void)voiceMessageController:(VoiceMessageController *)voiceMessageController didRequestSendForFileAtURL:(NSURL *)url duration:(NSUInteger)duration diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift index b24b8cd2d..fa5058a3f 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageController.swift @@ -20,6 +20,7 @@ import DSWaveformImage @objc public protocol VoiceMessageControllerDelegate: AnyObject { func voiceMessageControllerDidRequestMicrophonePermission(_ voiceMessageController: VoiceMessageController) + func voiceMessageControllerDidRequestRecording(_ voiceMessageController: VoiceMessageController) -> Bool func voiceMessageController(_ voiceMessageController: VoiceMessageController, didRequestSendForFileAtURL url: URL, duration: UInt, samples: [Float]?, completion: @escaping (Bool) -> Void) } @@ -106,6 +107,13 @@ public class VoiceMessageController: NSObject, VoiceMessageToolbarViewDelegate, guard let temporaryFileURL = temporaryFileURL else { return } + + // Ask our delegate if we can start recording + let canStartRecording = delegate?.voiceMessageControllerDidRequestRecording(self) ?? true + guard canStartRecording else { + return + } + guard AVAudioSession.sharedInstance().recordPermission == .granted else { delegate?.voiceMessageControllerDidRequestMicrophonePermission(self) return diff --git a/changelog.d/7235.bugfix b/changelog.d/7235.bugfix new file mode 100644 index 000000000..0e8a70a7f --- /dev/null +++ b/changelog.d/7235.bugfix @@ -0,0 +1 @@ +Voice Broadcast: Prevent sending voice message during a voice broadcast recording From 0dfa85d7628a20459f9d4a89dd3b5dcbbfaf9e51 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Fri, 6 Jan 2023 22:36:55 +0100 Subject: [PATCH 107/228] Delete during broadcasting (#7227) --- .../VoiceBroadcastRecorderCoordinator.swift | 4 +++ .../VoiceBroadcastRecorderProvider.swift | 27 ++++++++++++++++++- .../VoiceBroadcastRecorderService.swift | 15 +++++++++++ ...oiceBroadcastRecorderServiceProtocol.swift | 3 +++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift index e5e0afe3c..2a9fe90b8 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderCoordinator.swift @@ -50,6 +50,10 @@ final class VoiceBroadcastRecorderCoordinator: Coordinator, Presentable { recorderService: voiceBroadcastRecorderService) voiceBroadcastRecorderViewModel = viewModel } + + deinit { + voiceBroadcastRecorderService.cancelRecordingVoiceBroadcast() + } // MARK: - Public diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderProvider.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderProvider.swift index 3db5cad54..e7f998716 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderProvider.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Coordinator/VoiceBroadcastRecorderProvider.swift @@ -33,7 +33,19 @@ import Foundation } } } - var coordinatorsForEventIdentifiers = [String: VoiceBroadcastRecorderCoordinator]() + private var coordinatorsForEventIdentifiers = [String: VoiceBroadcastRecorderCoordinator]() { + didSet { + if !self.coordinatorsForEventIdentifiers.isEmpty && self.redactionsListener == nil { + redactionsListener = session?.listenToEvents([MXEventType(identifier: kMXEventTypeStringRoomRedaction)], self.handleRedactedEvent) + } + + if self.coordinatorsForEventIdentifiers.isEmpty && self.redactionsListener != nil { + session?.removeListener(self.redactionsListener) + self.redactionsListener = nil + } + } + } + private var redactionsListener: Any? // MARK: Private private var currentEventIdentifier: String? @@ -83,4 +95,17 @@ import Foundation return coordinatorsForEventIdentifiers[currentEventIdentifier] } + + private func handleRedactedEvent(event: MXEvent, direction: MXTimelineDirection, customObject: Any?) { + if direction == .backwards { + // ignore backwards events + return + } + + var coordinator = coordinatorsForEventIdentifiers.removeValue(forKey: event.redacts) + + coordinator?.toPresentable().dismiss(animated: false) { + coordinator = nil + } + } } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift index 5c4eae74a..437abbe3c 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/MatrixSDK/VoiceBroadcastRecorderService.swift @@ -151,6 +151,21 @@ class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol { }) } + func cancelRecordingVoiceBroadcast() { + MXLog.debug("[VoiceBroadcastRecorderService] Cancel recording voice broadcast") + audioEngine.stop() + audioEngine.inputNode.removeTap(onBus: audioNodeBus) + UIApplication.shared.isIdleTimerDisabled = false + + // Remove current chunk + if self.chunkFile != nil { + self.deleteRecording(at: self.chunkFile.url) + self.chunkFile = nil + } + + self.tearDownVoiceBroadcastService() + } + // MARK: - Private /// Reset chunk values. private func resetValues() { diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/VoiceBroadcastRecorderServiceProtocol.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/VoiceBroadcastRecorderServiceProtocol.swift index e457eb843..9e48e2e9a 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/VoiceBroadcastRecorderServiceProtocol.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastRecorder/Service/VoiceBroadcastRecorderServiceProtocol.swift @@ -36,4 +36,7 @@ protocol VoiceBroadcastRecorderServiceProtocol { /// Resume voice broadcast recording after paused it. func resumeRecordingVoiceBroadcast() + + /// Cancel voice broadcast recording after redacted it. + func cancelRecordingVoiceBroadcast() } From b8923460df5f1613ff817d9f3735a132bad734c7 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Mon, 9 Jan 2023 14:18:31 +0100 Subject: [PATCH 108/228] Allow to add reactions on VB using a long press gesture --- .../Playback/VoiceBroadcastPlaybackPlainCell.swift | 8 ++++++++ .../Recorder/VoiceBroadcastRecorderPlainCell.swift | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift index f673bebee..85a697fb1 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Playback/VoiceBroadcastPlaybackPlainCell.swift @@ -55,6 +55,14 @@ class VoiceBroadcastPlaybackPlainCell: SizableBaseRoomCell, RoomCellReactionsDis delegate.cell(self, didRecognizeAction: kMXKRoomBubbleCellTapOnContentView, userInfo: [kMXKRoomBubbleCellEventKey: event]) } + + // The normal flow for long press on cell content views doesn't work for bubbles without attributed strings + override func onLongPressGesture(_ longPressGestureRecognizer: UILongPressGestureRecognizer!) { + guard let event = self.event else { + return + } + delegate.cell(self, didRecognizeAction: kMXKRoomBubbleCellLongPressOnEvent, userInfo: [kMXKRoomBubbleCellEventKey: event]) + } } extension VoiceBroadcastPlaybackPlainCell: RoomCellThreadSummaryDisplayable {} diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainCell.swift index 43047cfba..9cc4df73b 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceBroadcast/Recorder/VoiceBroadcastRecorderPlainCell.swift @@ -54,6 +54,14 @@ class VoiceBroadcastRecorderPlainCell: SizableBaseRoomCell, RoomCellReactionsDis delegate.cell(self, didRecognizeAction: kMXKRoomBubbleCellTapOnContentView, userInfo: [kMXKRoomBubbleCellEventKey: event]) } + // The normal flow for long press on cell content views doesn't work for bubbles without attributed strings + override func onLongPressGesture(_ longPressGestureRecognizer: UILongPressGestureRecognizer!) { + guard let event = self.event else { + return + } + delegate.cell(self, didRecognizeAction: kMXKRoomBubbleCellLongPressOnEvent, userInfo: [kMXKRoomBubbleCellEventKey: event]) + } + func addVoiceBroadcastView(_ voiceBroadcastView: UIView, on contentView: UIView) { self.voiceBroadcastView?.removeFromSuperview() From 15c68bfe3de22c0542560a6c2b03bb590510cb1d Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Mon, 9 Jan 2023 14:46:26 +0100 Subject: [PATCH 109/228] Ensures that we only support reactions for a start event --- Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m index 7e9522bfc..14684ac5d 100644 --- a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m +++ b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m @@ -2886,8 +2886,14 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) { } // Specific case for voice broadcast event - if (event.eventType == MXEventTypeCustom && [event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]) { - return YES; + if (event.eventType == MXEventTypeCustom && + [event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]) { + + // Ensures that we only support reactions for a start event + VoiceBroadcastInfo* voiceBroadcastInfo = [VoiceBroadcastInfo modelFromJSON: event.content]; + if ([VoiceBroadcastInfo isStartedFor: voiceBroadcastInfo.state]) { + return YES; + } } BOOL isRoomMessage = (event.eventType == MXEventTypeRoomMessage); From 10b86f18edd6ef4c1982866083204b8525038b18 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 9 Jan 2023 15:48:35 +0100 Subject: [PATCH 110/228] revert changes --- .../Coordinator/TimelinePollCoordinator.swift | 4 - .../Coordinator/TimelinePollProvider.swift | 75 ++++--------------- 2 files changed, 13 insertions(+), 66 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift index 0f4d8a7d9..3520588e2 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift @@ -83,10 +83,6 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel func start() { } - func handleErroredRelatedEventsIds(_ ids: Set?, to parentEvent: String) { - pollAggregator.handleErroredRelatedEventsIds(ids, to: parentEvent) - } - func toPresentable() -> UIViewController { VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context)) } diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift index bf5aa4adc..a11cb3a2e 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift @@ -20,20 +20,17 @@ import Foundation class TimelinePollProvider: NSObject { static let shared = TimelinePollProvider() - private var decryptionErrorsObserver: NSObjectProtocol? - var session: MXSession? { willSet { - guard newValue != session else { - return - } + guard let currentSession = self.session else { return } - reset() - updateDecryptionErrorsObserver(newSession: newValue) + if currentSession != newValue { + // Clear all stored coordinators on new session + coordinatorsForEventIdentifiers.removeAll() + } } } var coordinatorsForEventIdentifiers = [String: TimelinePollCoordinator]() - var erroredEventIdsByRelatedEvent = [String: Set]() /// Create or retrieve the poll timeline coordinator for this event and return /// a view to be displayed in the timeline @@ -42,20 +39,16 @@ class TimelinePollProvider: NSObject { return nil } - let coordinator: TimelinePollCoordinator - - if let cachedCoordinator = coordinatorsForEventIdentifiers[event.eventId] { - coordinator = cachedCoordinator - } else { - let parameters = TimelinePollCoordinatorParameters(session: session, room: room, pollStartEvent: event) - guard let newCoordinator = try? TimelinePollCoordinator(parameters: parameters) else { - return nil - } - coordinator = newCoordinator - coordinatorsForEventIdentifiers[event.eventId] = newCoordinator + if let coordinator = coordinatorsForEventIdentifiers[event.eventId] { + return coordinator.toPresentable() } - coordinator.handleErroredRelatedEventsIds(erroredEventIdsByRelatedEvent[event.eventId], to: event.eventId) + let parameters = TimelinePollCoordinatorParameters(session: session, room: room, pollStartEvent: event) + guard let coordinator = try? TimelinePollCoordinator(parameters: parameters) else { + return nil + } + + coordinatorsForEventIdentifiers[event.eventId] = coordinator return coordinator.toPresentable() } @@ -67,47 +60,5 @@ class TimelinePollProvider: NSObject { func reset() { coordinatorsForEventIdentifiers.removeAll() - erroredEventIdsByRelatedEvent.removeAll() - removeObserverIfNeeded() - } -} - -private extension TimelinePollProvider { - func updateDecryptionErrorsObserver(newSession: MXSession?) { - removeObserverIfNeeded() - - guard let newSession = newSession else { - return - } - - decryptionErrorsObserver = NotificationCenter.default.addObserver(forName: .mxSessionDidFailToDecryptEvents, object: newSession, queue: .main) { [weak self] notification in - guard - notification.object as? MXSession == newSession, - let failedEvents = notification.userInfo?[kMXSessionNotificationEventsArrayKey] as? [MXEvent] - else { - return - } - - self?.storeErroredEvents(failedEvents) - } - } - - func removeObserverIfNeeded() { - decryptionErrorsObserver.map(NotificationCenter.default.removeObserver(_:)) - } - - func storeErroredEvents(_ events: [MXEvent]) { - let groupedByParentEvents = events.group(by: \.relatesTo?.eventId) - - for (parentEvent, childrenEvents) in groupedByParentEvents { - guard let parentEvent = parentEvent else { - continue - } - - let currentEvents = erroredEventIdsByRelatedEvent[parentEvent] ?? [] - let updatedEvents = currentEvents.union(childrenEvents.map(\.eventId)) - self.erroredEventIdsByRelatedEvent[parentEvent] = updatedEvents - coordinatorsForEventIdentifiers[parentEvent]?.handleErroredRelatedEventsIds(updatedEvents, to: parentEvent) - } } } From 9fe320f3deffaf9252227ec60a7f6c99614ecccc Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 9 Jan 2023 16:17:21 +0100 Subject: [PATCH 111/228] Fix UTs --- .../TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift index 0e102dc39..be73d7f04 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift @@ -35,7 +35,8 @@ class TimelinePollViewModelTests: XCTestCase { totalAnswerCount: 3, type: .disclosed, maxAllowedSelections: 1, - hasBeenEdited: false) + hasBeenEdited: false, + hasDecryptionError: false) viewModel = TimelinePollViewModel(timelinePollDetails: timelinePoll) context = viewModel.context From aedd289169549bf1ebd5d80c1cd97eae0be01ae6 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Mon, 9 Jan 2023 09:30:25 +0100 Subject: [PATCH 112/228] Pause voice broadcast listening on new voice broadcast recording (#7192) --- Riot/Modules/Room/RoomViewController.m | 3 +++ changelog.d/7192.bugfix | 1 + 2 files changed, 4 insertions(+) create mode 100644 changelog.d/7192.bugfix diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index b26a29a86..873ddab2f 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2451,6 +2451,9 @@ static CGSize kThreadListBarButtonItemImageSize; return; } + // Prevents listening a VB when recording a new one + [VoiceBroadcastPlaybackProvider.shared pausePlaying]; + // 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) { diff --git a/changelog.d/7192.bugfix b/changelog.d/7192.bugfix new file mode 100644 index 000000000..53eff608b --- /dev/null +++ b/changelog.d/7192.bugfix @@ -0,0 +1 @@ +Voice Broadcast: Pause voice broadcast listening on new voice broadcast recording From c3322ea7474df981177abedb4aa0a2a48d16896d Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Tue, 10 Jan 2023 09:32:18 +0000 Subject: [PATCH 113/228] Delete an existing broadcast (#7219) --- .../VoiceBroadcastAggregator.swift | 20 +++++++---- .../VoiceBroadcastPlaybackCoordinator.swift | 4 +++ .../VoiceBroadcastPlaybackProvider.swift | 34 +++++++++++++++---- .../VoiceBroadcastPlaybackViewModel.swift | 12 ++++--- .../VoiceBroadcastPlaybackModels.swift | 1 + .../VoiceBroadcastPlaybackScreenState.swift | 2 +- ...ceBroadcastPlaybackViewModelProtocol.swift | 2 +- 7 files changed, 54 insertions(+), 21 deletions(-) diff --git a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift index f85945dce..f9afbadc1 100644 --- a/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift +++ b/Riot/Modules/VoiceBroadcast/VoiceBroadcastSDK/VoiceBroadcastAggregator.swift @@ -68,12 +68,7 @@ public class VoiceBroadcastAggregator { public var delegate: VoiceBroadcastAggregatorDelegate? deinit { - if let referenceEventsListener = referenceEventsListener { - room.removeListener(referenceEventsListener) - } - - NotificationCenter.default.removeObserver(self, name: NSNotification.Name.mxEventDidDecrypt, object: nil) - NotificationCenter.default.removeObserver(self, name: NSNotification.Name.mxRoomDidFlushData, object: nil) + self.stop() } public init(session: MXSession, room: MXRoom, voiceBroadcastStartEventId: String, voiceBroadcastState: VoiceBroadcastInfoState) throws { @@ -201,7 +196,9 @@ public class VoiceBroadcastAggregator { self.events.append(contentsOf: filteredChunk) let eventTypes = [VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType, kMXEventTypeStringRoomMessage] - self.referenceEventsListener = self.room.listen(toEventsOfTypes: eventTypes, onEvent: self.handleEvent) as Any + self.referenceEventsListener = self.room.listen(toEventsOfTypes: eventTypes, onEvent: { [weak self] event, direction, roomState in + self?.handleEvent(event: event, direction: direction, roomState: roomState) + }) as Any self.registerEventDidDecryptNotification() self.events.forEach { event in @@ -234,4 +231,13 @@ public class VoiceBroadcastAggregator { self.delegate?.voiceBroadcastAggregator(self, didFailWithError: error) } } + + func stop() { + if let referenceEventsListener = referenceEventsListener { + room.removeListener(referenceEventsListener) + } + + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.mxEventDidDecrypt, object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.mxRoomDidFlushData, object: nil) + } } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift index 652d6f7b2..3f5e55c6e 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackCoordinator.swift @@ -56,6 +56,10 @@ final class VoiceBroadcastPlaybackCoordinator: Coordinator, Presentable { } + deinit { + viewModel.context.send(viewAction: .redact) + } + // MARK: - Public func start() { } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift index eaae1b11f..3f28ab081 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/Coordinator/VoiceBroadcastPlaybackProvider.swift @@ -29,7 +29,19 @@ import Foundation } } } - var coordinatorsForEventIdentifiers = [String: VoiceBroadcastPlaybackCoordinator]() + private var coordinatorsForEventIdentifiers = [String: VoiceBroadcastPlaybackCoordinator]() { + didSet { + if !self.coordinatorsForEventIdentifiers.isEmpty && self.redactionsListener == nil { + redactionsListener = session?.listenToEvents([MXEventType(identifier: kMXEventTypeStringRoomRedaction)], self.handleEvent) + } + + if self.coordinatorsForEventIdentifiers.isEmpty && self.redactionsListener != nil { + session?.removeListener(self.redactionsListener) + self.redactionsListener = nil + } + } + } + private var redactionsListener: Any? private override init() { } @@ -58,16 +70,24 @@ import Foundation return coordinator.toPresentable() } - - /// Retrieve the voiceBroadcast timeline coordinator for the given event or nil if it hasn't been created yet - func voiceBroadcastPlaybackCoordinatorForEventIdentifier(_ eventIdentifier: String) -> VoiceBroadcastPlaybackCoordinator? { - coordinatorsForEventIdentifiers[eventIdentifier] - } - + /// Pause current voice broadcast playback. @objc public func pausePlaying() { coordinatorsForEventIdentifiers.forEach { _, coordinator in coordinator.pausePlaying() } } + + private func handleEvent(event: MXEvent, direction: MXTimelineDirection, customObject: Any?) { + if direction == .backwards { + // ignore backwards events + return + } + + var coordinator = coordinatorsForEventIdentifiers.removeValue(forKey: event.redacts) + + coordinator?.toPresentable().dismiss(animated: false) { + coordinator = nil + } + } } diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift index feeb1c183..1f5ac9872 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/MatrixSDK/VoiceBroadcastPlaybackViewModel.swift @@ -102,10 +102,9 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic private func release() { MXLog.debug("[VoiceBroadcastPlaybackViewModel] release") - if let audioPlayer = audioPlayer { - audioPlayer.deregisterDelegate(self) - self.audioPlayer = nil - } + self.stop() + self.voiceBroadcastAggregator.delegate = nil + self.voiceBroadcastAggregator.stop() } // MARK: - Public @@ -116,6 +115,8 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic play() case .pause: pause() + case .redact: + release() case .sliderChange(let didChange): didSliderChanged(didChange) case .backward: @@ -468,7 +469,8 @@ extension VoiceBroadcastPlaybackViewModel: VoiceMessageAudioPlayerDelegate { MXLog.debug("[VoiceBroadcastPlaybackViewModel] audioPlayerDidStopPlaying") state.playbackState = .stopped state.playingState.isLive = false - release() + audioPlayer.deregisterDelegate(self) + self.audioPlayer = nil } func audioPlayer(_ audioPlayer: VoiceMessageAudioPlayer, didFailWithError error: Error) { diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift index c2ca1ad5f..488b65c1d 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackModels.swift @@ -23,6 +23,7 @@ enum VoiceBroadcastPlaybackViewAction { case sliderChange(didChange: Bool) case backward case forward + case redact } enum VoiceBroadcastPlaybackState { diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift index 0172597b7..306a5be8c 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackScreenState.swift @@ -18,7 +18,7 @@ import Foundation import SwiftUI typealias MockVoiceBroadcastPlaybackViewModelType = StateStoreViewModel -class MockVoiceBroadcastPlaybackViewModel: MockVoiceBroadcastPlaybackViewModelType, VoiceBroadcastPlaybackViewModelProtocol { +class MockVoiceBroadcastPlaybackViewModel: MockVoiceBroadcastPlaybackViewModelType, VoiceBroadcastPlaybackViewModelProtocol { } /// Using an enum for the screen allows you define the different state cases with diff --git a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackViewModelProtocol.swift b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackViewModelProtocol.swift index 1ad8d64c5..e89df8828 100644 --- a/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackViewModelProtocol.swift +++ b/RiotSwiftUI/Modules/Room/VoiceBroadcastPlayback/VoiceBroadcastPlaybackViewModelProtocol.swift @@ -19,5 +19,5 @@ import Foundation typealias VoiceBroadcastPlaybackViewModelType = StateStoreViewModel protocol VoiceBroadcastPlaybackViewModelProtocol { - var context: VoiceBroadcastPlaybackViewModelType.Context { get } + var context: VoiceBroadcastPlaybackViewModelType.Context { get } } From 8b4d2e3155bd2c32f297b31e4448d3acb0c0e5ed Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Tue, 10 Jan 2023 09:54:17 +0000 Subject: [PATCH 114/228] Ignore the voice broadcast chunks at the notifications level (#7230) --- Riot/Assets/en.lproj/Localizable.strings | 3 +++ RiotNSE/NotificationService.swift | 9 ++++++++- changelog.d/pr-7230.change | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 changelog.d/pr-7230.change diff --git a/Riot/Assets/en.lproj/Localizable.strings b/Riot/Assets/en.lproj/Localizable.strings index f35b9b8a9..965fcfd4a 100644 --- a/Riot/Assets/en.lproj/Localizable.strings +++ b/Riot/Assets/en.lproj/Localizable.strings @@ -83,6 +83,9 @@ /* Sticker from a specific person, not referencing a room. */ "STICKER_FROM_USER" = "%@ sent a sticker"; +/* New voice broadcast from a specific person, not referencing a room. */ +"VOICE_BROADCAST_FROM_USER" = "%@ started a voice broadcast"; + /** Notification messages **/ /* New message indicator on unknown room */ diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 1d7f5cf07..977a71e82 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -471,7 +471,14 @@ class NotificationService: UNNotificationServiceExtension { notificationBody = NotificationService.localizedString(forKey: "VIDEO_FROM_USER", eventSenderName) case kMXMessageTypeAudio: if event.isVoiceMessage() { - notificationBody = NotificationService.localizedString(forKey: "VOICE_MESSAGE_FROM_USER", eventSenderName) + // Ignore voice broadcast chunk event except the first one. + if let chunkInfo = event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType] as? [String: UInt] { + if chunkInfo[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkSequence] == 1 { + notificationBody = NotificationService.localizedString(forKey: "VOICE_BROADCAST_FROM_USER", eventSenderName) + } + } else { + notificationBody = NotificationService.localizedString(forKey: "VOICE_MESSAGE_FROM_USER", eventSenderName) + } } else { notificationBody = NotificationService.localizedString(forKey: "AUDIO_FROM_USER", eventSenderName, messageContent) } diff --git a/changelog.d/pr-7230.change b/changelog.d/pr-7230.change new file mode 100644 index 000000000..d5b48aadc --- /dev/null +++ b/changelog.d/pr-7230.change @@ -0,0 +1 @@ +Ignore the voice broadcast chunks at the notifications level \ No newline at end of file From 06f204bbe9ddd8ffc80f9dd04c17314add2bdb8f Mon Sep 17 00:00:00 2001 From: Toomore Chiang Date: Fri, 16 Dec 2022 05:12:36 +0000 Subject: [PATCH 115/228] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (49 of 49 strings) Translation: Element iOS/Element iOS (Push) Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/zh_Hant/ --- Riot/Assets/zh_Hant.lproj/Localizable.strings | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/zh_Hant.lproj/Localizable.strings b/Riot/Assets/zh_Hant.lproj/Localizable.strings index 6f3abfe1c..d0d698910 100644 --- a/Riot/Assets/zh_Hant.lproj/Localizable.strings +++ b/Riot/Assets/zh_Hant.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "從 %@ 來的訊息"; +"MSG_FROM_USER" = "%@ 傳來的訊息"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ 在 %@ 貼文"; /* New message from a specific person, not referencing a room. Content included. */ @@ -51,3 +51,81 @@ "SINGLE_UNREAD" = "您收到了一則訊息"; /* Message title for a specific person in a named room */ "MSG_FROM_USER_IN_ROOM_TITLE" = "%@ 從 %@"; + +/* New message reply from a specific person in a named room. */ +"REPLY_FROM_USER_IN_ROOM_TITLE" = "%@ 在 %@ 已回覆"; + +/* New message reply from a specific person, not referencing a room. */ +"REPLY_FROM_USER_TITLE" = "%@ 已回覆"; +/** General **/ + +"Notification" = "通知"; + +/** Key verification **/ + +"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ 請求驗證"; + +/* Group call from user, CallKit caller name */ +"GROUP_CALL_FROM_USER" = "%@ (群組通話)"; + +/* A user added a Jitsi call to a room */ +"GROUP_CALL_STARTED" = "群組通話開始"; + +/* A user's membership has updated in an unknown way */ +"USER_MEMBERSHIP_UPDATED" = "%@ 更新了簡介"; + +/* A user has change their name to a new name which we don't know */ +"GENERIC_USER_UPDATED_DISPLAYNAME" = "%@ 變更名稱"; + +/** Membership Updates **/ + +/* A user has change their name to a new name */ +"USER_UPDATED_DISPLAYNAME" = "%@ 變更名稱為 %@"; + +/* A user has change their avatar */ +"USER_UPDATED_AVATAR" = "%@ 變更頭像"; + +/* A user has reacted to a message, but the reaction content is unknown */ +"GENERIC_REACTION_FROM_USER" = "%@ 送出一個反應"; + +/** Reactions **/ + +/* A user has reacted to a message, including the reaction e.g. "Alice reacted 👍". */ +"REACTION_FROM_USER" = "%@ 覺得 %@"; + +/* New message with hidden content due to PIN enabled */ +"MESSAGE_PROTECTED" = "新訊息"; + +/* New message indicator on a room */ +"MESSAGE_IN_X" = "在 %@ 的訊息"; + +/* New message indicator from a DM */ +"MESSAGE_FROM_X" = "來自 %@ 的訊息"; + +/** Notification messages **/ + +/* New message indicator on unknown room */ +"MESSAGE" = "訊息"; + +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ 戳你一下"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ 已分享他的位置"; + +/* New file message from a specific person, not referencing a room. */ +"FILE_FROM_USER" = "%@ 傳送一個檔案 %@"; + +/* New voice message from a specific person, not referencing a room. */ +"VOICE_MESSAGE_FROM_USER" = "%@ 傳送一個語音訊息"; + +/* New audio message from a specific person, not referencing a room. */ +"AUDIO_FROM_USER" = "%@ 傳送一個音訊檔案 %@"; + +/* New video message from a specific person, not referencing a room. */ +"VIDEO_FROM_USER" = "%@ 傳送一個影片"; + +/** Media Messages **/ + +/* New image message from a specific person, not referencing a room. */ +"PICTURE_FROM_USER" = "%@ 傳送一張圖片"; From d3146178a06a933aa6a80ee5298c240e32857fd3 Mon Sep 17 00:00:00 2001 From: Toomore Chiang Date: Fri, 16 Dec 2022 05:26:26 +0000 Subject: [PATCH 116/228] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (8 of 8 strings) Translation: Element iOS/Element iOS (Dialogs) Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/zh_Hant/ --- Riot/Assets/zh_Hant.lproj/InfoPlist.strings | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/zh_Hant.lproj/InfoPlist.strings b/Riot/Assets/zh_Hant.lproj/InfoPlist.strings index 26c3b7ea0..0fd9e3c84 100644 --- a/Riot/Assets/zh_Hant.lproj/InfoPlist.strings +++ b/Riot/Assets/zh_Hant.lproj/InfoPlist.strings @@ -1,5 +1,9 @@ // Permissions usage explanations -"NSCameraUsageDescription" = "相機權限會用來拍攝照片與影片,以及進行視訊通話。"; -"NSPhotoLibraryUsageDescription" = "照片圖庫的權限會用來傳送照片與影片。"; -"NSMicrophoneUsageDescription" = "Element需要麥克風的權限來拍攝影片、照片以及進行通話。"; -"NSContactsUsageDescription" = "為了要顯示您的聯絡人中哪些人已在使用 Element 或 Matrix,我們將會傳送聯絡資訊內的電子郵件位址與電話給您的 Matrix 身份伺服器。New Vector 不會儲存這些資訊,也不會將這些資訊用於其他目的。請檢視應用程式設定的隱私權政策頁面來取得更多資訊。"; +"NSCameraUsageDescription" = "相機權限會用來拍攝照片、影片,與進行視訊通話。"; +"NSPhotoLibraryUsageDescription" = "允許讀取照片圖庫權限並用來傳送照片與影片。"; +"NSMicrophoneUsageDescription" = "Element 需要麥克風的權限來進行語音通話、視訊通話與錄製語音訊息。"; +"NSContactsUsageDescription" = "這將會分享給身份伺服器以便在 Matrix 尋找您的聯絡人。"; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "當您分享您的位置給其他人時,Element 需要權限來顯示地圖。"; +"NSLocationWhenInUseUsageDescription" = "當您分享您的位置給其他人時,Element 需要權限來顯示地圖。"; +"NSFaceIDUsageDescription" = "已啟用 Face ID 來使用您的應用程式。"; +"NSCalendarsUsageDescription" = "檢視您已排定的會議。"; From 1b3553b412536c4bbfa5a836d2a663a075bef96c Mon Sep 17 00:00:00 2001 From: pk'Mysickz Date: Thu, 5 Jan 2023 12:24:24 +0000 Subject: [PATCH 117/228] Translated using Weblate (Thai) Currently translated at 100.0% (49 of 49 strings) Translation: Element iOS/Element iOS (Push) Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/th/ --- Riot/Assets/th.lproj/Localizable.strings | 57 ++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/Riot/Assets/th.lproj/Localizable.strings b/Riot/Assets/th.lproj/Localizable.strings index a6272922c..a1f012f4d 100644 --- a/Riot/Assets/th.lproj/Localizable.strings +++ b/Riot/Assets/th.lproj/Localizable.strings @@ -95,3 +95,60 @@ /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ ได้โพสต์ใน %@"; + +/* Group call from user, CallKit caller name */ +"GROUP_CALL_FROM_USER" = "%@ (การโทรแบบกลุ่ม)"; + +/* A user added a Jitsi call to a room */ +"GROUP_CALL_STARTED" = "เริ่มการโทรแบบกลุ่มแล้ว"; + +/* A user's membership has updated in an unknown way */ +"USER_MEMBERSHIP_UPDATED" = "%@ ได้อัพเดตรูปโปรไฟล์"; + +/* A user has change their avatar */ +"USER_UPDATED_AVATAR" = "%@ เปลี่ยนอวาตาร์ของเขา"; + +/* A user has change their name to a new name which we don't know */ +"GENERIC_USER_UPDATED_DISPLAYNAME" = "%@ เปลี่ยนชื่อของเขา"; + +/** Membership Updates **/ + +/* A user has change their name to a new name */ +"USER_UPDATED_DISPLAYNAME" = "%@ เปลี่ยนชื่อเป็น %@"; + +/* A user has reacted to a message, but the reaction content is unknown */ +"GENERIC_REACTION_FROM_USER" = "%@ ส่งความรู้สึก"; + +/** Reactions **/ + +/* A user has reacted to a message, including the reaction e.g. "Alice reacted 👍". */ +"REACTION_FROM_USER" = "%@ รู้สึก %@"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ แบ่งปันตำแหน่งของเขา"; + +/* New file message from a specific person, not referencing a room. */ +"FILE_FROM_USER" = "%@ ส่งไฟล์ %@"; + +/* New voice message from a specific person, not referencing a room. */ +"VOICE_MESSAGE_FROM_USER" = "%@ ส่งข้อความเสียง"; + +/* New audio message from a specific person, not referencing a room. */ +"AUDIO_FROM_USER" = "%@ ส่งไฟล์เสียง %@"; + +/* New video message from a specific person, not referencing a room. */ +"VIDEO_FROM_USER" = "%@ ส่งวีดีโอ"; + +/** Media Messages **/ + +/* New image message from a specific person, not referencing a room. */ +"PICTURE_FROM_USER" = "%@ ส่งรูปภาพ"; + +/* New message reply from a specific person in a named room. */ +"REPLY_FROM_USER_IN_ROOM_TITLE" = "%@ ตอบกลับใน %@"; + +/* New message reply from a specific person, not referencing a room. */ +"REPLY_FROM_USER_TITLE" = "%@ ตอบกลับ"; +/** General **/ + +"Notification" = "การแจ้งเตือน"; From 8d9d6c233566387cf8e7f45b3dd1dd7163809f2e Mon Sep 17 00:00:00 2001 From: Christian Paul Date: Thu, 5 Jan 2023 16:59:50 +0000 Subject: [PATCH 118/228] Translated using Weblate (Esperanto) Currently translated at 50.0% (4 of 8 strings) Translation: Element iOS/Element iOS (Dialogs) Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/eo/ --- Riot/Assets/eo.lproj/InfoPlist.strings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/eo.lproj/InfoPlist.strings b/Riot/Assets/eo.lproj/InfoPlist.strings index f6470d19a..982dfdbf3 100644 --- a/Riot/Assets/eo.lproj/InfoPlist.strings +++ b/Riot/Assets/eo.lproj/InfoPlist.strings @@ -4,6 +4,6 @@ "NSCalendarsUsageDescription" = "Vidu viajn planitajn renkontiĝojn en la aplikaĵo."; "NSContactsUsageDescription" = "Por trovi kontaktojn, kiuj jam estas ĉe Matrix, Element povas sendi retpoŝtadresojn kaj telefonnumerojn el via adresaro al via elektita identigila servilo de Matrix. Kiam eblas, personaj datumoj estas haketitaj antaŭ forsendo – bonvolu kontroli la politikon pri privateco de via identiga servilo por pliaj detaloj."; "NSMicrophoneUsageDescription" = "La mikrofono estas uzata por filmi, kaj ankaŭ por voki."; -"NSPhotoLibraryUsageDescription" = "La fotujo estas uzata por sendi fotojn kaj filmojn."; +"NSPhotoLibraryUsageDescription" = "Permesu aliron al fotoj por alŝuti fotojn kaj filmetojn el via biblioteko."; // Permissions usage explanations -"NSCameraUsageDescription" = "La filmilo estas uzata por foti kaj filmi, kaj ankaŭ por vidvoki."; +"NSCameraUsageDescription" = "La filmilo estas uzata por fari vidvokojn, aŭ preni kaj alŝuti fotojn kaj filmetojn."; From 65da4456a99f8c2753c5bfeafb610a17b7064e00 Mon Sep 17 00:00:00 2001 From: Vri Date: Tue, 29 Nov 2022 16:01:26 +0000 Subject: [PATCH 119/228] Translated using Weblate (German) Currently translated at 100.0% (2331 of 2331 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 766ff9904..6479ec188 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2669,3 +2669,11 @@ // Unverified sessions "key_verification_alert_title" = "Du hast nicht verifizierte Sitzungen"; +"launch_loading_processing_response" = "Verarbeite Daten\n%@ % %"; +"launch_loading_server_syncing_nth_attempt" = "Synchronisiere mit dem Server\n(%@ Versuch)"; + +// MARK: - Launch loading + +"launch_loading_server_syncing" = "Synchronisiere mit dem Server"; +"user_other_session_permanently_unverified_additional_info" = "Diese Sitzung kann nicht verifiziert werden, da sie keine Verschlüsselung unterstützt."; +"voice_broadcast_time_left" = "%@ übrig"; From 8b3f80a25bff279fc417449a00caacca9e63ea34 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 30 Nov 2022 17:33:00 +0000 Subject: [PATCH 120/228] Translated using Weblate (Albanian) Currently translated at 99.6% (2323 of 2331 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 24517ae23..c90ba63dd 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2595,7 +2595,7 @@ "manage_session_name_info" = "Ju lutemi, kini parasysh se emrat e sesioneve janë të dukshëm edhe për personat me të cilët komunikoni.%@"; "manage_session_name_hint" = "Emra vetjakë sesionesh mund t’ju ndihmojnë të njihni më kollaj pajisjet tuaja."; "settings_labs_enable_voice_broadcast" = "Aktivizoni transmetim zanor (nën zhvillim aktiv)"; -"settings_labs_enable_wysiwyg_composer" = "Provoni përpunuesin e teksteve të pasur (për tekst të thjeshtë vjen së shpejti)"; +"settings_labs_enable_wysiwyg_composer" = "Provoni përpunuesin e teksteve të pasur"; "settings_labs_enable_new_app_layout" = "Skemë e Re Aplikacioni"; "settings_labs_enable_new_client_info_feature" = "Regjistro emrin, versionin dhe URL-në e klientit, për të dalluar më kollaj sesionit te përgjegjës sesionesh"; "settings_labs_enable_new_session_manager" = "Përgjegjës i ri sesionesh"; @@ -2629,3 +2629,29 @@ "invite_to" = "Ftojeni te %@"; "all_chats_empty_list_placeholder_title" = "S’ka gjë tjetër për të parë."; "all_chats_edit_layout_add_filters_message" = "Filtroni automatikisht mesazhet tuaj në kategori që caktoni vetë"; +"device_name_desktop" = ""; +"user_session_rename_session_description" = "Përdorues të tjerë në mesazhe të drejtpërdrejtë dhe dhoma ku jeni në gjendje të shihni një listë të plotë të sesioneve tuaja.\n\nKjo u jep besim atyre se po flasin vërtet me ju, por do të thotë edhe se mund të shohin emrin e sesionit që jepni këtu."; +"user_session_inactive_session_description" = "Sesione jo aktive janë sesione që keni ca kohë që s’i keni përdorur, por që vazhdojnë të marrin kyçe fshehtëzimi.\n\nHeqja e sesioneve jo aktive përmirëson sigurinë dhe punimin dhe e bëjnë të lehtë për ju të identifikoni, nëse një sesion i ri është i dyshimtë."; +"user_session_unverified_session_description" = "Sesione të paverifikuar janë sesione ku keni bërë hyrjen me kredencialet tuaja, por që nuk janë ndër-verifikuar.\n\nDuhet ta bëni veçanërisht të qartë se i njihni këto sesione, ngaqë mund të përfaqësojnë përdorim të paautorizuar të llogarisë tuaj."; +"user_session_verified_session_description" = "Sesione të verifikuar janë ata kudo që përdorni Element-in pasi të keni dhënë frazëkalimin tuaj, ose pasi të keni ripohuar identitetin tuaj përmes një tjetër sesioni të verifikuar.\n\nKjo do të thotë se zotëroni krejt kyçet e nevojshëm për të shkyçur mesazhet tuaj të fshehtëzuar dhe ripohuar përdoruesve të tjerë se e besoni këtë sesion."; +"launch_loading_server_syncing_nth_attempt" = "Po njëkohësohet me shërbyesin\n(Përpjekja e %@)"; +"user_session_rename_session_title" = "Riemërtim sesionesh"; +"user_session_inactive_session_title" = "Sesione jo aktive"; +"user_session_unverified_session_title" = "Sesione të paverifikuar"; +"user_session_verified_session_title" = "Sesione të verifikuar"; +"user_session_got_it" = "E mora vesh"; +"user_other_session_permanently_unverified_additional_info" = "Ky sesion s’mund të verifikohet, ngaqë nuk mbulon fshehtëzim."; +"user_sessions_hide_location_info" = "Fshihe adresën IP"; +"user_sessions_show_location_info" = "Shfaq adresë IP"; +"voice_broadcast_time_left" = "Edhe %@"; +"voice_broadcast_tile" = "Transmetim zanor"; +"launch_loading_processing_response" = "Po përpunohen të dhëna\n%@ %%"; + +// MARK: - Launch loading + +"launch_loading_server_syncing" = "Po njëkohësohet me shërbyesin"; +"key_verification_alert_body" = "Shqyrtojini, që të siguroheni se llogaria juaj është e parrezik."; + +// Unverified sessions +"key_verification_alert_title" = "Keni sesione të paverifikuar"; +"manage_session_sign_out_other_sessions" = "Dilni prej krejt sesioneve të tjerë"; From bb60c9c385fe98b24e2241b5fba00a97662114c1 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Tue, 29 Nov 2022 15:10:01 +0000 Subject: [PATCH 121/228] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2331 of 2331 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ --- Riot/Assets/pt_BR.lproj/Vector.strings | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 3bf630208..0b8c35566 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -2623,3 +2623,24 @@ "manage_session_sign_out_other_sessions" = "Fazer signout de todas as outras sessões"; "voice_broadcast_tile" = "Broadcast de voz"; "voice_broadcast_live" = "Ao vivo"; +"user_session_rename_session_description" = "Outras(os) usuárias(os) em mensagens diretas e salas a que você se junta são capazes de visualizar uma lista completa de suas sessões.\n\nIsto as/os provê com confiança que elas(es) são estão realmente falando com você, mas também significa que elas(es) veem o nome da sessão que você entrar aqui."; +"user_session_rename_session_title" = "Renomear sessões"; +"user_session_inactive_session_description" = "Sessões inativas são sessões que você não tem usado em algum tempo, mas elas continuam a receber chaves de encriptação.\n\nRemover sessões inativas melhora segurança e performance, e torna mais fácil para você identificar se uma nova sessão é suspeita."; +"user_session_inactive_session_title" = "Sessões inativas"; +"user_session_unverified_session_description" = "Sessões não-verificadas são sessões que você tem feito login com suas credenciais mas não têm sido verificadas cruzado.\n\nVocê devia especialmente se certificar que você reconhece estas sessões já que elas podiam representar um uso não-autorizado de sua conta."; +"user_session_unverified_session_title" = "Sessão não-verificada"; +"user_session_verified_session_description" = "Sessões verificadas são onde quer que você esteja usando Element depois de entrar sua frasepasse ou confirmar sua identidade com uma outra sessões verificada.\n\nIsto significa que você tem todas as chaves necessárias para destrancar suas mensagens encriptadas e confirmar a outras(os) usuárias(os) que você confia nesta sessão."; +"user_session_verified_session_title" = "Sessões verificadas"; +"user_session_got_it" = "Entendido"; +"user_other_session_permanently_unverified_additional_info" = "Esta sessão não pode ser verificada porque ela não suporta encriptação."; +"voice_broadcast_time_left" = "%@ restando"; +"launch_loading_processing_response" = "Processando dados\n%@ %%"; +"launch_loading_server_syncing_nth_attempt" = "Sincando com o servidor\n(%@ tentativa)"; + +// MARK: - Launch loading + +"launch_loading_server_syncing" = "Sincando com o servidor"; +"key_verification_alert_body" = "Revise para assegurar que sua conta está segura."; + +// Unverified sessions +"key_verification_alert_title" = "Você tem sessões não-verificadas"; From e4113a60f6a3ce32df66f56be14152bf6e5d2291 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 29 Nov 2022 19:36:59 +0000 Subject: [PATCH 122/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2331 of 2331 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index f274a0dbe..4659bb085 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2495,7 +2495,7 @@ "location_sharing_live_list_item_last_update_invalid" = "Час останнього оновлення невідомий"; "location_sharing_live_list_item_last_update" = "Оновлено %@ тому"; "location_sharing_live_list_item_sharing_expired" = "Надсилання завершено"; -"location_sharing_live_list_item_time_left" = "%@ виходить"; +"location_sharing_live_list_item_time_left" = "Залишилося %@"; "location_sharing_live_viewer_title" = "Місце перебування"; "location_sharing_live_map_callout_title" = "Поділитися місцем перебування"; "room_access_settings_screen_upgrade_alert_note" = "Зауважте, що оновлення створить нову версію кімнати. Усі поточні повідомлення залишаться в цій архівованій кімнаті."; @@ -2860,3 +2860,11 @@ // Unverified sessions "key_verification_alert_title" = "У вас є не звірені сеанси"; +"user_other_session_permanently_unverified_additional_info" = "Цей сеанс не можна звірити, оскільки він не підтримує шифрування."; +"voice_broadcast_time_left" = "Залишилося %@"; +"launch_loading_processing_response" = "Обробка даних\n%@ %%"; +"launch_loading_server_syncing_nth_attempt" = "Синхронізація з сервером\n(%@ спроба)"; + +// MARK: - Launch loading + +"launch_loading_server_syncing" = "Синхронізація з сервером"; From de986a99aabb0db38b0427185589dda859723aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 29 Nov 2022 14:37:14 +0000 Subject: [PATCH 123/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2331 of 2331 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 3797c222b..138725679 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2607,3 +2607,11 @@ // Unverified sessions "key_verification_alert_title" = "Sul on verifitseerimata sessioone"; +"user_other_session_permanently_unverified_additional_info" = "Seda sessiooni ei saa verifitseerida, sest seal puudub krüptimise tugi."; +"voice_broadcast_time_left" = "aega jäänud %@"; +"launch_loading_processing_response" = "Töötleme andmeid\n%@ %%"; +"launch_loading_server_syncing_nth_attempt" = "Sünkroniseerime andmeid serveriga\n(katse: %@)"; + +// MARK: - Launch loading + +"launch_loading_server_syncing" = "Sünkroniseerimine serveriga"; From b8655d884a5913ef896f7fefe4941806ce95653d Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 29 Nov 2022 23:31:35 +0000 Subject: [PATCH 124/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2331 of 2331 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index a4ae14538..8450e77a3 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2862,3 +2862,11 @@ // Unverified sessions "key_verification_alert_title" = "Anda punya sesi yang belum diverifikasi"; +"user_other_session_permanently_unverified_additional_info" = "Sesi ini tidak dapat diverifikasi karena sesinya tidak mendukung enkripsi."; +"voice_broadcast_time_left" = "Tersisa %@"; +"launch_loading_processing_response" = "Memroses data\n%@ %%"; +"launch_loading_server_syncing_nth_attempt" = "Menyinkron dengan server\n(%@ percobaan)"; + +// MARK: - Launch loading + +"launch_loading_server_syncing" = "Menyinkron dengan server"; From f345ffd725f7fbba143d71061538f78adcb18b08 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Tue, 29 Nov 2022 15:52:35 +0000 Subject: [PATCH 125/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2331 of 2331 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 8290307fa..9d1c7c4ad 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2858,3 +2858,11 @@ // Unverified sessions "key_verification_alert_title" = "Máte neoverené relácie"; +"user_other_session_permanently_unverified_additional_info" = "Túto reláciu nemožno overiť, pretože nepodporuje šifrovanie."; +"voice_broadcast_time_left" = "%@ ostáva"; +"launch_loading_processing_response" = "Spracovanie údajov\n%@ %%"; +"launch_loading_server_syncing_nth_attempt" = "Synchronizácia so serverom\n(%@ pokus)"; + +// MARK: - Launch loading + +"launch_loading_server_syncing" = "Synchronizácia so serverom"; From 8c751fd83f29f388eb209a235d3d04411d7b2283 Mon Sep 17 00:00:00 2001 From: Christina Klaas Date: Fri, 2 Dec 2022 08:42:02 +0000 Subject: [PATCH 126/228] Translated using Weblate (German) Currently translated at 100.0% (2332 of 2332 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 6479ec188..cc0c11e69 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -1506,12 +1506,12 @@ "poll_edit_form_add_option" = "Option hinzufügen"; "poll_edit_form_option_number" = "Option %lu"; "poll_edit_form_question_or_topic" = "Frage oder Thematik"; -"room_event_action_end_poll" = "Abstimmung beenden"; -"room_event_action_remove_poll" = "Abstimmung entfernen"; +"room_event_action_end_poll" = "Umfrage beenden"; +"room_event_action_remove_poll" = "Umfrage entfernen"; // Mark: - Polls -"poll_edit_form_create_poll" = "Abstimmung erstellen"; +"poll_edit_form_create_poll" = "Umfrage erstellen"; "settings_labs_enabled_polls" = "Umfragen"; "share_extension_send_now" = "Jetzt senden"; "accessibility_button_label" = "Knopf"; @@ -1538,7 +1538,7 @@ "poll_edit_form_poll_question_or_topic" = "Frage oder Thema der Umfrage"; "poll_edit_form_input_placeholder" = "Schreib etwas"; "analytics_prompt_terms_link_upgrade" = "hier"; -"poll_timeline_not_closed_title" = "Beenden der Abstimmung fehlgeschlagen"; +"poll_timeline_not_closed_title" = "Beenden der Umfrage fehlgeschlagen"; "poll_timeline_vote_not_registered_subtitle" = "Wir konnten deine Stimme leider nicht erfassen. Versuche es bitte erneut"; "poll_timeline_total_final_results" = "Es wurden %lu Stimmen abgegeben"; "poll_timeline_total_final_results_one_vote" = "Es wurde 1 Stimme abgegeben"; @@ -1547,7 +1547,7 @@ "poll_timeline_not_closed_subtitle" = "Versuche es bitte erneut"; "poll_timeline_vote_not_registered_title" = "Stimme nicht erfasst"; "poll_edit_form_post_failure_subtitle" = "Versuche es bitte erneut"; -"poll_edit_form_post_failure_title" = "Absenden der Abstimmung fehlgeschlagen"; +"poll_edit_form_post_failure_title" = "Absenden der Umfrage fehlgeschlagen"; "share_extension_low_quality_video_message" = "Für eine bessere Qualität sende es in %@ oder sende es in niedriger Qualität."; "share_extension_low_quality_video_title" = "Das Video wird in niedriger Qualität gesendet werden"; "analytics_prompt_stop" = "Teilen beenden"; @@ -1590,9 +1590,9 @@ "poll_edit_form_update_failure_subtitle" = "Bitte erneut versuchen"; "poll_edit_form_poll_type" = "Abstimmungsart"; "poll_edit_form_poll_type_closed_description" = "Die Ergebnisse werden erst sichtbar, sobald du die Umfrage beendest"; -"poll_edit_form_poll_type_closed" = "Abgeschlossene Abstimmung"; +"poll_edit_form_poll_type_closed" = "Versteckte Umfrage"; "poll_edit_form_poll_type_open_description" = "Abstimmende können die Ergebnisse nach Stimmabgabe sehen"; -"poll_edit_form_poll_type_open" = "Laufende Abstimmung"; +"poll_edit_form_poll_type_open" = "Offene Umfrage"; "poll_edit_form_update_failure_title" = "Aktualisierung der Umfrage fehlgeschlagen"; "threads_empty_tip" = "Hinweis: Tippe auf eine Nachricht und wähle „Thread“ um einen neuen zu starten."; "threads_empty_info_my" = "Antworte auf einen laufenden Thread oder tippe auf eine Nachricht und wähle „Thread“ um einen neuen zu starten."; From a5b85856a95723e5a6ed29ed0672979e6a54c401 Mon Sep 17 00:00:00 2001 From: Vri Date: Thu, 1 Dec 2022 07:51:45 +0000 Subject: [PATCH 127/228] Translated using Weblate (German) Currently translated at 100.0% (2332 of 2332 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index cc0c11e69..af4cc454f 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2677,3 +2677,4 @@ "launch_loading_server_syncing" = "Synchronisiere mit dem Server"; "user_other_session_permanently_unverified_additional_info" = "Diese Sitzung kann nicht verifiziert werden, da sie keine Verschlüsselung unterstützt."; "voice_broadcast_time_left" = "%@ übrig"; +"voice_broadcast_buffering" = "Puffere …"; From 37d18b912bb2414e155d4abda73f0e3563918116 Mon Sep 17 00:00:00 2001 From: random Date: Thu, 1 Dec 2022 09:13:36 +0000 Subject: [PATCH 128/228] Translated using Weblate (Italian) Currently translated at 100.0% (2332 of 2332 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index b0ee26dc2..764853cfb 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -2635,3 +2635,12 @@ // Unverified sessions "key_verification_alert_title" = "Hai sessioni non verificate"; +"user_other_session_permanently_unverified_additional_info" = "Questa sessione non può essere verificata perché non supporta la crittografia."; +"voice_broadcast_buffering" = "Buffer..."; +"voice_broadcast_time_left" = "%@ rimasti"; +"launch_loading_processing_response" = "Elaborazione dati\n%@ %%"; +"launch_loading_server_syncing_nth_attempt" = "Sincronizzazione con il server\n(%@ tentativo)"; + +// MARK: - Launch loading + +"launch_loading_server_syncing" = "Sincronizzazione con il server"; From 5db4365ab1348c2ffd164394118cadf981e9c9fe Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Thu, 1 Dec 2022 09:17:03 +0000 Subject: [PATCH 129/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2332 of 2332 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 4659bb085..3cbfd58d7 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2868,3 +2868,4 @@ // MARK: - Launch loading "launch_loading_server_syncing" = "Синхронізація з сервером"; +"voice_broadcast_buffering" = "Буферизація..."; From 7708e6731d6182cb9ce3f18b6ead706099ca387c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Thu, 1 Dec 2022 08:00:47 +0000 Subject: [PATCH 130/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2332 of 2332 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 138725679..674737d04 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2615,3 +2615,4 @@ // MARK: - Launch loading "launch_loading_server_syncing" = "Sünkroniseerimine serveriga"; +"voice_broadcast_buffering" = "Andmed on puhverdamisel…"; From d5a8ec85fe26d417b927b874f890423437708a82 Mon Sep 17 00:00:00 2001 From: Linerly Date: Thu, 1 Dec 2022 11:57:27 +0000 Subject: [PATCH 131/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2332 of 2332 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 8450e77a3..178cfa82c 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2870,3 +2870,4 @@ // MARK: - Launch loading "launch_loading_server_syncing" = "Menyinkron dengan server"; +"voice_broadcast_buffering" = "Memuat…"; From 2aff132710a82d9a1a3c3dcad865ddcc4b215b70 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Thu, 1 Dec 2022 21:32:55 +0000 Subject: [PATCH 132/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2332 of 2332 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 9d1c7c4ad..d5a3f18e2 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2866,3 +2866,4 @@ // MARK: - Launch loading "launch_loading_server_syncing" = "Synchronizácia so serverom"; +"voice_broadcast_buffering" = "Načítavanie do vyrovnávacej pamäte…"; From f7105edb821f17a6238212445e2313e26579fa25 Mon Sep 17 00:00:00 2001 From: Vri Date: Fri, 2 Dec 2022 11:51:30 +0000 Subject: [PATCH 133/228] Translated using Weblate (German) Currently translated at 100.0% (2335 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index af4cc454f..26b4de94c 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2678,3 +2678,6 @@ "user_other_session_permanently_unverified_additional_info" = "Diese Sitzung kann nicht verifiziert werden, da sie keine Verschlüsselung unterstützt."; "voice_broadcast_time_left" = "%@ übrig"; "voice_broadcast_buffering" = "Puffere …"; +"voice_broadcast_stop_alert_agree_button" = "Ja, beende"; +"voice_broadcast_stop_alert_description" = "Bist du sicher, dass du deine Live-Übertragung abbrechen möchtest? Dies wird die Übertragung beenden und die vollständige Aufnahme wird im Raum verfügbar sein."; +"voice_broadcast_stop_alert_title" = "Live-Übertragung abbrechen?"; From 5d7f9823747fa0b91ef3bc2cfdb8d0bf652deb61 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 2 Dec 2022 10:48:29 +0000 Subject: [PATCH 134/228] Translated using Weblate (Albanian) Currently translated at 99.6% (2327 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index c90ba63dd..011244a7f 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2655,3 +2655,7 @@ // Unverified sessions "key_verification_alert_title" = "Keni sesione të paverifikuar"; "manage_session_sign_out_other_sessions" = "Dilni prej krejt sesioneve të tjerë"; +"user_other_session_menu_sign_out_sessions" = "Dilni nga %@ sesione"; +"voice_broadcast_stop_alert_agree_button" = "Po, ndaleni"; +"voice_broadcast_stop_alert_description" = "Jeni i sigurt se doni të ndalet transmetimi juaj i drejtpërdrejtë? Kjo do të ndalë transmetimin dhe regjistrimi i plotë do të jetë i passhëm te dhoma."; +"voice_broadcast_stop_alert_title" = "Të ndalet transmetimi i drejtpërdrejtë?"; From a89545c0bb1d355d0b18602d2ae5222ba63e4df7 Mon Sep 17 00:00:00 2001 From: Linerly Date: Fri, 2 Dec 2022 09:16:45 +0000 Subject: [PATCH 135/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2335 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 178cfa82c..e2fb7b6d1 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2871,3 +2871,6 @@ "launch_loading_server_syncing" = "Menyinkron dengan server"; "voice_broadcast_buffering" = "Memuat…"; +"voice_broadcast_stop_alert_agree_button" = "Ya, batalkan"; +"voice_broadcast_stop_alert_description" = "Apakah Anda ingin menghentikan siaran langsung Anda? Ini akan mengakhiri siarannya, dan rekamanan lengkap akan tersedia dalam ruangan."; +"voice_broadcast_stop_alert_title" = "Berhenti menyiarkan langsung?"; From 1567f8aecd38ac51bedaf046306f4393000e6d93 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Fri, 2 Dec 2022 09:48:20 +0000 Subject: [PATCH 136/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2335 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index d5a3f18e2..4256de738 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2867,3 +2867,6 @@ "launch_loading_server_syncing" = "Synchronizácia so serverom"; "voice_broadcast_buffering" = "Načítavanie do vyrovnávacej pamäte…"; +"voice_broadcast_stop_alert_agree_button" = "Áno, zastaviť"; +"voice_broadcast_stop_alert_description" = "Určite chcete zastaviť vysielanie naživo? Tým sa vysielanie ukončí a v miestnosti bude k dispozícii celý záznam."; +"voice_broadcast_stop_alert_title" = "Zastaviť vysielanie naživo?"; From 718048754247beb252849e8a6d98f14ddcbf0f22 Mon Sep 17 00:00:00 2001 From: AnnaKuznietsovaua Date: Sun, 4 Dec 2022 19:27:27 +0000 Subject: [PATCH 137/228] Translated using Weblate (Russian) Currently translated at 82.0% (1917 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/ --- Riot/Assets/ru.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index c02dfd82d..afceb6269 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -2168,3 +2168,4 @@ "authentication_qr_login_start_subtitle" = "Используйте камеру на этом устройстве, чтобы сканировать QR-код, отображённый на вашем другом устройстве:"; "authentication_qr_login_start_title" = "Сканировать QR-код"; "authentication_terms_policy_url_error" = "Не получилось найти выбранные правила. Пожалуйста, попробуйте снова позже."; +"threads_empty_tip" = "Подсказка: Нажмите на сообщение и используйте \"Поток\" что бы начать переписку."; From 678d0842d7d5743e8215afc6ce03c72d0ab6f20b Mon Sep 17 00:00:00 2001 From: Platon Terekhov Date: Sun, 4 Dec 2022 19:14:48 +0000 Subject: [PATCH 138/228] Translated using Weblate (Russian) Currently translated at 82.0% (1917 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/ --- Riot/Assets/ru.lproj/Vector.strings | 49 ++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index afceb6269..7e2f2b0c1 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -44,7 +44,7 @@ "auth_optional_phone_placeholder" = "Номер телефона (не обязательно)"; "auth_phone_placeholder" = "Номер телефона"; "auth_repeat_password_placeholder" = "Повторите пароль"; -"auth_repeat_new_password_placeholder" = "Подтвердите свой новый пароль"; +"auth_repeat_new_password_placeholder" = "Подтвердите свой новый пароль учётной записи Matrix"; "auth_invalid_login_param" = "Неверное имя пользователя и/или пароль"; "auth_invalid_user_name" = "Имена пользователей могут содержать только буквы, цифры, точки, дефисы и символы подчеркивания"; "auth_invalid_password" = "Пароль слишком короткий (мин. 6 символов)"; @@ -65,7 +65,7 @@ "auth_untrusted_id_server" = "Этот сервер идентификации не является доверенным"; "auth_password_dont_match" = "Пароли не совпадают"; "auth_username_in_use" = "Имя пользователя занято"; -"auth_forgot_password" = "Забыли пароль?"; +"auth_forgot_password" = "Забыли пароль учётной записи Matrix?"; "auth_email_not_found" = "Не удалось отправить email: этот адрес электронной почты не найден"; "auth_use_server_options" = "Использовать пользовательские параметры сервера (дополнительно)"; "auth_email_validation_message" = "Проверьте электронную почту, чтобы продолжить регистрацию"; @@ -73,14 +73,14 @@ "auth_msisdn_validation_message" = "Мы отправили SMS с кодом активации. Введите этот код ниже."; "auth_msisdn_validation_error" = "Не удалось проверить номер телефона."; "auth_recaptcha_message" = "Этот домашний сервер хочет проверить, что вы не робот"; -"auth_reset_password_message" = "Чтобы сбросить пароль, введите адрес электронной почты, связанный с вашей учетной записью:"; +"auth_reset_password_message" = "Чтобы сбросить пароль учётной записи Matrix, введите адрес электронной почты, связанный с вашей учетной записью:"; "auth_reset_password_missing_email" = "Необходимо ввести адрес электронной почты, связанный с вашей учетной записью."; "auth_reset_password_missing_password" = "Необходимо ввести новый пароль."; "auth_reset_password_email_validation_message" = "Письмо было отправлено на %@. После перехода по ссылке из письма, нажмите ниже."; "auth_reset_password_next_step_button" = "Я подтвердил свой адрес электронной почты"; "auth_reset_password_error_unauthorized" = "Не удалось проверить адрес электронной почты: убедитесь, что вы нажали на ссылку в письме"; "auth_reset_password_error_not_found" = "Ваш адрес электронной почты, кажется, не связан с Matrix ID на этом домашнем сервере."; -"auth_reset_password_success_message" = "Ваш пароль был сброшен.\n\nВы вышли со всех сессий и больше не будете получать push-уведомления. Чтобы вновь активировать уведомления, заново авторизуйтесь на каждом устройстве."; +"auth_reset_password_success_message" = "Ваш пароль учётной записи Matrix был сброшен.\n\nВы вышли со всех сессий и больше не будете получать push-уведомления. Чтобы вновь активировать уведомления, заново авторизуйтесь на каждом устройстве."; "auth_add_email_and_phone_warning" = "Регистрация с электронной почтой и номером телефона одновременно не поддерживается до тех пор, пока не появится API. Будет учитываться только номер телефона. Вы можете добавить свою электронную почту в свой профиль в настройках."; // Chat creation "room_creation_title" = "Новый чат"; @@ -166,8 +166,8 @@ "room_creation_appearance" = "Внешний вид"; "directory_cell_description" = "%tu комнат"; "directory_search_results_title" = "Просмотр результатов поиска"; -"directory_search_results" = "%tu результатов поиска для %@"; -"directory_search_results_more_than" = ">%tu результатов поиска для %@"; +"directory_search_results" = "%1$tu результатов поиска для %2$@"; +"directory_search_results_more_than" = ">%1$tu результатов поиска для %2$@"; "room_participants_invite_malformed_id" = "Неверный идентификатор. Должен быть адрес электронной почты или идентификатор Matrix, например '@thomas:matrix.org'"; "room_participants_now" = "сейчас"; "room_participants_ago" = "назад"; @@ -196,7 +196,7 @@ "room_event_action_redact" = "Удалить"; "room_event_action_more" = "Больше"; "room_event_action_share" = "Поделиться"; -"room_event_action_permalink" = "Постоянная ссылка"; +"room_event_action_permalink" = "Скопировать ссылку на сообщение"; "room_event_action_view_source" = "Посмотреть источник"; "room_event_action_report" = "Сообщить о недопустимом контенте"; "room_event_action_report_prompt_reason" = "Причина сообщениея о недопустимом контенте"; @@ -504,7 +504,7 @@ "room_do_not_have_permission_to_post" = "У вас нет разрешения на публикацию в этой комнате"; "settings_flair" = "Покажите настроение, где это разрешено"; "room_details_flair_section" = "Показать настроение для сообществ"; -"room_event_action_kick_prompt_reason" = "Причина по которой этот пользователь будет выкинут"; +"room_event_action_kick_prompt_reason" = "Причина исключения пользователя"; "room_event_action_ban_prompt_reason" = "Причина по которой этот пользователь будет забанен"; // GDPR "gdpr_consent_not_given_alert_message" = "Для продолжения использования сервера %@ вы должны принять условия и положения."; @@ -675,7 +675,7 @@ "settings_labs_message_reaction" = "Реагировать на сообщения с Emoji"; "settings_key_backup_button_connect" = "Подключите этот сеанс к резервному копированию ключей"; "close" = "Закрыть"; -"auth_forgot_password_error_no_configured_identity_server" = "Сервер идентификации не настроен: добавьте один для сброса пароля."; +"auth_forgot_password_error_no_configured_identity_server" = "Сервер идентификации не настроен: добавьте один для сброса пароля учётной записи Matrix."; "auth_softlogout_signed_out" = "Вы вышли"; "auth_softlogout_sign_in" = "Войти"; "auth_softlogout_clear_data" = "Очистить личные данные"; @@ -820,9 +820,9 @@ "auth_add_email_message_2" = "Установите адрес электронной почты для восстановления учетной записи, а затем ее можно будет найти людям, которые вас знают."; "auth_add_phone_message_2" = "Задайте телефон, и позже его могут найти люди, которые вас знают."; "auth_add_email_phone_message_2" = "Установка адреса электронной почты для восстановления учетной записи. Используйте позже электронную почту или телефон, чтобы люди, которые вас знают, могли их по желанию найти."; -"auth_email_is_required" = "Сервер идентификации не настроен, поэтому вы не можете добавить адрес электронной почты, чтобы в будущем сбросить пароль."; -"auth_phone_is_required" = "Сервер идентификации не настроен, поэтому вы не можете добавить номер телефона, чтобы в будущем сбросить пароль."; -"auth_reset_password_error_is_required" = "Сервер идентификации не настроен: добавьте ID-Сервер в параметры сервера для сброса пароля."; +"auth_email_is_required" = "Сервер идентификации не настроен, поэтому вы не можете добавить адрес электронной почты, чтобы в будущем сбросить пароль учётной записи Matrix."; +"auth_phone_is_required" = "Сервер идентификации не настроен, поэтому вы не можете добавить номер телефона, чтобы в будущем сбросить пароль учётной записи Matrix."; +"auth_reset_password_error_is_required" = "Сервер идентификации не настроен: добавьте ID-Сервер в параметры сервера для сброса пароля учётной записи Matrix."; "room_accessiblity_scroll_to_bottom" = "Прокрутить вниз"; "room_accessibility_search" = "Поиск"; "room_accessibility_integrations" = "Интеграция"; @@ -2169,3 +2169,28 @@ "authentication_qr_login_start_title" = "Сканировать QR-код"; "authentication_terms_policy_url_error" = "Не получилось найти выбранные правила. Пожалуйста, попробуйте снова позже."; "threads_empty_tip" = "Подсказка: Нажмите на сообщение и используйте \"Поток\" что бы начать переписку."; +"threads_empty_info_my" = "Ответьте в текущую ветку, или нажмите на «Ветка», чтобы начать новую."; +"threads_empty_info_all" = "Ветки помогают придерживаться темы разговора и легко отслеживаются."; +"threads_empty_title" = "Организуйте свои обсуждения при помощи веток"; +"room_first_message_placeholder" = "Отправьте своё первое сообщение…"; +"room_participants_leave_processing" = "Выход"; +"authentication_qr_login_failure_retry" = "Попробовать снова"; +"authentication_qr_login_failure_request_timed_out" = "Привязка не была совершена за нужное время."; +"authentication_qr_login_failure_request_denied" = "Запрос был отклонён на другом устройстве."; +"authentication_qr_login_failure_invalid_qr" = "Недействительный 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" = "Разместите QR-код в квадрате снизу"; +"authentication_qr_login_scan_title" = "Сканировать QR-код"; +"authentication_qr_login_display_step2" = "Виберите «Войти при помощи QR-кода»"; +"authentication_qr_login_display_step1" = "Откройте Element на вашем другом устройстве"; +"authentication_qr_login_display_subtitle" = "Сканируйте QR-код снизу со своего устройства, с которого вы вышли."; +"authentication_qr_login_display_title" = "Привязать устройство"; +"authentication_qr_login_start_display_qr" = "Показать QR-код на этом устройстве"; +"authentication_qr_login_start_need_alternative" = "Нуждаетесь в альтернативном методе?"; +"authentication_qr_login_start_step4" = "Выберите «Показать QR-код на этом устройстве»"; From 5155d09a811248b96a470982f4d286ba3efc07c1 Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Sun, 4 Dec 2022 12:56:16 +0000 Subject: [PATCH 139/228] Translated using Weblate (Chinese (Simplified)) Currently translated at 80.8% (1889 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/zh_Hans/ --- Riot/Assets/zh_Hans.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index 307ccbc18..3ba778c8c 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -2215,3 +2215,4 @@ "authentication_forgot_password_input_message" = "%@将给你发一条验证链接"; "authentication_forgot_password_input_title" = "输入你的电子邮件"; "authentication_verify_email_waiting_button" = "重发电子邮件"; +"ignore_user" = "忽略用户"; From dea0dd017d2c06e5abf8b889201c80f1d71c05d5 Mon Sep 17 00:00:00 2001 From: Platon Terekhov Date: Sun, 4 Dec 2022 19:29:08 +0000 Subject: [PATCH 140/228] Translated using Weblate (Russian) Currently translated at 82.0% (1917 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/ --- Riot/Assets/ru.lproj/Vector.strings | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index 7e2f2b0c1..fa6cdf6af 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -2168,10 +2168,10 @@ "authentication_qr_login_start_subtitle" = "Используйте камеру на этом устройстве, чтобы сканировать QR-код, отображённый на вашем другом устройстве:"; "authentication_qr_login_start_title" = "Сканировать QR-код"; "authentication_terms_policy_url_error" = "Не получилось найти выбранные правила. Пожалуйста, попробуйте снова позже."; -"threads_empty_tip" = "Подсказка: Нажмите на сообщение и используйте \"Поток\" что бы начать переписку."; -"threads_empty_info_my" = "Ответьте в текущую ветку, или нажмите на «Ветка», чтобы начать новую."; -"threads_empty_info_all" = "Ветки помогают придерживаться темы разговора и легко отслеживаются."; -"threads_empty_title" = "Организуйте свои обсуждения при помощи веток"; +"threads_empty_tip" = "Подсказка: Нажмите на сообщение и используйте «Поток», чтобы начать переписку."; +"threads_empty_info_my" = "Ответьте в действующий поток, или нажмите на «Поток», чтобы начать новый."; +"threads_empty_info_all" = "Потоки помогают придерживаться темы разговора и легко отслеживаются."; +"threads_empty_title" = "Организуйте свои обсуждения при помощи потоков"; "room_first_message_placeholder" = "Отправьте своё первое сообщение…"; "room_participants_leave_processing" = "Выход"; "authentication_qr_login_failure_retry" = "Попробовать снова"; From 0afc1f2db1c3d03141dff06996c0f4c653955312 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Sun, 4 Dec 2022 01:57:06 +0000 Subject: [PATCH 141/228] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2335 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ --- Riot/Assets/pt_BR.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 0b8c35566..234af2399 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -2644,3 +2644,7 @@ // Unverified sessions "key_verification_alert_title" = "Você tem sessões não-verificadas"; +"voice_broadcast_stop_alert_agree_button" = "Sim, parar"; +"voice_broadcast_stop_alert_description" = "Tem certeza que você quer parar seu broadcast ao vivo? Isto vai terminar o broadcast, e a gravação completa vai estar disponível na sala."; +"voice_broadcast_stop_alert_title" = "Parar broadcasting ao vivo?"; +"voice_broadcast_buffering" = "Buffering…"; From a79adff13173bbbdc048c3e30ba463c961e026d0 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Fri, 2 Dec 2022 17:59:41 +0000 Subject: [PATCH 142/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2335 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 3cbfd58d7..1411ca5ae 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2869,3 +2869,6 @@ "launch_loading_server_syncing" = "Синхронізація з сервером"; "voice_broadcast_buffering" = "Буферизація..."; +"voice_broadcast_stop_alert_agree_button" = "Так, припинити"; +"voice_broadcast_stop_alert_description" = "Ви впевнені, що хочете припинити голосову трансляцію? На цьому трансляція завершиться, і повний запис буде доступний у кімнаті."; +"voice_broadcast_stop_alert_title" = "Припинити голосову трансляцію?"; From 2b530aa4449a9b6ac1845eedacec0bc8fcef1d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Sat, 3 Dec 2022 10:38:28 +0000 Subject: [PATCH 143/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2335 of 2335 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 674737d04..535abd998 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2616,3 +2616,6 @@ "launch_loading_server_syncing" = "Sünkroniseerimine serveriga"; "voice_broadcast_buffering" = "Andmed on puhverdamisel…"; +"voice_broadcast_stop_alert_agree_button" = "Jah, lõpetame"; +"voice_broadcast_stop_alert_description" = "Kas sa oled kindel, et soovid otseeetri lõpetada? Sellega ringhäälingukõne salvestamine lõppeb ja salvestis on kättesaadav kõigile jututoas."; +"voice_broadcast_stop_alert_title" = "Kas lõpetame otseeetri?"; From 37ec34d813bdc27e80a3560134a5c08c29ce6d11 Mon Sep 17 00:00:00 2001 From: Vri Date: Mon, 5 Dec 2022 16:49:02 +0000 Subject: [PATCH 144/228] Translated using Weblate (German) Currently translated at 100.0% (2338 of 2338 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 26b4de94c..e1b364475 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2681,3 +2681,8 @@ "voice_broadcast_stop_alert_agree_button" = "Ja, beende"; "voice_broadcast_stop_alert_description" = "Bist du sicher, dass du deine Live-Übertragung abbrechen möchtest? Dies wird die Übertragung beenden und die vollständige Aufnahme wird im Raum verfügbar sein."; "voice_broadcast_stop_alert_title" = "Live-Übertragung abbrechen?"; +"password_policy_weak_pwd_error" = "Dieses Passwort ist zu schwach. Es muss mindestens 8 Zeichen enthalten, davon mindestens ein Zeichen jeder Art: Großbuchstabe, Kleinbuchstabe, Ziffer und Sonderzeichen."; +"password_policy_pwd_in_dict_error" = "Dieses Passwort wurde in einem Wörterbuch gefunden und nicht erlaubt."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "Zu kurzes Passwort"; From 63d0529992d0e529efbd5cac7d2f3b7c70868f3d Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Mon, 5 Dec 2022 10:27:17 +0000 Subject: [PATCH 145/228] Translated using Weblate (Albanian) Currently translated at 99.6% (2330 of 2338 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 011244a7f..c899e6c28 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2659,3 +2659,8 @@ "voice_broadcast_stop_alert_agree_button" = "Po, ndaleni"; "voice_broadcast_stop_alert_description" = "Jeni i sigurt se doni të ndalet transmetimi juaj i drejtpërdrejtë? Kjo do të ndalë transmetimin dhe regjistrimi i plotë do të jetë i passhëm te dhoma."; "voice_broadcast_stop_alert_title" = "Të ndalet transmetimi i drejtpërdrejtë?"; +"password_policy_pwd_in_dict_error" = "Ky fjalëkalim gjendet në një fjalor dhe nuk lejohet."; +"password_policy_weak_pwd_error" = "Ky fjalëkalim është shumë i shkurtër. Duhet të përmbajë të paktën 8 shenja, me të paktën një shenjë nga çdo lloj: të mëdha, të vogla, shifra dhe shenja speciale."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "Fjalëkalim shumë i shkurtër"; From 4b95d4f9a6376a96f95bdb040973ea1c40c41775 Mon Sep 17 00:00:00 2001 From: random Date: Mon, 5 Dec 2022 13:04:56 +0000 Subject: [PATCH 146/228] Translated using Weblate (Italian) Currently translated at 100.0% (2338 of 2338 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 764853cfb..af1d16b02 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -2644,3 +2644,11 @@ // MARK: - Launch loading "launch_loading_server_syncing" = "Sincronizzazione con il server"; +"voice_broadcast_stop_alert_agree_button" = "Sì, ferma"; +"voice_broadcast_stop_alert_description" = "Vuoi davvero fermare la tua trasmissione in diretta? Verrà terminata la trasmissione e la registrazione completa sarà disponibile nella stanza."; +"voice_broadcast_stop_alert_title" = "Fermare la trasmissione in diretta?"; +"password_policy_pwd_in_dict_error" = "Questa password è stata trovata in un dizionario, perciò non è permessa."; +"password_policy_weak_pwd_error" = "Questa password è troppo debole. Deve contenere almeno 8 caratteri, con almeno un carattere di ogni tipo: maiuscole, minuscole, numeri e caratteri speciali."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "Password troppo corta"; From cf8d2a3a32ab8c668500febacc014a1d630c7f3f Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Mon, 5 Dec 2022 10:23:33 +0000 Subject: [PATCH 147/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2338 of 2338 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 1411ca5ae..6a24a1243 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2872,3 +2872,8 @@ "voice_broadcast_stop_alert_agree_button" = "Так, припинити"; "voice_broadcast_stop_alert_description" = "Ви впевнені, що хочете припинити голосову трансляцію? На цьому трансляція завершиться, і повний запис буде доступний у кімнаті."; "voice_broadcast_stop_alert_title" = "Припинити голосову трансляцію?"; +"password_policy_pwd_in_dict_error" = "Цей пароль знайдений у словнику і недопустимий."; +"password_policy_weak_pwd_error" = "Цей пароль занадто слабкий. Він повинен містити щонайменше 8 символів, причому хоча б по одному символу кожного типу: великі букви, малі букви, цифри та спеціальні символи."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "Пароль закороткий"; From d2924386fd1b76a6eb2993b32ea1c64fb9a00014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Mon, 5 Dec 2022 17:32:23 +0000 Subject: [PATCH 148/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2338 of 2338 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 535abd998..22cd956d2 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2619,3 +2619,8 @@ "voice_broadcast_stop_alert_agree_button" = "Jah, lõpetame"; "voice_broadcast_stop_alert_description" = "Kas sa oled kindel, et soovid otseeetri lõpetada? Sellega ringhäälingukõne salvestamine lõppeb ja salvestis on kättesaadav kõigile jututoas."; "voice_broadcast_stop_alert_title" = "Kas lõpetame otseeetri?"; +"password_policy_pwd_in_dict_error" = "See salasõna leidub levinud salasõnade sõnastikus ning seda sa ei saa kasutada."; +"password_policy_weak_pwd_error" = "See salasõna on liiga lihtne. Ta peaks olema vähemalt 8 tähemärki pikk ning seal peaks leiduma vähemalt üks väiketäht, suurtäht, number ja erimärk."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "Liiga lühike salasõna"; From 8fbff32b5630e99e2b28bbda2970aefce3f7f047 Mon Sep 17 00:00:00 2001 From: Linerly Date: Mon, 5 Dec 2022 13:42:51 +0000 Subject: [PATCH 149/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2338 of 2338 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index e2fb7b6d1..eb8d54e23 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2874,3 +2874,8 @@ "voice_broadcast_stop_alert_agree_button" = "Ya, batalkan"; "voice_broadcast_stop_alert_description" = "Apakah Anda ingin menghentikan siaran langsung Anda? Ini akan mengakhiri siarannya, dan rekamanan lengkap akan tersedia dalam ruangan."; "voice_broadcast_stop_alert_title" = "Berhenti menyiarkan langsung?"; +"password_policy_pwd_in_dict_error" = "Kata sandi ini telah ditemukan dalam sebuah kamus dan tidak diperbolehkan."; +"password_policy_weak_pwd_error" = "Kata sandi ini terlalu lemah. Kata sandi harus berisi setidaknya 8 karakter, dengan setidaknya satu karakter dari setiap jenis: huruf besar, huruf kecil, angka, dan karakter spesial."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "Kata sandi terlalu pendek"; From 7aa744923883aa6e9564aca9fabdd563699b8f32 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Mon, 5 Dec 2022 13:47:09 +0000 Subject: [PATCH 150/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2338 of 2338 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 4256de738..1030d150f 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2870,3 +2870,8 @@ "voice_broadcast_stop_alert_agree_button" = "Áno, zastaviť"; "voice_broadcast_stop_alert_description" = "Určite chcete zastaviť vysielanie naživo? Tým sa vysielanie ukončí a v miestnosti bude k dispozícii celý záznam."; "voice_broadcast_stop_alert_title" = "Zastaviť vysielanie naživo?"; +"password_policy_pwd_in_dict_error" = "Toto heslo bolo nájdené v slovníku a nie je povolené."; +"password_policy_weak_pwd_error" = "Toto heslo je príliš slabé. Musí obsahovať aspoň 8 znakov, pričom musí obsahovať aspoň jeden znak z každého typu: veľké a malé písmená, číslice a špeciálny symbol."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "Príliš krátke heslo"; From 032542248aee7cc71fe94c1e858d12657c79caba Mon Sep 17 00:00:00 2001 From: Vri Date: Tue, 6 Dec 2022 15:47:38 +0000 Subject: [PATCH 151/228] Translated using Weblate (German) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index e1b364475..56f698361 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2675,7 +2675,7 @@ // MARK: - Launch loading "launch_loading_server_syncing" = "Synchronisiere mit dem Server"; -"user_other_session_permanently_unverified_additional_info" = "Diese Sitzung kann nicht verifiziert werden, da sie keine Verschlüsselung unterstützt."; +"user_other_session_permanently_unverified_additional_info" = "Diese Sitzung unterstützt keine Verschlüsselung und kann deshalb nicht verifiziert werden."; "voice_broadcast_time_left" = "%@ übrig"; "voice_broadcast_buffering" = "Puffere …"; "voice_broadcast_stop_alert_agree_button" = "Ja, beende"; @@ -2686,3 +2686,4 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Zu kurzes Passwort"; +"user_session_permanently_unverified_session_description" = "Diese Sitzung unterstützt keine Verschlüsselung, weshalb sie nicht verifiziert werden kann.\n\nDu wirst dich mit dieser Sitzung nicht an Unterhaltungen in Räumen mit aktivierter Verschlüsselung beteiligen können.\n\nAus Sicherheits- und Datenschutzgründen, wird die Nutzung von verschlüsselungsfähigen Matrix-Anwendungen empfohlen."; From 97bb2d8569e9e0989ff68bfe65d909a075cfacbe Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 6 Dec 2022 20:21:32 +0000 Subject: [PATCH 152/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 6a24a1243..248038408 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2860,7 +2860,7 @@ // Unverified sessions "key_verification_alert_title" = "У вас є не звірені сеанси"; -"user_other_session_permanently_unverified_additional_info" = "Цей сеанс не можна звірити, оскільки він не підтримує шифрування."; +"user_other_session_permanently_unverified_additional_info" = "Цей сеанс не підтримує шифрування, і його не можна звірити."; "voice_broadcast_time_left" = "Залишилося %@"; "launch_loading_processing_response" = "Обробка даних\n%@ %%"; "launch_loading_server_syncing_nth_attempt" = "Синхронізація з сервером\n(%@ спроба)"; @@ -2877,3 +2877,4 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Пароль закороткий"; +"user_session_permanently_unverified_session_description" = "Цей сеанс не підтримує шифрування, тому його неможливо звірити.\n\nПід час користування цим сеансом ви не зможете брати участь у кімнатах, в яких увімкнено шифрування.\n\nДля найкращої безпеки та приватності радимо користуватися клієнтами Matrix, які підтримують шифрування."; From c3f7f4a0db82a264516ad1288d19fc0d6cfbf6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 6 Dec 2022 17:50:00 +0000 Subject: [PATCH 153/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 22cd956d2..4faa12062 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2624,3 +2624,4 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Liiga lühike salasõna"; +"user_session_permanently_unverified_session_description" = "Seda sessiooni ei saa verifitseerida, sest seal puudub krüptimise tugi.\n\nSelle sessiooniga ei saa sa osaleda krüptitud jututubades.\n\nParima turvalisuse ja privaatsuse nimel palun kasuta selliseid Matrix'i kliente, mis toetavad krüptimist."; From 929668bea98fc069288e9fec8062976888c1133c Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 6 Dec 2022 22:43:35 +0000 Subject: [PATCH 154/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index eb8d54e23..f12e221df 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2862,7 +2862,7 @@ // Unverified sessions "key_verification_alert_title" = "Anda punya sesi yang belum diverifikasi"; -"user_other_session_permanently_unverified_additional_info" = "Sesi ini tidak dapat diverifikasi karena sesinya tidak mendukung enkripsi."; +"user_other_session_permanently_unverified_additional_info" = "Sesi ini tidak mendukung enkripsi jadi tidak dapat diverifikasi."; "voice_broadcast_time_left" = "Tersisa %@"; "launch_loading_processing_response" = "Memroses data\n%@ %%"; "launch_loading_server_syncing_nth_attempt" = "Menyinkron dengan server\n(%@ percobaan)"; @@ -2879,3 +2879,4 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Kata sandi terlalu pendek"; +"user_session_permanently_unverified_session_description" = "Sesi ini tidak mendukung enkripsi, sehingga tidak dapat diverifikasi.\n\nAnda tidak akan dapat berpartisipasi dalan ruangan di mana enkripsi diaktifkan saat menggunakan sesi ini.\n\nUntuk keamanan dan privasi terbaik, disarankan untuk menggunakan klien Matrix yang mendukung enkripsi."; From bdc233b893d6a95df4bdc1a2c172a035b78274d5 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Tue, 6 Dec 2022 19:13:57 +0000 Subject: [PATCH 155/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 1030d150f..9c12be5c4 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2858,7 +2858,7 @@ // Unverified sessions "key_verification_alert_title" = "Máte neoverené relácie"; -"user_other_session_permanently_unverified_additional_info" = "Túto reláciu nemožno overiť, pretože nepodporuje šifrovanie."; +"user_other_session_permanently_unverified_additional_info" = "Táto relácia nepodporuje šifrovanie, a preto ju nemožno overiť."; "voice_broadcast_time_left" = "%@ ostáva"; "launch_loading_processing_response" = "Spracovanie údajov\n%@ %%"; "launch_loading_server_syncing_nth_attempt" = "Synchronizácia so serverom\n(%@ pokus)"; @@ -2875,3 +2875,4 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Príliš krátke heslo"; +"user_session_permanently_unverified_session_description" = "Táto relácia nepodporuje šifrovanie, takže ju nemožno overiť.\n\nPri používaní tejto relácie sa nebudete môcť zúčastňovať konverzácií v miestnostiach, kde je zapnuté šifrovanie.\n\nNa dosiahnutie čo najlepšieho zabezpečenia a súkromia sa odporúča používať Matrix klientov, ktoré podporujú šifrovanie."; From a0e5da4118538790850871640c0b7e49ba7e242d Mon Sep 17 00:00:00 2001 From: phardyle Date: Sun, 11 Dec 2022 06:47:05 +0000 Subject: [PATCH 156/228] Translated using Weblate (Chinese (Simplified)) Currently translated at 81.4% (1904 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/zh_Hans/ --- Riot/Assets/zh_Hans.lproj/Vector.strings | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index 3ba778c8c..d7a775d4b 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -20,7 +20,7 @@ "cancel" = "取消"; "save" = "保存"; "join" = "加入"; -"decline" = "取消"; +"decline" = "拒绝"; "accept" = "接受"; "preview" = "预览"; "camera" = "摄像头"; @@ -522,7 +522,7 @@ "room_resource_usage_limit_reached_message_contact_3" = " 以提高限制。"; // String for App Store "store_short_description" = "安全、去中心化的聊天及 VoIP 应用"; -"store_full_description" = "Element 是一种新型的通讯与协作应用:\n\n1. 使您可以掌控您的隐私\n2. 使您与 Matrix 网络中的任何人交流,甚至可以通过集成功能与如 Slack 之类的其他应用通讯\n3. 保护您免受广告,大数据挖掘和封闭服务的侵害\n4. 通过端到端加密保证安全,通过交叉签名验证其他人\n\nElement 与其他通讯与协作应用完全不同,因为它是去中心化且开源的。\n\nElement 允许您自托管——或者选择托管商——因此,您能拥有数据和会话的隐私权,所有权和控制权。它允许您访问开放网络;因此,您可以与 Element 用户以外的人交流。并且它非常安全。\n\nElement 之所以可以做到这些,是因为它在 Matrix 上运行——开放,去中心化通讯的标准。\n\n通过让您选择由谁来托管您的会话,Element 让您掌控一切。在 Element 应用中,您可以选择不同的托管方式:\n\n1. 在由 Matrix 开发者托管的 matrix.org 公共服务器上获取免费账户,或从志愿者托管的上千个公共服务器中选择\n2. 在您自己的硬件上运行服务器,自托管您的会话\n3. 通过订阅 Element Matrix Services 托管平台,简单地在自定义服务器上注册账户\n\n为什么选择 Element?\n\n掌控您的数据:您来决定存放您的数据和消息的位置。拥有并控制它的是您,而不是挖掘您的数据或与第三方分享的巨型企业。\n\n开放通讯与协作:您可以与 Matrix 网络中的任何人聊天,不论他们使用 Element 还是其他 Matrix 应用,甚至/即使他们在使用不同的通讯系统,例如 Slack,IRC 或 XMPP。\n\n超级安全:支持真正的端到端加密(仅有会话中的人可以解密消息),还有能够验证会话参与方的设备的交叉签名。\n\n完善的通讯方式:消息,语音和视频通话,文件共享,屏幕共享和大量集成功能,机器人和挂件。建立房间与社区,保持联系并完成工作。\n\n随时随地:消息历史可在您的全部设备和 https://app.element.io 网页端之间完全同步,无论您在哪里,都可以保持联系。"; +"store_full_description" = "Element 是一种新型的通讯与协作应用:\n\n1. 使您可以掌控您的隐私\n2. 使您与 Matrix 网络中的任何人交流,甚至可以通过集成功能与如 Slack 之类的其他应用通讯\n3. 保护您免受广告,大数据挖掘和封闭服务的侵害\n4. 通过端到端加密保证安全,通过交叉签名验证其他人\n\nElement 与其他通讯与协作应用完全不同,因为它是去中心化且开源的。\n\nElement 允许您自托管——或者选择托管商——因此,您能拥有数据和会话的隐私权,所有权和控制权。它允许您访问开放网络;因此,您可以与 Element 用户以外的人交流。并且它非常安全。\n\nElement 之所以可以做到这些,是因为它在 Matrix 上运行——开放,去中心化通讯的标准。\n\n通过让您选择由谁来托管您的会话,Element 让您掌控一切。在 Element 应用中,您可以选择不同的托管方式:\n\n1. 在由 Matrix 开发者托管的 matrix.org 公共服务器上获取免费账户,或从志愿者托管的上千个公共服务器中选择\n2. 在您自己的硬件上运行服务器,自托管您的会话\n3. 通过订阅 Element Matrix Services 托管平台,简单地在自定义服务器上注册账户\n\n为什么选择 Element?\n\n掌控您的数据:您来决定存放您的数据和消息的位置。拥有并控制它的是您,而不是挖掘您的数据或与第三方分享的巨型企业。\n\n开放通讯与协作:您可以与 Matrix 网络中的任何人聊天,不论他们使用 Element 还是其他 Matrix 应用,甚至/即使他们在使用不同的通讯系统,例如 Slack、IRC 或 XMPP。\n\n超级安全:支持真正的端到端加密(仅有会话中的人可以解密消息),还有能够验证会话参与方的设备的交叉签名。\n\n完善的通讯方式:消息,语音和视频通话,文件共享,屏幕共享和大量集成功能,机器人和挂件。建立房间与社区,保持联系并完成工作。\n\n随时随地:消息历史可在您的全部设备和 https://app.element.io 网页端之间完全同步,无论您在哪里,都可以保持联系。"; "auth_accept_policies" = "请查看并接受此主页服务器的服务条款:"; "room_replacement_information" = "这个房间已被替换,不再有效。"; "settings_flair" = "在允许的地方显示个性徽章"; @@ -1090,7 +1090,7 @@ "more" = "更多"; "switch" = "开关"; "joined" = "已加入"; -"store_promotional_text" = "在开放网络上保护隐私的聊天和协作应用程序。分散权力让你掌控一切。没有数据挖掘,没有后门,也没有第三方访问。"; +"store_promotional_text" = "在开放网络上保护隐私的聊天和协作应用程序。去中心化让你掌控一切。没有数据挖掘,没有后门,也没有第三方访问。"; "social_login_button_title_sign_up" = "使用 %@ 注册"; "social_login_button_title_sign_in" = "使用 %@ 登录"; "social_login_button_title_continue" = "使用 %@ 继续"; @@ -2216,3 +2216,18 @@ "authentication_forgot_password_input_title" = "输入你的电子邮件"; "authentication_verify_email_waiting_button" = "重发电子邮件"; "ignore_user" = "忽略用户"; +"authentication_qr_login_start_need_alternative" = "需要替代方法?"; +"authentication_qr_login_start_step4" = "选择“在此设备显示QR码”"; +"authentication_qr_login_start_display_qr" = "在此设备显示QR码"; +"user_sessions_overview_link_device" = "关联设备"; +"authentication_qr_login_display_title" = "关联设备"; +"authentication_qr_login_start_step3" = "选择“关联设备”"; +"authentication_qr_login_start_step2" = "前往设置->安全与隐私"; +"authentication_qr_login_start_step1" = "打开你的其他设备上的Element"; +"authentication_qr_login_start_subtitle" = "用此设备的相机扫描显示在你的其他设备上的QR码:"; +"authentication_qr_login_start_title" = "扫描QR码"; +"authentication_choose_password_signout_all_devices" = "登出全部设备"; +"authentication_login_with_qr" = "用QR码登录"; +"onboarding_congratulations_home_button" = "带我到主页"; +"onboarding_use_case_message" = "我们将帮助你连接"; +"invite_to" = "邀请到%@"; From d11c9bcaf77f65f7e44603612548cb0d8f727eb7 Mon Sep 17 00:00:00 2001 From: random Date: Sat, 10 Dec 2022 17:35:28 +0000 Subject: [PATCH 157/228] Translated using Weblate (Italian) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index af1d16b02..56b72a2e7 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -2635,7 +2635,7 @@ // Unverified sessions "key_verification_alert_title" = "Hai sessioni non verificate"; -"user_other_session_permanently_unverified_additional_info" = "Questa sessione non può essere verificata perché non supporta la crittografia."; +"user_other_session_permanently_unverified_additional_info" = "Questa sessione non supporta la crittografia, perciò non può essere verificata."; "voice_broadcast_buffering" = "Buffer..."; "voice_broadcast_time_left" = "%@ rimasti"; "launch_loading_processing_response" = "Elaborazione dati\n%@ %%"; @@ -2652,3 +2652,4 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Password troppo corta"; +"user_session_permanently_unverified_session_description" = "Questa sessione non supporta la crittografia, perciò non può essere verificata.\n\nNon potrai partecipare in stanze dove la crittografia è attiva mentre usi questa sessione.\n\nPer maggiore sicurezza e privacy, è consigliabile usare i client di Matrix che supportano la crittografia."; From 9b380a57ac51440d320afdf999840a0f7963d858 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Mon, 12 Dec 2022 13:52:16 +0000 Subject: [PATCH 158/228] Translated using Weblate (Hungarian) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index f1f4e16ab..ebb84de9f 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -2655,3 +2655,21 @@ "voice_broadcast_tile" = "Hang közvetítés"; "voice_broadcast_live" = "Élő"; "key_verification_alert_body" = "Tekintsd át, hogy meggyőződj arról, hogy a fiókod biztonságban van."; +"user_session_permanently_unverified_session_description" = "Ez a munkamenet nem támogatja a titkosítást, így nem lehet ellenőrizni sem.\n\nEzzel a munkamenettel nem tudsz részt venni olyan szobákban ahol a titkosítás be van kapcsolva.\n\nA biztonság és a adatbiztonsági okokból javasolt olyan Matrix kliens használata ami támogatja a titkosítást."; +"user_other_session_permanently_unverified_additional_info" = "Ez a munkamenet nem támogatja a titkosítást, így nem lehet ellenőrizni sem."; +"voice_broadcast_stop_alert_agree_button" = "Igen, befejez"; +"voice_broadcast_stop_alert_description" = "Biztos, hogy befejezed az élő közvetítést? Ez befejezi a közvetítést és a felvétel az egész szoba számára elérhető lesz."; +"voice_broadcast_stop_alert_title" = "Megszakítod az élő közvetítést?"; +"voice_broadcast_buffering" = "Pufferelés…"; +"voice_broadcast_time_left" = "%@ van vissza"; +"launch_loading_processing_response" = "Adat feldolgozása\n%@ %%"; +"launch_loading_server_syncing_nth_attempt" = "Szinkronizálás a szerverrel\n(%@ próbálkozás)"; + +// MARK: - Launch loading + +"launch_loading_server_syncing" = "Szinkronizálás a szerverrel"; +"password_policy_pwd_in_dict_error" = "Ez a jelszó megtalálható a szótárban ezért nem engedélyezett."; +"password_policy_weak_pwd_error" = "Ez a jelszó túl gyenge. Legalább 8 karakternek kell lennie és minden típusból legalább egy: nagybetű, kisbetű, szám és speciális karakter."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "A jelszó túl rövid"; From f4a86d604c7e07114a5f096a4b65791fea937aad Mon Sep 17 00:00:00 2001 From: Linerly Date: Mon, 12 Dec 2022 11:35:33 +0000 Subject: [PATCH 159/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index f12e221df..4c7182fac 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -1754,7 +1754,7 @@ "home_context_menu_make_room" = "Pindah ke Ruangan"; "home_context_menu_make_dm" = "Pindah ke Orang"; "event_formatter_message_deleted" = "Pesan dihapus"; -"settings_labs_enable_threads" = "Perpesanan utasan"; +"settings_labs_enable_threads" = "Utasan pesan"; "message_from_a_thread" = "Dari sebuah utasan"; "threads_empty_show_all_threads" = "Tampilkan semua utasan"; "threads_empty_tip" = "Tip: Ketuk pada sebuah pesan dan gunakan “Utasan” untuk memulai yang baru."; From fdc20353acf7be9b9d782241723021be792eaf0b Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 13 Dec 2022 08:54:02 +0000 Subject: [PATCH 160/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 4c7182fac..9b75cc2b8 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2205,8 +2205,8 @@ "room_no_power_to_create_conference_call" = "Anda membutuhkan izin untuk mengundang untuk memulai konferensi di ruangan ini"; "room_left_for_dm" = "Anda keluar"; "room_left" = "Anda meninggalkan ruangan ini"; -"room_error_timeline_event_not_found" = "Aplikasi ini sedang mencoba untuk memuat titik tertenu di linimasa ruangan ini tetapi tidak dapat menemukannya"; -"room_error_timeline_event_not_found_title" = "Gagal untuk memuat posisi linimasa"; +"room_error_timeline_event_not_found" = "Aplikasi ini sedang mencoba untuk memuat titik tertentu di lini masa ruangan ini tetapi tidak dapat menemukannya"; +"room_error_timeline_event_not_found_title" = "Gagal memuat posisi lini masa"; "room_error_cannot_load_timeline" = "Gagal untuk memuat linimasa"; "room_error_topic_edition_not_authorized" = "Anda tidak diizinkan untuk mengubah topik ruangan ini"; "room_error_name_edition_not_authorized" = "Anda tidak diizinkan untuk mengubah nama ruangan ini"; From 2d40025bec27feddd0737c0d4c84f9db8288f54e Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Wed, 14 Dec 2022 00:13:42 +0000 Subject: [PATCH 161/228] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2339 of 2339 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ --- Riot/Assets/pt_BR.lproj/Vector.strings | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 234af2399..6bc50fcbe 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -1604,7 +1604,7 @@ "home_context_menu_make_room" = "Mover para Salas"; "home_context_menu_make_dm" = "Mover para Pessoas"; "event_formatter_message_deleted" = "Mensagem deletada"; -"settings_labs_enable_threads" = "Mensageria com threads"; +"settings_labs_enable_threads" = "Mensagens com threads"; "message_from_a_thread" = "De uma thread"; "threads_empty_show_all_threads" = "Mostrar todas as threads"; "threads_empty_tip" = "Dica: Toque numa mensagem e use “Thread” para começar uma."; @@ -2632,7 +2632,7 @@ "user_session_verified_session_description" = "Sessões verificadas são onde quer que você esteja usando Element depois de entrar sua frasepasse ou confirmar sua identidade com uma outra sessões verificada.\n\nIsto significa que você tem todas as chaves necessárias para destrancar suas mensagens encriptadas e confirmar a outras(os) usuárias(os) que você confia nesta sessão."; "user_session_verified_session_title" = "Sessões verificadas"; "user_session_got_it" = "Entendido"; -"user_other_session_permanently_unverified_additional_info" = "Esta sessão não pode ser verificada porque ela não suporta encriptação."; +"user_other_session_permanently_unverified_additional_info" = "Esta sessão não suporta encriptação e assim não pode ser verificada."; "voice_broadcast_time_left" = "%@ restando"; "launch_loading_processing_response" = "Processando dados\n%@ %%"; "launch_loading_server_syncing_nth_attempt" = "Sincando com o servidor\n(%@ tentativa)"; @@ -2648,3 +2648,9 @@ "voice_broadcast_stop_alert_description" = "Tem certeza que você quer parar seu broadcast ao vivo? Isto vai terminar o broadcast, e a gravação completa vai estar disponível na sala."; "voice_broadcast_stop_alert_title" = "Parar broadcasting ao vivo?"; "voice_broadcast_buffering" = "Buffering…"; +"user_session_permanently_unverified_session_description" = "Esta sessão não suporta encriptação, então ela não pode ser verificada.\n\nVocê não vai ser capaz de participar em salas onde encriptação é habilitada quando usando esta sessão.\n\nPara a melhor segurança e privacidade, é recomendado usar clientes Matrix que suportam encriptação."; +"password_policy_pwd_in_dict_error" = "Esta senha tem sido encontrada em um dicionário, e não é permitida."; +"password_policy_weak_pwd_error" = "Esta senha é fraca demais. Ela deve conter ao menos 8 caracteres, com ao menos um caractere de cada tipo: caractere maiúsculo, minúsculo, dígito, e especial."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "Senha curta demais"; From 07b153584a946da607e8faed5246d97b43b84399 Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 14 Dec 2022 12:39:36 +0000 Subject: [PATCH 162/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2344 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 9b75cc2b8..40818181f 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2880,3 +2880,10 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Kata sandi terlalu pendek"; "user_session_permanently_unverified_session_description" = "Sesi ini tidak mendukung enkripsi, sehingga tidak dapat diverifikasi.\n\nAnda tidak akan dapat berpartisipasi dalan ruangan di mana enkripsi diaktifkan saat menggunakan sesi ini.\n\nUntuk keamanan dan privasi terbaik, disarankan untuk menggunakan klien Matrix yang mendukung enkripsi."; +"wysiwyg_composer_link_action_edit_title" = "Suntiing tautan"; +"wysiwyg_composer_link_action_create_title" = "Buat sebuah tautan"; +"wysiwyg_composer_link_action_link" = "Tautan"; + +// Links +"wysiwyg_composer_link_action_text" = "Teks"; +"wysiwyg_composer_format_action_link" = "Terapkan format tautan"; From bedfda83572e8c8a386915454c98ec8e63f195e4 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Wed, 14 Dec 2022 13:45:53 +0000 Subject: [PATCH 163/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2344 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 9c12be5c4..b23ed9d1a 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2876,3 +2876,10 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Príliš krátke heslo"; "user_session_permanently_unverified_session_description" = "Táto relácia nepodporuje šifrovanie, takže ju nemožno overiť.\n\nPri používaní tejto relácie sa nebudete môcť zúčastňovať konverzácií v miestnostiach, kde je zapnuté šifrovanie.\n\nNa dosiahnutie čo najlepšieho zabezpečenia a súkromia sa odporúča používať Matrix klientov, ktoré podporujú šifrovanie."; +"wysiwyg_composer_link_action_edit_title" = "Upraviť odkaz"; +"wysiwyg_composer_link_action_create_title" = "Vytvoriť odkaz"; +"wysiwyg_composer_link_action_link" = "Odkaz"; + +// Links +"wysiwyg_composer_link_action_text" = "Text"; +"wysiwyg_composer_format_action_link" = "Použiť formát odkazu"; From 2f68c90f54986c462ccaf96a427acbbcf406d258 Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 14 Dec 2022 17:50:32 +0000 Subject: [PATCH 164/228] Translated using Weblate (German) Currently translated at 99.9% (2343 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 56f698361..a16d530dd 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2687,3 +2687,10 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Zu kurzes Passwort"; "user_session_permanently_unverified_session_description" = "Diese Sitzung unterstützt keine Verschlüsselung, weshalb sie nicht verifiziert werden kann.\n\nDu wirst dich mit dieser Sitzung nicht an Unterhaltungen in Räumen mit aktivierter Verschlüsselung beteiligen können.\n\nAus Sicherheits- und Datenschutzgründen, wird die Nutzung von verschlüsselungsfähigen Matrix-Anwendungen empfohlen."; + +// Links +"wysiwyg_composer_link_action_text" = "Text"; +"wysiwyg_composer_format_action_link" = "Als Link formatieren"; +"wysiwyg_composer_link_action_edit_title" = "Link bearbeiten"; +"wysiwyg_composer_link_action_create_title" = "Link erstellen"; +"wysiwyg_composer_link_action_link" = "Link"; From e01de7725b97b5dd4b2db52bfdb38bde6788f21d Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 15 Dec 2022 07:21:11 +0000 Subject: [PATCH 165/228] Translated using Weblate (Hungarian) Currently translated at 100.0% (2344 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index ebb84de9f..76cf8859e 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -2673,3 +2673,10 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "A jelszó túl rövid"; +"wysiwyg_composer_link_action_edit_title" = "Hivatkozás szerkesztése"; +"wysiwyg_composer_link_action_create_title" = "Hivatkozás készítése"; +"wysiwyg_composer_link_action_link" = "Hivatkozás"; + +// Links +"wysiwyg_composer_link_action_text" = "Szöveg"; +"wysiwyg_composer_format_action_link" = "Hivatkozás"; From e386f2a6ef879cdc7b5b844a1881269be06d4fad Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Wed, 14 Dec 2022 20:54:52 +0000 Subject: [PATCH 166/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2344 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 248038408..adff521e2 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2878,3 +2878,10 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Пароль закороткий"; "user_session_permanently_unverified_session_description" = "Цей сеанс не підтримує шифрування, тому його неможливо звірити.\n\nПід час користування цим сеансом ви не зможете брати участь у кімнатах, в яких увімкнено шифрування.\n\nДля найкращої безпеки та приватності радимо користуватися клієнтами Matrix, які підтримують шифрування."; +"wysiwyg_composer_link_action_edit_title" = "Змінити посилання"; +"wysiwyg_composer_link_action_create_title" = "Створити посилання"; +"wysiwyg_composer_link_action_link" = "Посилання"; + +// Links +"wysiwyg_composer_link_action_text" = "Текст"; +"wysiwyg_composer_format_action_link" = "Застосувати формат посилання"; From ed4e72700986b9c6705921df31df898211f51f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 14 Dec 2022 22:39:28 +0000 Subject: [PATCH 167/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2344 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 4faa12062..3849de6d6 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2625,3 +2625,10 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Liiga lühike salasõna"; "user_session_permanently_unverified_session_description" = "Seda sessiooni ei saa verifitseerida, sest seal puudub krüptimise tugi.\n\nSelle sessiooniga ei saa sa osaleda krüptitud jututubades.\n\nParima turvalisuse ja privaatsuse nimel palun kasuta selliseid Matrix'i kliente, mis toetavad krüptimist."; +"wysiwyg_composer_format_action_link" = "Muuda lingi vormingut"; + +// Links +"wysiwyg_composer_link_action_text" = "Tekst"; +"wysiwyg_composer_link_action_link" = "Link"; +"wysiwyg_composer_link_action_edit_title" = "Muuda linki"; +"wysiwyg_composer_link_action_create_title" = "Loo link"; From be14c8256f48032e7559148487f52e860e0fd4fd Mon Sep 17 00:00:00 2001 From: Linerly Date: Thu, 15 Dec 2022 11:11:12 +0000 Subject: [PATCH 168/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2344 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 40818181f..9f6cf0939 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -792,7 +792,7 @@ "bug_crash_report_description" = "Jelaskan apa yang Anda lakukan sebelum crashnya:"; "directory_server_type_homeserver" = "Ketik sebuah homeserver untuk menampilkan daftar ruangan publik"; "room_details_access_section_anyone_apart_from_guest" = "Siapa saja yang tahu tautan ruangan, selain dari tamu"; -"security_settings_complete_security_alert_message" = "Anda seharusnya menyelesaikan keamanan di sesi Anda saat ini dulu."; +"security_settings_complete_security_alert_message" = "Anda seharusnya menyelesaikan keamanan di sesi Anda saat ini dahulu."; "key_verification_scan_confirmation_scanned_device_information" = "Apakah perangkat yang lain menampilkan perisai yang sama?"; "key_verification_verify_qr_code_information" = "Pindai kodenya untuk memverifikasi dengan sesama dengan aman."; "error_not_supported_on_mobile" = "Anda tidak melakukannya dari %@ mobile."; @@ -848,7 +848,7 @@ "key_verification_scan_confirmation_scanned_user_information" = "Apakah %@ menampilkan perisai yang sama?"; "key_verification_verify_qr_code_scan_other_code_success_message" = "Kode QR berhasil divalidasi."; "key_verification_verify_qr_code_information_other_device" = "Pindai kode di bawah untuk memverifikasi:"; -"key_verification_bootstrap_not_setup_message" = "Anda harus mem-bootstrap penandatanganan-silang dulu."; +"key_verification_bootstrap_not_setup_message" = "Anda harus mem-bootstrap penandatanganan silang dahulu."; "device_verification_self_verify_wait_recover_secrets_checking_availability" = "Memeriksa untuk kemampuan verifikasi lain ..."; "key_verification_self_verify_current_session_alert_message" = "Pengguna yang lain mungkin tidak mempercayainya."; "device_verification_cancelled" = "Pengguna yang lain membatalkan verifikasinya."; @@ -1549,7 +1549,7 @@ "settings_discovery_three_pids_management_information_part1" = "Kelola alamat email atau nomor telepon apa saja yang pengguna lain dapat menggunakan untuk menemukan Anda dan menggunakannya untuk mengundang Anda ke ruangan. Tambahkan atau hapus alamat email atau nomor telepon dari daftar ini di "; "room_preview_unlinked_email_warning" = "Undangan ini telah dikirim ke %@, yang tidak diasosiasikan dengan akun ini. Anda mungkin ingin masuk ke akun yang lain, atau tambahkan email ini ke akun Anda."; "unknown_devices_alert" = "Ruangan ini berisi sesi tidak dikenal yang belum diverifikasi.\nIni berarti tidak ada jaminan bahwa sesi tersebut adalah milik pengguna yang mereka klaim.\nKami menyarankan Anda memverifikasinya untuk setiap sesi sebelum melanjutkan, tetapi Anda dapat mengirim ulang pesan tanpa memverifikasi jika Anda ingin."; -"room_warning_about_encryption" = "Enkripsi ujung ke ujung masih dalam beta dan mungkin tidak dapat diandalkan.\n\nAnda seharusnya tidak mempercayainya dulu untuk mengamankan data.\n\nPerangkat masih belum dapat mendekripsi riwayat sebelum mereka bergabung ke ruangannya.\n\nPesan terenkripsi masih belum terlihat di klien yang belum mengimplementasikan enkripsi."; +"room_warning_about_encryption" = "Enkripsi ujung ke ujung masih dalam beta dan mungkin tidak dapat diandalkan.\n\nAnda seharusnya tidak mempercayainya dahulu untuk mengamankan data.\n\nPerangkat masih belum dapat mendekripsi riwayat sebelum mereka bergabung ke ruangannya.\n\nPesan terenkripsi masih belum terlihat di klien yang belum mengimplementasikan enkripsi."; "auth_add_email_and_phone_warning" = "Pendaftaran dengan email dan nomor telepon sekaligus belum didukung sampai API-nya sudah ada. Hanya nomor telepon yang akan diperhitungkan. Anda dapat menambahkan email Anda di profil Anda di pengaturan."; "auth_reset_password_success_message" = "Kata sandi akun Matrix Anda telah diatur ulang.\n\nAnda telah dikeluarkan dari semua sesi dan tidak akan menerima lagi notifikasi push. Untuk mengaktifkan ulang notifikasi, masuk ulang di setiap perangkat."; "spaces_add_rooms_coming_soon_title" = "Penambahan ruangan akan segera datang"; From 11bfd9142030cb9a4e09e17161a463a4d115780a Mon Sep 17 00:00:00 2001 From: Toomore Chiang Date: Fri, 16 Dec 2022 05:39:37 +0000 Subject: [PATCH 169/228] Translated using Weblate (Chinese (Traditional)) Currently translated at 43.0% (1009 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/zh_Hant/ --- Riot/Assets/zh_Hant.lproj/Vector.strings | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Riot/Assets/zh_Hant.lproj/Vector.strings b/Riot/Assets/zh_Hant.lproj/Vector.strings index 28ec3da61..64bd9c7a1 100644 --- a/Riot/Assets/zh_Hant.lproj/Vector.strings +++ b/Riot/Assets/zh_Hant.lproj/Vector.strings @@ -88,7 +88,7 @@ "bug_report_send" = "傳送"; "room_event_action_resend" = "重新傳送"; "room_event_action_view_source" = "檢視來源"; -"room_event_action_permalink" = "永久連結"; +"room_event_action_permalink" = "複製訊息永久連結"; "room_event_action_quote" = "引用"; "room_participants_online" = "線上"; "room_details_favourite_tag" = "我的最愛"; @@ -164,9 +164,9 @@ "e2e_room_key_request_title" = "加密金鑰請求"; "e2e_room_key_request_share_without_verifying" = "不驗證就分享"; "e2e_room_key_request_ignore_request" = "忽略請求"; -"auth_reset_password_message" = "要重設您的密碼,輸入連結到您的帳號的電子郵件地址:"; +"auth_reset_password_message" = "為了重設密碼,請輸入您的電子郵件地址:"; "auth_reset_password_email_validation_message" = "電子郵件已傳送至 %@。您必須跟隨其中包含了連結,點按下面的連結。"; -"auth_reset_password_success_message" = "您的密碼已重設。\n\n您已在所有工作階段上登出,並且不會再收到推送通知。要重新啟用通知,再次於每個裝置上登入。"; +"auth_reset_password_success_message" = "您的密碼已重設。\n\n您已登出所有工作階段,並且不會再收到推送通知。如要重新啟用通知,請於每個裝置重新登入。"; "auth_add_email_and_phone_warning" = "直到 API 存在之前,尚不支援同時使用電子郵件地址和電話號碼註冊,因此只有電話號碼會被採用,但您可以在基本資料中新增電子郵件地址。"; // Chat creation "room_creation_title" = "新的聊天"; @@ -601,10 +601,10 @@ "auth_softlogout_signed_out" = "你已登出"; "auth_autodiscover_invalid_response" = "無效的自家伺服器發現回應"; "auth_accept_policies" = "請查看並接受此自家伺服器的策略:"; -"auth_reset_password_error_is_required" = "未配置身份伺服器:在伺服器選項中添加一個以便日後重置密碼。"; -"auth_forgot_password_error_no_configured_identity_server" = "未配置身份伺服器:添加一台以便重置密碼。"; -"auth_phone_is_required" = "沒有配置身份伺服器,因此您無法添加電話號碼以便將來重設密碼。"; -"auth_email_is_required" = "沒有配置身份伺服器,因此您無法添加電子郵件地址以便將來重設密碼。"; +"auth_reset_password_error_is_required" = "未設置身份伺服器:在伺服器選項中新增一個來重置密碼。"; +"auth_forgot_password_error_no_configured_identity_server" = "未設置身份伺服器:新增設置來重置密碼。"; +"auth_phone_is_required" = "沒有設置身份伺服器,因此無法新增電話號碼以便將來重設密碼。"; +"auth_email_is_required" = "沒有設置身份伺服器,因此無法新增電子郵件地址以便將來重設密碼。"; "auth_add_email_phone_message_2" = "設置一個電子郵件以便日後恢復帳戶和使以後可以由認識您的人發現你。"; "auth_add_email_message_2" = "設置一個電子郵件以便日後恢復帳戶和使以後可以由認識您的人發現你。"; "auth_add_phone_message_2" = "設置一個電話號碼,以後可以由認識您的人發現你。"; @@ -687,7 +687,7 @@ // MARK: - Call Transfer "call_transfer_title" = "傳輸"; "room_info_list_section_other" = "其他"; -"create_room_placeholder_topic" = "主題"; +"create_room_placeholder_topic" = "聊天室主題為何?"; "create_room_placeholder_name" = "名稱"; "biometrics_cant_unlocked_alert_message_retry" = "重試"; "pin_protection_reset_alert_action_reset" = "重設"; @@ -1142,3 +1142,4 @@ "stop" = "停止"; "joining" = "正在加入"; "enable" = "啓用"; +"service_terms_modal_policy_checkbox_accessibility_hint" = "確認接受 %@"; From bcc83ded8c00c46fb462218ed9eb6a2bbaa9f9f3 Mon Sep 17 00:00:00 2001 From: Knugi Date: Fri, 16 Dec 2022 05:39:58 +0000 Subject: [PATCH 170/228] Translated using Weblate (Chinese (Traditional)) Currently translated at 43.0% (1009 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/zh_Hant/ --- Riot/Assets/zh_Hant.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/zh_Hant.lproj/Vector.strings b/Riot/Assets/zh_Hant.lproj/Vector.strings index 64bd9c7a1..94b0f45f5 100644 --- a/Riot/Assets/zh_Hant.lproj/Vector.strings +++ b/Riot/Assets/zh_Hant.lproj/Vector.strings @@ -1142,4 +1142,4 @@ "stop" = "停止"; "joining" = "正在加入"; "enable" = "啓用"; -"service_terms_modal_policy_checkbox_accessibility_hint" = "確認接受 %@"; +"service_terms_modal_policy_checkbox_accessibility_hint" = "剔選複選框以接受 %@"; From 3507a71736c1072de7e8754988b4b0ead9a67586 Mon Sep 17 00:00:00 2001 From: Toomore Chiang Date: Fri, 16 Dec 2022 07:48:25 +0000 Subject: [PATCH 171/228] Translated using Weblate (Chinese (Traditional)) Currently translated at 47.5% (1114 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/zh_Hant/ --- Riot/Assets/zh_Hant.lproj/Vector.strings | 151 +++++++++++++++++++++-- 1 file changed, 139 insertions(+), 12 deletions(-) diff --git a/Riot/Assets/zh_Hant.lproj/Vector.strings b/Riot/Assets/zh_Hant.lproj/Vector.strings index 94b0f45f5..25c64fe45 100644 --- a/Riot/Assets/zh_Hant.lproj/Vector.strings +++ b/Riot/Assets/zh_Hant.lproj/Vector.strings @@ -298,7 +298,7 @@ // Room Preview "room_preview_invitation_format" = "您已經透過 %@ 的邀請而加入聊天室"; "room_preview_subtitle" = "這是該聊天室的預覽。聊天室互動已被禁用。"; -"room_preview_unlinked_email_warning" = "該邀請已傳送至 %@, 但和此帳號沒有關聯。你或許會希望使用其他帳號登入,或把該電子郵件加入到你的帳戶。"; +"room_preview_unlinked_email_warning" = "該邀請已傳送至 %@, 但和此帳號沒有關聯。你或許會希望使用其他帳號登入,或把該電子郵件加入到你的帳戶。"; // Settings "settings_title" = "設定"; "room_preview_try_join_an_unknown_room" = "你正在嘗試訪問%@。您要加入已參加討論嗎?"; @@ -437,7 +437,7 @@ "group_participants_remove_prompt_title" = "確認"; "group_participants_remove_prompt_msg" = "您確定要從該群組移除 %@ 嗎?"; "group_participants_invite_prompt_title" = "確認"; -"group_participants_invite_prompt_msg" = "您確定要邀請 %@ 加入此群組嗎?"; +"group_participants_invite_prompt_msg" = "您確定要邀請 %@ 加入此群組嗎?"; "group_participants_invite_another_user" = "使用使用者 ID 或名稱搜尋/邀請使用者"; "group_participants_invite_malformed_id_title" = "邀請錯誤"; "group_participants_invite_malformed_id" = "ID 格式錯誤。一個 Matrix ID 看起來應該像 '@localpart:domain'"; @@ -472,7 +472,7 @@ "camera_access_not_granted" = "%@ 沒有使用相機的權限,請修改隱私權設定"; "large_badge_value_k_format" = "%.1fK"; // Call -"call_incoming_voice_prompt" = "來自 %@ 的語音通話"; +"call_incoming_voice_prompt" = "來自 %@ 的語音通話"; "call_incoming_video_prompt" = "來自 %@ 的視訊通話"; "call_incoming_voice" = "收到來電…"; "call_incoming_video" = "收到視訊來電…"; @@ -500,8 +500,8 @@ "bug_crash_report_description" = "請描述您在崩潰前做了什麼:"; "bug_report_logs_description" = "為了診斷問題,此用戶端的記錄檔將會隨此錯誤報告送出。 如果您只想傳送上面的文字,請取消:"; "widget_integration_missing_room_id" = "在請求中遺失 room_id 。"; -"widget_integration_missing_user_id" = "在請求中遺失 user_id 。"; -"widget_integration_room_not_visible" = "%@ 聊天室不可見。"; +"widget_integration_missing_user_id" = "在請求中遺失 user_id 。"; +"widget_integration_room_not_visible" = "%@ 聊天室為隱藏。"; // Share extension "share_extension_auth_prompt" = "登入主應用程式以分享內容"; "share_extension_failed_to_encrypt" = "傳送失敗。 檢查主應用程式對此聊天室的加密設定"; @@ -944,7 +944,7 @@ "room_event_encryption_info_block" = "黑名單"; "room_event_encryption_info_unblock" = "解除黑名單"; "room_event_encryption_verify_title" = "驗證裝置\n\n"; -"room_event_encryption_verify_message" = "若要檢查這個裝置是可被信任的,請透過其他方法聯絡所有者(例如面對面或是在電話中),並詢問在其使用者設定中以下金鑰是否是一致的:\n\n\n\t裝置名稱:%@\n\t裝置 ID:%@\n\t裝置金鑰:%@\n\n若相同,請點選下面的「驗證確認」按鈕。如果不相同,表示有人從中攔截這個裝置,您可能要點選「黑名單」按鈕。\n\n未來驗證手續會更加簡單,若有不便敬請見諒。"; +"room_event_encryption_verify_message" = "若要檢查這個裝置是可被信任的,請透過其他方法聯絡所有者(例如面對面或是在電話中),並詢問在其使用者設定中以下金鑰是否是一致的:\n\n\t裝置名稱:%@\n\t裝置 ID:%@\n\t裝置金鑰:%@\n\n若相同,請點選下面的「驗證確認」按鈕。如果不相同,表示有人從中攔截這個裝置,您可能要點選「黑名單」按鈕。\n\n未來驗證手續會更加簡單,若有不便敬請見諒。"; "room_event_encryption_verify_ok" = "驗證確認"; // Account "account_save_changes" = "儲存修改"; @@ -976,7 +976,7 @@ "microphone_access_not_granted_for_call" = "電話需要使用麥克風權限,但是 %@ 沒有存取權限"; "local_contacts_access_not_granted" = "從本機的聯絡資訊探索使用者,需要存取聯絡資訊的權限,但是 %@ 沒有存取權限"; "local_contacts_access_discovery_warning_title" = "使用者探索"; -"local_contacts_access_discovery_warning" = "%@ 要從您的聯絡資訊上傳電子郵件位址跟電話號碼來探索使用者"; +"local_contacts_access_discovery_warning" = "為了找查您的通訊錄中是否已有 Matrix 帳號的聯絡人,%@ 將會從您的通訊錄中傳送電子郵件與電話號碼到您所選擇的身分伺服器中。個人資料會在傳送前雜湊內容(無法解讀的內容),若需要更多細節,請查閱您的身分伺服器的隱私權政策。"; // Country picker "country_picker_title" = "選擇國家"; // Language picker @@ -997,8 +997,8 @@ "notice_display_name_set" = "%@ 設定了自己的顯示名稱為 %@"; "notice_display_name_changed_from" = "%@ 將自己的顯示名稱從 %@ 改為 %@"; "notice_display_name_removed" = "%@ 移除了自己的顯示名稱"; -"notice_topic_changed" = "%@ 已經變更主題為:%@"; -"notice_room_name_changed" = "%@ 將房間名稱變更為 %@"; +"notice_topic_changed" = "%@ 已變更主題為 %@。"; +"notice_room_name_changed" = "%@ 將聊天室名稱變更為 %@。"; "notice_placed_voice_call" = "%@ 開始了語音通話"; "notice_placed_video_call" = "%@ 開始了視訊通話"; "notice_answered_video_call" = "%@ 接聽了通話"; @@ -1099,7 +1099,7 @@ "notice_event_redacted_by" = " 由 %@"; "notice_event_redacted_reason" = " [理由:%@]"; "notice_profile_change_redacted" = "%@ 已更新他的個人檔案 %@"; -"notice_room_created" = "%@ 創建了該聊天室"; +"notice_room_created" = "%@ 已建立與設定此聊天室。"; "notice_room_join_rule" = "加入規則: %@"; "notice_room_power_level_intro" = "聊天室成員們的權限级别是:"; "notice_event_redacted" = "<撤回%@>"; @@ -1122,7 +1122,7 @@ "device_details_name" = "名稱\n"; "device_details_identifier" = "裝置代碼\n"; "device_details_last_seen" = "上次使用\n"; -"device_details_rename_prompt_message" = "裝置名稱:"; +"device_details_rename_prompt_message" = "公開名稱可見於與您聊天的對象"; "login_error_resource_limit_exceeded_title" = "超過資源限制"; "login_error_resource_limit_exceeded_message_default" = "此家伺服器已經超過其中一項資源限制。"; "login_error_resource_limit_exceeded_message_monthly_active_user" = "此家伺服器已經達到其每月活躍使用者限制。"; @@ -1142,4 +1142,131 @@ "stop" = "停止"; "joining" = "正在加入"; "enable" = "啓用"; -"service_terms_modal_policy_checkbox_accessibility_hint" = "剔選複選框以接受 %@"; +"service_terms_modal_policy_checkbox_accessibility_hint" = "確認接受 %@"; +/* The placeholder will show the homeserver's domain */ +"authentication_terms_message" = "請閱讀 %@ 的條款與政策"; +"authentication_terms_title" = "隱私權政策"; +"authentication_verify_msisdn_invalid_phone_number" = "無效的電話號碼或格式"; +"authentication_verify_msisdn_waiting_button" = "重新傳送驗證碼"; +/* The placeholder will show the phone number that was entered. */ +"authentication_verify_msisdn_waiting_message" = "驗證碼已傳送到 %@"; +"authentication_verify_msisdn_waiting_title" = "確認與驗證您的電話號碼"; +"authentication_verify_msisdn_otp_text_field_placeholder" = "驗證碼"; +"authentication_verify_msisdn_text_field_placeholder" = "電話號碼"; +/* The placeholder will show the homeserver's domain */ +"authentication_verify_msisdn_input_message" = "%@ 需要驗證您的帳號"; +"authentication_verify_msisdn_input_title" = "輸入您的電話號碼"; +"authentication_choose_password_not_verified_message" = "檢查您的郵件收件夾"; +"authentication_choose_password_not_verified_title" = "郵件信箱尚未確認與驗證"; +"authentication_choose_password_submit_button" = "重新設定密碼"; +"authentication_choose_password_signout_all_devices" = "登出所有裝置"; +"authentication_choose_password_text_field_placeholder" = "新密碼"; +"authentication_choose_password_input_message" = "確認至少 8 字元或更多"; +"authentication_choose_password_input_title" = "選擇新密碼"; +"authentication_forgot_password_waiting_button" = "重新寄送"; +/* The placeholder will show the email address that was entered. */ +"authentication_forgot_password_waiting_message" = "依照指示已寄送到 %@"; +"authentication_forgot_password_waiting_title" = "檢查或確認您的郵件信箱。"; +"authentication_forgot_password_text_field_placeholder" = "郵件信箱"; +/* The placeholder will show the homeserver's domain */ +"authentication_forgot_password_input_message" = "%@ 將會傳送驗證連結給您"; +"authentication_forgot_password_input_title" = "輸入您的電子郵件信箱"; +"authentication_verify_email_waiting_button" = "重新寄送"; +"authentication_verify_email_waiting_hint" = "還未收到信件?"; +/* The placeholder will show the email address that was entered. */ +"authentication_verify_email_waiting_message" = "依照指示已寄送到 %@"; +"authentication_verify_email_waiting_title" = "驗證您的郵件信箱。"; +"authentication_verify_email_text_field_placeholder" = "郵件信箱"; +/* The placeholder will show the homeserver's domain */ +"authentication_verify_email_input_message" = "%@ 需要驗證您的帳號"; +"authentication_verify_email_input_title" = "輸入您的電子郵件信箱"; +"authentication_cancel_flow_confirmation_message" = "您的帳號尚未建立完成,確定要退出註冊過程?"; +"authentication_server_selection_generic_error" = "這個 URL 無法找到伺服器,請確認它是否正確。"; +"authentication_server_selection_server_url" = "主伺服器 URL"; +"authentication_server_selection_register_message" = "屬於您伺服器的位置為何?是存放您所有資訊的主要地方"; +"authentication_server_selection_register_title" = "選擇您的主伺服器"; +"authentication_server_selection_login_message" = "屬於您伺服器的位置為何?"; +"authentication_server_selection_login_title" = "連線到主伺服器"; +"authentication_login_with_qr" = "透過 QR code 登入"; +"authentication_server_info_title_login" = "您的對話訊息將會被保存的位置"; +"authentication_login_forgot_password" = "忘記密碼"; +"authentication_login_username" = "使用者帳號 / 電子郵件 / 電話號碼"; +"authentication_login_title" = "歡迎回來!"; +"authentication_server_info_title" = "您的對話訊息將會被保存的位置"; +"authentication_registration_password_footer" = "至少 8 字元或更多"; +/* The placeholder will show the full Matrix ID that has been entered. */ +"authentication_registration_username_footer_available" = "其他人可以找到您 %@"; +"authentication_registration_username_footer" = "選定後就無法在之後變更修改"; +"authentication_registration_username" = "使用者帳號"; + +// MARK: Authentication +"authentication_registration_title" = "建立您的帳號"; +"onboarding_celebration_button" = "開始吧"; +"onboarding_celebration_message" = "隨時都可更新您的個人簡介"; +"onboarding_celebration_title" = "看起來不錯喔!"; +"onboarding_avatar_accessibility_label" = "簡介圖片"; +"onboarding_avatar_message" = "是時候將名字與臉孔聯繫在一起了"; +"onboarding_avatar_title" = "新增簡介圖片"; +"onboarding_display_name_max_length" = "您的顯示名稱必須小於 256 字元"; +"onboarding_display_name_hint" = "您可以之後再變更"; +"onboarding_display_name_placeholder" = "顯示名稱"; +"onboarding_display_name_message" = "當您傳送訊息這將會被顯示。"; +"onboarding_display_name_title" = "選擇顯示名稱"; +"onboarding_personalization_skip" = "略過此步驟"; +"onboarding_personalization_save" = "儲存並繼續"; +"onboarding_congratulations_home_button" = "帶我回家"; +"onboarding_congratulations_personalize_button" = "個人簡介"; +/* The placeholder string contains the user's matrix ID */ +"onboarding_congratulations_message" = "您的帳號 %@ 已建立了"; +"onboarding_congratulations_title" = "恭喜!"; +"onboarding_use_case_existing_server_button" = "連線到伺服器"; +"onboarding_use_case_existing_server_message" = "尋找加入一個存在的伺服器?"; +"onboarding_use_case_skip_button" = "略過這個問題"; +/* The placeholder string contains onboarding_use_case_skip_button as a tappable action */ +"onboarding_use_case_not_sure_yet" = "還不確定? %@"; +"onboarding_use_case_community_messaging" = "社群"; +"onboarding_use_case_work_messaging" = "團隊"; +"onboarding_use_case_personal_messaging" = "朋友與家人"; +"onboarding_use_case_message" = "我們會協助您連線"; +"onboarding_use_case_title" = "您最常聊天的對象是誰?"; +"onboarding_splash_page_4_message" = "Element 也非常適合工作場域使用。它受到世界上最安全的組織所信任。"; +"onboarding_splash_page_4_title_no_pun" = "與您的團隊通訊。"; +"onboarding_splash_page_3_message" = "端到端加密與不需要電話號碼。無廣告或資料蒐集。"; +"onboarding_splash_page_1_message" = "為您提供與在家中面對面交談時相同的隱私等級、安全且獨立的通訊。"; +"onboarding_splash_page_3_title" = "安全通訊中。"; +"onboarding_splash_page_2_message" = "選擇您的對話將保存在哪裡,一切將由您獨立掌控。透過 Matrix 連線。"; +"onboarding_splash_page_2_title" = "一切都在您的掌控之中。"; +"onboarding_splash_page_1_title" = "掌握您的對話。"; +"onboarding_splash_login_button_title" = "我已經有帳號了"; + +// MARK: Onboarding +"onboarding_splash_register_button_title" = "建立帳號"; +"accessibility_button_label" = "按鈕"; +"callbar_only_single_active_group" = "點擊加入群組通話 (%@)"; +"callbar_only_multiple_paused" = "%@ 通暫停通話"; +"callbar_only_single_paused" = "暫停通話"; +"callbar_active_and_multiple_paused" = "1 個正在進行通話 (%@) · %@ 個暫停的通話"; +"callbar_active_and_single_paused" = "1 個正在進行通話 (%@) · 1 個暫停的通話"; + +// Call Bar +"callbar_only_single_active" = "點擊返回通話 (%@)"; +"saving" = "儲存中"; + +// Activities +"loading" = "讀取中"; +"invite_to" = "邀請到 %@"; +"confirm" = "確認"; +"edit" = "編輯"; +"suggest" = "建議"; +"add" = "新增"; +"existing" = "已存在"; +"new_word" = "新增"; + +// GDPR +"gdpr_consent_not_given_alert_message" = "如要繼續使用 %@ 服務伺服器,您必須檢視與同意條款與條件。"; +"settings_callkit_info" = "在鎖定畫面接聽來電。顯示您的 %@ 通話於系統通話紀錄。若啟用 iCloud,通話紀錄將被分享給 Apple。"; +"room_many_users_are_typing" = "%@, %@ 與其他人正在輸入 …"; +/* The placeholder %1$tu will be replaced with a number and %2$@ with the user's search terms. Note the > at the start indicates "more than 20 results". */ +"directory_search_results_more_than" = ">%1$tu 個搜尋結果關於 %2$@"; +/* The placeholder %1$tu will be replaced with a number and %2$@ with the user's search terms. */ +"directory_search_results" = "%1$tu 個搜尋結果關於 %2$@"; From d1b5ac81ab3d3e95bf22e212676cb71589ee40bf Mon Sep 17 00:00:00 2001 From: random Date: Fri, 16 Dec 2022 14:44:22 +0000 Subject: [PATCH 172/228] Translated using Weblate (Italian) Currently translated at 100.0% (2344 of 2344 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 56b72a2e7..1f8627bc8 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -2653,3 +2653,10 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Password troppo corta"; "user_session_permanently_unverified_session_description" = "Questa sessione non supporta la crittografia, perciò non può essere verificata.\n\nNon potrai partecipare in stanze dove la crittografia è attiva mentre usi questa sessione.\n\nPer maggiore sicurezza e privacy, è consigliabile usare i client di Matrix che supportano la crittografia."; +"wysiwyg_composer_link_action_edit_title" = "Modifica collegamento"; +"wysiwyg_composer_link_action_create_title" = "Crea un collegamento"; +"wysiwyg_composer_link_action_link" = "Collegamento"; + +// Links +"wysiwyg_composer_link_action_text" = "Testo"; +"wysiwyg_composer_format_action_link" = "Applica formato collegamento"; From 90b008c6fa3273acfe94727564557531f86c9d3f Mon Sep 17 00:00:00 2001 From: Vri Date: Sat, 17 Dec 2022 12:57:07 +0000 Subject: [PATCH 173/228] Translated using Weblate (German) Currently translated at 100.0% (2345 of 2345 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index a16d530dd..407a6cc84 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -922,7 +922,7 @@ "manage_session_trusted" = "Von dir vertraut"; "manage_session_not_trusted" = "Nicht vertraut"; "manage_session_sign_out" = "Von dieser Sitzung abmelden"; -"widget_picker_manage_integrations" = "Integrationen verwalten…"; +"widget_picker_manage_integrations" = "Integrationen verwalten …"; // Room widget permissions "room_widget_permission_title" = "Widget laden"; "room_widget_permission_creator_info_title" = "Dieses Widget wurde hinzugefügt von:"; @@ -2694,3 +2694,4 @@ "wysiwyg_composer_link_action_edit_title" = "Link bearbeiten"; "wysiwyg_composer_link_action_create_title" = "Link erstellen"; "wysiwyg_composer_link_action_link" = "Link"; +"wysiwyg_composer_format_action_inline_code" = "Als Inline-Code formatieren"; From 1217923eb109c3ddbd7abee220c919c14cd2c1f4 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 16 Dec 2022 19:14:44 +0000 Subject: [PATCH 174/228] Translated using Weblate (Albanian) Currently translated at 99.6% (2337 of 2345 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index c899e6c28..2aaf887eb 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2640,7 +2640,7 @@ "user_session_unverified_session_title" = "Sesione të paverifikuar"; "user_session_verified_session_title" = "Sesione të verifikuar"; "user_session_got_it" = "E mora vesh"; -"user_other_session_permanently_unverified_additional_info" = "Ky sesion s’mund të verifikohet, ngaqë nuk mbulon fshehtëzim."; +"user_other_session_permanently_unverified_additional_info" = "Ky sesion s’mbulon fshehtëzim, ndaj s’mund të verifikohet."; "user_sessions_hide_location_info" = "Fshihe adresën IP"; "user_sessions_show_location_info" = "Shfaq adresë IP"; "voice_broadcast_time_left" = "Edhe %@"; @@ -2664,3 +2664,12 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Fjalëkalim shumë i shkurtër"; +"user_session_permanently_unverified_session_description" = "Ky sesion nuk mbulon fshehtëzim, ndaj s’mund të verifikohet.\n\nS’do të jeni në gjendje të merrni pjesë në dhoma ku fshehtëzimi është i aktivizuar, kur përdorni këtë sesion.\n\nPër sigurinë dhe privatësinë më të mirë, rekomandohet të përdorni klientë Matrix që mbulojnë fshehtëzimin."; +"wysiwyg_composer_link_action_edit_title" = "Përpunoni një lidhje"; +"wysiwyg_composer_link_action_create_title" = "Krijoni një lidhje"; +"wysiwyg_composer_link_action_link" = "Lidhje"; + +// Links +"wysiwyg_composer_link_action_text" = "Tekst"; +"wysiwyg_composer_format_action_inline_code" = "Apliko formatim kodi brendazi"; +"wysiwyg_composer_format_action_link" = "Apliko formatim lidhjeje"; From 62715ddc1a63549d346a5fb1b6d311efe12fd4ac Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Fri, 16 Dec 2022 18:18:30 +0000 Subject: [PATCH 175/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2345 of 2345 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index adff521e2..291f09083 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2885,3 +2885,4 @@ // Links "wysiwyg_composer_link_action_text" = "Текст"; "wysiwyg_composer_format_action_link" = "Застосувати формат посилання"; +"wysiwyg_composer_format_action_inline_code" = "Застосовувати вбудований формат коду"; From de67d9b53a929ea9571b820f8fa9ee2181d6ea4c Mon Sep 17 00:00:00 2001 From: Linerly Date: Sat, 17 Dec 2022 10:12:22 +0000 Subject: [PATCH 176/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2345 of 2345 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 9f6cf0939..3f7f2f031 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2887,3 +2887,4 @@ // Links "wysiwyg_composer_link_action_text" = "Teks"; "wysiwyg_composer_format_action_link" = "Terapkan format tautan"; +"wysiwyg_composer_format_action_inline_code" = "Terapkan format kode dalam baris"; From 953f2d868759dfd79f62ca95f26958673b39865b Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Fri, 16 Dec 2022 18:07:31 +0000 Subject: [PATCH 177/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2345 of 2345 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index b23ed9d1a..362d9f374 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2883,3 +2883,4 @@ // Links "wysiwyg_composer_link_action_text" = "Text"; "wysiwyg_composer_format_action_link" = "Použiť formát odkazu"; +"wysiwyg_composer_format_action_inline_code" = "Použiť formát riadkového kódu"; From 3a7a86d06abdbed56beff09dbbfb6d20222e92a5 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sun, 18 Dec 2022 20:32:14 +0000 Subject: [PATCH 178/228] Translated using Weblate (Hungarian) Currently translated at 100.0% (2345 of 2345 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 76cf8859e..0dd4e1cd8 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -2680,3 +2680,4 @@ // Links "wysiwyg_composer_link_action_text" = "Szöveg"; "wysiwyg_composer_format_action_link" = "Hivatkozás"; +"wysiwyg_composer_format_action_inline_code" = "Beágyazott kód formátum alkalmazása"; From 51a823df9c7d11b965b892bac5613b6b55545587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Mon, 19 Dec 2022 03:12:18 +0000 Subject: [PATCH 179/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2345 of 2345 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 3849de6d6..9cbe0a88b 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2632,3 +2632,4 @@ "wysiwyg_composer_link_action_link" = "Link"; "wysiwyg_composer_link_action_edit_title" = "Muuda linki"; "wysiwyg_composer_link_action_create_title" = "Loo link"; +"wysiwyg_composer_format_action_inline_code" = "Kasuta lõimitud koodi vormingut"; From e91f611a038a83488b89225fc6d61b90a718768e Mon Sep 17 00:00:00 2001 From: Szimszon Date: Tue, 20 Dec 2022 08:35:16 +0000 Subject: [PATCH 180/228] Translated using Weblate (Hungarian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 0dd4e1cd8..613ec5e65 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -2681,3 +2681,7 @@ "wysiwyg_composer_link_action_text" = "Szöveg"; "wysiwyg_composer_format_action_link" = "Hivatkozás"; "wysiwyg_composer_format_action_inline_code" = "Beágyazott kód formátum alkalmazása"; +"notice_voice_broadcast_ended_by_you" = "A hang közvetítést befejezted."; +"notice_voice_broadcast_ended" = "%@ befejezte a hang közvetítést."; +"notice_voice_broadcast_live" = "Élő közvetítés"; +"user_other_session_security_recommendation_title" = "További munkamenetek"; From d54806e262419c5548e998e56e6ec466b8737511 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Mon, 19 Dec 2022 19:00:06 +0000 Subject: [PATCH 181/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 291f09083..cea62c1b8 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2886,3 +2886,7 @@ "wysiwyg_composer_link_action_text" = "Текст"; "wysiwyg_composer_format_action_link" = "Застосувати формат посилання"; "wysiwyg_composer_format_action_inline_code" = "Застосовувати вбудований формат коду"; +"notice_voice_broadcast_ended_by_you" = "Ви завершили голосову трансляцію."; +"notice_voice_broadcast_ended" = "%@ завершує голосову трансляцію."; +"notice_voice_broadcast_live" = "Трансляція наживо"; +"user_other_session_security_recommendation_title" = "Інші сеанси"; From d7246131f15766cd5c614ed48a79325776471eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 20 Dec 2022 05:58:16 +0000 Subject: [PATCH 182/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 9cbe0a88b..6ff2fd6e9 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2633,3 +2633,7 @@ "wysiwyg_composer_link_action_edit_title" = "Muuda linki"; "wysiwyg_composer_link_action_create_title" = "Loo link"; "wysiwyg_composer_format_action_inline_code" = "Kasuta lõimitud koodi vormingut"; +"notice_voice_broadcast_ended_by_you" = "Sa lõpetasid ringhäälingukõne."; +"notice_voice_broadcast_ended" = "%@ lõpetas ringhäälingukõne."; +"notice_voice_broadcast_live" = "Ringhäälingukõne on eetris"; +"user_other_session_security_recommendation_title" = "Muud sessioonid"; From 2c806abe4b07f48fb3a81be05f594dc1cddf47cc Mon Sep 17 00:00:00 2001 From: Linerly Date: Mon, 19 Dec 2022 23:08:12 +0000 Subject: [PATCH 183/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 3f7f2f031..0ff61c291 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2888,3 +2888,7 @@ "wysiwyg_composer_link_action_text" = "Teks"; "wysiwyg_composer_format_action_link" = "Terapkan format tautan"; "wysiwyg_composer_format_action_inline_code" = "Terapkan format kode dalam baris"; +"notice_voice_broadcast_ended_by_you" = "Anda mengakhiri sebuah siaran suara."; +"notice_voice_broadcast_ended" = "%@ mengakhiri sebuah siaran suara."; +"notice_voice_broadcast_live" = "Siaran langsung"; +"user_other_session_security_recommendation_title" = "Sesi lainnya"; From aea00cc8af4b51c6ab0a13ba08c9d50800061c46 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Mon, 19 Dec 2022 20:59:01 +0000 Subject: [PATCH 184/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 362d9f374..e553d8320 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2884,3 +2884,7 @@ "wysiwyg_composer_link_action_text" = "Text"; "wysiwyg_composer_format_action_link" = "Použiť formát odkazu"; "wysiwyg_composer_format_action_inline_code" = "Použiť formát riadkového kódu"; +"notice_voice_broadcast_ended_by_you" = "Ukončili ste hlasové vysielanie."; +"notice_voice_broadcast_ended" = "%@ ukončil/a hlasové vysielanie."; +"notice_voice_broadcast_live" = "Živé vysielanie"; +"user_other_session_security_recommendation_title" = "Iné relácie"; From 820d665ed65a14ff7093b17f1e040b765835febd Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 21 Dec 2022 08:02:50 +0000 Subject: [PATCH 185/228] Translated using Weblate (German) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 407a6cc84..89241013c 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -438,7 +438,7 @@ // Widget Integration Manager "widget_integration_need_to_be_able_to_invite" = "Du musst Benutzer einladen dürfen, um dies zu tun."; "widget_integration_unable_to_create" = "Erstellen des Widgets nicht möglich."; -"widget_integration_failed_to_send_request" = "Senden der Anfrage fehlgeschlagen."; +"widget_integration_failed_to_send_request" = "Übertragung der Anfrage fehlgeschlagen."; "widget_integration_room_not_recognised" = "Dieser Raum wurde nicht erkannt."; "widget_integration_positive_power_level" = "Berechtigungslevel muss eine positive Zahl sein."; "widget_integration_must_be_in_room" = "Du bist nicht in diesem Raum."; @@ -2679,8 +2679,8 @@ "voice_broadcast_time_left" = "%@ übrig"; "voice_broadcast_buffering" = "Puffere …"; "voice_broadcast_stop_alert_agree_button" = "Ja, beende"; -"voice_broadcast_stop_alert_description" = "Bist du sicher, dass du deine Live-Übertragung abbrechen möchtest? Dies wird die Übertragung beenden und die vollständige Aufnahme wird im Raum verfügbar sein."; -"voice_broadcast_stop_alert_title" = "Live-Übertragung abbrechen?"; +"voice_broadcast_stop_alert_description" = "Möchtest du die Übertragung wirklich beenden? Dies wird die Übertragung beenden und die vollständige Aufnahme im Raum bereitstellen."; +"voice_broadcast_stop_alert_title" = "Live-Übertragung beenden?"; "password_policy_weak_pwd_error" = "Dieses Passwort ist zu schwach. Es muss mindestens 8 Zeichen enthalten, davon mindestens ein Zeichen jeder Art: Großbuchstabe, Kleinbuchstabe, Ziffer und Sonderzeichen."; "password_policy_pwd_in_dict_error" = "Dieses Passwort wurde in einem Wörterbuch gefunden und nicht erlaubt."; @@ -2695,3 +2695,7 @@ "wysiwyg_composer_link_action_create_title" = "Link erstellen"; "wysiwyg_composer_link_action_link" = "Link"; "wysiwyg_composer_format_action_inline_code" = "Als Inline-Code formatieren"; +"notice_voice_broadcast_ended_by_you" = "Du hast eine Sprachübertragung beendet."; +"notice_voice_broadcast_ended" = "%@ beendete eine Sprachübertragung."; +"notice_voice_broadcast_live" = "Echtzeitübertragung"; +"user_other_session_security_recommendation_title" = "Andere Sitzungen"; From f6888574b944e985a7acbdb8564dbb677d510db8 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 22 Dec 2022 08:46:07 +0000 Subject: [PATCH 186/228] Translated using Weblate (Hungarian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 613ec5e65..3acce2ba6 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -2493,7 +2493,7 @@ // First item is client name and second item is session display name "user_session_name" = "%@: %@"; -"user_session_unverified_additional_info" = "Az aktuális munkamenet készen áll a biztonságos üzenetküldésre."; +"user_session_unverified_additional_info" = "Ellenőrizd az aktuális munkamenetet a biztonságos üzenetküldéshez."; "user_session_verified_additional_info" = "Az aktuális munkamenet készen áll a biztonságos üzenetküldésre."; "user_session_learn_more" = "Tudj meg többet"; "user_session_view_details" = "Részletek megtekintése"; From 9c2fcc97ed739906b68e4fb0ef23a4bbcd64f118 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 21 Dec 2022 16:34:38 +0000 Subject: [PATCH 187/228] Translated using Weblate (Albanian) Currently translated at 99.6% (2340 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 2aaf887eb..1dd99a152 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2673,3 +2673,7 @@ "wysiwyg_composer_link_action_text" = "Tekst"; "wysiwyg_composer_format_action_inline_code" = "Apliko formatim kodi brendazi"; "wysiwyg_composer_format_action_link" = "Apliko formatim lidhjeje"; +"notice_voice_broadcast_ended_by_you" = "Përfunduar një transmetim zanor."; +"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ë"; From 747ea7bd1e7ec56d9a188dc62b4613dd438d67fd Mon Sep 17 00:00:00 2001 From: random Date: Wed, 21 Dec 2022 09:45:02 +0000 Subject: [PATCH 188/228] Translated using Weblate (Italian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 1f8627bc8..49aa38c26 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -2660,3 +2660,8 @@ // Links "wysiwyg_composer_link_action_text" = "Testo"; "wysiwyg_composer_format_action_link" = "Applica formato collegamento"; +"notice_voice_broadcast_ended_by_you" = "Hai terminato una trasmissione vocale."; +"notice_voice_broadcast_ended" = "%@ ha terminato una trasmissione vocale."; +"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"; From ba9cf31f4574c72da0ee5e10eae2e05bfbac4684 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Sat, 24 Dec 2022 18:40:28 +0000 Subject: [PATCH 189/228] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ --- Riot/Assets/pt_BR.lproj/Vector.strings | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 6bc50fcbe..ddcff6857 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -2654,3 +2654,15 @@ // MARK: Password policy errors "password_policy_too_short_pwd_error" = "Senha curta demais"; +"notice_voice_broadcast_ended_by_you" = "Você terminou um broadcast de voz."; +"notice_voice_broadcast_ended" = "%@ terminou um broadcast de voz."; +"notice_voice_broadcast_live" = "Broadcast ao vivo"; +"wysiwyg_composer_link_action_edit_title" = "Editar link"; +"wysiwyg_composer_link_action_create_title" = "Criar um link"; +"wysiwyg_composer_link_action_link" = "Link"; + +// Links +"wysiwyg_composer_link_action_text" = "Texto"; +"wysiwyg_composer_format_action_inline_code" = "Aplicar formato de código inline"; +"wysiwyg_composer_format_action_link" = "Aplicar formato de link"; +"user_other_session_security_recommendation_title" = "Outras sessões"; From 206e325b521cd1fcd5ffb913bcebcec03f302e51 Mon Sep 17 00:00:00 2001 From: Linerly Date: Sun, 25 Dec 2022 15:46:06 +0000 Subject: [PATCH 190/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 0ff61c291..7865dda90 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -690,7 +690,7 @@ "identity_server_settings_description" = "Anda saat ini menggunakan %@ untuk menemukan dan dapat ditemukan oleh kontak yang Anda tahu."; // AuthenticatedSessionViewControllerFactory -"authenticated_session_flow_not_supported" = "Aplikasi ini tidak mendukung mekanisme otentikasi di homeserver Anda."; +"authenticated_session_flow_not_supported" = "Aplikasi ini tidak mendukung mekanisme autentikasi di homeserver Anda."; "security_settings_coming_soon" = "Maaf. Aksi ini belum tersedia di %@ iOS. Mohon gunakan klien Matrix yang lain untuk menyiapkannya. %@ iOS akan menggunakannya."; "security_settings_blacklist_unverified_devices_description" = "Verifikasi semua sesi pengguna untuk menandainya sebagai terpercaya dan kirim pesan ke mereka."; "security_settings_crosssigning_info_exists" = "Akun Anda memiliki identitas penandatanganan silang, tetapi belum dipercayai oleh sesi ini. Selesaikan keamanan sesi ini."; @@ -765,7 +765,7 @@ "room_intro_cell_information_dm_sentence1_part1" = "Ini adalah awal dari pesan langsung Anda dengan "; "room_intro_cell_information_room_without_topic_sentence2_part2" = " supaya orang tahu tentang ruangan ini."; "invite_friends_share_text" = "Hai, bicara kepada saya di %@: %@"; -"biometrics_usage_reason" = "Otentikasi diperlukan untuk mengakses aplikasi ini"; +"biometrics_usage_reason" = "Autentikasi diperlukan untuk mengakses aplikasi ini"; "pin_protection_kick_user_alert_message" = "Terlalu banyak kesalahan, Anda telah dikeluarkan"; "secrets_setup_recovery_passphrase_confirm_information" = "Masukkan Frasa Keamanan Anda untuk mengkonfirmasi."; "secrets_recovery_with_key_information_verify_device" = "Gunakan Kunci Keamanan Anda untuk memverifikasi perangkat ini."; @@ -1873,7 +1873,7 @@ "login_error_forbidden" = "Nama pengguna/kata sandi tidak absah"; "login_error_registration_is_not_supported" = "Pendaftaran saat ini tidak didukung"; "login_error_do_not_support_login_flows" = "Saat ini kami tidak mendukung salah satu atau semua alur masuk yang ditentukan oleh homeserver ini"; -"login_error_no_login_flow" = "Kami gagal untuk menerima informasi otentikasi dari homeserver ini"; +"login_error_no_login_flow" = "Kami gagal untuk menerima informasi autentikasi dari homeserver ini"; "login_error_title" = "Login Gagal"; "login_prompt_email_token" = "Harap masukkan token validasi email Anda:"; "login_email_placeholder" = "Alamat email"; @@ -2270,8 +2270,8 @@ // Encryption information "room_event_encryption_info_title" = "Informasi enkripsi ujung-ke-ujung\n\n"; -"device_details_delete_prompt_message" = "Operasi ini membutuhkan otentikasi tambahan.\nUntuk melanjutkan, silakan masukkan kata sandi Anda."; -"device_details_delete_prompt_title" = "Otentikasi"; +"device_details_delete_prompt_message" = "Operasi ini membutuhkan autentikasi tambahan.\nUntuk melanjutkan, silakan masukkan kata sandi Anda."; +"device_details_delete_prompt_title" = "Autentikasi"; "device_details_rename_prompt_message" = "Nama publik sesi dapat dilihat oleh orang yang berkomunikasi dengan Anda"; "device_details_rename_prompt_title" = "Nama Sesi"; "device_details_last_seen_format" = "%@ @ %@\n"; From 24c024827616ec23aa77ddd2d82cd2f72c5d0e9d Mon Sep 17 00:00:00 2001 From: Aleksey Grigoryev Date: Thu, 29 Dec 2022 12:31:28 +0000 Subject: [PATCH 191/228] Translated using Weblate (Russian) Currently translated at 81.7% (1920 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/ --- Riot/Assets/ru.lproj/Vector.strings | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index fa6cdf6af..d473c0e07 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -2194,3 +2194,9 @@ "authentication_qr_login_start_display_qr" = "Показать QR-код на этом устройстве"; "authentication_qr_login_start_need_alternative" = "Нуждаетесь в альтернативном методе?"; "authentication_qr_login_start_step4" = "Выберите «Показать QR-код на этом устройстве»"; +"password_policy_pwd_in_dict_error" = "Этот пароль был найден в словаре и не разрешен."; +"password_policy_weak_pwd_error" = "Этот пароль слишком слаб. Он должен содержать не менее 8 символов, по крайней мере по одному символу каждого типа: прописные, строчные, цифры и специальные символы."; + +// MARK: Password policy errors +"password_policy_too_short_pwd_error" = "Очень короткий пароль"; +"settings_enable_room_message_bubbles" = "Сообщения пузырями"; From cd414e43ced63f713c7c21532e104a94cfd317e9 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:44:56 +0000 Subject: [PATCH 192/228] Translated using Weblate (Albanian) Currently translated at 99.6% (2340 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 1dd99a152..ce8af2d72 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2594,7 +2594,7 @@ /* The placeholder will be replaces with manage_session_name_info_link */ "manage_session_name_info" = "Ju lutemi, kini parasysh se emrat e sesioneve janë të dukshëm edhe për personat me të cilët komunikoni.%@"; "manage_session_name_hint" = "Emra vetjakë sesionesh mund t’ju ndihmojnë të njihni më kollaj pajisjet tuaja."; -"settings_labs_enable_voice_broadcast" = "Aktivizoni transmetim zanor (nën zhvillim aktiv)"; +"settings_labs_enable_voice_broadcast" = "Aktivizoni transmetim zanor"; "settings_labs_enable_wysiwyg_composer" = "Provoni përpunuesin e teksteve të pasur"; "settings_labs_enable_new_app_layout" = "Skemë e Re Aplikacioni"; "settings_labs_enable_new_client_info_feature" = "Regjistro emrin, versionin dhe URL-në e klientit, për të dalluar më kollaj sesionit te përgjegjës sesionesh"; From c975267d097dc344d638373658d9bbf96ff58356 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:47:54 +0000 Subject: [PATCH 193/228] Translated using Weblate (Dutch) Currently translated at 98.3% (2310 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/nl/ --- Riot/Assets/nl.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index 5c02db6a0..ab294231c 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -2774,7 +2774,7 @@ /* The placeholder will be replaces with manage_session_name_info_link */ "manage_session_name_info" = "Houd er rekening mee dat sessienamen ook zichtbaar zijn voor mensen met wie je communiceert. %@"; "manage_session_name_hint" = "Met aangepaste sessienamen kan je jouw apparaten gemakkelijker herkennen."; -"settings_labs_enable_voice_broadcast" = "Voice-uitzending (in actieve ontwikkeling)"; +"settings_labs_enable_voice_broadcast" = "Voice-uitzending"; "settings_labs_enable_wysiwyg_composer" = "Probeer de rich-text-editor (platte tekst-modus komt binnenkort)"; "settings_labs_enable_new_session_manager" = "Nieuwe sessiemanager"; "room_first_message_placeholder" = "Stuur je eerste bericht…"; From b1e3790aadfdc0418c893b2ca639125be0952928 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:45:26 +0000 Subject: [PATCH 194/228] Translated using Weblate (German) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 89241013c..e43cdc0c4 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2645,7 +2645,7 @@ // Mark: - Voice broadcast "voice_broadcast_unauthorized_title" = "Sprachübertragung kann nicht gestartet werden"; -"settings_labs_enable_voice_broadcast" = "Sprachübertragung (in aktiver Entwicklung)"; +"settings_labs_enable_voice_broadcast" = "Sprachübertragung"; "voice_broadcast_playback_loading_error" = "Wiedergabe der Sprachübertragung nicht möglich."; "deselect_all" = "Alle abwählen"; "user_other_session_menu_select_sessions" = "Sitzungen auswählen"; From 45720ed3943cbf954ebca48d86558647c63e442a Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:46:23 +0000 Subject: [PATCH 195/228] Translated using Weblate (Hungarian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 3acce2ba6..999743e63 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -2624,7 +2624,7 @@ "authentication_qr_login_start_subtitle" = "Használd a kamerát ezen az eszközön a másik eszközödön megjelenő QR kód beolvasására:"; "authentication_qr_login_start_title" = "QR kód beolvasása"; "authentication_login_with_qr" = "Belépés QR kóddal"; -"settings_labs_enable_voice_broadcast" = "Hang közvetítés (aktív fejlesztés alatt)"; +"settings_labs_enable_voice_broadcast" = "Hang közvetítés"; "wysiwyg_composer_start_action_voice_broadcast" = "Hang közvetítés"; "voice_broadcast_playback_loading_error" = "A hang közvetítés nem játszható le."; "voice_broadcast_already_in_progress_message" = "Egy hang közvetítés már folyamatban van. Először fejezd be a jelenlegi közvetítést egy új indításához."; From e97b3101e156735c4d6850f7965062bb400c912c Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 4 Jan 2023 17:01:34 +0000 Subject: [PATCH 196/228] Translated using Weblate (Albanian) Currently translated at 99.6% (2340 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index ce8af2d72..1dd99a152 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2594,7 +2594,7 @@ /* The placeholder will be replaces with manage_session_name_info_link */ "manage_session_name_info" = "Ju lutemi, kini parasysh se emrat e sesioneve janë të dukshëm edhe për personat me të cilët komunikoni.%@"; "manage_session_name_hint" = "Emra vetjakë sesionesh mund t’ju ndihmojnë të njihni më kollaj pajisjet tuaja."; -"settings_labs_enable_voice_broadcast" = "Aktivizoni transmetim zanor"; +"settings_labs_enable_voice_broadcast" = "Aktivizoni transmetim zanor (nën zhvillim aktiv)"; "settings_labs_enable_wysiwyg_composer" = "Provoni përpunuesin e teksteve të pasur"; "settings_labs_enable_new_app_layout" = "Skemë e Re Aplikacioni"; "settings_labs_enable_new_client_info_feature" = "Regjistro emrin, versionin dhe URL-në e klientit, për të dalluar më kollaj sesionit te përgjegjës sesionesh"; From 4a4c6ca74f4c8c88a7006d2ed85cd89cc4e7068c Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:47:35 +0000 Subject: [PATCH 197/228] Translated using Weblate (Italian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 49aa38c26..35bd94c35 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -2604,7 +2604,7 @@ "manage_session_name_info" = "Ricorda che i nomi di sessione sono anche visibili alle persone con cui comunichi. %@"; "manage_session_name_hint" = "I nomi di sessione personalizzati possono aiutarti a riconoscere i tuoi dispositivi più facilmente."; "settings_labs_enable_wysiwyg_composer" = "Prova l'editor in rich text"; -"settings_labs_enable_voice_broadcast" = "Trasmissione vocale (in sviluppo attivo)"; +"settings_labs_enable_voice_broadcast" = "Trasmissione vocale"; "wysiwyg_composer_start_action_voice_broadcast" = "Trasmissione vocale"; "voice_broadcast_playback_loading_error" = "Impossibile avviare questa trasmissione vocale."; "voice_broadcast_already_in_progress_message" = "Stai già registrando una trasmissione vocale. Termina quella in corso per iniziarne una nuova."; From 0d9e46df352a15d79a3228cbe1c6e1e44da76b8d Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:48:15 +0000 Subject: [PATCH 198/228] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ --- Riot/Assets/pt_BR.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index ddcff6857..582951c39 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -2613,7 +2613,7 @@ // Mark: - Voice broadcast "voice_broadcast_unauthorized_title" = "Não dá para começar um novo broadcast de voz"; -"settings_labs_enable_voice_broadcast" = "Broadcast de voz (sob desenvolvimento ativo)"; +"settings_labs_enable_voice_broadcast" = "Broadcast de voz"; "deselect_all" = "Desselecionar Todas(os)"; "user_other_session_menu_select_sessions" = "Selecionar sessões"; "user_other_session_selected_count" = "%@ selecionadas"; From 60904f571ca6b133d76856a444b0510413bf05ca Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:48:45 +0000 Subject: [PATCH 199/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index cea62c1b8..a56ae1576 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2837,7 +2837,7 @@ // Mark: - Voice broadcast "voice_broadcast_unauthorized_title" = "Не вдалося розпочати нову голосову трансляцію"; -"settings_labs_enable_voice_broadcast" = "Голосові трансляції (в активній розробці)"; +"settings_labs_enable_voice_broadcast" = "Голосові трансляції"; "deselect_all" = "Скасувати вибір усіх"; "user_other_session_menu_select_sessions" = "Вибрати сеанси"; "user_other_session_selected_count" = "Вибрано %@"; From 2f5d826c67c98cd2f64b5b89588a75ab25fcf1ee Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:45:56 +0000 Subject: [PATCH 200/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 6ff2fd6e9..5adcb1a44 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2573,7 +2573,7 @@ /* The placeholder will be replaces with manage_session_name_info_link */ "manage_session_name_info" = "Palun arvesta, et sessioonide nimed on näha ka kõikidele osapooltele, kellega sa suhtled. %@"; "manage_session_name_hint" = "Sinu enda kirjutatud sessiooninimede alusel on sul oma seadmeid lihtsam ära tunda."; -"settings_labs_enable_voice_broadcast" = "Ringhäälingukõne (aktiivses arenduses)"; +"settings_labs_enable_voice_broadcast" = "Ringhäälingukõne"; "settings_labs_enable_wysiwyg_composer" = "Proovi vormindatud teksti alusel töötavat tekstitoimetit"; "authentication_qr_login_failure_retry" = "Proovi uuesti"; "authentication_qr_login_failure_request_timed_out" = "Sidumine ei lõppenud etteantud aja jooksul."; From 0e8a7c8215f688f55f7b305002a6c70c10694e19 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:47:22 +0000 Subject: [PATCH 201/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 7865dda90..0a224e221 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2839,7 +2839,7 @@ // Mark: - Voice broadcast "voice_broadcast_unauthorized_title" = "Tidak dapat memulai sebuah siaran suara baru"; -"settings_labs_enable_voice_broadcast" = "Siaran suara (dalam pengembangan aktif)"; +"settings_labs_enable_voice_broadcast" = "Siaran suara"; "deselect_all" = "Batalkan Semua Pilihan"; "user_other_session_menu_select_sessions" = "Pilih sesi"; "user_other_session_selected_count" = "%@ dipilih"; From e3eaa97e015b7b19afd7b5d430e1a60287bd3fbf Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:48:32 +0000 Subject: [PATCH 202/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2348 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index e553d8320..c71f05b12 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2834,7 +2834,7 @@ // Mark: - Voice broadcast "voice_broadcast_unauthorized_title" = "Nie je možné spustiť nové hlasové vysielanie"; -"settings_labs_enable_voice_broadcast" = "Hlasové vysielanie (v štádiu aktívneho vývoja)"; +"settings_labs_enable_voice_broadcast" = "Hlasové vysielanie"; "voice_broadcast_playback_loading_error" = "Toto hlasové vysielanie nie je možné prehrať."; "deselect_all" = "Zrušiť výber všetkých"; "user_other_session_selected_count" = "%@ vybratých"; From bad42dbd58585eb409ec2a1a2564db8461a2b23f Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Thu, 5 Jan 2023 16:59:21 +0000 Subject: [PATCH 203/228] Translated using Weblate (Albanian) Currently translated at 99.6% (2340 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 1dd99a152..ce8af2d72 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2594,7 +2594,7 @@ /* The placeholder will be replaces with manage_session_name_info_link */ "manage_session_name_info" = "Ju lutemi, kini parasysh se emrat e sesioneve janë të dukshëm edhe për personat me të cilët komunikoni.%@"; "manage_session_name_hint" = "Emra vetjakë sesionesh mund t’ju ndihmojnë të njihni më kollaj pajisjet tuaja."; -"settings_labs_enable_voice_broadcast" = "Aktivizoni transmetim zanor (nën zhvillim aktiv)"; +"settings_labs_enable_voice_broadcast" = "Aktivizoni transmetim zanor"; "settings_labs_enable_wysiwyg_composer" = "Provoni përpunuesin e teksteve të pasur"; "settings_labs_enable_new_app_layout" = "Skemë e Re Aplikacioni"; "settings_labs_enable_new_client_info_feature" = "Regjistro emrin, versionin dhe URL-në e klientit, për të dalluar më kollaj sesionit te përgjegjës sesionesh"; From 4dbc60ac0dd888af689ad6f71761ef528fe0f6e4 Mon Sep 17 00:00:00 2001 From: Christian Paul Date: Mon, 9 Jan 2023 10:42:26 +0000 Subject: [PATCH 204/228] Translated using Weblate (Esperanto) Currently translated at 63.8% (1500 of 2348 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/eo/ --- Riot/Assets/eo.lproj/Vector.strings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index b0ae5a061..2e91114ed 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -589,7 +589,7 @@ "create_room_section_footer_encryption" = "Ne eblas malŝalti ĉifradon poste."; "create_room_enable_encryption" = "Ŝalti ĉifradon"; "create_room_section_header_encryption" = "Ĉifrado de ĉambro"; -"create_room_placeholder_topic" = "Temo"; +"create_room_placeholder_topic" = "Pri kio temas ĉi tiu ĉambro?"; "create_room_section_header_topic" = "Temo de ĉambro (nedeviga)"; "create_room_placeholder_name" = "Nomo"; "create_room_section_header_name" = "Nomo de ĉambro"; @@ -2032,3 +2032,4 @@ "login_error_resource_limit_exceeded_message_default" = "Ĉi tiu hejmservilo atingis unu el siaj rimedaj limoj."; "login_error_resource_limit_exceeded_title" = "Rimeda limo estas atingita"; "login_error_forgot_password_is_not_supported" = "Forgesado de pasvorto nun ne estas subtenata"; +"identity_server_settings_alert_change" = "Ĉu malkonektu de identiga servilo %1$@ kaj anstataŭe konektu al %2$@?"; From 5c44e895c71df305af9991645dff0a4c8878b1b0 Mon Sep 17 00:00:00 2001 From: Vri Date: Tue, 10 Jan 2023 05:25:49 +0000 Subject: [PATCH 205/228] Translated using Weblate (German) Currently translated at 99.9% (2350 of 2351 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index e43cdc0c4..78d9ffef2 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2699,3 +2699,6 @@ "notice_voice_broadcast_ended" = "%@ beendete eine Sprachübertragung."; "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"; From 6511a193fe743e38000168ef739676506289d75f Mon Sep 17 00:00:00 2001 From: Szimszon Date: Mon, 9 Jan 2023 19:50:23 +0000 Subject: [PATCH 206/228] Translated using Weblate (Hungarian) Currently translated at 100.0% (2351 of 2351 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 999743e63..53debbedb 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -2685,3 +2685,6 @@ "notice_voice_broadcast_ended" = "%@ befejezte a hang közvetítést."; "notice_voice_broadcast_live" = "Élő közvetítés"; "user_other_session_security_recommendation_title" = "További munkamenetek"; +"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"; From db1c73d19906180e8bd39e2283b4c4841f55cfac Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Mon, 9 Jan 2023 18:04:25 +0000 Subject: [PATCH 207/228] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2351 of 2351 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index a56ae1576..96e8166d7 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2890,3 +2890,6 @@ "notice_voice_broadcast_ended" = "%@ завершує голосову трансляцію."; "notice_voice_broadcast_live" = "Трансляція наживо"; "user_other_session_security_recommendation_title" = "Інші сеанси"; +"poll_timeline_decryption_error" = "Через помилки під час розшифрування деякі голоси можуть бути не враховані"; +"voice_message_broadcast_in_progress_title" = "Неможливо розпочати запис голосового повідомлення"; +"voice_message_broadcast_in_progress_message" = "Ви не можете розпочати запис голосового повідомлення, оскільки зараз триває запис трансляції наживо. Будь ласка, завершіть трансляцію, щоб розпочати запис голосового повідомлення"; From b1120c107da7b1bfe03c2401b3b26005b1b55a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Mon, 9 Jan 2023 19:20:02 +0000 Subject: [PATCH 208/228] Translated using Weblate (Estonian) Currently translated at 100.0% (2351 of 2351 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 5adcb1a44..d84b26c76 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2637,3 +2637,6 @@ "notice_voice_broadcast_ended" = "%@ lõpetas ringhäälingukõne."; "notice_voice_broadcast_live" = "Ringhäälingukõne on eetris"; "user_other_session_security_recommendation_title" = "Muud sessioonid"; +"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"; From 285dd8eed51acba98bf1efd0c15df7b25613f230 Mon Sep 17 00:00:00 2001 From: Linerly Date: Mon, 9 Jan 2023 17:28:57 +0000 Subject: [PATCH 209/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (2351 of 2351 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 0a224e221..d6449680c 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2892,3 +2892,6 @@ "notice_voice_broadcast_ended" = "%@ mengakhiri sebuah siaran suara."; "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_title" = "Tidak dapat memulai pesan suara"; From 221fdfb5e2046cc2bbaddf353c0e07502dbb6458 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Mon, 9 Jan 2023 23:51:52 +0000 Subject: [PATCH 210/228] Translated using Weblate (Slovak) Currently translated at 100.0% (2351 of 2351 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index c71f05b12..87edea36c 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2888,3 +2888,6 @@ "notice_voice_broadcast_ended" = "%@ ukončil/a hlasové vysielanie."; "notice_voice_broadcast_live" = "Živé vysielanie"; "user_other_session_security_recommendation_title" = "Iné relácie"; +"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"; From 888d059fe0a9ebb667145ecab36a2b8c60fd2b36 Mon Sep 17 00:00:00 2001 From: Vri Date: Tue, 10 Jan 2023 10:02:44 +0000 Subject: [PATCH 211/228] Translated using Weblate (German) Currently translated at 100.0% (50 of 50 strings) Translation: Element iOS/Element iOS (Push) Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/de/ --- Riot/Assets/de.lproj/Localizable.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/de.lproj/Localizable.strings b/Riot/Assets/de.lproj/Localizable.strings index cc3120d86..c3528b764 100644 --- a/Riot/Assets/de.lproj/Localizable.strings +++ b/Riot/Assets/de.lproj/Localizable.strings @@ -118,3 +118,6 @@ /* New file message from a specific person, not referencing a room. */ "LOCATION_FROM_USER" = "%@ hat den eigenen Standort geteilt"; + +/* New voice broadcast from a specific person, not referencing a room. */ +"VOICE_BROADCAST_FROM_USER" = "%@ begann eine Sprachübertragung"; From d5e2c92b3f3485c7d1276a01b9dfa39f188e640d Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 10 Jan 2023 10:32:32 +0000 Subject: [PATCH 212/228] Translated using Weblate (Indonesian) Currently translated at 100.0% (50 of 50 strings) Translation: Element iOS/Element iOS (Push) Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/id/ --- Riot/Assets/id.lproj/Localizable.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/id.lproj/Localizable.strings b/Riot/Assets/id.lproj/Localizable.strings index 4b2a6be15..f8200cb0b 100644 --- a/Riot/Assets/id.lproj/Localizable.strings +++ b/Riot/Assets/id.lproj/Localizable.strings @@ -172,3 +172,6 @@ /* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */ "MSGS_FROM_TWO_PLUS_USERS" = "%@ pesan baru dari %@, %@ dan lainnya"; + +/* New voice broadcast from a specific person, not referencing a room. */ +"VOICE_BROADCAST_FROM_USER" = "%# memulai sebuah siaran suara"; From 96fca0447135f29e8cdaa6f2c22efbb292ee3799 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 28 Dec 2022 15:02:32 +0100 Subject: [PATCH 213/228] Remove filter for poll.end events --- Riot/Modules/MatrixKit/Models/MXKAppSettings.m | 4 ++++ Riot/Modules/Room/CellData/RoomBubbleCellData.m | 1 + 2 files changed, 5 insertions(+) diff --git a/Riot/Modules/MatrixKit/Models/MXKAppSettings.m b/Riot/Modules/MatrixKit/Models/MXKAppSettings.m index 40cf2ebd3..ed523160e 100644 --- a/Riot/Modules/MatrixKit/Models/MXKAppSettings.m +++ b/Riot/Modules/MatrixKit/Models/MXKAppSettings.m @@ -148,6 +148,8 @@ static NSString *const kMXAppGroupID = @"group.org.matrix"; kMXEventTypeStringKeyVerificationDone, kMXEventTypeStringPollStart, kMXEventTypeStringPollStartMSC3381, + kMXEventTypeStringPollEnd, + kMXEventTypeStringPollEndMSC3381, kMXEventTypeStringBeaconInfo, kMXEventTypeStringBeaconInfoMSC3672 ].mutableCopy; @@ -181,6 +183,8 @@ static NSString *const kMXAppGroupID = @"group.org.matrix"; kMXEventTypeStringKeyVerificationDone, kMXEventTypeStringPollStart, kMXEventTypeStringPollStartMSC3381, + kMXEventTypeStringPollEnd, + kMXEventTypeStringPollEndMSC3381, kMXEventTypeStringBeaconInfo, kMXEventTypeStringBeaconInfoMSC3672 ].mutableCopy; diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index f3c7db526..f4ae23f1f 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -152,6 +152,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat break; } case MXEventTypePollStart: + case MXEventTypePollEnd: { self.tag = RoomBubbleCellDataTagPoll; self.collapsable = NO; From d982174efe47c2d42e8aba3fec99d24e26c5bf16 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 28 Dec 2022 15:18:50 +0100 Subject: [PATCH 214/228] Push poll.end event to PollAggregator --- .../Styles/Plain/Cells/Poll/PollPlainCell.swift | 13 ++++++++----- .../Coordinator/TimelinePollCoordinator.swift | 8 ++++---- .../Coordinator/TimelinePollProvider.swift | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollPlainCell.swift index 70cf4370f..19d2cda1c 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollPlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollPlainCell.swift @@ -19,15 +19,18 @@ import Foundation class PollPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable { private var event: MXEvent? + private var supportedEventTypes: Set<__MXEventType> = [.pollStart, .pollEnd] override func render(_ cellData: MXKCellData!) { super.render(cellData) - guard let contentView = roomCellContentView?.innerContentView, - let bubbleData = cellData as? RoomBubbleCellData, - let event = bubbleData.events.last, - event.eventType == __MXEventType.pollStart, - let controller = TimelinePollProvider.shared.buildTimelinePollVCForEvent(event) else { + guard + let contentView = roomCellContentView?.innerContentView, + let bubbleData = cellData as? RoomBubbleCellData, + let event = bubbleData.events.last, + supportedEventTypes.contains(event.eventType), + let controller = TimelinePollProvider.shared.buildTimelinePollVCForEvent(event) + else { return } diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift index 3520588e2..d0a9f7043 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift @@ -21,7 +21,7 @@ import SwiftUI struct TimelinePollCoordinatorParameters { let session: MXSession let room: MXRoom - let pollStartEvent: MXEvent + let pollEvent: MXEvent } final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDelegate { @@ -46,7 +46,7 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel init(parameters: TimelinePollCoordinatorParameters) throws { self.parameters = parameters - try pollAggregator = PollAggregator(session: parameters.session, room: parameters.room, pollStartEventId: parameters.pollStartEvent.eventId) + try pollAggregator = PollAggregator(session: parameters.session, room: parameters.room, pollEvent: parameters.pollEvent) pollAggregator.delegate = self viewModel = TimelinePollViewModel(timelinePollDetails: buildTimelinePollFrom(pollAggregator.poll)) @@ -65,7 +65,7 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel .sink { [weak self] identifiers in guard let self = self else { return } - self.parameters.room.sendPollResponse(for: parameters.pollStartEvent, + self.parameters.room.sendPollResponse(for: parameters.pollEvent, withAnswerIdentifiers: identifiers, threadId: nil, localEcho: nil, success: nil) { [weak self] error in @@ -96,7 +96,7 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel } func endPoll() { - parameters.room.sendPollEnd(for: parameters.pollStartEvent, threadId: nil, localEcho: nil, success: nil) { [weak self] _ in + parameters.room.sendPollEnd(for: parameters.pollEvent, threadId: nil, localEcho: nil, success: nil) { [weak self] _ in self?.viewModel.showClosingFailure() } } diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift index a11cb3a2e..8e5a04cd9 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollProvider.swift @@ -43,7 +43,7 @@ class TimelinePollProvider: NSObject { return coordinator.toPresentable() } - let parameters = TimelinePollCoordinatorParameters(session: session, room: room, pollStartEvent: event) + let parameters = TimelinePollCoordinatorParameters(session: session, room: room, pollEvent: event) guard let coordinator = try? TimelinePollCoordinator(parameters: parameters) else { return nil } From d95a47ba8041418d5de42abda4038be6c8feb6af Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 28 Dec 2022 16:35:49 +0100 Subject: [PATCH 215/228] Customize TimePollView for poll.end events --- Riot/Assets/en.lproj/Vector.strings | 2 ++ Riot/Generated/Strings.swift | 4 ++++ .../Coordinator/TimelinePollCoordinator.swift | 1 + .../Room/TimelinePoll/TimelinePollModels.swift | 12 ++++++++++++ .../Room/TimelinePoll/TimelinePollScreenState.swift | 2 ++ .../View/TimelinePollAnswerOptionButton.swift | 1 + .../Room/TimelinePoll/View/TimelinePollView.swift | 6 ++++++ 7 files changed, 28 insertions(+) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index dc3ba51cb..04d0cd693 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2351,6 +2351,8 @@ Tap the + to start adding people."; "poll_timeline_decryption_error" = "Due to decryption errors, some votes may not be counted"; +"poll_timeline_ended_text" = "Ended the poll"; + // MARK: - Location sharing "location_sharing_title" = "Location"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 05460082a..1dde36ec4 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -4843,6 +4843,10 @@ public class VectorL10n: NSObject { public static var pollTimelineDecryptionError: String { return VectorL10n.tr("Vector", "poll_timeline_decryption_error") } + /// Ended the poll + public static var pollTimelineEndedText: String { + return VectorL10n.tr("Vector", "poll_timeline_ended_text") + } /// Please try again public static var pollTimelineNotClosedSubtitle: String { return VectorL10n.tr("Vector", "poll_timeline_not_closed_subtitle") diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift index d0a9f7043..c3c1cf327 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift @@ -131,6 +131,7 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel closed: poll.isClosed, totalAnswerCount: poll.totalAnswerCount, type: pollKindToTimelinePollType(poll.kind), + eventType: parameters.pollEvent.eventType == .pollStart ? .started : .ended, maxAllowedSelections: poll.maxAllowedSelections, hasBeenEdited: poll.hasBeenEdited, hasDecryptionError: poll.hasDecryptionError) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollModels.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollModels.swift index fa0d2ac49..3629aae3e 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollModels.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollModels.swift @@ -32,6 +32,11 @@ enum TimelinePollType { case undisclosed } +enum TimelinePollEventType { + case started + case ended +} + struct TimelinePollAnswerOption: Identifiable { var id: String var text: String @@ -62,6 +67,7 @@ struct TimelinePollDetails { var closed: Bool var totalAnswerCount: UInt var type: TimelinePollType + var eventType: TimelinePollEventType var maxAllowedSelections: UInt var hasBeenEdited = true var hasDecryptionError: Bool @@ -70,6 +76,7 @@ struct TimelinePollDetails { closed: Bool, totalAnswerCount: UInt, type: TimelinePollType, + eventType: TimelinePollEventType, maxAllowedSelections: UInt, hasBeenEdited: Bool, hasDecryptionError: Bool) { @@ -78,6 +85,7 @@ struct TimelinePollDetails { self.closed = closed self.totalAnswerCount = totalAnswerCount self.type = type + self.eventType = eventType self.maxAllowedSelections = maxAllowedSelections self.hasBeenEdited = hasBeenEdited self.hasDecryptionError = hasDecryptionError @@ -94,6 +102,10 @@ struct TimelinePollDetails { return type == .disclosed && totalAnswerCount > 0 && hasCurrentUserVoted } } + + var representsPollEndedEvent: Bool { + eventType == .ended + } } struct TimelinePollViewState: BindableState { diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollScreenState.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollScreenState.swift index 5a9a9e0a1..a53a745b8 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollScreenState.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollScreenState.swift @@ -22,6 +22,7 @@ enum MockTimelinePollScreenState: MockScreenState, CaseIterable { case closedDisclosed case openUndisclosed case closedUndisclosed + case closedPollEnded var screenType: Any.Type { TimelinePollDetails.self @@ -37,6 +38,7 @@ enum MockTimelinePollScreenState: MockScreenState, CaseIterable { closed: self == .closedDisclosed || self == .closedUndisclosed ? true : false, totalAnswerCount: 20, type: self == .closedDisclosed || self == .openDisclosed ? .disclosed : .undisclosed, + eventType: self == .closedPollEnded ? .ended : .started, maxAllowedSelections: 1, hasBeenEdited: false, hasDecryptionError: false) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollAnswerOptionButton.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollAnswerOptionButton.swift index 04451bd92..85309c31c 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollAnswerOptionButton.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollAnswerOptionButton.swift @@ -151,6 +151,7 @@ struct TimelinePollAnswerOptionButton_Previews: PreviewProvider { closed: closed, totalAnswerCount: 100, type: type, + eventType: .started, maxAllowedSelections: 1, hasBeenEdited: false, hasDecryptionError: false) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift index cc00f017a..2109a0e8a 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift @@ -31,6 +31,12 @@ struct TimelinePollView: View { let poll = viewModel.viewState.poll VStack(alignment: .leading, spacing: 16.0) { + if poll.representsPollEndedEvent { + Text(VectorL10n.pollTimelineEndedText) + .font(theme.fonts.footnote) + .foregroundColor(theme.colors.tertiaryContent) + } + Text(poll.question) .font(theme.fonts.bodySB) .foregroundColor(theme.colors.primaryContent) + From c18f3a1be5b2e4d79bdf816c7355f9b4cec7ce98 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 29 Dec 2022 16:30:59 +0100 Subject: [PATCH 216/228] Fix reload bug --- Riot/Modules/Room/CellData/RoomBubbleCellData.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index f4ae23f1f..288d148a1 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -1194,6 +1194,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat shouldAddEvent = NO; break; case MXEventTypePollStart: + case MXEventTypePollEnd: shouldAddEvent = NO; break; case MXEventTypeBeaconInfo: From ea183bedc62c5fab4489fdaaaa57232203ce3776 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 10 Jan 2023 11:19:10 +0000 Subject: [PATCH 217/228] Fix broken strings --- Riot/Assets/de.lproj/Vector.strings | 2 +- Riot/Assets/id.lproj/Localizable.strings | 2 +- Riot/Assets/sq.lproj/Vector.strings | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 78d9ffef2..f1886a04c 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -2669,7 +2669,7 @@ // Unverified sessions "key_verification_alert_title" = "Du hast nicht verifizierte Sitzungen"; -"launch_loading_processing_response" = "Verarbeite Daten\n%@ % %"; +"launch_loading_processing_response" = "Verarbeite Daten\n%@ %%"; "launch_loading_server_syncing_nth_attempt" = "Synchronisiere mit dem Server\n(%@ Versuch)"; // MARK: - Launch loading diff --git a/Riot/Assets/id.lproj/Localizable.strings b/Riot/Assets/id.lproj/Localizable.strings index f8200cb0b..da5e860c5 100644 --- a/Riot/Assets/id.lproj/Localizable.strings +++ b/Riot/Assets/id.lproj/Localizable.strings @@ -174,4 +174,4 @@ "MSGS_FROM_TWO_PLUS_USERS" = "%@ pesan baru dari %@, %@ dan lainnya"; /* New voice broadcast from a specific person, not referencing a room. */ -"VOICE_BROADCAST_FROM_USER" = "%# memulai sebuah siaran suara"; +"VOICE_BROADCAST_FROM_USER" = "%@ memulai sebuah siaran suara"; diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index ce8af2d72..f484f7275 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -2629,7 +2629,6 @@ "invite_to" = "Ftojeni te %@"; "all_chats_empty_list_placeholder_title" = "S’ka gjë tjetër për të parë."; "all_chats_edit_layout_add_filters_message" = "Filtroni automatikisht mesazhet tuaj në kategori që caktoni vetë"; -"device_name_desktop" = ""; "user_session_rename_session_description" = "Përdorues të tjerë në mesazhe të drejtpërdrejtë dhe dhoma ku jeni në gjendje të shihni një listë të plotë të sesioneve tuaja.\n\nKjo u jep besim atyre se po flasin vërtet me ju, por do të thotë edhe se mund të shohin emrin e sesionit që jepni këtu."; "user_session_inactive_session_description" = "Sesione jo aktive janë sesione që keni ca kohë që s’i keni përdorur, por që vazhdojnë të marrin kyçe fshehtëzimi.\n\nHeqja e sesioneve jo aktive përmirëson sigurinë dhe punimin dhe e bëjnë të lehtë për ju të identifikoni, nëse një sesion i ri është i dyshimtë."; "user_session_unverified_session_description" = "Sesione të paverifikuar janë sesione ku keni bërë hyrjen me kredencialet tuaja, por që nuk janë ndër-verifikuar.\n\nDuhet ta bëni veçanërisht të qartë se i njihni këto sesione, ngaqë mund të përfaqësojnë përdorim të paautorizuar të llogarisë tuaj."; From 64f904f8dd288b35687439d35f40f8a8a316d738 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 29 Dec 2022 17:17:57 +0100 Subject: [PATCH 218/228] Fix reactions layout --- Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m | 4 ++++ Riot/Modules/Room/CellData/RoomBubbleCellData.m | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m index 14684ac5d..b54aa1de6 100644 --- a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m +++ b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m @@ -2885,6 +2885,10 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) { return YES; } + if (event.eventType == MXEventTypePollEnd) { + return YES; + } + // Specific case for voice broadcast event if (event.eventType == MXEventTypeCustom && [event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]) { diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 288d148a1..aa48fac44 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -636,7 +636,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat __block NSInteger firstVisibleComponentIndex = NSNotFound; MXEvent *firstEvent = self.events.firstObject; - BOOL isPoll = (firstEvent.eventType == MXEventTypePollStart); + BOOL isPoll = (self.events.firstObject.eventType == MXEventTypePollStart || self.events.firstObject.eventType == MXEventTypePollEnd); BOOL isVoiceBroadcast = (firstEvent.eventType == MXEventTypeCustom && [firstEvent.type isEqualToString: VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]); if ((isPoll || self.attachment || isVoiceBroadcast) && self.bubbleComponents.count) From 43d4ceb10127a5cda2c9d55218b4fb05634463f1 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 29 Dec 2022 17:38:09 +0100 Subject: [PATCH 219/228] Cleanup code --- Riot/Categories/MXEvent.swift | 10 ++++++++++ Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m | 6 +----- Riot/Modules/Room/CellData/RoomBubbleCellData.m | 2 +- Riot/Modules/Room/RoomViewController.m | 7 ++++--- .../Styles/Plain/Cells/Poll/PollPlainCell.swift | 3 +-- RiotNSE/target.yml | 1 + RiotShareExtension/target.yml | 1 + SiriIntents/target.yml | 1 + 8 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Riot/Categories/MXEvent.swift b/Riot/Categories/MXEvent.swift index 5b1a85263..f0d10b3da 100644 --- a/Riot/Categories/MXEvent.swift +++ b/Riot/Categories/MXEvent.swift @@ -46,4 +46,14 @@ extension MXEvent { return self } } + + @objc + var isTimelinePollEvent: Bool { + switch eventType { + case .pollStart, .pollEnd: + return true + default: + return false + } + } } diff --git a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m index b54aa1de6..d8f55bf14 100644 --- a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m +++ b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m @@ -2881,11 +2881,7 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) { return NO; } - if (event.eventType == MXEventTypePollStart) { - return YES; - } - - if (event.eventType == MXEventTypePollEnd) { + if (event.isTimelinePollEvent) { return YES; } diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index aa48fac44..219f67481 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -636,7 +636,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat __block NSInteger firstVisibleComponentIndex = NSNotFound; MXEvent *firstEvent = self.events.firstObject; - BOOL isPoll = (self.events.firstObject.eventType == MXEventTypePollStart || self.events.firstObject.eventType == MXEventTypePollEnd); + BOOL isPoll = firstEvent.isTimelinePollEvent; BOOL isVoiceBroadcast = (firstEvent.eventType == MXEventTypeCustom && [firstEvent.type isEqualToString: VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]); if ((isPoll || self.attachment || isVoiceBroadcast) && self.bubbleComponents.count) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index b26a29a86..58debed44 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -3971,7 +3971,7 @@ static CGSize kThreadListBarButtonItemImageSize; }]]; } - if (!isJitsiCallEvent && selectedEvent.eventType != MXEventTypePollStart && + if (!isJitsiCallEvent && !selectedEvent.isTimelinePollEvent && selectedEvent.eventType != MXEventTypeBeaconInfo) { [self.eventMenuBuilder addItemWithType:EventMenuItemTypeQuote @@ -3992,7 +3992,7 @@ static CGSize kThreadListBarButtonItemImageSize; } if (selectedEvent.sentState == MXEventSentStateSent && - selectedEvent.eventType != MXEventTypePollStart && + !selectedEvent.isTimelinePollEvent && // Forwarding of live-location shares still to be implemented selectedEvent.eventType != MXEventTypeBeaconInfo) { @@ -4008,7 +4008,7 @@ static CGSize kThreadListBarButtonItemImageSize; }]]; } - if (!isJitsiCallEvent && BuildSettings.messageDetailsAllowShare && selectedEvent.eventType != MXEventTypePollStart && + if (!isJitsiCallEvent && BuildSettings.messageDetailsAllowShare && !selectedEvent.isTimelinePollEvent && selectedEvent.eventType != MXEventTypeBeaconInfo) { [self.eventMenuBuilder addItemWithType:EventMenuItemTypeShare @@ -7147,6 +7147,7 @@ static CGSize kThreadListBarButtonItemImageSize; case MXEventTypeKeyVerificationDone: case MXEventTypeKeyVerificationCancel: case MXEventTypePollStart: + case MXEventTypePollEnd: case MXEventTypeBeaconInfo: result = NO; break; diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollPlainCell.swift index 19d2cda1c..13ed8711d 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollPlainCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollPlainCell.swift @@ -19,7 +19,6 @@ import Foundation class PollPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable { private var event: MXEvent? - private var supportedEventTypes: Set<__MXEventType> = [.pollStart, .pollEnd] override func render(_ cellData: MXKCellData!) { super.render(cellData) @@ -28,7 +27,7 @@ class PollPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCell let contentView = roomCellContentView?.innerContentView, let bubbleData = cellData as? RoomBubbleCellData, let event = bubbleData.events.last, - supportedEventTypes.contains(event.eventType), + event.isTimelinePollEvent, let controller = TimelinePollProvider.shared.buildTimelinePollVCForEvent(event) else { return diff --git a/RiotNSE/target.yml b/RiotNSE/target.yml index 08515998b..415f8447a 100644 --- a/RiotNSE/target.yml +++ b/RiotNSE/target.yml @@ -54,6 +54,7 @@ targets: - path: ../Riot/Managers/Locale/LocaleProviderType.swift - path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift - path: ../Riot/Categories/Bundle.swift + - path: ../Riot/Categories/MXEvent.swift - path: ../Riot/Generated/Strings.swift - path: ../Riot/Generated/Images.swift - path: ../Riot/Managers/KeyValueStorage/KeychainStore.swift diff --git a/RiotShareExtension/target.yml b/RiotShareExtension/target.yml index d89575738..2d398950a 100644 --- a/RiotShareExtension/target.yml +++ b/RiotShareExtension/target.yml @@ -42,6 +42,7 @@ targets: - path: . - path: ../Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m - path: ../Riot/Categories/Bundle.swift + - path: ../Riot/Categories/MXEvent.swift - path: ../Riot/Managers/Theme/ - path: ../Riot/Utils/AvatarGenerator.m - path: ../Config/BuildSettings.swift diff --git a/SiriIntents/target.yml b/SiriIntents/target.yml index 72bbf91d4..5b63a95b3 100644 --- a/SiriIntents/target.yml +++ b/SiriIntents/target.yml @@ -42,6 +42,7 @@ targets: sources: - path: . - path: ../Riot/Categories/Bundle.swift + - path: ../Riot/Categories/MXEvent.swift - path: ../Config/CommonConfiguration.swift - path: ../Config/BuildSettings.swift - path: ../Config/Configurable.swift From d965becd7a2751a4a45b7e617627a47469909be0 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 09:41:45 +0100 Subject: [PATCH 220/228] Fix poll.end read receipts --- .../MatrixKit/Utils/EventFormatter/MXKEventFormatter.m | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m index eaeaa269a..19f093a5d 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m @@ -1606,6 +1606,16 @@ static NSString *const kHTMLATagRegexPattern = @"( } break; } + case MXEventTypePollEnd: + { + if (event.isEditEvent) + { + return nil; + } + + displayText = [VectorL10n pollTimelineEndedText]; + break; + } case MXEventTypePollStart: { if (event.isEditEvent) From 2ed4231ac07e8b985709ecdcdeee42fcee8a8675 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 09:54:22 +0100 Subject: [PATCH 221/228] Add push notification body for poll.end --- RiotNSE/NotificationService.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 1d7f5cf07..34cd416f3 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -544,7 +544,7 @@ class NotificationService: UNNotificationServiceExtension { // Otherwise show a generic reaction. notificationBody = NotificationService.localizedString(forKey: "GENERIC_REACTION_FROM_USER", eventSenderName) } - + case .custom: if (event.type == kWidgetMatrixEventTypeString || event.type == kWidgetModularEventTypeString), let type = event.content?["type"] as? String, @@ -561,14 +561,19 @@ class NotificationService: UNNotificationServiceExtension { additionalUserInfo = [Constants.userInfoKeyPresentNotificationOnForeground: true] } } + case .pollStart: notificationTitle = self.messageTitle(for: eventSenderName, in: roomDisplayName) notificationBody = MXEventContentPollStart(fromJSON: event.content)?.question + + case .pollEnd: + notificationTitle = self.messageTitle(for: eventSenderName, in: roomDisplayName) + notificationBody = VectorL10n.pollTimelineEndedText + default: break } - self.validateNotificationContentAndComplete( notificationTitle: notificationTitle, notificationBody: notificationBody, From 493c91b2da7bc61858573a769f9fa0401b5e4f1b Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 11:25:42 +0100 Subject: [PATCH 222/228] Add warnings --- RiotNSE/NotificationService.swift | 1 + .../Modules/Room/TimelinePoll/View/TimelinePollView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 34cd416f3..2ff305077 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -568,6 +568,7 @@ class NotificationService: UNNotificationServiceExtension { case .pollEnd: notificationTitle = self.messageTitle(for: eventSenderName, in: roomDisplayName) + #warning("alfogrillo: TBC") notificationBody = VectorL10n.pollTimelineEndedText default: diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift index 2109a0e8a..40af7e975 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift @@ -32,6 +32,7 @@ struct TimelinePollView: View { VStack(alignment: .leading, spacing: 16.0) { if poll.representsPollEndedEvent { + #warning("alfogrillo: TBC") Text(VectorL10n.pollTimelineEndedText) .font(theme.fonts.footnote) .foregroundColor(theme.colors.tertiaryContent) From 2ec0777b06a234a53cf36ea89d94e369da49641b Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 4 Jan 2023 15:02:51 +0100 Subject: [PATCH 223/228] Remove warnings --- RiotNSE/NotificationService.swift | 1 - .../Modules/Room/TimelinePoll/View/TimelinePollView.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 2ff305077..34cd416f3 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -568,7 +568,6 @@ class NotificationService: UNNotificationServiceExtension { case .pollEnd: notificationTitle = self.messageTitle(for: eventSenderName, in: roomDisplayName) - #warning("alfogrillo: TBC") notificationBody = VectorL10n.pollTimelineEndedText default: diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift index 40af7e975..2109a0e8a 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/View/TimelinePollView.swift @@ -32,7 +32,6 @@ struct TimelinePollView: View { VStack(alignment: .leading, spacing: 16.0) { if poll.representsPollEndedEvent { - #warning("alfogrillo: TBC") Text(VectorL10n.pollTimelineEndedText) .font(theme.fonts.footnote) .foregroundColor(theme.colors.tertiaryContent) From c156f76ec5febaf9d9323b069dd9b5cd70d2fd04 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 4 Jan 2023 15:20:13 +0100 Subject: [PATCH 224/228] Refine MXKEventFormatter --- .../MatrixKit/Utils/EventFormatter/MXKEventFormatter.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m index 19f093a5d..21be58eb1 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m @@ -1613,7 +1613,14 @@ static NSString *const kHTMLATagRegexPattern = @"( return nil; } - displayText = [VectorL10n pollTimelineEndedText]; + MXEvent* pollStartedEvent = [self->mxSession.store eventWithEventId:event.relatesTo.eventId inRoom:event.roomId]; + + if (pollStartedEvent) { + displayText = [MXEventContentPollStart modelFromJSON:pollStartedEvent.content].question; + } else { + displayText = [VectorL10n pollTimelineEndedText]; + } + break; } case MXEventTypePollStart: From e1e870c883c94f96f09bb2245dffb6ad2a263ee6 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 4 Jan 2023 15:29:01 +0100 Subject: [PATCH 225/228] Add changelog.d file --- changelog.d/pr-7231.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-7231.change diff --git a/changelog.d/pr-7231.change b/changelog.d/pr-7231.change new file mode 100644 index 000000000..288daeae1 --- /dev/null +++ b/changelog.d/pr-7231.change @@ -0,0 +1 @@ +Polls: render the poll ended event in the timeline. From 0ed86d2b26022f15525a6ee2d9e1bcf013c52e3c Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 4 Jan 2023 17:06:20 +0100 Subject: [PATCH 226/228] Fix ut build errors --- .../Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift index be73d7f04..cd806da54 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift @@ -34,6 +34,7 @@ class TimelinePollViewModelTests: XCTestCase { closed: false, totalAnswerCount: 3, type: .disclosed, + eventType: .started, maxAllowedSelections: 1, hasBeenEdited: false, hasDecryptionError: false) From bbfbb924c9837143ff88e3708554b6c680c737be Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 10 Jan 2023 14:13:24 +0000 Subject: [PATCH 227/228] changelog.d: Upgrade MatrixSDK version ([v0.24.7](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.24.7)). --- Config/AppVersion.xcconfig | 4 ++-- Podfile | 2 +- changelog.d/x-nolink-0.change | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changelog.d/x-nolink-0.change diff --git a/Config/AppVersion.xcconfig b/Config/AppVersion.xcconfig index 7cf911f2b..ea1f94f28 100644 --- a/Config/AppVersion.xcconfig +++ b/Config/AppVersion.xcconfig @@ -15,5 +15,5 @@ // // Version -MARKETING_VERSION = 1.9.14 -CURRENT_PROJECT_VERSION = 1.9.14 +MARKETING_VERSION = 1.9.15 +CURRENT_PROJECT_VERSION = 1.9.15 diff --git a/Podfile b/Podfile index 14443360b..02f9e605f 100644 --- a/Podfile +++ b/Podfile @@ -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.6' +$matrixSDKVersion = '= 0.24.7' # $matrixSDKVersion = :local # $matrixSDKVersion = { :branch => 'develop'} # $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } } diff --git a/changelog.d/x-nolink-0.change b/changelog.d/x-nolink-0.change new file mode 100644 index 000000000..06a8c63b2 --- /dev/null +++ b/changelog.d/x-nolink-0.change @@ -0,0 +1 @@ +Upgrade MatrixSDK version ([v0.24.7](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.24.7)). \ No newline at end of file From 6fd8512008d93ab13c3e73bf98eab1053157c29a Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 10 Jan 2023 14:13:25 +0000 Subject: [PATCH 228/228] version++ --- CHANGES.md | 40 +++++++++++++++++++++++++++++++++++ changelog.d/6059.feature | 1 - changelog.d/6579.bugfix | 1 - changelog.d/7003.bugfix | 1 - changelog.d/7109.bugfix | 1 - changelog.d/7159.feature | 1 - changelog.d/7177.feature | 1 - changelog.d/7179.feature | 1 - changelog.d/7182.change | 1 - changelog.d/7192.bugfix | 1 - changelog.d/7232.bugfix | 1 - changelog.d/7235.bugfix | 1 - changelog.d/pr-7146.change | 1 - changelog.d/pr-7160.change | 1 - changelog.d/pr-7163.change | 1 - changelog.d/pr-7164.change | 1 - changelog.d/pr-7165.change | 1 - changelog.d/pr-7173.bugfix | 1 - changelog.d/pr-7175.change | 1 - changelog.d/pr-7178.change | 1 - changelog.d/pr-7180.change | 1 - changelog.d/pr-7188.change | 1 - changelog.d/pr-7206.change | 1 - changelog.d/pr-7214.change | 1 - changelog.d/pr-7220.bugfix | 1 - changelog.d/pr-7230.change | 1 - changelog.d/pr-7231.change | 1 - changelog.d/pr-7244.bugfix | 1 - changelog.d/x-nolink-0.change | 1 - 29 files changed, 40 insertions(+), 28 deletions(-) delete mode 100644 changelog.d/6059.feature delete mode 100644 changelog.d/6579.bugfix delete mode 100644 changelog.d/7003.bugfix delete mode 100644 changelog.d/7109.bugfix delete mode 100644 changelog.d/7159.feature delete mode 100644 changelog.d/7177.feature delete mode 100644 changelog.d/7179.feature delete mode 100644 changelog.d/7182.change delete mode 100644 changelog.d/7192.bugfix delete mode 100644 changelog.d/7232.bugfix delete mode 100644 changelog.d/7235.bugfix delete mode 100644 changelog.d/pr-7146.change delete mode 100644 changelog.d/pr-7160.change delete mode 100644 changelog.d/pr-7163.change delete mode 100644 changelog.d/pr-7164.change delete mode 100644 changelog.d/pr-7165.change delete mode 100644 changelog.d/pr-7173.bugfix delete mode 100644 changelog.d/pr-7175.change delete mode 100644 changelog.d/pr-7178.change delete mode 100644 changelog.d/pr-7180.change delete mode 100644 changelog.d/pr-7188.change delete mode 100644 changelog.d/pr-7206.change delete mode 100644 changelog.d/pr-7214.change delete mode 100644 changelog.d/pr-7220.bugfix delete mode 100644 changelog.d/pr-7230.change delete mode 100644 changelog.d/pr-7231.change delete mode 100644 changelog.d/pr-7244.bugfix delete mode 100644 changelog.d/x-nolink-0.change diff --git a/CHANGES.md b/CHANGES.md index fa9d863d3..f62115f7c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,43 @@ +## Changes in 1.9.15 (2023-01-10) + +✨ Features + +- Threads: Load the thread list using server-side sorting and pagination ([#6059](https://github.com/vector-im/element-ios/issues/6059)) +- Rich Text Composer: added link creation/editing feature. ([#7159](https://github.com/vector-im/element-ios/issues/7159)) +- Rich Text Composer: added inline code formatting feature. ([#7177](https://github.com/vector-im/element-ios/issues/7177)) +- Voice Broadcast: allow to react on Voice Broadcast. ([#7179](https://github.com/vector-im/element-ios/issues/7179)) + +🙌 Improvements + +- Labs: VoiceBroadcast: Add backward and forward buttons for playback ([#7146](https://github.com/vector-im/element-ios/pull/7146)) +- Update the room description in the rooms list in case of live broadcast (incoming or outgoing) ([#7160](https://github.com/vector-im/element-ios/pull/7160)) +- Labs: VoiceBroadcast: Link the live icon color to the recording state ([#7163](https://github.com/vector-im/element-ios/pull/7163)) +- Add old device data from user's account data events. ([#7164](https://github.com/vector-im/element-ios/pull/7164)) +- Labs: VoiceBroadcast: Replace the player timeline ([#7165](https://github.com/vector-im/element-ios/pull/7165)) +- Labs: VoiceBroadcast: Update Voice Broadcast recorder cell by adjusting some padding values ([#7175](https://github.com/vector-im/element-ios/pull/7175)) +- Labs: VoiceBroadcast: Update live badge layout for recorder and player cells ([#7178](https://github.com/vector-im/element-ios/pull/7178)) +- Updates on the UI/UX to conform the device manager to the design. ([#7180](https://github.com/vector-im/element-ios/pull/7180)) +- Labs: VoiceBroadcast: Handle potential crash whereas a voice broadcast is in progress ([#7188](https://github.com/vector-im/element-ios/pull/7188)) +- Polls: show decryption errors in timeline during aggregations. ([#7206](https://github.com/vector-im/element-ios/pull/7206)) +- Device Manager: change fallback display name for sessions. ([#7214](https://github.com/vector-im/element-ios/pull/7214)) +- Ignore the voice broadcast chunks at the notifications level ([#7230](https://github.com/vector-im/element-ios/pull/7230)) +- Polls: render the poll ended event in the timeline. ([#7231](https://github.com/vector-im/element-ios/pull/7231)) +- Upgrade MatrixSDK version ([v0.24.7](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.24.7)). +- Updated fastlane script to use Xcode v 14.2. ([#7182](https://github.com/vector-im/element-ios/issues/7182)) + +🐛 Bugfixes + +- Labs: Crash on new voice broadcast if the room has avatar ([#7173](https://github.com/vector-im/element-ios/pull/7173)) +- Fix hidden live location timeline tiles after text messages ([#7220](https://github.com/vector-im/element-ios/pull/7220)) +- Fix an issue preventing temporary audio files to be deleted. ([#7244](https://github.com/vector-im/element-ios/pull/7244)) +- App Layout: wrap Space names to 1 line only in the bottom sheet ([#6579](https://github.com/vector-im/element-ios/issues/6579)) +- Timeline: fixed navigation back from replies. ([#7003](https://github.com/vector-im/element-ios/issues/7003)) +- Timeline: fixed an issue where formatted links appeared in black. ([#7109](https://github.com/vector-im/element-ios/issues/7109)) +- Voice Broadcast: Pause voice broadcast listening on new voice broadcast recording ([#7192](https://github.com/vector-im/element-ios/issues/7192)) +- Direct Message: fixed a crash when a new DM room is created ([#7232](https://github.com/vector-im/element-ios/issues/7232)) +- Voice Broadcast: Prevent sending voice message during a voice broadcast recording ([#7235](https://github.com/vector-im/element-ios/issues/7235)) + + ## Changes in 1.9.14 (2022-12-13) 🙌 Improvements diff --git a/changelog.d/6059.feature b/changelog.d/6059.feature deleted file mode 100644 index c4a9e7221..000000000 --- a/changelog.d/6059.feature +++ /dev/null @@ -1 +0,0 @@ -Threads: Load the thread list using server-side sorting and pagination \ No newline at end of file diff --git a/changelog.d/6579.bugfix b/changelog.d/6579.bugfix deleted file mode 100644 index d1a3181c1..000000000 --- a/changelog.d/6579.bugfix +++ /dev/null @@ -1 +0,0 @@ -App Layout: wrap Space names to 1 line only in the bottom sheet \ No newline at end of file diff --git a/changelog.d/7003.bugfix b/changelog.d/7003.bugfix deleted file mode 100644 index 871f28ffe..000000000 --- a/changelog.d/7003.bugfix +++ /dev/null @@ -1 +0,0 @@ -Timeline: fixed navigation back from replies. diff --git a/changelog.d/7109.bugfix b/changelog.d/7109.bugfix deleted file mode 100644 index 964fb01fa..000000000 --- a/changelog.d/7109.bugfix +++ /dev/null @@ -1 +0,0 @@ -Timeline: fixed an issue where formatted links appeared in black. \ No newline at end of file diff --git a/changelog.d/7159.feature b/changelog.d/7159.feature deleted file mode 100644 index 5acb4948d..000000000 --- a/changelog.d/7159.feature +++ /dev/null @@ -1 +0,0 @@ -Rich Text Composer: added link creation/editing feature. \ No newline at end of file diff --git a/changelog.d/7177.feature b/changelog.d/7177.feature deleted file mode 100644 index 7b9eef21d..000000000 --- a/changelog.d/7177.feature +++ /dev/null @@ -1 +0,0 @@ -Rich Text Composer: added inline code formatting feature. \ No newline at end of file diff --git a/changelog.d/7179.feature b/changelog.d/7179.feature deleted file mode 100644 index d11f310a5..000000000 --- a/changelog.d/7179.feature +++ /dev/null @@ -1 +0,0 @@ -Voice Broadcast: allow to react on Voice Broadcast. diff --git a/changelog.d/7182.change b/changelog.d/7182.change deleted file mode 100644 index 353d878ae..000000000 --- a/changelog.d/7182.change +++ /dev/null @@ -1 +0,0 @@ -Updated fastlane script to use Xcode v 14.2. diff --git a/changelog.d/7192.bugfix b/changelog.d/7192.bugfix deleted file mode 100644 index 53eff608b..000000000 --- a/changelog.d/7192.bugfix +++ /dev/null @@ -1 +0,0 @@ -Voice Broadcast: Pause voice broadcast listening on new voice broadcast recording diff --git a/changelog.d/7232.bugfix b/changelog.d/7232.bugfix deleted file mode 100644 index 7f8e254b2..000000000 --- a/changelog.d/7232.bugfix +++ /dev/null @@ -1 +0,0 @@ - Direct Message: fixed a crash when a new DM room is created diff --git a/changelog.d/7235.bugfix b/changelog.d/7235.bugfix deleted file mode 100644 index 0e8a70a7f..000000000 --- a/changelog.d/7235.bugfix +++ /dev/null @@ -1 +0,0 @@ -Voice Broadcast: Prevent sending voice message during a voice broadcast recording diff --git a/changelog.d/pr-7146.change b/changelog.d/pr-7146.change deleted file mode 100644 index 0b5a8f2e6..000000000 --- a/changelog.d/pr-7146.change +++ /dev/null @@ -1 +0,0 @@ -Labs: VoiceBroadcast: Add backward and forward buttons for playback diff --git a/changelog.d/pr-7160.change b/changelog.d/pr-7160.change deleted file mode 100644 index d105a1dc8..000000000 --- a/changelog.d/pr-7160.change +++ /dev/null @@ -1 +0,0 @@ -Update the room description in the rooms list in case of live broadcast (incoming or outgoing) \ No newline at end of file diff --git a/changelog.d/pr-7163.change b/changelog.d/pr-7163.change deleted file mode 100644 index eb5289c8f..000000000 --- a/changelog.d/pr-7163.change +++ /dev/null @@ -1 +0,0 @@ -Labs: VoiceBroadcast: Link the live icon color to the recording state diff --git a/changelog.d/pr-7164.change b/changelog.d/pr-7164.change deleted file mode 100644 index 48a618f9d..000000000 --- a/changelog.d/pr-7164.change +++ /dev/null @@ -1 +0,0 @@ -Add old device data from user's account data events. diff --git a/changelog.d/pr-7165.change b/changelog.d/pr-7165.change deleted file mode 100644 index 0a4d54aee..000000000 --- a/changelog.d/pr-7165.change +++ /dev/null @@ -1 +0,0 @@ -Labs: VoiceBroadcast: Replace the player timeline diff --git a/changelog.d/pr-7173.bugfix b/changelog.d/pr-7173.bugfix deleted file mode 100644 index 64fb469a5..000000000 --- a/changelog.d/pr-7173.bugfix +++ /dev/null @@ -1 +0,0 @@ -Labs: Crash on new voice broadcast if the room has avatar diff --git a/changelog.d/pr-7175.change b/changelog.d/pr-7175.change deleted file mode 100644 index e29c02b44..000000000 --- a/changelog.d/pr-7175.change +++ /dev/null @@ -1 +0,0 @@ -Labs: VoiceBroadcast: Update Voice Broadcast recorder cell by adjusting some padding values diff --git a/changelog.d/pr-7178.change b/changelog.d/pr-7178.change deleted file mode 100644 index a5dd6e17c..000000000 --- a/changelog.d/pr-7178.change +++ /dev/null @@ -1 +0,0 @@ -Labs: VoiceBroadcast: Update live badge layout for recorder and player cells diff --git a/changelog.d/pr-7180.change b/changelog.d/pr-7180.change deleted file mode 100644 index 80cb1db19..000000000 --- a/changelog.d/pr-7180.change +++ /dev/null @@ -1 +0,0 @@ -Updates on the UI/UX to conform the device manager to the design. diff --git a/changelog.d/pr-7188.change b/changelog.d/pr-7188.change deleted file mode 100644 index bd6f7056d..000000000 --- a/changelog.d/pr-7188.change +++ /dev/null @@ -1 +0,0 @@ -Labs: VoiceBroadcast: Handle potential crash whereas a voice broadcast is in progress diff --git a/changelog.d/pr-7206.change b/changelog.d/pr-7206.change deleted file mode 100644 index 89d4c8e36..000000000 --- a/changelog.d/pr-7206.change +++ /dev/null @@ -1 +0,0 @@ -Polls: show decryption errors in timeline during aggregations. diff --git a/changelog.d/pr-7214.change b/changelog.d/pr-7214.change deleted file mode 100644 index e0b5e1f44..000000000 --- a/changelog.d/pr-7214.change +++ /dev/null @@ -1 +0,0 @@ -Device Manager: change fallback display name for sessions. diff --git a/changelog.d/pr-7220.bugfix b/changelog.d/pr-7220.bugfix deleted file mode 100644 index 2b8477eed..000000000 --- a/changelog.d/pr-7220.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix hidden live location timeline tiles after text messages diff --git a/changelog.d/pr-7230.change b/changelog.d/pr-7230.change deleted file mode 100644 index d5b48aadc..000000000 --- a/changelog.d/pr-7230.change +++ /dev/null @@ -1 +0,0 @@ -Ignore the voice broadcast chunks at the notifications level \ No newline at end of file diff --git a/changelog.d/pr-7231.change b/changelog.d/pr-7231.change deleted file mode 100644 index 288daeae1..000000000 --- a/changelog.d/pr-7231.change +++ /dev/null @@ -1 +0,0 @@ -Polls: render the poll ended event in the timeline. diff --git a/changelog.d/pr-7244.bugfix b/changelog.d/pr-7244.bugfix deleted file mode 100644 index 324e4e86a..000000000 --- a/changelog.d/pr-7244.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix an issue preventing temporary audio files to be deleted. diff --git a/changelog.d/x-nolink-0.change b/changelog.d/x-nolink-0.change deleted file mode 100644 index 06a8c63b2..000000000 --- a/changelog.d/x-nolink-0.change +++ /dev/null @@ -1 +0,0 @@ -Upgrade MatrixSDK version ([v0.24.7](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.24.7)). \ No newline at end of file