From 9797a61384ed576614fe667cac8f73dd84f3df64 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 7 Dec 2022 16:29:44 +0100 Subject: [PATCH 001/229] 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 6882f507a72d92e45605ad1212f2291725ca1d92 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 7 Dec 2022 18:17:28 +0100 Subject: [PATCH 002/229] 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 66c20094d22c33770adf583abc3e18fa871e3759 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 7 Dec 2022 19:22:33 +0100 Subject: [PATCH 003/229] 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 8f6505ccacb1cfe5f93ce86ce4d7b1296e3f33dd Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 8 Dec 2022 14:54:13 +0100 Subject: [PATCH 004/229] 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 b41870e5990fad3651b2be990d447a1936a6becc Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 8 Dec 2022 14:58:15 +0100 Subject: [PATCH 005/229] 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 133a66188bc08ba330dcd3ac774cea76437d307c Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 15:39:57 +0100 Subject: [PATCH 006/229] 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 8d41c5a5e2a54c1b572cd642c6968ace179fecff Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 15:40:21 +0100 Subject: [PATCH 007/229] 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 27c4ba45fd901aa7448e5f1b3632249417dbe54f Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 17:34:06 +0100 Subject: [PATCH 008/229] 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 26e735b0b99f7a0e516353e6360e579fd2dc45e0 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 18:29:13 +0100 Subject: [PATCH 009/229] 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 1dc8ecffa51b4d47fb5c188e66eaaccbb10e7ddb Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 18:50:26 +0100 Subject: [PATCH 010/229] 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 74a608b4abeeed8feb7fe9734e7103837e4dd6a3 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 19:09:15 +0100 Subject: [PATCH 011/229] 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 bdc94eb8b280617f688f1a319e1e16f5298ddf66 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 19:09:22 +0100 Subject: [PATCH 012/229] 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 fac9294fe12823c9c37755a83bb41f880e1a15cb Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Mon, 12 Dec 2022 19:26:45 +0100 Subject: [PATCH 013/229] 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 1c6373cd228767e5a52ea9c8ec618c432c63ac88 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 10:30:47 +0100 Subject: [PATCH 014/229] 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 5cc86f235d93b985d888602c71b13c64d33197e6 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 10:37:28 +0100 Subject: [PATCH 015/229] 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 0f8642181ecfa4727eb1f92a4b5c7f062ce463af Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 13:56:49 +0100 Subject: [PATCH 016/229] 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 a423199aa48431d01e074f2c544fef86418cfeb6 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 13 Dec 2022 14:19:55 +0100 Subject: [PATCH 017/229] 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 386c3580acf369f79498513c11665331a4314228 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 15:00:46 +0100 Subject: [PATCH 018/229] 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 70aa594d0f1e6456555a7ecf62735fcacb257071 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Tue, 13 Dec 2022 15:07:21 +0100 Subject: [PATCH 019/229] 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 4201c0060ec7fdfa4e4aa5b4e0dcc5d38f663092 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 13 Dec 2022 15:35:18 +0100 Subject: [PATCH 020/229] 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 da14628802533022fcf506a71db959585051486e Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 13 Dec 2022 15:45:37 +0100 Subject: [PATCH 021/229] 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 8d6a2931151845d6c72625d7a7008f302b1877f5 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 13 Dec 2022 15:46:53 +0100 Subject: [PATCH 022/229] 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 98d2a0d3cff187aa4650cb27d00eaaa552e59be4 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 15:58:33 +0100 Subject: [PATCH 023/229] 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 f56c085ee968c8b4dae2aaac3957a5b779ce90ec Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 16:58:26 +0100 Subject: [PATCH 024/229] 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 3ca926fd3ee3448566d5dcd903f47cd91081fd9c Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 17:06:12 +0100 Subject: [PATCH 025/229] 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 f830973d59ddb50ef1ba9c9f8f52c62c5bc42772 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Tue, 13 Dec 2022 18:12:47 +0100 Subject: [PATCH 026/229] 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 5b77d15e3c9be7ab989e8988807d158f8d849d9c Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Wed, 14 Dec 2022 17:12:06 +0100 Subject: [PATCH 027/229] 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 ad0ecfb843003a8c8f1b3f128b38c7b7807de9a9 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 10:34:43 +0100 Subject: [PATCH 028/229] 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 95b9f78388a925c11341cc5083cf57d125140288 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 10:38:30 +0100 Subject: [PATCH 029/229] 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 223cbd96720e745752d3fd0abd25f8584f7ccbfb Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 16:34:57 +0100 Subject: [PATCH 030/229] 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 ce49090cb81d2d8720d75d6f6de76af4f04fa17f Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 16:35:31 +0100 Subject: [PATCH 031/229] 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 62e1f76240204b692374842c31802280cf5daf87 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Wed, 14 Dec 2022 16:47:54 +0100 Subject: [PATCH 032/229] 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 556176606b78c81b32662a06d71c65bdbc54c07b Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Wed, 14 Dec 2022 16:30:53 +0000 Subject: [PATCH 033/229] 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 1935b4d6c666c1fb722353c0d4c6d8c1a2b16f07 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 00:33:28 +0100 Subject: [PATCH 034/229] 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 43ee462f5896b4b6f4ceb8c8c5b46a8e07e5115e Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 01:41:45 +0100 Subject: [PATCH 035/229] 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 abc8529c260421c44030808b12eac38aab25b173 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 01:44:26 +0100 Subject: [PATCH 036/229] 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 f4993bb317d3280664e3c8099246c2bffb3d1981 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 01:51:33 +0100 Subject: [PATCH 037/229] 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 01107d5f6318f0fdf248eebf6bd4790f41f4edae Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 03:37:15 +0100 Subject: [PATCH 038/229] 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 00af62ebf6ab9534e7527ab0fd1a51df152a14f4 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 03:40:15 +0100 Subject: [PATCH 039/229] 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 cc07823870575bdada774073c4c3faf0d8525010 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 04:28:01 +0100 Subject: [PATCH 040/229] 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 2054cf2adca33a91e5426313790554ccf47fe905 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 04:29:49 +0100 Subject: [PATCH 041/229] Revert "removed underline from links" This reverts commit 3744d03556ffb89be5c9c7980e7322bbfad894f2. --- .../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 afa6621eddc7426b4e221f4fa801ea1aa03c47ec Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 12:36:44 +0100 Subject: [PATCH 042/229] 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 3dd9636dbd487d1df5c12662bf3fc636a616d3f9 Mon Sep 17 00:00:00 2001 From: Phl-Pro Date: Thu, 15 Dec 2022 15:49:57 +0100 Subject: [PATCH 043/229] 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 6010e87d38c1c258100ba75f958b20a8e0a0ff38 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 16:33:25 +0100 Subject: [PATCH 044/229] 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 540e90da05a65e5ee95012259b5bd820d6df2f16 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 15 Dec 2022 15:42:58 +0100 Subject: [PATCH 045/229] 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 fd1947ad81108f78b4d20b0089a0091ba6be96e2 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 15 Dec 2022 15:44:28 +0100 Subject: [PATCH 046/229] 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 a908802360471adca56f833a63a4f7c2c696d07f Mon Sep 17 00:00:00 2001 From: giomfo Date: Thu, 15 Dec 2022 16:46:09 +0100 Subject: [PATCH 047/229] 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 1f6cd4ca4f9d59848287cdd835c76de9ff043a89 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 15 Dec 2022 17:01:52 +0100 Subject: [PATCH 048/229] 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 9f6732d33ab4f01d6aab8377d59fe9224d92561a Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Thu, 15 Dec 2022 17:04:10 +0100 Subject: [PATCH 049/229] 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 3953b7a3f2ce69fbeb62d2fda8ecd8913672e5fe Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 18:45:33 +0100 Subject: [PATCH 050/229] 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 d4075ba5e9827a21b71ee1e127a4052941a1efce Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Thu, 15 Dec 2022 18:49:43 +0100 Subject: [PATCH 051/229] 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 b621727a1bf7d52113d050f1336e09edc6db3315 Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Fri, 16 Dec 2022 16:40:46 +0100 Subject: [PATCH 052/229] 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 3b3ec455282f1c554b7d2cdad6365e1fd9fb519a Mon Sep 17 00:00:00 2001 From: Philippe Loriaux Date: Fri, 16 Dec 2022 16:43:14 +0100 Subject: [PATCH 053/229] 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 4b4ca8393263368387fae05eb93e1341e20d9964 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 16:00:37 +0100 Subject: [PATCH 054/229] 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 53f29e48308fe5b6adb6e7086aad3b5676e53c8f Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 16:06:17 +0100 Subject: [PATCH 055/229] 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 d1c63bd68512e79094c09fea08adbdcd04e51543 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:11:44 +0100 Subject: [PATCH 056/229] 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 aed8b495993033566a6caf449c9ad0228851fd5b Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:12:59 +0100 Subject: [PATCH 057/229] 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 4c8c54a301f17b135e20a960186c66cbffa460db Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:34:54 +0100 Subject: [PATCH 058/229] 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 cbfbf6c51b8b6bdc24b5059d9590585880ba77ec Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:38:05 +0100 Subject: [PATCH 059/229] 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 aae1a10630103f9989f84cbc20177e658634a878 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 18:47:18 +0100 Subject: [PATCH 060/229] 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 6f8d4f3c9717b3c1854d197738ede2722761f99b Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 21:28:49 +0100 Subject: [PATCH 061/229] 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 b918c2ccd35b314dfe1b933d927fc1e2d00597ea Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 15 Dec 2022 21:47:36 +0100 Subject: [PATCH 062/229] 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 3c8511273ba8414bd18427624d7ad6e843750e33 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 10:57:12 +0100 Subject: [PATCH 063/229] 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 5dc7386982e324fc994b77d0c22fef9d48f68c51 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 11:07:11 +0100 Subject: [PATCH 064/229] 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 4b8693e3562eb2693f3658da7d0c14acc16e313b Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 11:27:55 +0100 Subject: [PATCH 065/229] 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 115e1444ea570d9e61a1d7630cee08875f58b9e3 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 11:34:14 +0100 Subject: [PATCH 066/229] 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 c8490bf51f60b80f088a90da86a603fb43aeda28 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 11:48:39 +0100 Subject: [PATCH 067/229] 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 04df68cc185a5ee32212637c5b0789cab78c6e7d Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 14:52:46 +0100 Subject: [PATCH 068/229] 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 4b37f75444ed52f83bdcd346fc10b7cda9c65871 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 14:58:30 +0100 Subject: [PATCH 069/229] 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 e1291c8ff61bf863900e27ce8862c36e6a1744b3 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 15:09:28 +0100 Subject: [PATCH 070/229] 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 9d797d9eaaa8c46e262a0310e1cf00a65c4fc742 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 15:27:46 +0100 Subject: [PATCH 071/229] 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 995e67776b226b95090047e849f29697319e25ec Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:02:20 +0100 Subject: [PATCH 072/229] 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 fbbe69fc0d85f3a42a272eaecf6689c1a74a5331 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:05:55 +0100 Subject: [PATCH 073/229] =?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 f1f8544e7ce7b1e1251b896f946aa13736afc155 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:25:56 +0100 Subject: [PATCH 074/229] 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 aede0e3b8eec28a1b4c1d17900526fc4dbe84b96 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:36:58 +0100 Subject: [PATCH 075/229] 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 fdb258a85e914cef1b45fc42e765239cadedfefd Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 17:51:12 +0100 Subject: [PATCH 076/229] 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 16572120ee4db53db418c73f6f24b536117a0117 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 16 Dec 2022 18:23:40 +0100 Subject: [PATCH 077/229] 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 91686e77c98d96f028ab05db0f6dc02885aa0ea5 Mon Sep 17 00:00:00 2001 From: Mauro Romito Date: Fri, 16 Dec 2022 18:28:54 +0100 Subject: [PATCH 078/229] 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 173a7ed7ba806fcf94a2d34d17cf6d439f5d5f49 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 19 Dec 2022 14:56:06 +0100 Subject: [PATCH 079/229] 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 151f3ac81aac7632f50958f12b1332cbd007f321 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 19 Dec 2022 15:12:52 +0100 Subject: [PATCH 080/229] 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 09365943c35642253cf88b112491ac9ac957abc1 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 19 Dec 2022 17:59:38 +0100 Subject: [PATCH 081/229] 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 79e9f8ee40ee929fa4919482477ed70d43ffac97 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Mon, 19 Dec 2022 19:26:55 +0100 Subject: [PATCH 082/229] 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 887a703360290d081804ade7756d30ddba0d2cf3 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Mon, 19 Dec 2022 19:32:01 +0100 Subject: [PATCH 083/229] [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 ff075c7052d86736c2b776a0c125bf397951b436 Mon Sep 17 00:00:00 2001 From: Aleksandrs Proskurins Date: Tue, 20 Dec 2022 12:08:29 +0200 Subject: [PATCH 084/229] 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 c946009c247862231e65c5f44d061a3cb8a90fa0 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 23 Dec 2022 12:06:33 +0100 Subject: [PATCH 085/229] 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 359f44cb30f5b4984aba0c6e582621bf33f9c676 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 23 Dec 2022 12:08:25 +0100 Subject: [PATCH 086/229] 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 746391ae239619420eba66da67e9f380772594d1 Mon Sep 17 00:00:00 2001 From: Phl-Pro Date: Fri, 23 Dec 2022 15:25:52 +0100 Subject: [PATCH 087/229] 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 f67dfbabf7b3a27dda607953e7009f89a41a98df Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 23 Dec 2022 15:48:53 +0100 Subject: [PATCH 088/229] 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 353138a8b84aa0cc8bba4e91aa9b98aa893cfa79 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 23 Dec 2022 16:41:25 +0100 Subject: [PATCH 089/229] 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 3a333eff5d0e0e298249852504e23a540f648806 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 28 Dec 2022 11:03:15 +0100 Subject: [PATCH 090/229] 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 816ec1c457d51f4d85aea1923e770a993e819c28 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 11:37:06 +0100 Subject: [PATCH 091/229] 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 1ebefc514c82db39fce87d1af9d556d76a181fba Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 11:46:15 +0100 Subject: [PATCH 092/229] 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 3d61e335461c94da9c2a806c5a6f6cae0e6aafa3 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 12:13:56 +0100 Subject: [PATCH 093/229] 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 844c358af483efd700d1f508268b1b21a3ae7edd Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 2 Jan 2023 19:49:52 +0100 Subject: [PATCH 094/229] 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 03af34deacd2eeb27d99c2e7ec3bb2df129bf72f Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 3 Jan 2023 13:25:56 +0100 Subject: [PATCH 095/229] 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 7ce46679778bb6a7ec31f696e8a93cea0127e63a Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Tue, 3 Jan 2023 13:57:23 +0100 Subject: [PATCH 096/229] 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 40232ec721d3d2a9b5f4753de855df442c4dd995 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Tue, 3 Jan 2023 14:03:45 +0100 Subject: [PATCH 097/229] 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 c794a4f2668fd056221c9545388d3f56ade28ff3 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 3 Jan 2023 15:39:08 +0100 Subject: [PATCH 098/229] 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 38fd8705fd9e3ce85ef08713e4097d52f344b268 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Tue, 3 Jan 2023 15:54:05 +0100 Subject: [PATCH 099/229] 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 1d96d94e657d36197933d8428806f4ee884037d0 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Thu, 5 Jan 2023 10:47:01 +0100 Subject: [PATCH 100/229] 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 8df9c9677438317e65ae008f5d8b4787e69e569b Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Thu, 5 Jan 2023 13:59:28 +0100 Subject: [PATCH 101/229] 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 b334a4a46b8a3dd4ddbd6505abadb3503cc7c17d Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Thu, 5 Jan 2023 16:43:04 +0100 Subject: [PATCH 102/229] 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 315bed3fe663e84c4c43cfd556e583e4441df236 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Fri, 6 Jan 2023 12:14:08 +0100 Subject: [PATCH 103/229] 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 250e3c5a05b90079327be34c1afde76068e88b18 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Fri, 6 Jan 2023 16:14:43 +0100 Subject: [PATCH 104/229] 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 2f07359985943f99fec5d8930e10649151dbfe04 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Fri, 6 Jan 2023 16:22:19 +0100 Subject: [PATCH 105/229] 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 905a7bb3ea4b19ed10bca69e139397dd6ec17915 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Fri, 6 Jan 2023 17:24:07 +0100 Subject: [PATCH 106/229] 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 43fe62b25191c0d01dd22adfb54a1727ee236349 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Fri, 6 Jan 2023 22:36:55 +0100 Subject: [PATCH 107/229] 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 9a792f605344edb4e07326e841c52f0d9efb900f Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Mon, 9 Jan 2023 14:18:31 +0100 Subject: [PATCH 108/229] 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 9845d161a5e04190f2893d7f7303b7c6c5d667ab Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Mon, 9 Jan 2023 14:46:26 +0100 Subject: [PATCH 109/229] 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 69b37c09498b9cc83d16fd22a741b9dd81972826 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 9 Jan 2023 15:48:35 +0100 Subject: [PATCH 110/229] 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 adafc14eebfe9489ee84d3b765df8c1054b8691c Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Mon, 9 Jan 2023 16:17:21 +0100 Subject: [PATCH 111/229] 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 7b24fba5f51df407242165b0de7f23b7429661f4 Mon Sep 17 00:00:00 2001 From: Nicolas Mauri Date: Mon, 9 Jan 2023 09:30:25 +0100 Subject: [PATCH 112/229] 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 27023ef03765408a422b4e6b1ddd4352c586fbf9 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Tue, 10 Jan 2023 09:32:18 +0000 Subject: [PATCH 113/229] 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 df855e0553cbf9ba8e34a71e572b9ea0d547ff5f Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Tue, 10 Jan 2023 09:54:17 +0000 Subject: [PATCH 114/229] 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 51ce8cae932f5e2ad0920b0bb4b61d2173db9f4b Mon Sep 17 00:00:00 2001 From: Toomore Chiang Date: Fri, 16 Dec 2022 05:12:36 +0000 Subject: [PATCH 115/229] 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 50c13fef007ac950c4d8a00e14e1f28a539fbca3 Mon Sep 17 00:00:00 2001 From: Toomore Chiang Date: Fri, 16 Dec 2022 05:26:26 +0000 Subject: [PATCH 116/229] 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 978673bc4158a9924d65491bf1c2e9eab4015d11 Mon Sep 17 00:00:00 2001 From: pk'Mysickz Date: Thu, 5 Jan 2023 12:24:24 +0000 Subject: [PATCH 117/229] 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 c13e8c4efd81cedbf2061997981fdb5924271fb6 Mon Sep 17 00:00:00 2001 From: Christian Paul Date: Thu, 5 Jan 2023 16:59:50 +0000 Subject: [PATCH 118/229] 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 cc4fbfa661c404004c6607ec3d6b198d551510f7 Mon Sep 17 00:00:00 2001 From: Vri Date: Tue, 29 Nov 2022 16:01:26 +0000 Subject: [PATCH 119/229] 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 d2da677bdf0f1637710a544ea2f6de373364f4ad Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 30 Nov 2022 17:33:00 +0000 Subject: [PATCH 120/229] 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 72bf87fd51210ba189fc154b7ce657bae167c2ab 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/229] 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 6aeaef66df11879a10d97ca2b3d873b8ff953b86 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 29 Nov 2022 19:36:59 +0000 Subject: [PATCH 122/229] 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 a2a2ae34519e68a0a8583cd2700a5b7fe4139a2b 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/229] 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 e13e8738295a870d6ee82a844a0444941a46010f Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 29 Nov 2022 23:31:35 +0000 Subject: [PATCH 124/229] 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 8c29197f4fa1cf3ca7f6d0dcc9c8c231383d454e Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Tue, 29 Nov 2022 15:52:35 +0000 Subject: [PATCH 125/229] 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 c598c001d5d9126e9d96b7ef743c2f569cf6317d Mon Sep 17 00:00:00 2001 From: Christina Klaas Date: Fri, 2 Dec 2022 08:42:02 +0000 Subject: [PATCH 126/229] 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 86a31c94cabf6ba27672ae0ed143ab25690f37d0 Mon Sep 17 00:00:00 2001 From: Vri Date: Thu, 1 Dec 2022 07:51:45 +0000 Subject: [PATCH 127/229] 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 bd358cdf1e11da4c1bf37c5be1ce3eaaa756da11 Mon Sep 17 00:00:00 2001 From: random Date: Thu, 1 Dec 2022 09:13:36 +0000 Subject: [PATCH 128/229] 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 b733778ccb86a652bc7b26c505936616ca8e193d Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Thu, 1 Dec 2022 09:17:03 +0000 Subject: [PATCH 129/229] 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 df582ac24cf32c6bf3f7e613bf8942cd370ab9e1 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/229] 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 6b8aa2b1d05c956ae9eb7e1c41bc8666553a5764 Mon Sep 17 00:00:00 2001 From: Linerly Date: Thu, 1 Dec 2022 11:57:27 +0000 Subject: [PATCH 131/229] 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 5bae529c3825575897b5bd475e1c57b30f8800ed Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Thu, 1 Dec 2022 21:32:55 +0000 Subject: [PATCH 132/229] 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 88b94da8773fa9dc022d562ec77d8a6642855940 Mon Sep 17 00:00:00 2001 From: Vri Date: Fri, 2 Dec 2022 11:51:30 +0000 Subject: [PATCH 133/229] 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 1c8c2e1146f25313d19ed0a02941bd43e018374d Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 2 Dec 2022 10:48:29 +0000 Subject: [PATCH 134/229] 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 6d377aa2bdd7c677e7996543de83b06afc55c198 Mon Sep 17 00:00:00 2001 From: Linerly Date: Fri, 2 Dec 2022 09:16:45 +0000 Subject: [PATCH 135/229] 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 39d54fd589289e29a57c9d2254f00e46a5b607e7 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Fri, 2 Dec 2022 09:48:20 +0000 Subject: [PATCH 136/229] 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 0c1c8bbe6d54389d130155bc748ed5a0297f2cc9 Mon Sep 17 00:00:00 2001 From: AnnaKuznietsovaua Date: Sun, 4 Dec 2022 19:27:27 +0000 Subject: [PATCH 137/229] 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 268aaa9128e8c3503eab3a1003209993ca1b85e5 Mon Sep 17 00:00:00 2001 From: Platon Terekhov Date: Sun, 4 Dec 2022 19:14:48 +0000 Subject: [PATCH 138/229] 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 e291499da098bad86cde24f618478cc31197f2bd Mon Sep 17 00:00:00 2001 From: MomentQYC Date: Sun, 4 Dec 2022 12:56:16 +0000 Subject: [PATCH 139/229] 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 da767533e3e480ece8adf8df803ca94bf923f788 Mon Sep 17 00:00:00 2001 From: Platon Terekhov Date: Sun, 4 Dec 2022 19:29:08 +0000 Subject: [PATCH 140/229] 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 ba7b1de14f300d52282fd7ad76e7b4ebbb66018b 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/229] 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 5363daba73252ba9a431387814092d273dc3d757 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Fri, 2 Dec 2022 17:59:41 +0000 Subject: [PATCH 142/229] 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 9dce34f79d5d9fba0118d9ffa8f13d9d9c3d5a8a 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/229] 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 2823d2250950a9e23cbcf43fdb9f643b3b762015 Mon Sep 17 00:00:00 2001 From: Vri Date: Mon, 5 Dec 2022 16:49:02 +0000 Subject: [PATCH 144/229] 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 0b3a4d299eeb2ecc19c644a87b0c3451b5f3d22f Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Mon, 5 Dec 2022 10:27:17 +0000 Subject: [PATCH 145/229] 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 6dc23f589492c605fd14a210f75552c82873289e Mon Sep 17 00:00:00 2001 From: random Date: Mon, 5 Dec 2022 13:04:56 +0000 Subject: [PATCH 146/229] 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 00e450713ccd4a89fd1aad09e5045353b7148a5e Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Mon, 5 Dec 2022 10:23:33 +0000 Subject: [PATCH 147/229] 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 8c5dd40a5c4c82e8fe0683b6abc3b0c7129fc792 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/229] 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 a562cd42b35011eecabf7d3c374749c357e99d33 Mon Sep 17 00:00:00 2001 From: Linerly Date: Mon, 5 Dec 2022 13:42:51 +0000 Subject: [PATCH 149/229] 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 47893233702b5b2ed9c1dccaacabaca1e5cfeab8 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Mon, 5 Dec 2022 13:47:09 +0000 Subject: [PATCH 150/229] 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 aa4e3700e9952c968872733ed79f6b3b09776395 Mon Sep 17 00:00:00 2001 From: Vri Date: Tue, 6 Dec 2022 15:47:38 +0000 Subject: [PATCH 151/229] 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 36911f4884a5fb1020baf51ac1eadb2ed9b16aab Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 6 Dec 2022 20:21:32 +0000 Subject: [PATCH 152/229] 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 4809fd4bd8432e9290867a8afa2c12f2c8e769a1 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/229] 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 f2a45ff7086e3d027c8b8cfc55f19f71ad1ba125 Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 6 Dec 2022 22:43:35 +0000 Subject: [PATCH 154/229] 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 b76e3a122f86e13924fd77495cf3b21c3bf0be34 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Tue, 6 Dec 2022 19:13:57 +0000 Subject: [PATCH 155/229] 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 adc0edaf4bf241ab54cff074d5dbb2345895e0e8 Mon Sep 17 00:00:00 2001 From: phardyle Date: Sun, 11 Dec 2022 06:47:05 +0000 Subject: [PATCH 156/229] 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 cab62b23b36815d98b5c25cee9cd41cd5e03ecd6 Mon Sep 17 00:00:00 2001 From: random Date: Sat, 10 Dec 2022 17:35:28 +0000 Subject: [PATCH 157/229] 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 2d982017ad06c64b7af47fc9aa4e4c3a673964c9 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Mon, 12 Dec 2022 13:52:16 +0000 Subject: [PATCH 158/229] 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 cb0b20b67362b34d29a87304e3749d34e6ed49bd Mon Sep 17 00:00:00 2001 From: Linerly Date: Mon, 12 Dec 2022 11:35:33 +0000 Subject: [PATCH 159/229] 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 dd4f13ef6af3c05636e7ffcf37e54997a7853c4c Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 13 Dec 2022 08:54:02 +0000 Subject: [PATCH 160/229] 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 3ebeb8509e92591ae82c0a83d3e2f27e90b11a96 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/229] 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 b3148bc4a13851e1f9fc6c9a8e89808b8a5d2a4a Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 14 Dec 2022 12:39:36 +0000 Subject: [PATCH 162/229] 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 85086c6eb9b1a32d6aad3b46ca2ab428edf49a78 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Wed, 14 Dec 2022 13:45:53 +0000 Subject: [PATCH 163/229] 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 8cf3587cb3b81eba102387cc8396422ab497d788 Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 14 Dec 2022 17:50:32 +0000 Subject: [PATCH 164/229] 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 56f41e21e6ee3bfaac1a026c8380951b89ba5db1 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 15 Dec 2022 07:21:11 +0000 Subject: [PATCH 165/229] 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 595b3c562f5b87a52a9792ca16ba0d9577360f7e Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Wed, 14 Dec 2022 20:54:52 +0000 Subject: [PATCH 166/229] 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 ed184e3ea7794ac231ad9e981f6688a51e774e3b 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/229] 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 e7dfc01c454a1d9eb57367691462e6f47a08f8ba Mon Sep 17 00:00:00 2001 From: Linerly Date: Thu, 15 Dec 2022 11:11:12 +0000 Subject: [PATCH 168/229] 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 9cbd8c0d0919fc0b1e18b92a98df0e0f947d553b Mon Sep 17 00:00:00 2001 From: Toomore Chiang Date: Fri, 16 Dec 2022 05:39:37 +0000 Subject: [PATCH 169/229] 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 87b842e9877fceff2f0bd3ae7ccad0df32b7947d Mon Sep 17 00:00:00 2001 From: Knugi Date: Fri, 16 Dec 2022 05:39:58 +0000 Subject: [PATCH 170/229] 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 b34cc8be8676fd79f458ddd5d010ddb04fa07c0d Mon Sep 17 00:00:00 2001 From: Toomore Chiang Date: Fri, 16 Dec 2022 07:48:25 +0000 Subject: [PATCH 171/229] 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 0189962977aedf67461a3bab6fa194e6d523cf97 Mon Sep 17 00:00:00 2001 From: random Date: Fri, 16 Dec 2022 14:44:22 +0000 Subject: [PATCH 172/229] 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 a5ef290b2c65f854539156ab4bc53069eb41c886 Mon Sep 17 00:00:00 2001 From: Vri Date: Sat, 17 Dec 2022 12:57:07 +0000 Subject: [PATCH 173/229] 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 8c35db7169c48cbde157c322207cfe9e561fcd19 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 16 Dec 2022 19:14:44 +0000 Subject: [PATCH 174/229] 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 f9ae0dd3f1a2ec30e892eb186c7cc3c9bcb4e3e9 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Fri, 16 Dec 2022 18:18:30 +0000 Subject: [PATCH 175/229] 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 9229e7702fee644431fb1a3e913254a258e6dfe8 Mon Sep 17 00:00:00 2001 From: Linerly Date: Sat, 17 Dec 2022 10:12:22 +0000 Subject: [PATCH 176/229] 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 a4ed845ecfb2bd41816a0f885f2d5f6db4098659 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Fri, 16 Dec 2022 18:07:31 +0000 Subject: [PATCH 177/229] 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 58e514bf4bda4f9d6743a33e184cf6283f006de3 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sun, 18 Dec 2022 20:32:14 +0000 Subject: [PATCH 178/229] 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 95de88ebb60bd30e0ffaa765769a4f0eb264e190 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/229] 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 30e5a80ad7799de761a538bf877be8f11f85fa66 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Tue, 20 Dec 2022 08:35:16 +0000 Subject: [PATCH 180/229] 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 958f0797c21098f7f6f38b97e68c4b1119b0e8d8 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Mon, 19 Dec 2022 19:00:06 +0000 Subject: [PATCH 181/229] 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 514c1cb567f508d22592f7678e080e23ba164dee 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/229] 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 2c865e4bc8314ae971562e86ee70d374c473b1e0 Mon Sep 17 00:00:00 2001 From: Linerly Date: Mon, 19 Dec 2022 23:08:12 +0000 Subject: [PATCH 183/229] 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 11eac37da35da0142cca87789fec6bd570f4fba7 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Mon, 19 Dec 2022 20:59:01 +0000 Subject: [PATCH 184/229] 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 0ff3ab26848e28914824f4ccceb5ea55ed4e74d8 Mon Sep 17 00:00:00 2001 From: Vri Date: Wed, 21 Dec 2022 08:02:50 +0000 Subject: [PATCH 185/229] 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 2a07dd7f93c7084399e9eb7153bc39f0b672e468 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 22 Dec 2022 08:46:07 +0000 Subject: [PATCH 186/229] 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 70888755532301a9ca0a6e33137cdd092576ae5b Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 21 Dec 2022 16:34:38 +0000 Subject: [PATCH 187/229] 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 c6db782fcab374a7faa9bf1c534b727b76d73023 Mon Sep 17 00:00:00 2001 From: random Date: Wed, 21 Dec 2022 09:45:02 +0000 Subject: [PATCH 188/229] 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 fbacd66a1806dbb77fc3a74eedd9ce0bcc8511ae 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/229] 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 520b5efca24e762ba1ec04c3530438164368cefb Mon Sep 17 00:00:00 2001 From: Linerly Date: Sun, 25 Dec 2022 15:46:06 +0000 Subject: [PATCH 190/229] 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 ee8ad490f55902f51e67f882da8ea2c5a8c4202d Mon Sep 17 00:00:00 2001 From: Aleksey Grigoryev Date: Thu, 29 Dec 2022 12:31:28 +0000 Subject: [PATCH 191/229] 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 0da1234fd284a0bc20ae2d2e34e3e828bdb2eb9e Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:44:56 +0000 Subject: [PATCH 192/229] 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 406cebabe14ad77b321427fd5aa550c1151bc844 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:47:54 +0000 Subject: [PATCH 193/229] 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 fd51de605f82357736914d51561a268b711083e0 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:45:26 +0000 Subject: [PATCH 194/229] 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 2a0e941bc953f08fc0ad9ebeb21e217b465d44e4 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:46:23 +0000 Subject: [PATCH 195/229] 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 e78901359446263d4197a07c710beada7c17aa80 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Wed, 4 Jan 2023 17:01:34 +0000 Subject: [PATCH 196/229] 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 6e4685b23d1fc8cf6a491adc42289fbe86d51db5 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:47:35 +0000 Subject: [PATCH 197/229] 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 70d3ff00f8e3ad8d7b66ba563a5343bc968df180 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:48:15 +0000 Subject: [PATCH 198/229] 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 9f5ad0aa1c3b50f99da30ae4328d08509dddb53e Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:48:45 +0000 Subject: [PATCH 199/229] 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 fba3c93f0674204f42b1ef6a5d29067adddeeb37 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:45:56 +0000 Subject: [PATCH 200/229] 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 7bb85f5f4692fe5f2c6fe1a3f03ff8bca374aed4 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:47:22 +0000 Subject: [PATCH 201/229] 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 c11cc2310a7aa63a4418d04ffbd482c8d16c9d25 Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Wed, 4 Jan 2023 15:48:32 +0000 Subject: [PATCH 202/229] 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 753c98e7c030a63cf50f354915375a72c18e0502 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Thu, 5 Jan 2023 16:59:21 +0000 Subject: [PATCH 203/229] 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 d04cc05ea957173b440229fe4b1cdc6a0767b515 Mon Sep 17 00:00:00 2001 From: Christian Paul Date: Mon, 9 Jan 2023 10:42:26 +0000 Subject: [PATCH 204/229] 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 6c63a1469b9306b5dee09f9981cda74b66600c75 Mon Sep 17 00:00:00 2001 From: Vri Date: Tue, 10 Jan 2023 05:25:49 +0000 Subject: [PATCH 205/229] 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 4ac853181ac70c2a57f5ef3ffa3402845144cd6d Mon Sep 17 00:00:00 2001 From: Szimszon Date: Mon, 9 Jan 2023 19:50:23 +0000 Subject: [PATCH 206/229] 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 a5f96a2be1db5f8683c26e7b97250b50973c98d9 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Mon, 9 Jan 2023 18:04:25 +0000 Subject: [PATCH 207/229] 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 68ece018a7850b8b7f62c0b2612c9c3d183baecb 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/229] 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 dc675d4582aa8d76caa0de93bad90218694164ec Mon Sep 17 00:00:00 2001 From: Linerly Date: Mon, 9 Jan 2023 17:28:57 +0000 Subject: [PATCH 209/229] 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 55f9abc623190a7273c36bea58940674af7fce95 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Mon, 9 Jan 2023 23:51:52 +0000 Subject: [PATCH 210/229] 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 66197676faba437465d58387a599c663b4d94961 Mon Sep 17 00:00:00 2001 From: Vri Date: Tue, 10 Jan 2023 10:02:44 +0000 Subject: [PATCH 211/229] 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 604f6da94f8ebe30463974a064f033e5d57371e2 Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 10 Jan 2023 10:32:32 +0000 Subject: [PATCH 212/229] 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 562f36775d343a67160fa1c0214e2334907c6e59 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 28 Dec 2022 15:02:32 +0100 Subject: [PATCH 213/229] 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 39bb927fd5fe00cd325b87eb385e41003bdd77c5 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 28 Dec 2022 15:18:50 +0100 Subject: [PATCH 214/229] 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 6b477c764cc3d504e1a6ca9b859e2e0e974823c8 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 28 Dec 2022 16:35:49 +0100 Subject: [PATCH 215/229] 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 d0ed4639660d58f30c4344387229b291d6a40a5d Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 29 Dec 2022 16:30:59 +0100 Subject: [PATCH 216/229] 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 984874f97e5d24738b9251d2b3715455658d5e68 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 10 Jan 2023 11:19:10 +0000 Subject: [PATCH 217/229] 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 4bc37ba14618323844bf11e6d4a43b9e27c5d222 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 29 Dec 2022 17:17:57 +0100 Subject: [PATCH 218/229] 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 8596b3625780d7d32c010e87b424248d1c659c75 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Thu, 29 Dec 2022 17:38:09 +0100 Subject: [PATCH 219/229] 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 8b9de882e9d69a9cde87f49957c73f51cfc72f2d Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 09:41:45 +0100 Subject: [PATCH 220/229] 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 db7abaa7f5e99d0deebafbd65b1a609d7e157981 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 09:54:22 +0100 Subject: [PATCH 221/229] 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 69a85a40d2f3bf08a1c18eb4995b54fbf555b567 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 30 Dec 2022 11:25:42 +0100 Subject: [PATCH 222/229] 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 8eac6d053ce024f32efa76939e172d7d40d93d2b Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 4 Jan 2023 15:02:51 +0100 Subject: [PATCH 223/229] 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 40ea4406dcecd4d2e53876747db2a47355ac553a Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 4 Jan 2023 15:20:13 +0100 Subject: [PATCH 224/229] 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 659ffe82b0d1cc832275cc5a924dd46b8f9b4215 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 4 Jan 2023 15:29:01 +0100 Subject: [PATCH 225/229] 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 10b306abc69f09cfcb1cc3d81ac9ed522eb556a3 Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Wed, 4 Jan 2023 17:06:20 +0100 Subject: [PATCH 226/229] 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 8653be3f940bbefaac37cace80f0b9c7abe67e2f Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 10 Jan 2023 14:13:24 +0000 Subject: [PATCH 227/229] 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 a7a8fd4c63e58c4266406bac359c980d1bb620fb Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 10 Jan 2023 14:13:25 +0000 Subject: [PATCH 228/229] 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 From 939f873ffba0a38195632d76ed7a76706ce0f253 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 10 Jan 2023 16:50:13 +0000 Subject: [PATCH 229/229] finish version++ --- Podfile.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Podfile.lock b/Podfile.lock index 5244de7b2..dbc139f68 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -55,9 +55,9 @@ PODS: - LoggerAPI (1.9.200): - Logging (~> 1.1) - Logging (1.4.0) - - MatrixSDK (0.24.5): - - MatrixSDK/Core (= 0.24.5) - - MatrixSDK/Core (0.24.5): + - MatrixSDK (0.24.7): + - MatrixSDK/Core (= 0.24.7) + - MatrixSDK/Core (0.24.7): - AFNetworking (~> 4.0.0) - GZIP (~> 1.3.0) - libbase58 (~> 0.1.4) @@ -65,12 +65,12 @@ PODS: - OLMKit (~> 3.2.5) - Realm (= 10.27.0) - SwiftyBeaver (= 1.9.5) - - MatrixSDK/CryptoSDK (0.24.5): - - MatrixSDKCrypto (= 0.1.5) - - MatrixSDK/JingleCallStack (0.24.5): + - MatrixSDK/CryptoSDK (0.24.7): + - MatrixSDKCrypto (= 0.1.7) + - MatrixSDK/JingleCallStack (0.24.7): - JitsiMeetSDK (= 5.0.2) - MatrixSDK/Core - - MatrixSDKCrypto (0.1.5) + - MatrixSDKCrypto (0.1.7) - OLMKit (3.2.12): - OLMKit/olmc (= 3.2.12) - OLMKit/olmcpp (= 3.2.12) @@ -122,8 +122,8 @@ DEPENDENCIES: - KeychainAccess (~> 4.2.2) - KTCenterFlowLayout (~> 1.3.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.24.5) - - MatrixSDK/JingleCallStack (= 0.24.5) + - MatrixSDK (= 0.24.7) + - MatrixSDK/JingleCallStack (= 0.24.7) - OLMKit - PostHog (~> 1.4.4) - ReadMoreTextView (~> 3.0.1) @@ -220,8 +220,8 @@ SPEC CHECKSUMS: libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75 LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d Logging: beeb016c9c80cf77042d62e83495816847ef108b - MatrixSDK: 1557b3ed0a211db43a865cfdad93f07c2be92c9e - MatrixSDKCrypto: dcab554bc7157cad31c01fc1137cf5acb01959a4 + MatrixSDK: 895929fad10b7ec9aa96d557403b44c5e3522211 + MatrixSDKCrypto: 2bd9ca41b2c644839f4e680a64897d56b3f95392 OLMKit: da115f16582e47626616874e20f7bb92222c7a51 PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d @@ -241,6 +241,6 @@ SPEC CHECKSUMS: zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: c93b326deaf9de3916d42a49d39d737612ab1d94 +PODFILE CHECKSUM: 56782e2abd382278b3c5b23824ca74994fd0a97e -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3