diff --git a/.swiftlint.yml b/.swiftlint.yml index 4d215eb98..f210c1ac3 100755 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -36,7 +36,7 @@ line_length: error: 1000 file_length: - warning: 700 + warning: 800 error: 1000 type_body_length: diff --git a/CHANGES.md b/CHANGES.md index c8f9b1ae3..78d2df426 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,55 @@ +## Changes in 1.8.2 (2022-02-22) + +✨ Features + +- Add Onboarding Use Case selection screen after the splash screen. ([#5160](https://github.com/vector-im/element-ios/issues/5160)) + +🙌 Improvements + +- Upgrade MatrixSDK version ([v0.22.2](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.22.2)). +- ActivityCenter: Use ActivityCenter to show loading indicators on the home screen (in DEBUG builds only) ([#4829](https://github.com/vector-im/element-ios/issues/4829)) +- Enabled poll editing and undisclosed polls. Added support for unstable poll prefixes. ([#5114](https://github.com/vector-im/element-ios/issues/5114)) +- Filter: update placeholder text and icon ([#5250](https://github.com/vector-im/element-ios/issues/5250)) +- Create Room: Update avatar placeholder & add remove button ([#5251](https://github.com/vector-im/element-ios/issues/5251)) +- Search: remove bubbles background ([#5471](https://github.com/vector-im/element-ios/issues/5471)) +- Exclude all files and directories from iCloud and iTunes backup ([#5498](https://github.com/vector-im/element-ios/issues/5498)) +- ThreadListViewModel: Use new apis to fetch threads. ([#5540](https://github.com/vector-im/element-ios/issues/5540)) +- Search: Use bundled aggregations if provided. ([#5562](https://github.com/vector-im/element-ios/issues/5562)) +- MXKRoomDataSource: Stop pagination in a thread when the root event received. ([#5582](https://github.com/vector-im/element-ios/issues/5582)) +- Add support for UserProperties to analytics and capture FTUE use case selection. ([#5590](https://github.com/vector-im/element-ios/issues/5590)) +- Add attribution to location sharing maps. ([#5609](https://github.com/vector-im/element-ios/issues/5609)) +- Onboarding: Use a different green spinner during onboarding and use the one presented by the LegacyAppDelegate only when logged in. ([#5621](https://github.com/vector-im/element-ios/issues/5621)) +- MXKRoomDataSource: Enable usage of thread timelines. ([#5629](https://github.com/vector-im/element-ios/issues/5629)) + +🐛 Bugfixes + +- Home Tab: Initial support for navigating through the room lists using voiceover. ([#1433](https://github.com/vector-im/element-ios/issues/1433)) +- Authent: fix phone number validation through custom URL ([#3562](https://github.com/vector-im/element-ios/issues/3562)) +- Fix registration to be compliant with the Matrix specification. This allows registering for accounts on Conduit servers. Contributed by @aaronraimist. ([#3736](https://github.com/vector-im/element-ios/issues/3736)) +- Fix proximity sensor staying on and sleep timer staying disabled after call ends ([#4103](https://github.com/vector-im/element-ios/issues/4103)) +- Fonts: Fix dynamic type only working after a fresh launch on SwiftUI views. ([#5027](https://github.com/vector-im/element-ios/issues/5027)) +- Fixed arithmetical exception errors when changing poll responses. ([#5114](https://github.com/vector-im/element-ios/issues/5114)) +- Wordings: Replace "kick" and all affiliate word by "remove" ([#5346](https://github.com/vector-im/element-ios/issues/5346)) +- Markdown/HTML: Fix HTTP links containing Markdown formatting ([#5355](https://github.com/vector-im/element-ios/issues/5355)) +- Message Bubbles: Fix read marker appearing part way thru a message. ([#5521](https://github.com/vector-im/element-ios/issues/5521)) +- HomeViewController: Refresh section badges and tab bar badges on updates. ([#5537](https://github.com/vector-im/element-ios/issues/5537)) +- Update the tintColor in ThemeV1 to sRGB to match the Compound and ThemeV2. ([#5545](https://github.com/vector-im/element-ios/issues/5545)) +- Message bubbles: Increase text message width. ([#5550](https://github.com/vector-im/element-ios/issues/5550)) +- Message bubbles: Fix edited text message `edited` link not working. ([#5553](https://github.com/vector-im/element-ios/issues/5553)) +- Message bubbles: Fix horizontal lines between messages. ([#5555](https://github.com/vector-im/element-ios/issues/5555)) +- App Launch: Fix a potential issue where the green spinner is kept on screen when the room lists are ready. ([#5559](https://github.com/vector-im/element-ios/issues/5559)) +- Authentication: Fix reCaptcha failing to indicate success. ([#5602](https://github.com/vector-im/element-ios/issues/5602)) +- Timeline: scroll to the bottom when opening a notification ([#5639](https://github.com/vector-im/element-ios/issues/5639)) + +Others + +- Fixed or ignored various project warnings for better DevX ([#5513](https://github.com/vector-im/element-ios/pull/5513)) +- SwiftGen: Objective-C support for assets helpers ([#5533](https://github.com/vector-im/element-ios/pull/5533)) +- Fix introspect not being able to theme the SwiftUI navigation bars. ([#5556](https://github.com/vector-im/element-ios/pull/5556)) +- Message bubbles: Reduce sender name bottom margin for text message. ([#5634](https://github.com/vector-im/element-ios/pull/5634)) +- Message bubbles: Use layout constants instead magic numbers. ([#5409](https://github.com/vector-im/element-ios/issues/5409)) + + ## Changes in 1.8.1 (2022-02-16) 🙌 Improvements diff --git a/CommonKit/Common.xcconfig b/CommonKit/Common.xcconfig new file mode 100644 index 000000000..38afbcfe3 --- /dev/null +++ b/CommonKit/Common.xcconfig @@ -0,0 +1,28 @@ +// +// Copyright 2021 Vector Creations 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. +// + +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +#include "Config/AppIdentifiers.xcconfig" +#include "Config/AppVersion.xcconfig" + +PRODUCT_NAME = CommonKit +PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).commonkit + +INFOPLIST_FILE = CommonKit/Info.plist + +SKIP_INSTALL = YES diff --git a/CommonKit/CommonKit.h b/CommonKit/CommonKit.h new file mode 100644 index 000000000..b057961e6 --- /dev/null +++ b/CommonKit/CommonKit.h @@ -0,0 +1,27 @@ +// +// Copyright 2021 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 + +//! Project version number for CommonKit. +FOUNDATION_EXPORT double CommonKitVersionNumber; + +//! Project version string for CommonKit. +FOUNDATION_EXPORT const unsigned char CommonKitVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/CommonKit/Debug.xcconfig b/CommonKit/Debug.xcconfig new file mode 100644 index 000000000..11a7288a4 --- /dev/null +++ b/CommonKit/Debug.xcconfig @@ -0,0 +1,20 @@ +// +// Copyright 2021 Vector Creations 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. +// + +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +#include "Common.xcconfig" diff --git a/CommonKit/Info.plist b/CommonKit/Info.plist new file mode 100644 index 000000000..c0701c6d7 --- /dev/null +++ b/CommonKit/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/CommonKit/Release.xcconfig b/CommonKit/Release.xcconfig new file mode 100644 index 000000000..11a7288a4 --- /dev/null +++ b/CommonKit/Release.xcconfig @@ -0,0 +1,20 @@ +// +// Copyright 2021 Vector Creations 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. +// + +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +#include "Common.xcconfig" diff --git a/CommonKit/Source/Activity/Activity.swift b/CommonKit/Source/Activity/Activity.swift new file mode 100644 index 000000000..d72e87800 --- /dev/null +++ b/CommonKit/Source/Activity/Activity.swift @@ -0,0 +1,94 @@ +// +// Copyright 2021 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 UIKit + +/// An `Activity` represents the state of a temporary visual indicator, such as activity indicator, success notification or an error message. It does not directly manage the UI, instead it delegates to a `presenter` +/// whenever the UI should be shown or hidden. +/// +/// More than one `Activity` may be requested by the system at the same time (e.g. global syncing vs local refresh), +/// and the `ActivityCenter` will ensure that only one activity is shown at a given time, putting the other in a pending queue. +/// +/// A client that requests an activity can specify a default timeout after which the activity is dismissed, or it has to be manually +/// responsible for dismissing it via `cancel` method, or by deallocating itself. +public class Activity { + enum State { + case pending + case executing + case completed + } + + private let request: ActivityRequest + private let completion: () -> Void + + private(set) var state: State + + public init(request: ActivityRequest, completion: @escaping () -> Void) { + self.request = request + self.completion = completion + + state = .pending + } + + deinit { + cancel() + } + + internal func start() { + guard state == .pending else { + return + } + + state = .executing + request.presenter.present() + + switch request.dismissal { + case .manual: + break + case .timeout(let interval): + Timer.scheduledTimer(withTimeInterval: interval, repeats: false) { [weak self] _ in + self?.complete() + } + } + } + + /// Cancel the activity, triggering any dismissal action / animation + /// + /// Note: clients can call this method directly, if they have access to the `Activity`. + /// Once cancelled, `ActivityCenter` will automatically start the next `Activity` in the queue. + func cancel() { + complete() + } + + private func complete() { + guard state != .completed else { + return + } + if state == .executing { + request.presenter.dismiss() + } + + state = .completed + completion() + } +} + +public extension Activity { + func store(in collection: inout C) where C: RangeReplaceableCollection, C.Element == Activity { + collection.append(self) + } +} diff --git a/CommonKit/Source/Activity/ActivityCenter.swift b/CommonKit/Source/Activity/ActivityCenter.swift new file mode 100644 index 000000000..9d9e0c704 --- /dev/null +++ b/CommonKit/Source/Activity/ActivityCenter.swift @@ -0,0 +1,60 @@ +// +// Copyright 2021 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 + +/// A shared activity center with a single FIFO queue which will ensure only one activity is shown at a given time. +/// +/// `ActivityCenter` offers a `shared` center that can be used by any clients, but clients are also allowed +/// to create local `ActivityCenter` if the context requres multiple simultaneous activities. +public class ActivityCenter { + private class Weak { + weak var element: T? + init(_ element: T) { + self.element = element + } + } + + public static let shared = ActivityCenter() + private var queue = [Weak]() + + /// Add a new activity to the queue by providing a request. + /// + /// The queue will start the activity right away, if there are no currently running activities, + /// otherwise the activity will be put on hold. + public func add(_ request: ActivityRequest) -> Activity { + let activity = Activity(request: request) { [weak self] in + self?.startNextIfIdle() + } + + queue.append(Weak(activity)) + startNextIfIdle() + return activity + } + + private func startNextIfIdle() { + cleanup() + if let activity = queue.first?.element, activity.state == .pending { + activity.start() + } + } + + private func cleanup() { + queue.removeAll { + $0.element == nil || $0.element?.state == .completed + } + } +} diff --git a/CommonKit/Source/Activity/ActivityDismissal.swift b/CommonKit/Source/Activity/ActivityDismissal.swift new file mode 100644 index 000000000..69405c579 --- /dev/null +++ b/CommonKit/Source/Activity/ActivityDismissal.swift @@ -0,0 +1,25 @@ +// +// Copyright 2021 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 + +/// Different ways in which an `Activity` can be dismissed +public enum ActivityDismissal { + /// The `Activity` will not manage the dismissal, but will expect the calling client to do so manually + case manual + /// The `Activity` will be automatically dismissed after `TimeInterval` + case timeout(TimeInterval) +} diff --git a/CommonKit/Source/Activity/ActivityPresentable.swift b/CommonKit/Source/Activity/ActivityPresentable.swift new file mode 100644 index 000000000..5bb489fcc --- /dev/null +++ b/CommonKit/Source/Activity/ActivityPresentable.swift @@ -0,0 +1,25 @@ +// +// Copyright 2021 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 + +/// A presenter associated with and called by an `Activity`, and responsible for the underlying view shown on the screen. +public protocol ActivityPresentable { + /// Called when the `Activity` is started (manually or by the `ActivityCenter`) + func present() + /// Called when the `Activity` is manually cancelled or completed + func dismiss() +} diff --git a/Riot/Modules/Common/Alert/AlertPresentable.swift b/CommonKit/Source/Activity/ActivityRequest.swift similarity index 52% rename from Riot/Modules/Common/Alert/AlertPresentable.swift rename to CommonKit/Source/Activity/ActivityRequest.swift index d0ee0dcd4..f009fc211 100644 --- a/Riot/Modules/Common/Alert/AlertPresentable.swift +++ b/CommonKit/Source/Activity/ActivityRequest.swift @@ -16,21 +16,13 @@ import Foundation -/// AlertPresentable absracts an alert presenter -protocol AlertPresentable { +/// A request used to create an underlying `Activity`, allowing clients to only specify the visual aspects of an activity. +public struct ActivityRequest { + internal let presenter: ActivityPresentable + internal let dismissal: ActivityDismissal - func showError(_ error: Error, animated: Bool, completion: (() -> Void)?) - func show(title: String?, message: String?, animated: Bool, completion: (() -> Void)?) -} - -// MARK: Default implementation -extension AlertPresentable { - - func showError(_ error: Error) { - self.showError(error, animated: true, completion: nil) - } - - func show(title: String?, message: String?) { - self.show(title: title, message: message, animated: true, completion: nil) + public init(presenter: ActivityPresentable, dismissal: ActivityDismissal) { + self.presenter = presenter + self.dismissal = dismissal } } diff --git a/CommonKit/Source/Activity/Tests/ActivityCenterTests.swift b/CommonKit/Source/Activity/Tests/ActivityCenterTests.swift new file mode 100644 index 000000000..a1e375851 --- /dev/null +++ b/CommonKit/Source/Activity/Tests/ActivityCenterTests.swift @@ -0,0 +1,55 @@ +// +// Copyright 2021 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 XCTest + +class ActivityCenterTests: XCTestCase { + var activities: [Activity]! + var center: ActivityCenter! + + override func setUp() { + activities = [] + center = ActivityCenter() + } + + func makeRequest() -> ActivityRequest { + return ActivityRequest( + presenter: ActivityPresenterSpy(), + dismissal: .manual + ) + } + + func testStartsActivityWhenAdded() { + let activity = center.add(makeRequest()) + XCTAssertEqual(activity.state, .executing) + } + + func testSecondActivityIsPending() { + center.add(makeRequest()).store(in: &activities) + let activity = center.add(makeRequest()) + XCTAssertEqual(activity.state, .pending) + } + + func testSecondActivityIsExecutingWhenFirstCompleted() { + let first = center.add(makeRequest()) + let second = center.add(makeRequest()) + + first.cancel() + + XCTAssertEqual(second.state, .executing) + } +} diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomCellLayoutConstants.swift b/CommonKit/Source/Activity/Tests/ActivityPresenterSpy.swift similarity index 58% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomCellLayoutConstants.swift rename to CommonKit/Source/Activity/Tests/ActivityPresenterSpy.swift index 2a978f82d..2a0d03249 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomCellLayoutConstants.swift +++ b/CommonKit/Source/Activity/Tests/ActivityPresenterSpy.swift @@ -16,14 +16,14 @@ import Foundation - -/// Bubble style room cell layout constants -@objcMembers -final class BubbleRoomCellLayoutConstants: NSObject { +class ActivityPresenterSpy: ActivityPresentable { + var intel = [String]() - static let outgoingBubbleBackgroundMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 80, bottom: 0, right: 34) - - static let incomingBubbleBackgroundMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 80) + func present() { + intel.append(#function) + } - static let threadSummaryViewMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 5, right: 0) + func dismiss() { + intel.append(#function) + } } diff --git a/CommonKit/Source/Activity/Tests/ActivityTests.swift b/CommonKit/Source/Activity/Tests/ActivityTests.swift new file mode 100644 index 000000000..11cc7eec2 --- /dev/null +++ b/CommonKit/Source/Activity/Tests/ActivityTests.swift @@ -0,0 +1,127 @@ +// +// Copyright 2021 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 XCTest + +class ActivityTests: XCTestCase { + var presenter: ActivityPresenterSpy! + + override func setUp() { + super.setUp() + presenter = ActivityPresenterSpy() + } + + func makeActivity(dismissal: ActivityDismissal = .manual, callback: @escaping () -> Void = {}) -> Activity { + let request = ActivityRequest( + presenter: presenter, + dismissal: dismissal + ) + return Activity( + request: request, + completion: callback + ) + } + + // MARK: - State + + func testNewActivityIsPending() { + let activity = makeActivity() + XCTAssertEqual(activity.state, .pending) + } + + func testStartedActivityIsExecuting() { + let activity = makeActivity() + activity.start() + XCTAssertEqual(activity.state, .executing) + } + + func testCancelledActivityIsCompleted() { + let activity = makeActivity() + activity.cancel() + XCTAssertEqual(activity.state, .completed) + } + + // MARK: - Presenter + + func testStartingActivityPresentsUI() { + let activity = makeActivity() + activity.start() + XCTAssertEqual(presenter.intel, ["present()"]) + } + + func testAllowStartingOnlyOnce() { + let activity = makeActivity() + activity.start() + presenter.intel = [] + + activity.start() + + XCTAssertEqual(presenter.intel, []) + } + + func testCancellingActivityDismissesUI() { + let activity = makeActivity() + activity.start() + presenter.intel = [] + + activity.cancel() + + XCTAssertEqual(presenter.intel, ["dismiss()"]) + } + + func testAllowCancellingOnlyOnce() { + let activity = makeActivity() + activity.start() + activity.cancel() + presenter.intel = [] + + activity.cancel() + + XCTAssertEqual(presenter.intel, []) + } + + // MARK: - Dismissal + + func testDismissAfterTimeout() { + let interval: TimeInterval = 0.01 + let activity = makeActivity(dismissal: .timeout(interval)) + + activity.start() + + let exp = expectation(description: "") + DispatchQueue.main.asyncAfter(deadline: .now() + interval) { + exp.fulfill() + } + waitForExpectations(timeout: 1) + + XCTAssertEqual(activity.state, .completed) + } + + // MARK: - Completion callback + + func testTriggersCallbackWhenCompleted() { + var didComplete = false + let activity = makeActivity { + didComplete = true + } + activity.start() + + activity.cancel() + + XCTAssertTrue(didComplete) + } +} diff --git a/CommonKit/target.yml b/CommonKit/target.yml new file mode 100644 index 000000000..fad6e8375 --- /dev/null +++ b/CommonKit/target.yml @@ -0,0 +1,40 @@ +name: CommonKit + +schemes: + CommonKit: + analyze: + config: Debug + archive: + config: Release + build: + targets: + CommonKit: + - running + - testing + - profiling + - analyzing + - archiving + profile: + config: Release + run: + config: Debug + disableMainThreadChecker: true + test: + config: Debug + disableMainThreadChecker: true + targets: + - CommonKitUnitTests + +targets: + CommonKit: + type: framework + platform: iOS + + configFiles: + Debug: Debug.xcconfig + Release: Release.xcconfig + + sources: + - path: . + excludes: + - "**/Tests/**" diff --git a/CommonKit/targetUnitTests.yml b/CommonKit/targetUnitTests.yml new file mode 100644 index 000000000..4a8f8a7b2 --- /dev/null +++ b/CommonKit/targetUnitTests.yml @@ -0,0 +1,42 @@ +name: CommonKitUnitTests + +schemes: + CommonKitUnitTests: + analyze: + config: Debug + archive: + config: Release + build: + targets: + CommonKitUnitTests: + - running + - testing + - profiling + - analyzing + - archiving + profile: + config: Release + run: + config: Debug + disableMainThreadChecker: true + test: + config: Debug + disableMainThreadChecker: true + targets: + - CommonKitUnitTests + +targets: + CommonKitUnitTests: + type: bundle.unit-test + platform: iOS + + dependencies: + - target: CommonKit + + configFiles: + Debug: Debug.xcconfig + Release: Release.xcconfig + + sources: + - path: . + diff --git a/Config/AppVersion.xcconfig b/Config/AppVersion.xcconfig index 3fe634b3f..de6ddc959 100644 --- a/Config/AppVersion.xcconfig +++ b/Config/AppVersion.xcconfig @@ -15,5 +15,5 @@ // // Version -MARKETING_VERSION = 1.8.1 -CURRENT_PROJECT_VERSION = 1.8.1 +MARKETING_VERSION = 1.8.2 +CURRENT_PROJECT_VERSION = 1.8.2 diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 305f4dddc..3348ba964 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -146,8 +146,6 @@ final class BuildSettings: NSObject { static let stunServerFallbackUrlString: String? = "stun:turn.matrix.org" // MARK: - Public rooms Directory - #warning("Unused build setting: should this be implemented in ShowDirectory?") - static let publicRoomsAllowServerChange: Bool = true // List of homeservers for the public rooms directory static let publicRoomsDirectoryServers = [ "matrix.org", @@ -212,6 +210,15 @@ final class BuildSettings: NSObject { static let allowInviteExernalUsers: Bool = true + /// Whether a screen uses legacy local activity indicators or improved app-wide indicators + static var appActivityIndicators: Bool { + #if DEBUG + return false + #else + return false + #endif + } + // MARK: - Side Menu static let enableSideMenu: Bool = true static let sideMenuShowInviteFriends: Bool = true diff --git a/Config/Project-Warnings.xcconfig b/Config/Project-Warnings.xcconfig index 8c9512128..350f6dd29 100644 --- a/Config/Project-Warnings.xcconfig +++ b/Config/Project-Warnings.xcconfig @@ -46,3 +46,7 @@ GCC_WARN_UNDECLARED_SELECTOR = YES GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE GCC_WARN_UNUSED_FUNCTION = YES GCC_WARN_UNUSED_VARIABLE = YES + +WARNING_CFLAGS = $(inherited) -Wno-nullability-completeness; +OTHER_SWIFT_FLAGS = $(inherited) -Xcc -Wno-nullability-completeness + diff --git a/DesignKit/Source/FontsSwiftUI.swift b/DesignKit/Source/FontsSwiftUI.swift index ddf6a1754..a12a537ff 100644 --- a/DesignKit/Source/FontsSwiftUI.swift +++ b/DesignKit/Source/FontsSwiftUI.swift @@ -23,7 +23,7 @@ import SwiftUI @available(iOS 14.0, *) public struct FontSwiftUI: Fonts { - public let uiFonts: ElementFonts + public let uiFonts: FontsUIKit public var largeTitle: Font @@ -66,27 +66,27 @@ public struct FontSwiftUI: Fonts { public var caption2SB: Font public init(values: ElementFonts) { - self.uiFonts = values + self.uiFonts = FontsUIKit(values: values) - self.largeTitle = Font(values.largeTitle) - self.largeTitleB = Font(values.largeTitleB) - self.title1 = Font(values.title1) - self.title1B = Font(values.title1B) - self.title2 = Font(values.title2) - self.title2B = Font(values.title2B) - self.title3 = Font(values.title3) - self.title3SB = Font(values.title3SB) - self.headline = Font(values.headline) - self.subheadline = Font(values.subheadline) - self.body = Font(values.body) - self.bodySB = Font(values.bodySB) - self.callout = Font(values.callout) - self.calloutSB = Font(values.calloutSB) - self.footnote = Font(values.footnote) - self.footnoteSB = Font(values.footnoteSB) - self.caption1 = Font(values.caption1) - self.caption1SB = Font(values.caption1SB) - self.caption2 = Font(values.caption2) - self.caption2SB = Font(values.caption2SB) + self.largeTitle = values.largeTitle.font + self.largeTitleB = values.largeTitleB.font + self.title1 = values.title1.font + self.title1B = values.title1B.font + self.title2 = values.title2.font + self.title2B = values.title2B.font + self.title3 = values.title3.font + self.title3SB = values.title3SB.font + self.headline = values.headline.font + self.subheadline = values.subheadline.font + self.body = values.body.font + self.bodySB = values.bodySB.font + self.callout = values.callout.font + self.calloutSB = values.calloutSB.font + self.footnote = values.footnote.font + self.footnoteSB = values.footnoteSB.font + self.caption1 = values.caption1.font + self.caption1SB = values.caption1SB.font + self.caption2 = values.caption2.font + self.caption2SB = values.caption2SB.font } } diff --git a/DesignKit/Source/FontsUIkit.swift b/DesignKit/Source/FontsUIkit.swift index 3067d03a1..ec65cdaa6 100644 --- a/DesignKit/Source/FontsUIkit.swift +++ b/DesignKit/Source/FontsUIkit.swift @@ -63,25 +63,25 @@ import UIKit public var caption2SB: UIFont public init(values: ElementFonts) { - self.largeTitle = values.largeTitle - self.largeTitleB = values.largeTitleB - self.title1 = values.title1 - self.title1B = values.title1B - self.title2 = values.title2 - self.title2B = values.title2B - self.title3 = values.title3 - self.title3SB = values.title3SB - self.headline = values.headline - self.subheadline = values.subheadline - self.body = values.body - self.bodySB = values.bodySB - self.callout = values.callout - self.calloutSB = values.calloutSB - self.footnote = values.footnote - self.footnoteSB = values.footnoteSB - self.caption1 = values.caption1 - self.caption1SB = values.caption1SB - self.caption2 = values.caption2 - self.caption2SB = values.caption2SB + self.largeTitle = values.largeTitle.uiFont + self.largeTitleB = values.largeTitleB.uiFont + self.title1 = values.title1.uiFont + self.title1B = values.title1B.uiFont + self.title2 = values.title2.uiFont + self.title2B = values.title2B.uiFont + self.title3 = values.title3.uiFont + self.title3SB = values.title3SB.uiFont + self.headline = values.headline.uiFont + self.subheadline = values.subheadline.uiFont + self.body = values.body.uiFont + self.bodySB = values.bodySB.uiFont + self.callout = values.callout.uiFont + self.calloutSB = values.calloutSB.uiFont + self.footnote = values.footnote.uiFont + self.footnoteSB = values.footnoteSB.uiFont + self.caption1 = values.caption1.uiFont + self.caption1SB = values.caption1SB.uiFont + self.caption2 = values.caption2.uiFont + self.caption2SB = values.caption2SB.uiFont } } diff --git a/DesignKit/Variants/Fonts/ElementFonts.swift b/DesignKit/Variants/Fonts/ElementFonts.swift index 6612d3dc1..d7538c905 100644 --- a/DesignKit/Variants/Fonts/ElementFonts.swift +++ b/DesignKit/Variants/Fonts/ElementFonts.swift @@ -14,12 +14,42 @@ // limitations under the License. // -import UIKit +import SwiftUI /// Fonts at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0 @objcMembers public class ElementFonts { + // MARK: - Types + + /// A wrapper to provide both a `UIFont` and a SwiftUI `Font` in the same type. + /// The need for this comes from `Font` not adapting for dynamic type until the app + /// is restarted (or working at all in Xcode Previews) when initialised from a `UIFont` + /// (even if that font was created with the appropriate metrics). + public struct SharedFont { + public let uiFont: UIFont + /// The underlying font for the `font` property. This is stored + /// as an optional `Any` due to unavailability on iOS 12. + private let _font: Any? + + @available(iOS 13.0, *) + public var font: Font { + _font as! Font + } + + @available(iOS, deprecated: 13.0, message: "Use init(uiFont:font:) instead and remove this initialiser.") + init(uiFont: UIFont) { + self.uiFont = uiFont + self._font = nil + } + + @available(iOS 13.0, *) + init(uiFont: UIFont, font: Font) { + self.uiFont = uiFont + self._font = font + } + } + // MARK: - Setup public init() { @@ -35,85 +65,217 @@ public class ElementFonts { } // MARK: - Fonts protocol -extension ElementFonts: Fonts { +extension ElementFonts: Fonts { - public var largeTitle: UIFont { - return self.font(forTextStyle: .largeTitle) + public var largeTitle: SharedFont { + let uiFont = self.font(forTextStyle: .largeTitle) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .largeTitle) + } else { + return SharedFont(uiFont: uiFont) + } } - public var largeTitleB: UIFont { - return self.largeTitle.vc_bold + public var largeTitleB: SharedFont { + let uiFont = self.largeTitle.uiFont.vc_bold + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .largeTitle.bold()) + } else { + return SharedFont(uiFont: uiFont) + } } - public var title1: UIFont { - return self.font(forTextStyle: .title1) + public var title1: SharedFont { + let uiFont = self.font(forTextStyle: .title1) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .title) + } else { + return SharedFont(uiFont: uiFont) + } } - public var title1B: UIFont { - return self.title1.vc_bold + public var title1B: SharedFont { + let uiFont = self.title1.uiFont.vc_bold + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .title.bold()) + } else { + return SharedFont(uiFont: uiFont) + } } - public var title2: UIFont { - return self.font(forTextStyle: .title2) + public var title2: SharedFont { + let uiFont = self.font(forTextStyle: .title2) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: Font(uiFont)) + } else if #available(iOS 14.0, *) { + return SharedFont(uiFont: uiFont, font: .title2) + } else { + return SharedFont(uiFont: uiFont) + } } - public var title2B: UIFont { - return self.title2.vc_bold + public var title2B: SharedFont { + let uiFont = self.title2.uiFont.vc_bold + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: Font(uiFont)) + } else if #available(iOS 14.0, *) { + return SharedFont(uiFont: uiFont, font: .title2.bold()) + } else { + return SharedFont(uiFont: uiFont) + } } - public var title3: UIFont { - return self.font(forTextStyle: .title3) + public var title3: SharedFont { + let uiFont = self.font(forTextStyle: .title3) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: Font(uiFont)) + } else if #available(iOS 14.0, *) { + return SharedFont(uiFont: uiFont, font: .title3) + } else { + return SharedFont(uiFont: uiFont) + } } - public var title3SB: UIFont { - return self.title3.vc_semiBold + public var title3SB: SharedFont { + let uiFont = self.title3.uiFont.vc_semiBold + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: Font(uiFont)) + } else if #available(iOS 14.0, *) { + return SharedFont(uiFont: uiFont, font: .title3.weight(.semibold)) + } else { + return SharedFont(uiFont: uiFont) + } } - public var headline: UIFont { - return self.font(forTextStyle: .headline) + public var headline: SharedFont { + let uiFont = self.font(forTextStyle: .headline) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .headline) + } else { + return SharedFont(uiFont: uiFont) + } } - public var subheadline: UIFont { - return self.font(forTextStyle: .subheadline) + public var subheadline: SharedFont { + let uiFont = self.font(forTextStyle: .subheadline) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .subheadline) + } else { + return SharedFont(uiFont: uiFont) + } } - public var body: UIFont { - return self.font(forTextStyle: .body) + public var body: SharedFont { + let uiFont = self.font(forTextStyle: .body) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .body) + } else { + return SharedFont(uiFont: uiFont) + } } - public var bodySB: UIFont { - return self.body.vc_semiBold + public var bodySB: SharedFont { + let uiFont = self.body.uiFont.vc_semiBold + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .body.weight(.semibold)) + } else { + return SharedFont(uiFont: uiFont) + } } - public var callout: UIFont { - return self.font(forTextStyle: .callout) + public var callout: SharedFont { + let uiFont = self.font(forTextStyle: .callout) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .callout) + } else { + return SharedFont(uiFont: uiFont) + } } - public var calloutSB: UIFont { - return self.callout.vc_semiBold + public var calloutSB: SharedFont { + let uiFont = self.callout.uiFont.vc_semiBold + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .callout.weight(.semibold)) + } else { + return SharedFont(uiFont: uiFont) + } } - public var footnote: UIFont { - return self.font(forTextStyle: .footnote) + public var footnote: SharedFont { + let uiFont = self.font(forTextStyle: .footnote) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .footnote) + } else { + return SharedFont(uiFont: uiFont) + } } - public var footnoteSB: UIFont { - return self.footnote.vc_semiBold + public var footnoteSB: SharedFont { + let uiFont = self.footnote.uiFont.vc_semiBold + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .footnote.weight(.semibold)) + } else { + return SharedFont(uiFont: uiFont) + } } - public var caption1: UIFont { - return self.font(forTextStyle: .caption1) + public var caption1: SharedFont { + let uiFont = self.font(forTextStyle: .caption1) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .caption) + } else { + return SharedFont(uiFont: uiFont) + } } - public var caption1SB: UIFont { - return self.caption1.vc_semiBold + public var caption1SB: SharedFont { + let uiFont = self.caption1.uiFont.vc_semiBold + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: .caption.weight(.semibold)) + } else { + return SharedFont(uiFont: uiFont) + } } - public var caption2: UIFont { - return self.font(forTextStyle: .caption2) + public var caption2: SharedFont { + let uiFont = self.font(forTextStyle: .caption2) + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: Font(uiFont)) + } else if #available(iOS 14.0, *) { + return SharedFont(uiFont: uiFont, font: .caption2) + } else { + return SharedFont(uiFont: uiFont) + } } - public var caption2SB: UIFont { - return self.caption2.vc_semiBold + public var caption2SB: SharedFont { + let uiFont = self.caption2.uiFont.vc_semiBold + + if #available(iOS 13.0, *) { + return SharedFont(uiFont: uiFont, font: Font(uiFont)) + } else if #available(iOS 14.0, *) { + return SharedFont(uiFont: uiFont, font: .caption2.weight(.semibold)) + } else { + return SharedFont(uiFont: uiFont) + } } } diff --git a/Podfile b/Podfile index 523f61fc5..4fd6535ef 100644 --- a/Podfile +++ b/Podfile @@ -13,7 +13,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.22.1' +$matrixSDKVersion = '= 0.22.2' # $matrixSDKVersion = :local # $matrixSDKVersion = { :branch => 'develop'} # $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } } @@ -49,7 +49,6 @@ end ######################################## def import_MatrixKit_pods - pod 'HPGrowingTextView', '~> 1.1' pod 'libPhoneNumber-iOS', '~> 0.9.13' pod 'DTCoreText', '~> 1.6.25' #pod 'DTCoreText/Extension', '~> 1.6.25' @@ -98,7 +97,7 @@ abstract_target 'RiotPods' do pod 'DSWaveformImage', '~> 6.1.1' pod 'ffmpeg-kit-ios-audio', '4.5.1' - pod 'FLEX', '~> 4.5.0', :configurations => ['Debug'] + pod 'FLEX', '~> 4.5.0', :configurations => ['Debug'], :inhibit_warnings => true target 'RiotTests' do inherit! :search_paths @@ -149,6 +148,10 @@ post_install do |installer| # Stop Xcode 12 complaining about old IPHONEOS_DEPLOYMENT_TARGET from pods config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' + + # Disable nullability checks + config.build_settings['WARNING_CFLAGS'] ||= ['$(inherited)','-Wno-nullability-completeness'] + config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)', '-Xcc', '-Wno-nullability-completeness'] end end end \ No newline at end of file diff --git a/Podfile.lock b/Podfile.lock index 942bc098f..6d6029a4c 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -45,7 +45,6 @@ PODS: - GBDeviceInfo/Core (= 6.6.0) - GBDeviceInfo/Core (6.6.0) - GZIP (1.3.0) - - HPGrowingTextView (1.1) - Introspect (0.1.3) - JitsiMeetSDK (3.10.2) - KeychainAccess (4.2.2) @@ -112,7 +111,6 @@ DEPENDENCIES: - FLEX (~> 4.5.0) - FlowCommoniOS (~> 1.12.0) - GBDeviceInfo (~> 6.6.0) - - HPGrowingTextView (~> 1.1) - Introspect (~> 0.1) - KeychainAccess (~> 4.2.2) - KTCenterFlowLayout (~> 1.3.1) @@ -149,7 +147,6 @@ SPEC REPOS: - FlowCommoniOS - GBDeviceInfo - GZIP - - HPGrowingTextView - Introspect - JitsiMeetSDK - KeychainAccess @@ -183,7 +180,7 @@ EXTERNAL SOURCES: CHECKOUT OPTIONS: AnalyticsEvents: - :commit: 8058dc6ec07ce0acfe5fdb19eb7e309b0c13845c + :commit: 53844e3f6f9fefa88384a996b2bf5e60bb301b94 :git: https://github.com/matrix-org/matrix-analytics-events.git SPEC CHECKSUMS: @@ -202,7 +199,6 @@ SPEC CHECKSUMS: FlowCommoniOS: ca92071ab526dc89905495a37844fd7e78d1a7f2 GBDeviceInfo: ed0db16230d2fa280e1cbb39a5a7f60f6946aaec GZIP: 416858efbe66b41b206895ac6dfd5493200d95b3 - HPGrowingTextView: 88a716d97fb853bcb08a4a08e4727da17efc9b19 Introspect: 2be020f30f084ada52bb4387fff83fa52c5c400e JitsiMeetSDK: 2f118fa770f23e518f3560fc224fae3ac7062223 KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51 @@ -229,6 +225,6 @@ SPEC CHECKSUMS: zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: ae70a46e98aae87f130ad3d246711fc6b6ae7286 +PODFILE CHECKSUM: 03d59acfc44f69c0727a98aff6c5cbc2470844ef COCOAPODS: 1.11.2 diff --git a/Riot/Assets/Base.lproj/LaunchScreen.storyboard b/Riot/Assets/Base.lproj/LaunchScreen.storyboard index 47f7c6d30..07c996ecf 100644 --- a/Riot/Assets/Base.lproj/LaunchScreen.storyboard +++ b/Riot/Assets/Base.lproj/LaunchScreen.storyboard @@ -1,9 +1,10 @@ - + - + + @@ -21,14 +22,14 @@ - + - + @@ -42,5 +43,8 @@ + + + diff --git a/Riot/Assets/Base.lproj/Main.storyboard b/Riot/Assets/Base.lproj/Main.storyboard index 4708d7dbc..885be7db7 100644 --- a/Riot/Assets/Base.lproj/Main.storyboard +++ b/Riot/Assets/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -46,24 +46,6 @@ - - - - - - - - - - - - - - - - - - @@ -116,14 +98,6 @@ - - - - - - - - @@ -501,24 +475,6 @@ - - - - - - - - - - - - - - - - - - diff --git a/Riot/Assets/Images.xcassets/Common/filter_off.imageset/Contents.json b/Riot/Assets/Images.xcassets/Common/filter_off.imageset/Contents.json new file mode 100644 index 000000000..0ada96c02 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Common/filter_off.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "filter_off.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "filter_off@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "filter_off@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off.png b/Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off.png new file mode 100644 index 000000000..7f42aaf6d Binary files /dev/null and b/Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off.png differ diff --git a/Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@2x.png b/Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@2x.png new file mode 100644 index 000000000..50b077fd2 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@3x.png b/Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@3x.png new file mode 100644 index 000000000..f03bf3c7b Binary files /dev/null and b/Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Search/search_bg.imageset/Contents.json b/Riot/Assets/Images.xcassets/Common/filter_on.imageset/Contents.json similarity index 58% rename from Riot/Assets/Images.xcassets/Search/search_bg.imageset/Contents.json rename to Riot/Assets/Images.xcassets/Common/filter_on.imageset/Contents.json index 568f8cb3f..7d1ae8514 100644 --- a/Riot/Assets/Images.xcassets/Search/search_bg.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/Common/filter_on.imageset/Contents.json @@ -1,23 +1,23 @@ { "images" : [ { + "filename" : "filter_on.png", "idiom" : "universal", - "filename" : "search_bg.png", "scale" : "1x" }, { + "filename" : "filter_on@2x.png", "idiom" : "universal", - "filename" : "search_bg@2x.png", "scale" : "2x" }, { + "filename" : "filter_on@3x.png", "idiom" : "universal", - "filename" : "search_bg@3x.png", "scale" : "3x" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on.png b/Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on.png new file mode 100644 index 000000000..7710fa90e Binary files /dev/null and b/Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on.png differ diff --git a/Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@2x.png b/Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@2x.png new file mode 100644 index 000000000..b9cff9c1b Binary files /dev/null and b/Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@3x.png b/Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@3x.png new file mode 100644 index 000000000..5fec0029d Binary files /dev/null and b/Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@3x.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Contents.json b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Contents.json index abf2a35a2..4d5b7ef83 100644 --- a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Contents.json @@ -1,17 +1,50 @@ { "images" : [ { - "filename" : "Group 3153.png", + "filename" : "capture_avatar.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "Group 3153@2x.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "capture_avatar_dark.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "capture_avatar@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "Group 3153@3x.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "capture_avatar_dark@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "capture_avatar@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "capture_avatar_dark@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Group 3153.png b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Group 3153.png deleted file mode 100644 index 621edca32..000000000 Binary files a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Group 3153.png and /dev/null differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Group 3153@2x.png b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Group 3153@2x.png deleted file mode 100644 index 8ea43080b..000000000 Binary files a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Group 3153@2x.png and /dev/null differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Group 3153@3x.png b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Group 3153@3x.png deleted file mode 100644 index c43b02a01..000000000 Binary files a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/Group 3153@3x.png and /dev/null differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar.png b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar.png new file mode 100644 index 000000000..ee020a419 Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar@2x.png b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar@2x.png new file mode 100644 index 000000000..db29b82de Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar@2x.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar@3x.png b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar@3x.png new file mode 100644 index 000000000..3ed02ca09 Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar@3x.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar_dark.png b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar_dark.png new file mode 100644 index 000000000..5036ec95f Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar_dark.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar_dark@2x.png b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar_dark@2x.png new file mode 100644 index 000000000..3dd16d8a8 Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar_dark@2x.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar_dark@3x.png b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar_dark@3x.png new file mode 100644 index 000000000..39bed95c0 Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/capture_avatar.imageset/capture_avatar_dark@3x.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/Contents.json b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/Contents.json new file mode 100644 index 000000000..a52435f30 --- /dev/null +++ b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/Contents.json @@ -0,0 +1,56 @@ +{ + "images" : [ + { + "filename" : "delete_avatar.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "delete_avatar_dark.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "delete_avatar@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "delete_avatar_dark@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "delete_avatar@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "delete_avatar_dark@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar.png b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar.png new file mode 100644 index 000000000..9a9633eb8 Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar@2x.png b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar@2x.png new file mode 100644 index 000000000..2d2aa1c8f Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar@2x.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar@3x.png b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar@3x.png new file mode 100644 index 000000000..e7c0e930c Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar@3x.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar_dark.png b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar_dark.png new file mode 100644 index 000000000..3326e8487 Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar_dark.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar_dark@2x.png b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar_dark@2x.png new file mode 100644 index 000000000..119522a87 Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar_dark@2x.png differ diff --git a/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar_dark@3x.png b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar_dark@3x.png new file mode 100644 index 000000000..2f9258882 Binary files /dev/null and b/Riot/Assets/Images.xcassets/CreateRoom/delete_avatar.imageset/delete_avatar_dark@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community.imageset/Contents.json new file mode 100644 index 000000000..9f6e8de0a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "onboarding_use_case_community.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community.imageset/onboarding_use_case_community.svg b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community.imageset/onboarding_use_case_community.svg new file mode 100644 index 000000000..6b58a7b56 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community.imageset/onboarding_use_case_community.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community_dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community_dark.imageset/Contents.json new file mode 100644 index 000000000..e34cfce4e --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community_dark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "onboarding_use_case_community_dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community_dark.imageset/onboarding_use_case_community_dark.svg b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community_dark.imageset/onboarding_use_case_community_dark.svg new file mode 100644 index 000000000..1fb2f8af1 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_community_dark.imageset/onboarding_use_case_community_dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_icon.imageset/Contents.json new file mode 100644 index 000000000..f4e9e56ac --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_icon.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "onboarding_use_case_icon.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_icon.imageset/onboarding_use_case_icon.svg b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_icon.imageset/onboarding_use_case_icon.svg new file mode 100644 index 000000000..2366becdb --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_icon.imageset/onboarding_use_case_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal.imageset/Contents.json new file mode 100644 index 000000000..1073b4087 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "onboarding_use_case_personal.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal.imageset/onboarding_use_case_personal.svg b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal.imageset/onboarding_use_case_personal.svg new file mode 100644 index 000000000..efd561c9a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal.imageset/onboarding_use_case_personal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal_dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal_dark.imageset/Contents.json new file mode 100644 index 000000000..081fb3fde --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal_dark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "onboarding_use_case_personal_dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal_dark.imageset/onboarding_use_case_personal_dark.svg b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal_dark.imageset/onboarding_use_case_personal_dark.svg new file mode 100644 index 000000000..b5c728adc --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_personal_dark.imageset/onboarding_use_case_personal_dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work.imageset/Contents.json new file mode 100644 index 000000000..40d6dcbdf --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "onboarding_use_case_work.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work.imageset/onboarding_use_case_work.svg b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work.imageset/onboarding_use_case_work.svg new file mode 100644 index 000000000..61cf7bd86 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work.imageset/onboarding_use_case_work.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work_dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work_dark.imageset/Contents.json new file mode 100644 index 000000000..da38b9aad --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work_dark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "onboarding_use_case_work_dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work_dark.imageset/onboarding_use_case_work_dark.svg b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work_dark.imageset/onboarding_use_case_work_dark.svg new file mode 100644 index 000000000..0f6ee387a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/onboarding_use_case_work_dark.imageset/onboarding_use_case_work_dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Riot/Assets/Images.xcassets/Search/Contents.json b/Riot/Assets/Images.xcassets/Search/Contents.json index da4a164c9..73c00596a 100644 --- a/Riot/Assets/Images.xcassets/Search/Contents.json +++ b/Riot/Assets/Images.xcassets/Search/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Riot/Assets/Images.xcassets/Search/search_bg.imageset/search_bg.png b/Riot/Assets/Images.xcassets/Search/search_bg.imageset/search_bg.png deleted file mode 100644 index b78748b04..000000000 Binary files a/Riot/Assets/Images.xcassets/Search/search_bg.imageset/search_bg.png and /dev/null differ diff --git a/Riot/Assets/Images.xcassets/Search/search_bg.imageset/search_bg@2x.png b/Riot/Assets/Images.xcassets/Search/search_bg.imageset/search_bg@2x.png deleted file mode 100644 index 284358c5e..000000000 Binary files a/Riot/Assets/Images.xcassets/Search/search_bg.imageset/search_bg@2x.png and /dev/null differ diff --git a/Riot/Assets/Images.xcassets/Search/search_bg.imageset/search_bg@3x.png b/Riot/Assets/Images.xcassets/Search/search_bg.imageset/search_bg@3x.png deleted file mode 100644 index 4525d4f5a..000000000 Binary files a/Riot/Assets/Images.xcassets/Search/search_bg.imageset/search_bg@3x.png and /dev/null differ diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 1c6bc5911..c8244e25a 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -45,7 +45,7 @@ "auth_optional_phone_placeholder" = "Telefon-Nummer (optional)"; "auth_phone_placeholder" = "Telefon-Nummer"; "auth_repeat_password_placeholder" = "Wiederhole Passwort"; -"auth_repeat_new_password_placeholder" = "Bestätige dein neues Passwort"; +"auth_repeat_new_password_placeholder" = "Bestätige das neue Passwort für dein Matrix-Konto"; "auth_invalid_user_name" = "Nutzernamen dürfen nur Buchstaben, Nummern, Punkte, Binde- und Unterstriche enthalten"; "auth_invalid_password" = "Passwort zu kurz (min. 6 Zeichen)"; "auth_invalid_email" = "Dies sieht nicht nach eine validen E-Mail-Adresse aus"; @@ -56,7 +56,7 @@ "auth_missing_email_or_phone" = "Fehlende E-Mail-Adresse oder Telefon-Nummer"; "auth_password_dont_match" = "Passwörter stimmen nicht überein"; "auth_username_in_use" = "Benutzername bereits verwendet"; -"auth_forgot_password" = "Passwort vergessen?"; +"auth_forgot_password" = "Passwort des Matrix-Kontos vergessen?"; "auth_msisdn_validation_title" = "Verifizierung ausstehend"; "auth_msisdn_validation_message" = "Bitte gib unten den Aktivierungs-Code ein, den wir per SMS verschickt haben."; "auth_msisdn_validation_error" = "Telefonnummer kann nicht verifiziert werden."; @@ -137,7 +137,7 @@ "room_participants_action_invite" = "Einladen"; "room_participants_action_leave" = "Diesen Raum verlassen"; "room_participants_action_remove" = "Aus diesem Raum entfernen"; -"room_participants_action_ban" = "Aus diesem Raum bannen"; +"room_participants_action_ban" = "Aus diesem Raum verbannen"; "room_participants_action_ignore" = "Alle Nachrichten dieser Person verbergen"; "room_participants_action_unignore" = "Alle Nachrichten dieser Person zeigen"; "room_participants_action_set_moderator" = "Moderationsrechte vergeben"; @@ -166,7 +166,7 @@ "room_event_action_quote" = "Zitieren"; "room_event_action_more" = "Mehr"; "room_event_action_share" = "Teilen"; -"room_event_action_permalink" = "Permalink"; +"room_event_action_permalink" = "Link zur Nachricht kopieren"; "room_event_action_view_source" = "Zeige Quelltext"; "room_event_action_report" = "Inhalt melden"; "room_event_action_report_prompt_reason" = "Grund fürs Melden dieses Inhalts"; @@ -234,11 +234,11 @@ "settings_term_conditions" = "Geschäftsbedingungen"; "settings_privacy_policy" = "Datenschutzerklärung"; "settings_send_crash_report" = "Sende anonyme Absturz- und Nutzungsdaten"; -"settings_change_password" = "Passwort ändern"; +"settings_change_password" = "Passwort deines Matrix-Kontos ändern"; "settings_old_password" = "altes Passwort"; "settings_new_password" = "neues Passwort"; "settings_confirm_password" = "bestätige Passwort"; -"settings_password_updated" = "Dein Passwort wurde aktualisiert"; +"settings_password_updated" = "Das Passwort deines Matrix-Kontos wurde aktualisiert"; "settings_crypto_device_name" = "Öffentlicher Sitzungs-Name: "; "settings_crypto_device_key" = "\nSitzungs-Schlüssel:\n"; "settings_crypto_export" = "Schlüssel exportieren"; @@ -262,7 +262,7 @@ "room_details_no_local_addresses" = "Dieser Raum hat keine lokalen Adressen"; "room_details_new_address" = "Füge neue Adresse hinzu"; "room_details_new_address_placeholder" = "Füge neue Adresse hinzu (z.B. #foo%@)"; -"room_details_banned_users_section" = "Gebannte Nutzer"; +"room_details_banned_users_section" = "Verbannte Personen"; "room_details_advanced_section" = "Erweitert"; "room_details_advanced_room_id" = "Raum-ID:"; "room_details_advanced_enable_e2e_encryption" = "Aktivere Verschlüsselung (Warnung: Nicht deaktivierbar!)"; @@ -279,11 +279,11 @@ "auth_use_server_options" = "Individuelle Server-Optionen"; "auth_email_validation_message" = "Prüfe dein E-Mail-Konto um mit der Registrierung fortzufahren"; "auth_recaptcha_message" = "Dieser Heimserver will sicherstellen dass du kein Roboter bist"; -"auth_reset_password_message" = "E-Mail-Adresse angeben, um das Passwort zurückzusetzen:"; +"auth_reset_password_message" = "Um das Passwort deines Matrix-Kontos zurückzusetzen, gib die mit deinem Konto verbundene E-Mail-Adresse an:"; "auth_reset_password_missing_email" = "Die E-Mail-Adresse die mit dem Konto verbunden ist muss eingegeben werden."; "auth_reset_password_error_unauthorized" = "Konnte E-Mail-Adresse nicht verifizieren. Klicke den Link in der Registrierungs-E-Mail"; "auth_reset_password_error_not_found" = "Diese E-Mail-Adresse ist nicht mit einer Matrix-ID auf diesem Heimserver verknüpft."; -"auth_reset_password_success_message" = "Dein Passwort wurde zurückgesetzt.\n\nDu wurdest aus allen Sitzungen abgemeldet, diese bekommen jetzt keine Push-Benachrichtigungen mehr. Um diese wieder einzuschalten, melde dich auf den Geräten erneut an."; +"auth_reset_password_success_message" = "Das Passwort deines Matrix-Kontos wurde zurückgesetzt.\n\nDu wurdest aus allen Sitzungen abgemeldet und bekommst keine Push-Benachrichtigungen mehr. Um diese zu reaktivieren, melde dich jeweils auf allen Geräten erneut an."; "auth_add_email_and_phone_warning" = "Registrierung mit E-Mail und Telefonnummer zugleich ist noch nicht unterstützt. Nur die Telefonnummer wird berücksichtigt. Du kannst deine E-Mail-Adresse in deinem Profil ergänzen."; "room_creation_make_public_prompt_msg" = "Sicher, dass du diesen Raum öffentlich machen willst? Jeder kann deine Nachrichten lesen und dem Raum beitreten."; "room_creation_invite_another_user" = "Benutzer-ID, Name oder E-Mail"; @@ -320,7 +320,7 @@ "settings_labs_e2e_encryption_prompt_message" = "Zum Fertigstellen der Verschlüsselung bitte neu anmelden."; "settings_third_party_notices" = "Anmerkungen von Dritten"; "settings_clear_cache" = "Cache löschen"; -"settings_fail_to_update_password" = "Passwortänderung fehlgeschlagen"; +"settings_fail_to_update_password" = "Passwortänderung des Matrix-Kontos fehlgeschlagen"; "settings_crypto_device_id" = "\nSitzungs-ID: "; "settings_crypto_blacklist_unverified_devices" = "Verschlüssele nur zu verifizierten Sitzungen"; "room_details_people" = "Mitglieder"; @@ -505,7 +505,7 @@ "e2e_room_key_request_message_new_device" = "Du hast die neue Sitzung '%@' hinzugefügt, welche Verschlüsselungs-Schlüssel anfordert."; "room_do_not_have_permission_to_post" = "Du hast keine Berechtigung Nachrichten in diesem Raum zu senden"; "room_event_action_kick_prompt_reason" = "Grund für das Entfernen des Benutzers"; -"room_event_action_ban_prompt_reason" = "Grund für die Verbannung des Benutzers"; +"room_event_action_ban_prompt_reason" = "Grund für die Verbannung der Person"; "room_action_send_photo_or_video" = "Foto oder Video senden"; "room_action_send_sticker" = "Aufkleber senden"; "settings_deactivate_account" = "DEAKTIVIERTES KONTO"; @@ -526,7 +526,7 @@ "deactivate_account_forget_messages_information_part3" = ": Dies wird dafür sorgen, dass zukünftige Nutzer eine unvollständige Konversation sehen)"; "deactivate_account_validate_action" = "Konto deaktiveren"; "deactivate_account_password_alert_title" = "Konto deaktivieren"; -"deactivate_account_password_alert_message" = "Zum Fortfahren, Passwort eingeben"; +"deactivate_account_password_alert_message" = "Zum Fortfahren, gib bitte das Passwort deines Matrix-Kontos ein"; "event_formatter_rerequest_keys_part1_link" = "Verschlüsselungsschlüssel neu anfordern"; "event_formatter_rerequest_keys_part2" = " von deinen anderen Sitzungen anfragen."; // Re-request confirmation dialog @@ -585,7 +585,7 @@ "key_backup_setup_intro_setup_action_without_existing_backup" = "Beginne Schlüsselsicherung zu nutzen"; "key_backup_setup_intro_setup_action_with_existing_backup" = "Benutze Schlüsselsicherung"; "key_backup_setup_passphrase_title" = "Sichere dein Backup mit einer Sicherungsphrase"; -"key_backup_setup_passphrase_info" = "Wir speichern eine verschlüsselte Kopie deiner Schlüssel auf unserem Server. Schütze sie mit einer Passphrase, um sie sicher zu halten.\n\nFür maximale Sicherheit sollte sich dies von deinem Kontopasswort unterscheiden."; +"key_backup_setup_passphrase_info" = "Wir speichern eine verschlüsselte Kopie deiner Schlüssel auf unserem Server. Schütze sie mit einer Passphrase, um sie sicher zu halten.\n\nFür maximale Sicherheit sollte sich dies von dem Passwort deines Matrix-Kontos unterscheiden."; "key_backup_setup_passphrase_passphrase_title" = "Eingeben"; "key_backup_setup_passphrase_passphrase_placeholder" = "Sicherungsphrase eingeben"; "key_backup_setup_passphrase_passphrase_valid" = "Gut!"; @@ -801,7 +801,7 @@ "image_picker_action_library" = "Aus der Mediathek auswählen"; "camera_unavailable" = "Die Kamera ist auf deinem Gerät nicht verfügbar"; "photo_library_access_not_granted" = "%@ hat keine Berechtigung zum Zugriff auf die Fotobibliothek. Bitte ändere die Datenschutzeinstellungen"; -"auth_forgot_password_error_no_configured_identity_server" = "Es ist kein Identitätsserver konfiguriert: Füge einen hinzu, um dein Kennwort zurückzusetzen."; +"auth_forgot_password_error_no_configured_identity_server" = "Es ist kein Identitätsserver konfiguriert: Füge einen hinzu, um das Kennwort deines Matrix-Kontos zurückzusetzen."; "room_creation_error_invite_user_by_email_without_identity_server" = "Es ist kein Identitätsserver konfiguriert, sodass du keine Teilnehmenden mit einer E-Mail hinzufügen kannst."; "room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Es ist kein Identitätsserver konfiguriert, sodass du keinen Chat mit einem Kontakt über eine E-Mail starten kannst."; // Service terms @@ -810,9 +810,9 @@ "service_terms_modal_accept_button" = "Akzeptieren"; "service_terms_modal_description_for_identity_server" = "Für andere auffindbar sein"; "service_terms_modal_description_for_integration_manager" = "Bots, Brücken, Widgets und Aufkleberpakete verwenden"; -"auth_email_is_required" = "Es ist kein Identitätsserver konfiguriert, sodass du keine E-Mail-Adresse hinzufügen kannst, um dein Kennwort in Zukunft zurückzusetzen."; -"auth_phone_is_required" = "Es ist kein Identitätsserver konfiguriert, sodass du keine Telefonnummer hinzufügen kannst, um dein Kennwort in Zukunft zurückzusetzen."; -"auth_reset_password_error_is_required" = "Es ist kein Identitätsserver konfiguriert: Füge einen in die Serveroptionen ein, um dein Kennwort zurückzusetzen."; +"auth_email_is_required" = "Es ist kein Identitätsserver konfiguriert, sodass du keine E-Mail-Adresse hinzufügen kannst, um das Kennwort deines Matrix-Kontos in Zukunft zurückzusetzen."; +"auth_phone_is_required" = "Es ist kein Identitätsserver konfiguriert, sodass du keine Telefonnummer hinzufügen kannst, um das Kennwort deines Matrix-Kontos in Zukunft zurückzusetzen."; +"auth_reset_password_error_is_required" = "Es ist kein Identitätsserver konfiguriert: Füge einen in die Serveroptionen ein, um das Kennwort deines Matrix-Kontos zurückzusetzen."; // Errors "error_user_already_logged_in" = "Du versuchst anscheinend, eine Verbindung zu einem anderen Heimserver herzustellen. Möchtest du dich abmelden?"; "contacts_address_book_no_identity_server" = "Kein Identitätsserver konfiguriert"; @@ -888,7 +888,7 @@ "identity_server_settings_alert_disconnect_still_sharing_3pid" = "Du teilst noch deine persönlichen Daten mit dem Identitätsserver %@.\n\nWir empfehlen dir deine E-Mail-Adresse und Telefonnummer zu entfernen, bevor du die Verbindung zum Identitätsserver trennst."; "settings_add_3pid_password_title_email" = "E-Mail-Adresse hinzufügen"; "settings_add_3pid_password_title_msidsn" = "Telefonnummer hinzufügen"; -"settings_add_3pid_password_message" = "Um fortzufahren, bitte Passwort eingeben"; +"settings_add_3pid_password_message" = "Um fortzufahren, gib bitte das Passwort deines Matrix-Kontos ein"; "settings_add_3pid_invalid_password_message" = "Ungültige Anmeldedaten"; "identity_server_settings_disconnect_info" = "Eine Trennung vom Identitätsserver bedeutet, dass du weder von anderen Nutzer gefunden werden, noch diese dich per E-Mail oder Telefonnummer einladen können."; "error_not_supported_on_mobile" = "Dies ist in der Mobilvariante von %@ nicht möglich."; @@ -1012,7 +1012,7 @@ "security_settings_crosssigning_info_ok" = "Quersignierung ist bereit zur Anwendung."; "security_settings_crosssigning_reset" = "Zurücksetzen"; "security_settings_coming_soon" = "Entschuldigung, diese Funktion ist noch nicht für %@ iOS verfügbar. Bitte nutze einen anderen Matrix-Client, um es einzurichten. %@ iOS wird es benutzen."; -"security_settings_user_password_description" = "Bestätige deine Identität durch Eingabe des Kontopassworts"; +"security_settings_user_password_description" = "Bestätige deine Identität durch Eingabe des Passworts deines Matrix-Kontos"; // AuthenticatedSessionViewControllerFactory "authenticated_session_flow_not_supported" = "Diese App unterstützt nicht diese Authentifizierungsmethode für deinen Heimserver."; "secure_key_backup_setup_intro_title" = "Sichere Datensicherung"; @@ -1020,7 +1020,7 @@ "room_participants_action_security_status_complete_security" = "Vollständige Sicherheit"; "external_link_confirmation_title" = "Überprüfe diesen Link genau"; "external_link_confirmation_message" = "Der Link %@ bringt dich auf eine andere Seite: %@\n\nSicher, dass du fortfahren möchtest?"; -"security_settings_crypto_sessions_description_2" = "Wenn du dich nicht angemeldet hast, ändere dein Passwort und setze die Sichere Sicherheitskopie zurück."; +"security_settings_crypto_sessions_description_2" = "Wenn du dich nicht angemeldet hast, ändere das Passwort deines Matrix-Kontos und setze die Sichere Sicherheitskopie zurück."; "security_settings_secure_backup_description" = "Sichere die Schlüssel, um Datenverlust zu verhindern. Sie werden mit einem Sicherungsschlüssel gesichert."; "security_settings_crosssigning_info_exists" = "Dein Konto hat eine Quersignatur-Identität, aber dieser Sitzung wird noch nicht vertraut. Vervollständige die Sicherheit auf diese Sitzung."; "security_settings_crosssigning_info_trusted" = "Quersignierung ist aktiviert. Du kannst anderen Nutzern und deinen anderen Sitzungen basierend auf der Quersignatur vertrauen, aber du kannst in dieser Sitzung keine Quersignierung durchführen, da sie keine privaten Quersignatur-Schlüssel enthält. Vervollständige die Sicherheit dieser Sitzung."; @@ -1076,8 +1076,8 @@ "key_verification_manually_verify_device_additional_information" = "Falls sie nicht übereinstimmen, wurde die Kommunikation vielleicht kompromittiert."; "key_verification_verified_new_session_title" = "Neue Sitzung verifiziert!"; "key_verification_verified_other_session_information" = "Du kannst nun sichere Nachrichten in deiner anderen Sitzung lesen. Andere Benutzer wissen, dass sie ihr vertrauen können."; -"key_verification_verified_new_session_information" = "Du kannst nun sichere Nachrichten auf deinem neuen Gerät lesen. Andere Benutzer wissen, dass sie es vertrauen können."; -"key_verification_verified_this_session_information" = "Du kannst nun sichere Nachrichten auf diesem Gerät lesen. Andere Benutzer wissen, dass sie es vertrauen können."; +"key_verification_verified_new_session_information" = "Du kannst nun sichere Nachrichten auf deinem neuen Gerät lesen. Andere Benutzer wissen, dass sie ihm vertrauen können."; +"key_verification_verified_this_session_information" = "Du kannst nun sichere Nachrichten auf diesem Gerät lesen. Andere Benutzer wissen, dass sie ihm vertrauen können."; "key_verification_verified_user_information" = "Nachrichten mit diesem Gegenüber sind Ende-zu-Ende verschlüsselt und können nicht von Dritten gelesen werden."; "key_verification_bootstrap_not_setup_title" = "Fehler"; "key_verification_bootstrap_not_setup_message" = "Du musst erst die Quersignatur einrichten."; @@ -1134,7 +1134,7 @@ "secrets_setup_recovery_key_storage_alert_message" = "✓ Drucke ihn aus und bewahre ihn an einem sicheren Ort auf\n✓ Speichere ihn auf einem USB-Stick oder einem Sicherungslaufwerk\n✓ Kopiere ihn zu deinem persönlichen Speicher im Netz"; "secrets_setup_recovery_passphrase_title" = "Setze Sicherheitsphrase"; "secrets_setup_recovery_passphrase_information" = "Gib eine Sicherheitsphrase ein, welche nur du kennst und deine Daten auf dem Server geheim halten soll."; -"secrets_setup_recovery_passphrase_additional_information" = "Benutze dein Kontopasswort nicht mehrfach."; +"secrets_setup_recovery_passphrase_additional_information" = "Benutze nicht das Passwort deines Matrix-Kontos."; "secrets_setup_recovery_passphrase_validate_action" = "Fertig"; "secrets_setup_recovery_passphrase_confirm_information" = "Gib deine Sicherheitsphrase zur Bestätigung erneut ein."; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Bestätigen"; @@ -1231,7 +1231,7 @@ "secrets_recovery_reset_action_part_1" = "Wiederherstellungsoptionen vergessen oder verloren? "; "less" = "Weniger"; -"secrets_reset_authentication_message" = "Gib dein Kontopasswort ein, um zu bestätigen"; +"secrets_reset_authentication_message" = "Gib zum Bestätigen das Passwort deines Matrix-Kontos ein"; "secrets_reset_reset_action" = "Zurücksetzen"; "secrets_reset_warning_message" = "Du wirst ohne Nachrichtenverlauf, Nachrichten, vertraute Geräte oder vertraute Benutzer neustarten."; "secrets_reset_warning_title" = "Falls du alles zurücksetzt"; @@ -1248,7 +1248,7 @@ // MARK: - Favourites -"favourites_empty_view_title" = "Lieblingsräume und -nutzer"; +"favourites_empty_view_title" = "Favorisierte Räume und Personen"; "favourites_empty_view_information" = "Um einen Lieblingschat hinzuzufügen, halte diesen gedrückt und wähle den Stern aus. Du findest deine Lieblingschats gesammelt hier."; "home_empty_view_information" = "Die sichere Chat-App für Teams, Freunde und Organisationen. Tippe unten auf das +, um Leute und Räume hinzuzufügen."; "rooms_empty_view_information" = "Noch keine Chat-Räume vorhanden. Tippe auf das +, um einem Raum beizutreten oder einen neuen zu erstellen."; @@ -1476,7 +1476,7 @@ "spaces_empty_space_title" = "Dieser Space hat (noch) keine Räume"; "spaces_explore_rooms" = "Räume erkunden"; "leave_space_and_all_rooms_action" = "Alle Räume und Spaces verlassen"; -"leave_space_only_action" = "Keine Räume verlassen"; +"leave_space_only_action" = "Keine Räume und Subspaces verlassen"; "spaces_left_panel_title" = "Spaces"; "room_recents_suggested_rooms_section" = "VORGESCHLAGENE RÄUME"; "spaces_home_space_title" = "Start"; @@ -1603,3 +1603,35 @@ "poll_edit_form_poll_type_open_description" = "Ergebnisse werden direkt nach Stimmabgabe angezeigt"; "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."; +"home_context_menu_normal_priority" = "Normale Priorität"; +"home_context_menu_low_priority" = "Niedrige Priorität"; +"home_context_menu_unfavourite" = "Aus Favoriten entfernen"; +"home_context_menu_favourite" = "Favorisieren"; +"location_sharing_post_failure_subtitle" = "%@ konnte deinen Standort nicht versenden. Bitte versuche es später erneut."; +"location_sharing_post_failure_title" = "Wir konnten deinen Standort nicht versenden"; +"home_context_menu_leave" = "Verlassen"; +"home_context_menu_unmute" = "Stummschaltung aufheben"; +"home_context_menu_mute" = "Stummschalten"; +"home_context_menu_notifications" = "Benachrichtigungen"; +"home_context_menu_make_room" = "Zu Räume verschieben"; +"home_context_menu_make_dm" = "Zu Personen verschieben"; +"event_formatter_message_deleted" = "Nachricht gelöscht"; +"settings_labs_enable_threads" = "Threads"; +"message_from_a_thread" = "Aus einem Thread"; +"threads_empty_show_all_threads" = "Alle Threads anzeigen"; +"threads_empty_info_all" = "Threads helfen dabei, dass deine Konversationen beim Thema und leicht nachverfolgbar bleiben."; +"threads_empty_title" = "Organisiere Diskussionen mit Threads"; +"threads_action_my_threads" = "Meine Threads"; +"threads_action_all_threads" = "Alle Threads"; +"threads_title" = "Threads"; +"thread_copy_link_to_thread" = "Link in Thread kopieren"; + +// MARK: Threads +"room_thread_title" = "Thread"; +"room_accessibility_thread_more" = "Mehr"; +"room_accessibility_threads" = "Threads"; +"room_event_copy_link_info" = "Link in die Zwischenablage kopiert."; +"room_event_action_reply_in_thread" = "Thread"; +"room_event_action_view_in_room" = "Im Raum anzeigen"; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index e05acff9b..63a0afa4d 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -93,6 +93,17 @@ "onboarding_splash_page_4_title_no_pun" = "Messaging for your team."; "onboarding_splash_page_4_message" = "Element is also great for the workplace. It’s trusted by the world’s most secure organisations."; +"onboarding_use_case_title" = "Who will you chat to the most?"; +"onboarding_use_case_message" = "We’ll help you get connected."; +"onboarding_use_case_personal_messaging" = "Friends and family"; +"onboarding_use_case_work_messaging" = "Teams"; +"onboarding_use_case_community_messaging" = "Communities"; +/* The placeholder string contains onboarding_use_case_skip_button as a tappable action */ +"onboarding_use_case_not_sure_yet" = "Not sure yet? You can %@"; +"onboarding_use_case_skip_button" = "skip this question"; +"onboarding_use_case_existing_server_message" = "Looking to join an existing server?"; +"onboarding_use_case_existing_server_button" = "Connect to server"; + // Authentication "auth_login" = "Log in"; "auth_register" = "Register"; @@ -239,6 +250,7 @@ Tap the + to start adding people."; "search_people" = "People"; "search_files" = "Files"; "search_default_placeholder" = "Search"; +"search_filter_placeholder" = "Filter"; "search_people_placeholder" = "Search by User ID, Name or email"; "search_no_result" = "No results"; "search_in_progress" = "Searching…"; @@ -385,7 +397,7 @@ Tap the + to start adding people."; "room_event_action_view_decrypted_source" = "View Decrypted Source"; "room_event_action_report" = "Report content"; "room_event_action_report_prompt_reason" = "Reason for reporting this content"; -"room_event_action_kick_prompt_reason" = "Reason for kicking this user"; +"room_event_action_kick_prompt_reason" = "Reason for removing this user"; "room_event_action_ban_prompt_reason" = "Reason for banning this user"; "room_event_action_report_prompt_ignore_user" = "Do you want to hide all messages from this user?"; "room_event_action_save" = "Save"; @@ -1933,6 +1945,8 @@ Tap the + to start adding people."; "location_sharing_open_google_maps" = "Open in Google Maps"; +"location_sharing_open_open_street_maps" = "Open in OpenStreetMap"; + "location_sharing_settings_header" = "Location sharing"; "location_sharing_settings_toggle_title" = "Enable location sharing"; diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 77da3b78c..172becd1e 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -53,7 +53,7 @@ "auth_optional_phone_placeholder" = "Telefoninumber (kui soovid)"; "auth_phone_placeholder" = "Telefoninumber"; "auth_repeat_password_placeholder" = "Korda salasõna"; -"auth_repeat_new_password_placeholder" = "Korda uut salasõna"; +"auth_repeat_new_password_placeholder" = "Korda uut Matrix'i konto salasõna"; "auth_home_server_placeholder" = "URL (näiteks https://matrix.org)"; "auth_identity_server_placeholder" = "URL (näiteks https://vector.im)"; "auth_invalid_login_param" = "Vigane kasutajanimi ja/või salasõna"; @@ -70,13 +70,13 @@ "auth_missing_email_or_phone" = "E-posti aadress või telefoninumber on puudu"; "auth_email_in_use" = "See e-posti aadress on juba kasutusel"; "auth_phone_in_use" = "See telefoninumber on juba kasutusel"; -"auth_email_is_required" = "Ühtegi isikutuvastusserverit pole seadistatud ning sul ei ole võimalik lisada oma e-posti aadressi hilisemaks võimalikuks salasõna muutmiseks."; -"auth_phone_is_required" = "Ühtegi isikutuvastusserverit pole seadistatud ning sul ei ole võimalik lisada oma telefoninumbrit hilisemaks võimalikuks salasõna muutmiseks."; +"auth_email_is_required" = "Ühtegi isikutuvastusserverit pole seadistatud ning sul ei ole võimalik lisada oma e-posti aadressi hilisemaks võimalikuks Matrix'i konto salasõna muutmiseks."; +"auth_phone_is_required" = "Ühtegi isikutuvastusserverit pole seadistatud ning sul ei ole võimalik lisada oma telefoninumbrit hilisemaks võimalikuks Matrix'i konto salasõna muutmiseks."; "auth_untrusted_id_server" = "See isikutuvastusserver pole usaldusväärne"; "auth_password_dont_match" = "Salasõnad ei klapi omavahel"; "auth_username_in_use" = "Selline kasutajanimi on juba olemas"; -"auth_forgot_password" = "Kas unustasid oma salasõna?"; -"auth_forgot_password_error_no_configured_identity_server" = "Ühtegi isikutuvastusserverit pole seadistatud: salasõna taastamiseks määra see."; +"auth_forgot_password" = "Kas unustasid oma Matrix'i konto salasõna?"; +"auth_forgot_password_error_no_configured_identity_server" = "Ühtegi isikutuvastusserverit pole seadistatud: Matrix'i konto salasõna taastamiseks määra see."; "auth_email_not_found" = "E-kirja saatmine ei õnnestunud: sellist e-posti aadressi ei leidu"; "auth_use_server_options" = "Kohandatud serveriseadistused"; "auth_email_validation_message" = "Registreerimise jätkamiseks palun vaata oma e-posti"; @@ -84,15 +84,15 @@ "auth_msisdn_validation_message" = "Me oleme SMS'iga saatnud aktiveerimiskoodi. Palun sisesta see kood siia."; "auth_msisdn_validation_error" = "Telefoninumbri verifitseerimine ei õnnestunud."; "auth_recaptcha_message" = "See server soovib kindlaks teha, et sa ei ole robot"; -"auth_reset_password_message" = "Salasõna taastamiseks sisesta sinu kontoga seotud e-posti aadress:"; +"auth_reset_password_message" = "Matrix'i konto salasõna taastamiseks sisesta sinu kontoga seotud e-posti aadress:"; "auth_reset_password_missing_email" = "Sa pead sisestama oma kontoga seotud e-posti aadressi."; "auth_reset_password_missing_password" = "Palun sisesta uus salasõna."; "auth_reset_password_email_validation_message" = "Saatsime e-kirja %@ aadressile. Kui oled avanud selles kirjas leidunud lingi, siis palun vajuta järgnevat nuppu."; "auth_reset_password_next_step_button" = "Ma olen teinud läbi oma e-posti aadressi kontrolli"; "auth_reset_password_error_unauthorized" = "E-posti aadressi kontrollimine ei õnnestunud: palun vaata, et sa kindlasti klõpsisid saabunud kirjas olnud viidet"; "auth_reset_password_error_not_found" = "Sinu e-posti aadress ei tundu olema selles koduserveris seotud Matrixi kasutajatunnusega."; -"auth_reset_password_error_is_required" = "Ühtegi isikutuvastusserverit pole seadistatud: salasõna taastamiseks määra see serveri sätetes."; -"auth_reset_password_success_message" = "Sinu salasõna on vahetatud. \n \nSa oled välja logitud kõikidest oma seni kasutusel olnud sessioonidest ega saa enam teateid ega sõnumeid. Nende uuesti lugemiseks palun logi igas seadmes tagasi Matrix'i võrku."; +"auth_reset_password_error_is_required" = "Ühtegi isikutuvastusserverit pole seadistatud: Matrix'i konto salasõna taastamiseks määra see serveri sätetes."; +"auth_reset_password_success_message" = "Sinu Matrix'i konto salasõna on vahetatud. \n \nSa oled välja logitud kõikidest oma seni kasutusel olnud sessioonidest ega saa enam teateid ega sõnumeid. Nende uuesti lugemiseks palun logi igas seadmes tagasi Matrix'i võrku."; "auth_add_email_and_phone_warning" = "Registreerimine kasutades korraga nii e-posti aadressi kui ka telefoninumbrit ei ole senise API versiooni puhul veel võimalik. Hetkel läheb arvesse vaid telefoninumber. Sa võid hiljem lisada e-posti aadressi oma kasutajaprofiili seadetest."; "auth_accept_policies" = "Palun vaata üle kõik selle koduserveri kasutustingimused ja nõustu nendega:"; "auth_autodiscover_invalid_response" = "Vigane koduserveri tuvastamise päringu vastus"; @@ -209,7 +209,7 @@ "deactivate_account_forget_messages_information_part2_emphasize" = "Hoiatus"; "deactivate_account_validate_action" = "Kas eemaldame selle konto kasutusest"; "deactivate_account_password_alert_title" = "Eemalda konto"; -"deactivate_account_password_alert_message" = "Jätkamiseks palun sisesta oma salasõna"; +"deactivate_account_password_alert_message" = "Jätkamiseks palun sisesta oma Matrix'i konto salasõna"; "auth_login_single_sign_on" = "Logi sisse"; // Room recents "room_recents_directory_section" = "JUTUTUBADE LOEND"; @@ -251,7 +251,7 @@ "room_event_action_redact" = "Eemalda"; "room_event_action_more" = "Veel"; "room_event_action_share" = "Jaga"; -"room_event_action_permalink" = "Püsiviide"; +"room_event_action_permalink" = "Kopeeri link sõnumisse"; "room_event_action_view_source" = "Vaata lähtekoodi"; "room_event_action_view_decrypted_source" = "Näita dekrüptitud lähtekoodi"; "room_event_action_report" = "Teata kahtlasest sisust"; @@ -487,7 +487,7 @@ "settings_add_email_address" = "Lisa e-posti aadress"; "settings_phone_number" = "Telefon"; "settings_add_phone_number" = "Lisa telefoninumber"; -"settings_change_password" = "Muuda salasõna"; +"settings_change_password" = "Muuda Matrix'i konto salasõna"; "settings_fail_to_update_profile" = "Profiili uuendamine ei õnnestunud"; "settings_global_settings_info" = "Üldised teavituste seadistused leiduvad sinu %@ veebikliendis"; "settings_pin_rooms_with_missed_notif" = "Klammerda jututoad, kus leidub lugemata teavitusi"; @@ -524,11 +524,11 @@ "settings_old_password" = "vana salasõna"; "settings_new_password" = "uus salasõna"; "settings_confirm_password" = "korda uut salasõna"; -"settings_fail_to_update_password" = "Salasõna uuendamine ei õnnestunud"; -"settings_password_updated" = "Sinu salasõna on muudetud"; +"settings_fail_to_update_password" = "Matrix'i konto salasõna uuendamine ei õnnestunud"; +"settings_password_updated" = "Sinu Matrix'i konto salasõna on muudetud"; "settings_add_3pid_password_title_email" = "Lisa e-posti aadress"; "settings_add_3pid_password_title_msidsn" = "Lisa telefoninumber"; -"settings_add_3pid_password_message" = "Jätkamiseks palun sisesta oma salasõna"; +"settings_add_3pid_password_message" = "Jätkamiseks palun sisesta oma Matrix'i konto salasõna"; "settings_add_3pid_invalid_password_message" = "Vigane kasutajanimi või salasõna"; "settings_crypto_device_name" = "Sessiooni nimi: "; "settings_crypto_device_id" = "\nSessiooni tunnus: "; @@ -537,7 +537,7 @@ "settings_crypto_blacklist_unverified_devices" = "Kasuta krüptimist vaid verifitseeritud sessioonides"; "settings_deactivate_my_account" = "Eemalda minu konto kasutusest"; "security_settings_crypto_sessions_loading" = "Laadin sessioone…"; -"security_settings_user_password_description" = "Tuvasta oma isik sisestades salasõna"; +"security_settings_user_password_description" = "Tuvasta oma isik sisestades Matrix'i konto salasõna"; // Manage session "manage_session_title" = "Halda sessiooni"; "manage_session_info" = "SESSIOONI TEAVE"; @@ -766,7 +766,7 @@ // Security settings "security_settings_title" = "Turvalisus"; "security_settings_crypto_sessions" = "MINU SESSIOONID"; -"security_settings_crypto_sessions_description_2" = "Kui sa ei tunne ära mõnda oma sisselogimissessiooni, siis muuda ära oma salasõna ja tee võtmetest uus turvaline varukoopia."; +"security_settings_crypto_sessions_description_2" = "Kui sa ei tunne ära mõnda oma sisselogimissessiooni, siis muuda ära oma Matrix'i konto salasõna ja tee võtmetest uus turvaline varukoopia."; "security_settings_secure_backup" = "TURVALINE VARUNDUS"; "security_settings_secure_backup_description" = "Selleks puhuks, kui sa kaotad ligipääsu kõikidele oma sessioonidele, tee varukoopia oma krüptovõtmetest ja kasutajakonto seadistustest. Unikaalse turvavõtmega tagad selle, et sinu varukoopia on kaitstud."; "security_settings_secure_backup_setup" = "Võta kasutusele"; @@ -794,7 +794,7 @@ "key_backup_setup_intro_manual_export_info" = "(Lisaseadistused)"; "key_backup_setup_intro_manual_export_action" = "Ekspordi võtmed käsitsi"; "key_backup_setup_passphrase_title" = "Krüpti oma varukoopia turvafraasiga"; -"key_backup_setup_passphrase_info" = "Me salvestame krüptitud varukoopia sinu krüptovõtmetest meie serveris. Tagamaks, et keegi ei saa seda kasutada, krüpti varukoopia paroolifraasiga.\n\n Parima turvalisuse jaoks peaks paroolifraas olema erinev sinu konto salasõnast."; +"key_backup_setup_passphrase_info" = "Me salvestame krüptitud varukoopia sinu krüptovõtmetest meie serveris. Tagamaks, et keegi ei saa seda kasutada, krüpti varukoopia paroolifraasiga.\n\n Parima turvalisuse jaoks peaks paroolifraas olema erinev sinu Matrix'i konto salasõnast."; "key_backup_setup_passphrase_passphrase_title" = "Sisesta"; "key_backup_setup_passphrase_passphrase_placeholder" = "Sisesta paroolifraas"; "key_backup_setup_passphrase_passphrase_valid" = "Tore!"; @@ -1057,7 +1057,7 @@ "secrets_setup_recovery_key_storage_alert_message" = "✓ Trüki välja ja hoia turvalises kohas\n✓ Salvesta ta mälupulgale või välisele kõvakattale\n✓ Kopeeri ta sinu isiklikku salvestusruumi mõnes andmepilves"; "secrets_setup_recovery_passphrase_title" = "Määra turvafraas"; "secrets_setup_recovery_passphrase_information" = "Andmete kaitsmiseks sinu koduserveris sisesta turvafraas, mida vaid sina tead."; -"secrets_setup_recovery_passphrase_additional_information" = "Palun ära kasuta selleks oma tavalist konto salasõna."; +"secrets_setup_recovery_passphrase_additional_information" = "Palun ära kasuta selleks oma Matrix'i konto salasõna."; "secrets_setup_recovery_passphrase_validate_action" = "Valmis"; "secrets_setup_recovery_passphrase_confirm_information" = "Kinnituseks sisesta turvafraas uuesti."; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Kinnita"; @@ -1181,7 +1181,7 @@ "room_details_advanced_e2e_encryption_enabled_for_dm" = "Krüptimine on selles jututoas kasutusel"; "room_details_advanced_e2e_encryption_disabled_for_dm" = "Krüptimine ei ole selles jututoas kasutusel."; "pin_protection_kick_user_alert_message" = "Liiga palju vigu PIN-koodi sisestamisel ning sa oled nüüd välja logitud"; -"secrets_reset_authentication_message" = "Kinnitamaks seda muudatust, sisesta oma konto salasõna"; +"secrets_reset_authentication_message" = "Kinnitamaks seda muudatust, sisesta oma Matrix'i konto salasõna"; "secrets_reset_reset_action" = "Taasta algolek"; "secrets_reset_warning_message" = "Siis alustad nii, et kadunud on ajalugu, sõnumid ning usaldatud seadmed ja kasutajad."; "secrets_reset_warning_title" = "Kui sa alustad kõigega algusest"; @@ -1563,3 +1563,35 @@ "poll_edit_form_update_failure_subtitle" = "Palun proovi uuesti"; "poll_edit_form_update_failure_title" = "Küsitluse muutmine ei õnnestunud"; "poll_edit_form_poll_type" = "Küsitluse tüüp"; +"location_sharing_post_failure_subtitle" = "%@ ei saanud sinu asukohta edastada. Palun proovi hiljem uuesti."; +"location_sharing_post_failure_title" = "Sinu asukoha saatmine ei õnnestunud"; +"home_context_menu_leave" = "Lahku"; +"home_context_menu_normal_priority" = "Tavalise tähtsusega"; +"home_context_menu_low_priority" = "Vähetähtis"; +"home_context_menu_unfavourite" = "Eemalda lemmikute hulgast"; +"home_context_menu_favourite" = "Lemmik"; +"home_context_menu_unmute" = "Eemalda summutamine"; +"home_context_menu_mute" = "Summuta"; +"home_context_menu_notifications" = "Teavitused"; +"home_context_menu_make_dm" = "Tõsta otsevestluste alla"; +"home_context_menu_make_room" = "Tõsta jututubade alla"; +"event_formatter_message_deleted" = "Sõnum on kustutatud"; +"settings_labs_enable_threads" = "Sõnumid jutulõngana"; +"message_from_a_thread" = "Jutulõngast"; +"threads_empty_show_all_threads" = "Näita kõiki jutulõngasid"; +"threads_empty_tip" = "Soovitus: uue jutulõnga alustamiseks klõpsi sõnumit ja vali „Jutulõng“."; +"threads_empty_info_my" = "Vasta olemasolevasse jutulõnga või uue jutulõnga alustamiseks klõpsi sõnumit ja vali „Jutulõng“."; +"threads_empty_info_all" = "Jutulõngad aitavad hoida vestlused teemakohastena ning mugavalt loetavatena."; +"threads_empty_title" = "Halda vestlusi jutulõngadena"; +"threads_action_my_threads" = "Minu jutulõngad"; +"threads_action_all_threads" = "Kõik jutulõngad"; +"threads_title" = "Jutulõngad"; +"thread_copy_link_to_thread" = "Kopeeri jutulõnga link"; + +// MARK: Threads +"room_thread_title" = "Jutulõng"; +"room_accessibility_thread_more" = "Veel"; +"room_accessibility_threads" = "Jutulõngad"; +"room_event_copy_link_info" = "Link on kopeeritud lõikelauale."; +"room_event_action_reply_in_thread" = "Jutulõng"; +"room_event_action_view_in_room" = "Vaata jututoas"; diff --git a/Riot/Assets/fr.lproj/Localizable.strings b/Riot/Assets/fr.lproj/Localizable.strings index 97b1481bb..84ef90f6f 100644 --- a/Riot/Assets/fr.lproj/Localizable.strings +++ b/Riot/Assets/fr.lproj/Localizable.strings @@ -116,3 +116,6 @@ /** General **/ "NOTIFICATION" = "Notification"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ a partagé sa localisation"; diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index d30a43e9e..0e0d9676b 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -47,7 +47,7 @@ "auth_optional_phone_placeholder" = "Numéro de téléphone (facultatif)"; "auth_phone_placeholder" = "Numéro de téléphone"; "auth_repeat_password_placeholder" = "Répéter le mot de passe"; -"auth_repeat_new_password_placeholder" = "Confirmer le nouveau mot de passe"; +"auth_repeat_new_password_placeholder" = "Confirmer le nouveau mot de passe de votre compte Matrix"; "auth_invalid_login_param" = "Nom d’utilisateur et/ou mot de passe incorrect"; "auth_invalid_user_name" = "Les noms d’utilisateur ne peuvent contenir que des lettres, des chiffres, des points, des traits d’union ou des tirets bas"; "auth_invalid_password" = "Mot de passe trop court (min 6)"; @@ -70,14 +70,14 @@ "auth_msisdn_validation_message" = "Nous vous avons envoyé un SMS avec un code d’activation. Merci de le recopier ci-dessous."; "auth_msisdn_validation_error" = "Impossible de vérifier votre numéro de téléphone."; "auth_recaptcha_message" = "Ce serveur d’accueil voudrait s’assurer que vous n’êtes pas un robot"; -"auth_reset_password_message" = "Pour réinitialiser votre mot de passe, saisissez l’adresse e-mail liée à votre compte :"; +"auth_reset_password_message" = "Pour réinitialiser le mot de passe de votre compte Matrix, saisissez l’adresse e-mail liée à votre compte :"; "auth_reset_password_missing_email" = "Vous devez saisir l’adresse e-mail liée à votre compte."; "auth_reset_password_missing_password" = "Vous devez spécifier un nouveau mot de passe."; "auth_reset_password_email_validation_message" = "Un e-mail a été envoyé à %@. Cliquez d’abord sur le lien dans l’e-mail, puis ci-dessous."; "auth_reset_password_next_step_button" = "J’ai vérifié mon adresse e-mail"; "auth_reset_password_error_unauthorized" = "Impossible de vérifier l’adresse e-mail : assurez-vous de cliquer sur le lien dans l’e-mail"; "auth_reset_password_error_not_found" = "Votre adresse e-mail ne semble pas associée à un identifiant Matrix sur ce serveur d’accueil."; -"auth_reset_password_success_message" = "Votre mot de passe a été réinitialisé.\n\nVous avez été déconnecté de toutes vos sessions et ne recevrez plus de notifications. Pour réactiver les notifications, reconnectez-vous sur chaque appareil."; +"auth_reset_password_success_message" = "Le mot de passe de votre compte Matrix a été réinitialisé.\n\nVous avez été déconnecté de toutes vos sessions et ne recevrez plus de notifications. Pour réactiver les notifications, reconnectez-vous sur chaque appareil."; "auth_add_email_and_phone_warning" = "L’inscription avec un e-mail et un numéro de téléphone à la fois n’est pas prise en charge tant que l’API n'existe pas. Seul votre numéro de téléphone sera pris en compte. Vous pourrez ajouter l’adresse e-mail dans vos options de profil."; // Chat creation "room_creation_title" = "Nouvelle discussion"; @@ -206,7 +206,7 @@ "room_event_action_redact" = "Effacer"; "room_event_action_more" = "Plus"; "room_event_action_share" = "Partager"; -"room_event_action_permalink" = "Permalien"; +"room_event_action_permalink" = "Copier le lien vers le message"; "room_event_action_view_source" = "Voir la source"; "room_event_action_report" = "Signaler le contenu"; "room_event_action_report_prompt_reason" = "Raison pour le signalement du contenu"; @@ -292,12 +292,12 @@ "settings_send_crash_report" = "Envoyer des rapports d’anomalies anonymes et des statistiques d’utilisation"; "settings_enable_rageshake" = "Secouer l’appareil pour signaler un bug"; "settings_clear_cache" = "Vider le cache"; -"settings_change_password" = "Changer de mot de passe"; +"settings_change_password" = "Changer le mot de passe de votre compte Matrix"; "settings_old_password" = "ancien mot de passe"; "settings_new_password" = "nouveau mot de passe"; "settings_confirm_password" = "confirmer le mot de passe"; -"settings_fail_to_update_password" = "Échec de la modification du mot de passe"; -"settings_password_updated" = "Votre mot de passe a été modifié"; +"settings_fail_to_update_password" = "Échec de la modification du mot de passe du compte Matrix"; +"settings_password_updated" = "Le mot de passe de votre compte Matrix a été modifié"; "settings_crypto_device_name" = "Nom de la session : "; "settings_crypto_device_id" = "\nIdentifiant de la session : "; "settings_crypto_device_key" = "\nClé de la session :\n"; @@ -526,7 +526,7 @@ "deactivate_account_forget_messages_information_part3" = ": les futurs utilisateurs auront alors une vue incomplète des conversations)"; "deactivate_account_validate_action" = "Désactiver le compte"; "deactivate_account_password_alert_title" = "Désactiver le compte"; -"deactivate_account_password_alert_message" = "Pour continuer, veuillez renseigner votre mot de passe"; +"deactivate_account_password_alert_message" = "Pour continuer, veuillez renseigner le mot de passe de votre compte Matrix"; "event_formatter_rerequest_keys_part1_link" = "Redemander les clés de chiffrement"; "event_formatter_rerequest_keys_part2" = " depuis vos autres sessions."; // Re-request confirmation dialog @@ -584,7 +584,7 @@ "key_backup_setup_intro_title" = "Ne perdez jamais vos messages chiffrés"; "key_backup_setup_intro_info" = "Les messages des salons chiffrés sont sécurisés avec un chiffrement de bout en bout. Seuls vous et le(s) destinataire(s) avez les clés pour lire ces messages.\n\nSauvegardez vos clés de façon sécurisée pour éviter de les perdre."; "key_backup_setup_intro_setup_action" = "Configurer"; -"key_backup_setup_passphrase_info" = "Nous conserverons une copie chiffrée de vos clés sur notre serveur. Protégez votre sauvegarde avec une phrase secrète pour qu’elle soit en sécurité.\n\nPour une sécurité maximale, elle devrait être différente du mot de passe de votre compte."; +"key_backup_setup_passphrase_info" = "Nous conserverons une copie chiffrée de vos clés sur notre serveur. Protégez votre sauvegarde avec une phrase secrète pour qu’elle soit en sécurité.\n\nPour une sécurité maximale, elle devrait être différente du mot de passe de votre compte Matrix."; "key_backup_setup_passphrase_passphrase_title" = "Saisir"; "key_backup_setup_passphrase_passphrase_placeholder" = "Saisir la phrase secrète"; "key_backup_setup_passphrase_passphrase_valid" = "Super !"; @@ -816,7 +816,7 @@ "image_picker_action_library" = "Choisir dans la médiathèque"; "camera_unavailable" = "L’appareil photo n’est pas disponible sur votre appareil"; "photo_library_access_not_granted" = "%@ n’a pas la permission pour accéder à la médiathèque, veuillez modifier les options de confidentialité"; -"auth_forgot_password_error_no_configured_identity_server" = "Aucun serveur d’identité n’est configuré : ajoutez-en un pour réinitialiser votre mot de passe."; +"auth_forgot_password_error_no_configured_identity_server" = "Aucun serveur d’identité n’est configuré : ajoutez-en un pour réinitialiser le mot de passe de votre compte Matrix."; "room_creation_error_invite_user_by_email_without_identity_server" = "Aucun serveur d’identité n’est configuré donc vous ne pouvez pas ajouter de participant avec un e-mail."; "room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Aucun serveur d’identité n’est configuré donc vous ne pouvez pas commencer de discussion avec un contact en utilisant un e-mail."; // Service terms @@ -845,9 +845,9 @@ "auth_add_email_message_2" = "Définissez une adresse e-mail pour la récupération de compte, et pour être éventuellement découvrable par les personnes qui vous connaissent."; "auth_add_phone_message_2" = "Définissez un numéro de téléphone pour être éventuellement découvrable par les personnes qui vous connaissent."; "auth_add_email_phone_message_2" = "Définissez une adresse e-mail pour la récupération de compte. Vous pouvez ensuite utiliser votre e-mail ou votre numéro de téléphone pour être découvrable par les personnes qui vous connaissent."; -"auth_email_is_required" = "Aucun serveur d’identité n’est configuré donc vous ne pouvez pas ajouter d’adresse e-mail pour pouvoir réinitialiser votre mot de passe ultérieurement."; -"auth_phone_is_required" = "Aucun serveur d’identité n’est configuré donc vous ne pouvez pas ajouter de numéro de téléphone pour pouvoir réinitialiser votre mot de passe ultérieurement."; -"auth_reset_password_error_is_required" = "Aucun serveur d’identité n’est configuré : ajoutez-en un dans les options du serveur pour réinitialiser votre mot de passe."; +"auth_email_is_required" = "Aucun serveur d’identité n’est configuré donc vous ne pouvez pas ajouter d’adresse e-mail pour pouvoir réinitialiser le mot de passe de votre compte Matrix ultérieurement."; +"auth_phone_is_required" = "Aucun serveur d’identité n’est configuré donc vous ne pouvez pas ajouter de numéro de téléphone pour pouvoir réinitialiser le mot de passe de votre compte Matrix ultérieurement."; +"auth_reset_password_error_is_required" = "Aucun serveur d’identité n’est configuré : ajoutez-en un dans les options du serveur pour réinitialiser le mot de passe de votre compte Matrix."; "contacts_address_book_no_identity_server" = "Aucun serveur d’identité n’est configuré"; "settings_discovery_settings" = "DÉCOUVERTE"; "settings_identity_server_settings" = "SERVEUR D’IDENTITÉ"; @@ -908,7 +908,7 @@ "service_terms_modal_message_identity_server" = "Acceptez les conditions du serveur d’identité (%@) pour découvrir des contacts."; "settings_add_3pid_password_title_email" = "Ajouter une adresse e-mail"; "settings_add_3pid_password_title_msidsn" = "Ajouter un numéro de téléphone"; -"settings_add_3pid_password_message" = "Pour continuer, saisissez votre mot de passe"; +"settings_add_3pid_password_message" = "Pour continuer, saisissez le mot de passe de votre compte Matrix"; "settings_add_3pid_invalid_password_message" = "Informations d’authentification invalides"; "error_not_supported_on_mobile" = "Vous ne pouvez pas faire cela depuis %@ mobile."; "widget_menu_refresh" = "Actualiser"; @@ -1130,7 +1130,7 @@ "secrets_setup_recovery_key_storage_alert_message" = "✓ Imprimez-la et stockez-la dans un endroit sûr\n✓ Sauvegardez-la sur une clé USB ou un disque de sauvegarde\n✓ Copiez-la sur votre stockage personnel dans le cloud"; "secrets_setup_recovery_passphrase_title" = "Définir une phrase de sécurité"; "secrets_setup_recovery_passphrase_information" = "Saisissez une phrase de sécurité que vous seul connaissez, utilisée pour sécuriser les secrets sur votre serveur."; -"secrets_setup_recovery_passphrase_additional_information" = "N’utilisez pas le mot de passe de votre compte."; +"secrets_setup_recovery_passphrase_additional_information" = "N’utilisez pas le mot de passe de votre compte Matrix."; "secrets_setup_recovery_passphrase_validate_action" = "Terminé"; "secrets_setup_recovery_passphrase_confirm_information" = "Saisissez à nouveau votre phrase de sécurité pour la confirmer."; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Confirmer"; @@ -1139,13 +1139,13 @@ "authenticated_session_flow_not_supported" = "Cette application ne prend par en charge le mécanisme d’authentification de votre serveur d’accueil."; "secure_backup_setup_banner_title" = "Sauvegarde sécurisée"; "secure_backup_setup_banner_subtitle" = "Protection afin d’éviter de perdre l’accès aux messages et données chiffrés"; -"security_settings_crypto_sessions_description_2" = "Si vous ne reconnaissez pas une connexion, changez votre mot de passe et réinitialisez la sauvegarde sécurisée."; +"security_settings_crypto_sessions_description_2" = "Si vous ne reconnaissez pas une connexion, changez le mot de passe de votre compte Matrix et réinitialisez la sauvegarde sécurisée."; "security_settings_secure_backup" = "SAUVEGARDE SÉCURISÉE"; "security_settings_secure_backup_description" = "Sauvegardez vos clés de chiffrement et les données de votre compte au où vous perdriez l’accès à vos sessions. Vos clés seront protégées par une clé de sécurité unique."; "security_settings_secure_backup_setup" = "Configurer"; "security_settings_secure_backup_synchronise" = "Synchroniser"; "security_settings_secure_backup_delete" = "Supprimer la sauvegarde"; -"security_settings_user_password_description" = "Confirmez votre identité en saisissant le mot de passe de votre compte"; +"security_settings_user_password_description" = "Confirmez votre identité en saisissant le mot de passe de votre compte Matrix"; "secure_key_backup_setup_existing_backup_error_title" = "Une sauvegarde pour les messages existe déjà"; "secure_key_backup_setup_existing_backup_error_info" = "Déverrouillez-la pour la réutiliser dans la sauvegarde sécurisée ou supprimez-la pour créer une nouvelle sauvegarde de messages dans la sauvegarde sécurisée."; "secure_key_backup_setup_existing_backup_error_unlock_it" = "La déverrouiller"; @@ -1285,7 +1285,7 @@ // MARK: - Major update "major_update_title" = "Riot est désormais %@"; -"secrets_reset_authentication_message" = "Saisir votre mot de passe de compte pour confirmer"; +"secrets_reset_authentication_message" = "Saisissez le mot de passe de votre compte Matrix pour confirmer"; "secrets_reset_reset_action" = "Réinitialiser"; "secrets_reset_warning_message" = "Vous allez reprendre sans historique, sans message, appareil ou utilisateur de confiance."; "secrets_reset_warning_title" = "Si vous réinitialisez tout"; @@ -1640,3 +1640,35 @@ "poll_edit_form_update_failure_subtitle" = "Veuillez réessayer"; "poll_edit_form_update_failure_title" = "Échec lors de la mise à jour du sondage"; "poll_edit_form_poll_type" = "Type de sondage"; +"location_sharing_post_failure_subtitle" = "%# n’a pas pu envoyer votre localisation. Merci de réessayer plus tard."; +"location_sharing_post_failure_title" = "Nous n’avons pas pu envoyer votre localisation"; +"home_context_menu_leave" = "Partir"; +"home_context_menu_unfavourite" = "Retirer des favoris"; +"home_context_menu_favourite" = "Mettre en favoris"; +"home_context_menu_unmute" = "Retirer la sourdine"; +"home_context_menu_mute" = "Mettre en sourdine"; +"home_context_menu_notifications" = "Notifications"; +"home_context_menu_make_room" = "Déplacer vers Salons"; +"home_context_menu_make_dm" = "Déplacer vers Personnes"; +"event_formatter_message_deleted" = "Message supprimé"; +"settings_labs_enable_threads" = "Fils de discussion"; +"message_from_a_thread" = "Depuis un fil de discussion"; +"threads_empty_show_all_threads" = "Afficher tous les fils de discussion"; +"threads_empty_tip" = "Astuce : appuyez sur un message puis sur « Fil » pour en commencer un nouveau."; +"threads_empty_info_my" = "Répondez à un fil de discussion actif, ou appuyez sur un message et utilisez « Fil » pour en commencer un nouveau."; +"threads_empty_info_all" = "Les fils de discussion vous permettent de centrer vos conversations sur un sujet, et de les suivre facilement."; +"threads_empty_title" = "Gardez vos conversations organisées avec les fils de discussion"; +"threads_action_my_threads" = "Mes fils"; +"threads_action_all_threads" = "Tous les fils"; +"threads_title" = "Fils"; +"thread_copy_link_to_thread" = "Copier le lien vers le fil"; + +// MARK: Threads +"room_thread_title" = "Fil"; +"room_accessibility_thread_more" = "Plus"; +"room_accessibility_threads" = "Fils"; +"room_event_copy_link_info" = "Lien copié dans le presse-papier."; +"room_event_action_reply_in_thread" = "Fil"; +"room_event_action_view_in_room" = "Afficher dans le salon"; +"home_context_menu_normal_priority" = "Priorité normale"; +"home_context_menu_low_priority" = "Basse priorité"; diff --git a/Riot/Assets/he.lproj/Localizable.strings b/Riot/Assets/he.lproj/Localizable.strings new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/Riot/Assets/he.lproj/Localizable.strings @@ -0,0 +1 @@ + diff --git a/Riot/Assets/he.lproj/Vector.strings b/Riot/Assets/he.lproj/Vector.strings new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/Riot/Assets/he.lproj/Vector.strings @@ -0,0 +1 @@ + diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index e7d700ffa..2b2bf8853 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -52,7 +52,7 @@ "auth_optional_phone_placeholder" = "Telefonszám (nem kötelező)"; "auth_phone_placeholder" = "Telefonszám"; "auth_repeat_password_placeholder" = "Jelszó megerősítés"; -"auth_repeat_new_password_placeholder" = "Új jelszó megerősítés"; +"auth_repeat_new_password_placeholder" = "Új Matrix fiók jelszó megerősítés"; "auth_home_server_placeholder" = "URL (pl. https://matrix.org)"; "auth_identity_server_placeholder" = "URL (pl. https://vector.im)"; "auth_invalid_login_param" = "Hibás felhasználói név és/vagy jelszó"; @@ -73,7 +73,7 @@ "auth_untrusted_id_server" = "Az azonosító szerver megbízhatatlan"; "auth_password_dont_match" = "A jelszavak nem egyeznek meg"; "auth_username_in_use" = "A felhasználónév foglalt"; -"auth_forgot_password" = "Elfelejtetted a jelszót?"; +"auth_forgot_password" = "Elfelejtetted a Matrix fiók jelszót?"; "auth_email_not_found" = "E-mail küldési hiba: Az e-mail cím nem található"; "auth_use_server_options" = "Egyedi szerver beállítások használata (haladó)"; "auth_email_validation_message" = "Ellenőrizd az e-mailedet a regisztráció folytatásához"; @@ -81,14 +81,14 @@ "auth_msisdn_validation_message" = "Elküldtük az SMS aktivációs kódot. Írd be a kódot ide."; "auth_msisdn_validation_error" = "Nem sikerült ellenőrizni a telefonszámot."; "auth_recaptcha_message" = "Ez a Matrix szerver szeretne megbizonyosodni arról, hogy nem vagy robot"; -"auth_reset_password_message" = "A jelszó visszaállításához add meg az e-mail címet amit a felhasználói fiókhoz kötöttél:"; +"auth_reset_password_message" = "A Matrix fiók jelszó visszaállításához add meg az e-mail címet amit a felhasználói fiókhoz kötöttél:"; "auth_reset_password_missing_email" = "A felhasználói fiókodhoz kötött jelszót kell megadni."; "auth_reset_password_missing_password" = "Add meg az új jelszót."; "auth_reset_password_email_validation_message" = "Az e-mailt elküldtük a %@ címre. Ha rákattintottál a linkre ami benne van utána kattints ide."; "auth_reset_password_next_step_button" = "Ellenőriztem az e-mail címet"; "auth_reset_password_error_unauthorized" = "E-mail cím ellenőrzése nem sikerült: bizonyosodj meg róla, hogy az e-mailben lévő linkre rákattintottál"; "auth_reset_password_error_not_found" = "Úgy tűnik az e-mail címed nincs összekötve a Matrix azonosítóddal a Matrix szerveren."; -"auth_reset_password_success_message" = "Visszaállítottuk a jelszavad.\n\nKijelentkeztettünk minden munkamenetedből és nem kapsz értesítéseket sem. Az értesítések újra engedélyezéséhez jelentkezz be újra az eszközökön."; +"auth_reset_password_success_message" = "Visszaállítottuk a Matrix fiók jelszavad.\n\nKijelentkeztettünk minden munkamenetedből és nem kapsz értesítéseket sem. Az értesítések újra engedélyezéséhez jelentkezz be újra az eszközökön."; // Chat creation "room_creation_title" = "Új csevegés"; "room_creation_account" = "Fiók"; @@ -224,7 +224,7 @@ "room_event_action_redact" = "Töröl"; "room_event_action_more" = "További"; "room_event_action_share" = "Megosztás"; -"room_event_action_permalink" = "Állandó hivatkozás"; +"room_event_action_permalink" = "Üzenet hivatkozás másolása"; "room_event_action_view_source" = "Forrás megjelenítése"; "room_event_action_view_decrypted_source" = "Visszafejtett forrás megjelenítése"; "room_event_action_report" = "Tartalom bejelentése"; @@ -338,12 +338,12 @@ "settings_send_crash_report" = "Személytelen összeomlás és felhasználási adatok küldése"; "settings_enable_rageshake" = "Eszköz megrázása a hiba bejelentéséhez"; "settings_clear_cache" = "Gyorsítótár kiürítése"; -"settings_change_password" = "Jelszó megváltoztatása"; +"settings_change_password" = "Matrix fiók jelszó megváltoztatása"; "settings_old_password" = "régi jelszó"; "settings_new_password" = "új jelszó"; "settings_confirm_password" = "jelszó megerősítése"; -"settings_fail_to_update_password" = "A jelszó frissítése nem sikerült"; -"settings_password_updated" = "A jelszavad frissítve"; +"settings_fail_to_update_password" = "A Matrix fiók jelszó frissítése nem sikerült"; +"settings_password_updated" = "A Matrix fiók jelszavad frissítve"; "settings_crypto_device_name" = "Munkamenet neve: "; "settings_crypto_device_id" = "\nMunkamenet azonosítója: "; "settings_crypto_device_key" = "\nMunkamenet kulcsa:\n"; @@ -543,7 +543,7 @@ "deactivate_account_forget_messages_information_part3" = ": ezzel a jövőben a felhasználók hiányos csevegést fognak csak látni)"; "deactivate_account_validate_action" = "Felhasználói fiók felfüggesztése"; "deactivate_account_password_alert_title" = "Felhasználói fiók felfüggesztése"; -"deactivate_account_password_alert_message" = "A folytatáshoz add meg a jelszavadat"; +"deactivate_account_password_alert_message" = "A folytatáshoz add meg a Matrix fiók jelszavadat"; // Re-request confirmation dialog "rerequest_keys_alert_title" = "Kérés elküldve"; "rerequest_keys_alert_message" = "Kérlek indítsd el a %@et egy másik eszközödön amelyik vissza tudja fejteni az üzenetet, hogy el tudja küldeni a kulcsokat ennek a munkamenetnek."; @@ -589,7 +589,7 @@ "key_backup_setup_intro_title" = "Soha ne veszítsd el a titkosított üzeneteidet"; "key_backup_setup_intro_info" = "Titkosított szobákban az üzenetek végponttól-végpontig vannak titkosítva. Csak te és a címzettek rendelkeznek a kulcsokkal az üzenetek visszafejtéséhez.\n\nMentsd el megfelelően a kulcsaidat, hogy ne veszítsd el őket."; "key_backup_setup_intro_setup_action" = "Beállítás"; -"key_backup_setup_passphrase_info" = "A kulcsaid titkosított másolatát elmentjük a szerverünkre. Védd a mentést jelmondattal.\n\nA maximális biztonság érdekében ez a jelmondat különbözzön a felhasználói fiókhoz használttól."; +"key_backup_setup_passphrase_info" = "A kulcsaid titkosított másolatát elmentjük a szerverünkre. Védd a mentést jelmondattal.\n\nA maximális biztonság érdekében ez a jelmondat különbözzön a felhasználói Matrix fiókhoz használttól."; "key_backup_setup_passphrase_passphrase_title" = "Bead"; "key_backup_setup_passphrase_passphrase_placeholder" = "Jelmondat bevitele"; "key_backup_setup_passphrase_passphrase_valid" = "Szuper!"; @@ -813,7 +813,7 @@ "room_event_action_reaction_history" = "Reakciók története"; // MARK: Reaction history "reaction_history_title" = "Reakciók"; -"auth_forgot_password_error_no_configured_identity_server" = "Azonosítási szerver nincs beállítva: a jelszó visszaállításhoz adj hozzá egyet."; +"auth_forgot_password_error_no_configured_identity_server" = "Azonosítási szerver nincs beállítva: a Matrix fiók jelszó visszaállításhoz adj hozzá egyet."; "room_creation_error_invite_user_by_email_without_identity_server" = "Azonosítási szerver nincs beállítva, így e-mail cím alapján nem tudsz hozzáadni résztvevőt."; "room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Azonosítási szerver nincs beállítva, így e-mail cím alapján nem tudsz beszélgetést kezdeményezni."; "room_action_camera" = "Fotó vagy videó készítése"; @@ -850,9 +850,9 @@ "auth_add_email_message_2" = "E-mail cím beállítása fiók visszaállításhoz és, hogy később az ismerősök megtalálhassanak."; "auth_add_phone_message_2" = "Telefonszám beállítása, hogy később az ismerősök megtalálhassanak."; "auth_add_email_phone_message_2" = "E-mail cím beállítása fiók visszaállításhoz. Használj később e-mail címet vagy telefonszámot, hogy az ismerősök megtalálhassanak."; -"auth_email_is_required" = "Azonosítási szerver nincs beállítva, így nem tudsz hozzáadni e-mail címet amivel vissza lehetne állítani a jelszót a későbbiekben."; -"auth_phone_is_required" = "Azonosítási szerver nincs beállítva, így nem tudsz hozzáadni telefonszámot, hogy vissza lehetne állítani a jelszót a későbbiekben."; -"auth_reset_password_error_is_required" = "Azonosítási szerver nincs beállítva: adj hozzá egyet a szerver beállításoknál, hogy a jelszót vissza lehessen állítani."; +"auth_email_is_required" = "Azonosítási szerver nincs beállítva, így nem tudsz hozzáadni e-mail címet amivel vissza lehetne állítani a Matrix fiók jelszót a későbbiekben."; +"auth_phone_is_required" = "Azonosítási szerver nincs beállítva, így nem tudsz hozzáadni telefonszámot, hogy vissza lehetne állítani a Matrix fiók jelszót a későbbiekben."; +"auth_reset_password_error_is_required" = "Azonosítási szerver nincs beállítva: adj hozzá egyet a szerver beállításoknál, hogy a Matrix fiók jelszót vissza lehessen állítani."; "contacts_address_book_no_identity_server" = "Azonosítási szerver nincs beállítva"; "settings_discovery_settings" = "FELDERÍTÉS"; "settings_identity_server_settings" = "AZONOSÍTÁSI SZERVER"; @@ -913,7 +913,7 @@ "service_terms_modal_message_identity_server" = "Az azonosítási szerver (%@) felhasználási feltételeit el kell fogadnod, hogy ismerősöket kereshess."; "settings_add_3pid_password_title_email" = "E-mail cím hozzáadása"; "settings_add_3pid_password_title_msidsn" = "Telefonszám hozzáadása"; -"settings_add_3pid_password_message" = "A folytatáshoz add meg a jelszavadat"; +"settings_add_3pid_password_message" = "A folytatáshoz add meg a Matrix fiók jelszavadat"; "settings_add_3pid_invalid_password_message" = "Érvénytelen jelszó"; "error_not_supported_on_mobile" = "%@ mobilról ezt nem teheted meg."; "widget_menu_refresh" = "Frissítés"; @@ -1119,7 +1119,7 @@ "security_settings_secure_backup_setup" = "Beállítás"; "security_settings_secure_backup_synchronise" = "Szinkronizál"; "security_settings_secure_backup_delete" = "Mentés törlése"; -"security_settings_user_password_description" = "A fiók jelszó megadásával erősítsd meg a személyazonosságodat"; +"security_settings_user_password_description" = "A Matrix fiók jelszó megadásával erősítsd meg a személyazonosságodat"; "secure_key_backup_setup_intro_title" = "Biztonsági Mentés"; "secure_key_backup_setup_intro_info" = "A titkosított üzenetekhez és adatokhoz való hozzáférés elvesztése esetén használható biztonsági tartalék a titkosított kulcsok a szerveredre való elmentésével."; "secure_key_backup_setup_intro_use_security_key_title" = "Használd a Biztonsági Kulcsot"; @@ -1139,14 +1139,14 @@ "secrets_setup_recovery_key_done_action" = "Kész"; "secrets_setup_recovery_key_storage_alert_title" = "Tartsd biztonságban"; "secrets_setup_recovery_passphrase_title" = "Biztonsági Jelmondat beállítása"; -"secrets_setup_recovery_passphrase_additional_information" = "Ne a fiók jelszavadat használd."; +"secrets_setup_recovery_passphrase_additional_information" = "Ne a Matrix fiók jelszavadat használd."; "secrets_setup_recovery_passphrase_validate_action" = "Kész"; "secrets_setup_recovery_passphrase_confirm_information" = "A megerősítéshez add meg újra a Biztonsági Jelmondatot."; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Megerősítés"; "secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Jelmondat megerősítése"; "cross_signing_setup_banner_title" = "Titkosítás beállítása"; "cross_signing_setup_banner_subtitle" = "A többi eszközödet ellenőrizd egyszerűen"; -"security_settings_crypto_sessions_description_2" = "Ha ez a bejelentkezés nem ismerős, akkor változtasd meg a jelszavad és újra állítsd be a Biztonsági Mentést."; +"security_settings_crypto_sessions_description_2" = "Ha ez a bejelentkezés nem ismerős, akkor változtasd meg a Matrix fiók jelszavad és újra állítsd be a Biztonsági Mentést."; "security_settings_secure_backup_description" = "Mentsd el a titkosítási kulcsokat a fiókadatokkal arra az esetre ha elvesztenéd a hozzáférést a munkameneteidhez. A kulcsok egy egyedi Biztonsági Kulccsal lesznek védve."; // AuthenticatedSessionViewControllerFactory "authenticated_session_flow_not_supported" = "Ez az alkalmazás nem támogatja a matrix szervered azonosítási mechanizmusát."; @@ -1244,7 +1244,7 @@ "room_info_list_one_member" = "1 tag"; "room_info_list_several_members" = "%@ tag"; "room_info_list_section_other" = "Más"; -"secrets_reset_authentication_message" = "A fiók jelszavaddal erősítsd meg"; +"secrets_reset_authentication_message" = "A Matrix fiók jelszavaddal erősítsd meg"; "secrets_reset_reset_action" = "Visszaállít"; "secrets_reset_warning_message" = "Tiszta lappal indulsz, üzenetek, megbízható eszközök és felhasználók nélkül."; "secrets_reset_warning_title" = "Ha mindent visszaállítasz"; @@ -1626,3 +1626,35 @@ "poll_edit_form_update_failure_subtitle" = "Kérlek próbáld újra"; "poll_edit_form_update_failure_title" = "A szavazást nem sikerült frissíteni"; "poll_edit_form_poll_type" = "Szavazás típusa"; +"location_sharing_post_failure_subtitle" = "%@ a helyadatodat nem lehet elküldeni. Próbáld újra később."; +"location_sharing_post_failure_title" = "A földrajzi helyzetét nem lehet elküldeni"; +"home_context_menu_leave" = "Elhagyás"; +"home_context_menu_normal_priority" = "Normál prioritás"; +"home_context_menu_low_priority" = "Alacsony prioritás"; +"home_context_menu_unfavourite" = "Törlés a kedvencekből"; +"home_context_menu_favourite" = "Kedvencek"; +"home_context_menu_unmute" = "Némítás kikapcsolása"; +"home_context_menu_mute" = "Némítás"; +"home_context_menu_notifications" = "Értesítések"; +"home_context_menu_make_room" = "Szobák közé mozgatás"; +"home_context_menu_make_dm" = "Közvetlen beszélgetések közé helyezés"; +"event_formatter_message_deleted" = "Üzenet törölve"; +"settings_labs_enable_threads" = "Üzenetszálas beszélgetés"; +"message_from_a_thread" = "Az üzenetszálból"; +"threads_empty_show_all_threads" = "Minden üzenetszál megjelenítése"; +"threads_empty_tip" = "Tipp: Koppints az üzenetre és használd az „Üzenetszál”-at új indításhoz."; +"threads_empty_info_my" = "Válaszolj egy már meglévő üzenetszálba vagy koppints az üzenetre és használd az „Üzenetszál”-at új indításhoz."; +"threads_empty_info_all" = "Az üzenetszálak segítenek a különböző témájú beszélgetések figyelemmel kísérésében."; +"threads_empty_title" = "Beszélgetések üzenetszálakba rendezése"; +"threads_action_my_threads" = "Üzenetszálaim"; +"threads_action_all_threads" = "Minden üzenetszál"; +"threads_title" = "Üzenetszálak"; +"thread_copy_link_to_thread" = "Üzenetszálra mutató hivatkozás másolása"; + +// MARK: Threads +"room_thread_title" = "Üzenetszál"; +"room_accessibility_thread_more" = "Több"; +"room_accessibility_threads" = "Üzenetszálak"; +"room_event_copy_link_info" = "Hivatkozás a vágólapra másolva."; +"room_event_action_reply_in_thread" = "Üzenetszál"; +"room_event_action_view_in_room" = "Megjelenítés szobában"; diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 28dd88f35..87c75e41c 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -3,7 +3,7 @@ "auth_invalid_login_param" = "Nama pengguna dan/atau kata sandi tidak benar"; "auth_identity_server_placeholder" = "URL (contoh https://vector.im)"; "auth_home_server_placeholder" = "URL (contoh https://matrix.org)"; -"auth_repeat_new_password_placeholder" = "Konfirmasi kata sandi baru Anda"; +"auth_repeat_new_password_placeholder" = "Konfirmasi kata sandi akun Matrix baru Anda"; "auth_repeat_password_placeholder" = "Ulangi kata sandi"; "auth_phone_placeholder" = "Nomor telepon"; "auth_optional_phone_placeholder" = "Nomor telepon (opsional)"; @@ -73,13 +73,13 @@ "auth_email_validation_message" = "Silakan periksa surel Anda untuk melanjutkan pendaftaran"; "auth_use_server_options" = "Gunakan opsi server khusus (lanjutan)"; "auth_email_not_found" = "Gagal mengirim surel: Alamat email ini tidak ditemukan"; -"auth_forgot_password_error_no_configured_identity_server" = "Tidak ada server identitas yang dikonfigurasikan: tambahkan satu untuk mengatur ulang kata sandi Anda."; -"auth_forgot_password" = "Lupa kata sandi?"; +"auth_forgot_password_error_no_configured_identity_server" = "Tidak ada server identitas yang dikonfigurasikan: tambahkan satu untuk mengatur ulang kata sandi akun Matrix Anda."; +"auth_forgot_password" = "Lupa kata sandi akun Matrix?"; "auth_username_in_use" = "Nama pengguna telah dipakai"; "auth_password_dont_match" = "Kata sandi tidak cocok"; "auth_untrusted_id_server" = "Server identitas tidak dipercaya"; -"auth_phone_is_required" = "Tidak ada server identitas yang dikonfigurasi sehingga Anda tidak dapat menambahkan nomor telepon untuk mengatur ulang kata sandi Anda di masa depan."; -"auth_email_is_required" = "Tidak ada server identitas yang dikonfigurasi sehingga Anda tidak dapat menambahkan alamat email untuk mengatur ulang kata sandi Anda di masa depan."; +"auth_phone_is_required" = "Tidak ada server identitas yang dikonfigurasi sehingga Anda tidak dapat menambahkan nomor telepon untuk mengatur ulang kata sandi akun Matrix Anda di masa depan."; +"auth_email_is_required" = "Tidak ada server identitas yang dikonfigurasi sehingga Anda tidak dapat menambahkan alamat email untuk mengatur ulang kata sandi akun Matrix Anda di masa depan."; "auth_phone_in_use" = "Nomor telepon ini sudah dipakai"; "auth_email_in_use" = "Surel ini sudah dipakai"; "auth_missing_email_or_phone" = "Tidak ada surel atau nomor telepon"; @@ -133,7 +133,7 @@ "settings_integrations_allow_button" = "Kelola integrasi"; "settings_enable_callkit" = "Panggilan yang diintegrasi"; "settings_night_mode" = "Mode Malam"; -"settings_change_password" = "Ubah kata sandi"; +"settings_change_password" = "Ubah kata sandi akun Matrix"; "settings_first_name" = "Nama Depan"; "settings_display_name" = "Nama Tampilan"; "settings_profile_picture" = "Gambar Profil"; @@ -498,7 +498,7 @@ "room_event_action_delete" = "Hapus"; "room_event_action_resend" = "Kirim Ulang"; "room_event_action_save" = "Simpan"; -"room_event_action_permalink" = "Tautan"; +"room_event_action_permalink" = "Salin tautan ke pesan"; "room_event_action_share" = "Bagikan"; "room_event_action_more" = "Lainnya"; "room_event_action_redact" = "Hapus"; @@ -522,8 +522,8 @@ "room_participants_now" = "sekarang"; "room_participants_idle" = "Idle"; "room_participants_unknown" = "Tidak diketahui"; -"room_participants_offline" = "Offline"; -"room_participants_online" = "Online"; +"room_participants_offline" = "Luring"; +"room_participants_online" = "Daring"; "room_participants_invited_section" = "DIUNDANG"; "room_participants_invite_prompt_title" = "Konfirmasi"; "room_participants_remove_prompt_title" = "Konfirmasi"; @@ -595,14 +595,14 @@ "room_creation_account" = "Akun"; "social_login_list_title_sign_up" = "Atau"; "social_login_list_title_sign_in" = "Atau"; -"auth_reset_password_error_is_required" = "Tidak ada server identitas yang diatur: tambahkan di pengaturan server untuk mengatur ulang kata sandi Anda."; +"auth_reset_password_error_is_required" = "Tidak ada server identitas yang diatur: tambahkan di pengaturan server untuk mengatur ulang kata sandi akun Matrix Anda."; "auth_reset_password_error_not_found" = "Alamat email Anda terlihat tidak diasosiasikan dengan sebuah ID Matrix di homeserver ini."; "auth_reset_password_error_unauthorized" = "Gagal untuk memverifikasi alamat email: pastikan Anda membuka tautan yang ada di email"; "auth_reset_password_next_step_button" = "Saya telah memverifikasi alamat email saya"; "auth_reset_password_email_validation_message" = "Sebuah email telah dikirim ke %@. Setelah Anda membuka tautan yang ada, klik di bawah."; "auth_reset_password_missing_password" = "Sebuah kata sandi baru harus dimasukkan."; "auth_reset_password_missing_email" = "Alamat email yang tertaut dengan akun Anda harus dimasukkan."; -"auth_reset_password_message" = "Untuk mengatur ulang kata sandi Anda, masukkan alamat email yang tertaut ke akun Anda:"; +"auth_reset_password_message" = "Untuk mengatur ulang kata sandi akun Matrix Anda, masukkan alamat email yang tertaut ke akun Anda:"; "auth_recaptcha_message" = "Homeserver ini ingin memastikan bahwa Anda bukan sebuah robot"; "auth_msisdn_validation_error" = "Tidak dapat memverifikasi nomor telepon."; "auth_msisdn_validation_message" = "Kami telah mengirim sebuah SMS dengan kode aktivasi. Silakan masukkan kodenya di bawah."; @@ -667,7 +667,7 @@ // GDPR "gdpr_consent_not_given_alert_message" = "Untuk terus menggunakan homeserver %@, Anda harus menyetujui syarat dan ketentuannya."; "share_extension_failed_to_encrypt" = "Gagal untuk mengirim. Periksa di aplikasi utama pengaturan enkripsi untuk ruangan ini"; -"bug_report_logs_description" = "Untuk mendiagnosis masalah, log dari klien ini akan dikirim dengan laporan bug ini. Jika Anda lebih memilih untuk hanya mengirim teks di atas, harap hapus centang:"; +"bug_report_logs_description" = "Untuk mendiagnosis masalah, catat dari klien ini akan dikirim dengan laporan kutu ini. Jika Anda lebih memilih untuk hanya mengirim teks di atas, harap hapus centang:"; "bug_report_description" = "Harap jelaskan bugnya. Apa yang Anda lakukan? Apa yang Anda harapkan terjadi? Apa yang sebenarnya terjadi?"; "e2e_key_backup_wrong_version" = "Cadangan kunci pesan aman baru telah terdeteksi.\n\nJika ini bukan Anda, atur Frasa Keamanan baru di Pengaturan."; @@ -680,7 +680,7 @@ "call_no_stun_server_error_message_1" = "Mohon hubungi administrator homeserver %@ Anda untuk mengatur sebuah server TURN supaya panggilan dapat bekerja dengan handal."; "rage_shake_prompt" = "Anda tampaknya menggoyangkan telepon dengan frustrasi. Apakah Anda ingin mengirimkan laporan bug?"; "bug_report_prompt" = "Aplikasi telah crash terakhir kali. Apakah Anda ingin mengirimkan laporan crash?"; -"room_notifs_settings_encrypted_room_notice" = "Harap dicatat bahwa notifikasi sebutan & keyword tidak tersedia di ruang terenkripsi di ponsel."; +"room_notifs_settings_encrypted_room_notice" = "Harap dicatat bahwa notifikasi sebutan & kata kunci tidak tersedia di ruang terenkripsi di ponsel."; "room_details_addresses_disable_main_address_prompt_msg" = "Anda tidak akan memiliki alamat utama yang ditentukan. Alamat utama default untuk ruangan ini akan dipilih secara acak"; "room_details_history_section_prompt_msg" = "Perubahan siapa yang dapat membaca riwayat hanya akan berlaku untuk pesan berikutnya di ruang ini. Visibilitas riwayat yang ada tidak akan berubah."; "identity_server_settings_alert_error_terms_not_accepted" = "Anda harus menerima syaratnya %@ untuk menyetelnya sebagai server identitas."; @@ -696,7 +696,7 @@ "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."; "security_settings_secure_backup_description" = "Cadangkan kunci enkripsi Anda dengan data akun Anda untuk berjaga-jaga jika Anda kehilangan akses ke sesi Anda. Kunci Anda akan diamankan dengan Kunci Keamanan yang unik."; -"security_settings_crypto_sessions_description_2" = "Jika Anda tidak tahu sebuah login, ubah kata sandi Anda dan atur ulang Cadangan Aman."; +"security_settings_crypto_sessions_description_2" = "Jika Anda tidak tahu sebuah login, ubah kata sandi akun Matrix Anda dan atur ulang Cadangan Aman."; "settings_identity_server_no_is_description" = "Anda saat ini tidak menggunakan sebuah server identitas. Untuk menemukan dan dapat ditemukan oleh kontak yang Anda tahu, tambahkan satu di atas."; "settings_identity_server_description" = "Dengan menggunakan set server identitas di atas, Anda dapat menemukan dan dapat ditemukan oleh kontak yang ada yang Anda kenal."; "settings_discovery_three_pid_details_information_phone_number" = "Kelola preferensi untuk nomor telepon ini, yang dapat digunakan pengguna lain untuk menemukan Anda dan digunakan untuk mengundang Anda ke ruangan. Tambahkan atau hapus nomor telepon di Akun."; @@ -823,7 +823,7 @@ "room_details_access_section_anyone_apart_from_guest_for_dm" = "Siapa saja yang tahu linknya, selain dari tamu"; "identity_server_settings_alert_error_invalid_identity_server" = "%@ bukan server identitas yang valid."; "identity_server_settings_alert_no_terms_title" = "Server identitas tidak mempunyai kebijakan layanan"; -"security_settings_user_password_description" = "Konfirmasi identitas Anda dengan memasukkan kata sandi akun Anda"; +"security_settings_user_password_description" = "Konfirmasi identitas Anda dengan memasukkan kata sandi akun Matrix Anda"; "security_settings_secure_backup_info_valid" = "Sesi ini mencadangkan kunci Anda."; "version_check_modal_title_deprecated" = "Kami tidak mendukung iOS %@ lagi"; "version_check_modal_title_supported" = "Kami mengakhiri dukungan untuk iOS %@"; @@ -838,7 +838,7 @@ "voice_message_release_to_send" = "Tahan untuk merekam, lepaskan untuk mengirim"; "spaces_empty_space_title" = "Space ini belum ada ruangan"; "create_room_show_in_directory" = "Tampilkan ruangan di direktori"; -"secrets_reset_authentication_message" = "Masukkan kata sandi akun Anda untuk mengkonfirmasi"; +"secrets_reset_authentication_message" = "Masukkan kata sandi akun Matrix Anda untuk mengkonfirmasi"; "secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Masukkan Kunci Keamanan Anda untuk melanjutkan."; "secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Masukkan Frasa Keamanan Anda untuk melanjutkan."; @@ -988,7 +988,7 @@ "settings_key_backup_info_progress_done" = "Semua kunci telah dicadangkan"; "settings_key_backup_info_progress" = "Mencadangkan %@ kunci…"; "settings_key_backup_info_version" = "Versi Cadangan Kunci: %@"; -"settings_fail_to_update_password" = "Gagal untuk memperbarui kata sandi"; +"settings_fail_to_update_password" = "Gagal untuk memperbarui kata sandi akun Matrix"; "settings_labs_enable_ringing_for_group_calls" = "Dering untuk panggilan grup"; "settings_enable_push_notif" = "Notifikasi di perangkat ini"; "settings_fail_to_update_profile" = "Gagal untuk memperbarui profil"; @@ -1418,7 +1418,7 @@ // Bug report "bug_report_title" = "Laporan Bug"; -"deactivate_account_password_alert_message" = "Untuk melanjutkan, mohon masukkan kata sandi Anda"; +"deactivate_account_password_alert_message" = "Untuk melanjutkan, mohon masukkan kata sandi akun Matrix Anda"; "widget_integration_must_be_in_room" = "Anda tidak berada di ruangan ini."; "settings_devices_description" = "Nama publik sesi dapat dilihat oleh orang yang berkomunikasi dengan Anda"; "settings_key_backup_delete_confirmation_prompt_msg" = "Apakah Anda yakin? Anda akan kehilangan pesan terenkripsi jika kunci Anda tidak dicadangkan secara benar."; @@ -1438,7 +1438,7 @@ "settings_ui_theme_picker_message_invert_colours" = "\"Otomatis\" menggunakan pengaturan \"Balikkan Warna\" perangkat Anda"; "settings_ui_theme_picker_message_match_system_theme" = "\"Otomatis\" mencocokkan dengan tema sistem perangkat Anda"; "settings_calls_stun_server_fallback_description" = "Izinkan server panggilan %@ cadangan saat server homeserver Anda tidak menawarkannya (alamat IP Anda akan dibagikan selama panggilan)."; -"settings_mentions_and_keywords_encryption_notice" = "Anda tidak akan dapat notifikasi untuk sebutan & keyword di ruangan terenkripsi di ponsel."; +"settings_mentions_and_keywords_encryption_notice" = "Anda tidak akan dapat notifikasi untuk sebutan & kata kunci di ruangan terenkripsi di ponsel."; "settings_notifications_disabled_alert_message" = "Untuk mengaktifkan notifikasi, pergi ke pengaturan perangkat Anda."; "settings_global_settings_info" = "Pengaturan notifikasi global tersedia di klien web %@ Anda"; "settings_confirm_media_size_description" = "Saat ini aktif, Anda akan diminta untuk mengkonfirmasi ukuran gambar dan video yang akan dikirim."; @@ -1523,7 +1523,7 @@ "security_settings_crosssigning_info_not_bootstrapped" = "Penandatanganan silang belum disiapkan."; "settings_key_backup_button_connect" = "Hubungkan sesi ini ke Cadangan Kunci"; "settings_key_backup_info_trust_signature_valid_device_unverified" = "Cadangan memiliki tanda tangan dari %@"; -"settings_add_3pid_password_message" = "Untuk melanjutkan, mohon masukkan kata sandi Anda"; +"settings_add_3pid_password_message" = "Untuk melanjutkan, mohon masukkan kata sandi akun Matrix Anda"; "settings_send_crash_report" = "Kirim crash & data penggunaan anonim"; "secure_key_backup_setup_cancel_alert_message" = "Jika Anda membatalkan sekarang, Anda mungkin kehilangan pesan & data terenkripsi jika Anda kehilangan akses ke login Anda.\n\nAnda juga dapat mengatur Cadangan Aman & kelola kunci Anda di Pengaturan."; "room_participants_security_information_room_encrypted" = "Pesan di ruangan ini dienkripsi secara ujung-ke-ujung.\n\nPesan Anda diamankan dengan kunci dan hanya Anda dan penerima punya kunci uniknya untuk membukanya."; @@ -1539,7 +1539,7 @@ // Success from passphrase "key_backup_setup_success_from_passphrase_info" = "Kunci Anda sedang dicadangkan.\n\nKunci Keamanan Anda adalah jaring pengaman — Anda dapat menggunakannya untuk memulihkan akses ke pesan terenkripsi jika Anda lupa frasa sandi.\n\nSimpan Kunci Keamanan Anda di suatu tempat yang sangat aman, seperti pengelola kata sandi (atau brankas)."; -"key_backup_setup_passphrase_info" = "Kami akan menyimpan salinan terenkripsi dari kunci Anda di server kami. Lindungi cadangan Anda dengan frasa agar tetap aman.\n\nUntuk keamanan maksimum, ini harus berbeda dari kata sandi akun Anda."; +"key_backup_setup_passphrase_info" = "Kami akan menyimpan salinan terenkripsi dari kunci Anda di server kami. Lindungi cadangan Anda dengan frasa agar tetap aman.\n\nUntuk keamanan maksimum, ini harus berbeda dari kata sandi akun Matrix Anda."; "key_backup_setup_intro_info" = "Pesan di ruang terenkripsi diamankan dengan enkripsi ujung-ke-ujung. Hanya Anda dan penerima yang memiliki kunci untuk membaca pesan ini.\n\nCadangkan kunci Anda dengan aman untuk menghindari kehilangannya."; "deactivate_account_informations_part5" = "Jika Anda ingin kami melupakan pesan Anda, silakan centang kotak di bawah ini\n\nVisibilitas pesan di Matrix mirip dengan email. Kami melupakan pesan Anda berarti bahwa pesan yang telah Anda kirim tidak akan dibagikan dengan pengguna baru atau tidak terdaftar, tetapi pengguna terdaftar yang sudah memiliki akses ke pesan ini akan tetap memiliki akses ke salinannya."; "deactivate_account_informations_part1" = "Ini akan membuat akun Anda tidak dapat digunakan secara permanen. Anda tidak akan dapat masuk, dan tidak seorang pun dapat mendaftarkan ulang ID pengguna yang sama. Ini akan menyebabkan akun Anda meninggalkan semua ruangan yang diikutinya, dan akan menghapus detail akun Anda dari server identitas Anda. "; @@ -1552,7 +1552,7 @@ "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 mau."; "room_warning_about_encryption" = "Enkripsi ujung-ke-ujung masih dalam beta dan mungkin tidak dapat dihandalkan.\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 client 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 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."; +"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"; "leave_space_and_all_rooms_action" = "Tinggalkan semua ruangan dan space"; "space_beta_announce_subtitle" = "Versi baru dari komunitas"; @@ -1562,7 +1562,7 @@ "pin_protection_confirm_pin_to_disable" = "Konfirmasi PIN untuk menonaktifkan PIN"; "pin_protection_choose_pin" = "Buat sebuah PIN untuk keamanan"; "cross_signing_setup_banner_subtitle" = "Verifikasi perangkat Anda yang lain dengan mudah"; -"secrets_setup_recovery_passphrase_additional_information" = "Jangan menggunakan kata sandi akun Anda."; +"secrets_setup_recovery_passphrase_additional_information" = "Jangan menggunakan kata sandi akun Matrix Anda."; "secrets_recovery_with_key_invalid_recovery_key_title" = "Tidak dapat mengakses penyimpanan rahasia"; "secrets_recovery_with_passphrase_invalid_passphrase_title" = "Tidak dapat mengakses penyimpanan rahasia"; @@ -1616,7 +1616,7 @@ "security_settings_crosssigning_info_ok" = "Penandatanganan silang siap dipakai."; "settings_discovery_error_message" = "Sebuah kesalahan terjadi. Mohon coba lagi."; "settings_crypto_blacklist_unverified_devices" = "Enkripsi ke sesi terverifikasi saja"; -"settings_password_updated" = "Kata sandi Anda telah diperbarui"; +"settings_password_updated" = "Kata sandi akun Matrix Anda telah diperbarui"; "settings_enable_rageshake" = "Rage shake untuk melaporkan bug"; // No VoIP support @@ -1653,11 +1653,11 @@ "poll_edit_form_create_options" = "Buat opsi"; "poll_edit_form_input_placeholder" = "Tulis sesuatu"; "poll_edit_form_question_or_topic" = "Pertanyaan atau topik"; -"poll_edit_form_poll_question_or_topic" = "Pertanyaan atau topik poll"; +"poll_edit_form_poll_question_or_topic" = "Pertanyaan atau topik pemungutan suara"; // Mark: - Polls -"poll_edit_form_create_poll" = "Buat poll"; +"poll_edit_form_create_poll" = "Buat pemungutan suara"; "share_extension_send_now" = "Kirim sekarang"; "share_extension_low_quality_video_message" = "Kirim dalam %@ untuk kualitas yang lebih baik, atau kirim dengan kualitas rendah di bawah."; "share_extension_low_quality_video_title" = "Video akan dikirim dalam kualitas rendah"; @@ -1665,7 +1665,7 @@ "settings_about" = "TENTANG"; "poll_timeline_not_closed_action" = "OK"; "poll_timeline_not_closed_subtitle" = "Mohon coba lagi"; -"poll_timeline_not_closed_title" = "Gagal untuk mengakhiri poll"; +"poll_timeline_not_closed_title" = "Gagal untuk mengakhiri pemungutan suara"; "poll_timeline_vote_not_registered_action" = "OK"; "poll_timeline_vote_not_registered_subtitle" = "Maaf, suara Anda tidak diberikan, mohon coba lagi"; "poll_timeline_vote_not_registered_title" = "Suara tidak diberikan"; @@ -1680,10 +1680,10 @@ "poll_timeline_one_vote" = "1 suara"; "poll_edit_form_post_failure_action" = "OK"; "poll_edit_form_post_failure_subtitle" = "Silakan coba lagi"; -"poll_edit_form_post_failure_title" = "Gagal untuk mengirim poll"; +"poll_edit_form_post_failure_title" = "Gagal untuk mengirim pemungutan suara"; "settings_labs_enabled_polls" = "Poll"; -"room_event_action_end_poll" = "Akhiri poll"; -"room_event_action_remove_poll" = "Hapus poll"; +"room_event_action_end_poll" = "Akhiri pemungutan suara"; +"room_event_action_remove_poll" = "Hapus pemungutan suara"; "analytics_prompt_stop" = "Berhenti membagikan"; "analytics_prompt_yes" = "Iya, saya tidak keberatan"; "analytics_prompt_not_now" = "Jangan sekarang"; @@ -1735,10 +1735,42 @@ // Onboarding "onboarding_splash_register_button_title" = "Buat akun"; -"poll_edit_form_poll_type_closed_description" = "Hasil akan ditampilkan ketika Anda mengakhiri poll-nya"; +"poll_edit_form_poll_type_closed_description" = "Hasil akan ditampilkan ketika Anda mengakhiri pemungutan suara"; "poll_edit_form_poll_type_closed" = "Poll tertutup"; "poll_edit_form_poll_type_open_description" = "Pemberi suara akan melihat hasilnya ketika telah memberikan suara"; -"poll_edit_form_poll_type_open" = "Buka poll"; +"poll_edit_form_poll_type_open" = "Buka pemungutan suara"; "poll_edit_form_update_failure_subtitle" = "Silakan coba lagi"; -"poll_edit_form_update_failure_title" = "Gagal untuk memperbarui poll"; -"poll_edit_form_poll_type" = "Tipe poll"; +"poll_edit_form_update_failure_title" = "Gagal untuk memperbarui pemungutan suara"; +"poll_edit_form_poll_type" = "Tipe pemungutan suara"; +"location_sharing_post_failure_subtitle" = "%@ tidak dapat mengirimkan lokasi Anda. Silakan coba lagi nanti."; +"location_sharing_post_failure_title" = "Kami tidak dapat mengirimkan lokasi Anda"; +"home_context_menu_leave" = "Tinggalkan"; +"home_context_menu_normal_priority" = "Prioritas normal"; +"home_context_menu_low_priority" = "Prioritas rendah"; +"home_context_menu_unfavourite" = "Hilangkan dari Favorit"; +"home_context_menu_favourite" = "Favorit"; +"home_context_menu_unmute" = "Hilangkan bisuan"; +"home_context_menu_mute" = "Bisukan"; +"home_context_menu_notifications" = "Notifikasi"; +"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"; +"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."; +"threads_empty_info_my" = "Balas ke utasan yang sedang terjadi atau ketuk pada sebuah pesan dan gunakan “Utasan” untuk memulai yang baru."; +"threads_empty_info_all" = "Utasan membantu membuat obrolan sesuai topik dan mudah untuk dilacak."; +"threads_empty_title" = "Buat diskusi tetap teratur dengan utasan"; +"threads_action_my_threads" = "Utasan saya"; +"threads_action_all_threads" = "Semua utasan"; +"threads_title" = "Utasan"; +"thread_copy_link_to_thread" = "Salin tautan ke utasan"; + +// MARK: Threads +"room_thread_title" = "Utasan"; +"room_accessibility_thread_more" = "Lainnya"; +"room_accessibility_threads" = "Utasan"; +"room_event_copy_link_info" = "Tautan disalin ke papan klip."; +"room_event_action_reply_in_thread" = "Utasan"; +"room_event_action_view_in_room" = "Tampilkan di ruangan"; diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 9a80d83b0..abb600606 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -34,7 +34,7 @@ "auth_optional_phone_placeholder" = "Numero di telefono (opzionale)"; "auth_phone_placeholder" = "Numero di telefono"; "auth_repeat_password_placeholder" = "Ripeti la password"; -"auth_repeat_new_password_placeholder" = "Conferma la nuova password"; +"auth_repeat_new_password_placeholder" = "Conferma la nuova password dell'account Matrix"; "auth_home_server_placeholder" = "URL (es. https://matrix.org)"; "auth_identity_server_placeholder" = "URL (es. https://vector.im)"; "auth_invalid_login_param" = "Nome utente e/o password non corretti"; @@ -52,12 +52,12 @@ "auth_missing_email_or_phone" = "Indirizzo email o numero di telefono mancante"; "auth_email_in_use" = "Questo indirizzo email è già in uso"; "auth_phone_in_use" = "Questo numero di telefono è già in uso"; -"auth_forgot_password" = "Password dimenticata?"; +"auth_forgot_password" = "Password dell'account Matrix dimenticata?"; "auth_email_validation_message" = "Per favore controlla la tua email per proseguire la registrazione"; "auth_msisdn_validation_title" = "In attesa di verifica"; "auth_msisdn_validation_message" = "Ti è stato spedito un SMS con il codice di attivazione. Per favore inserisci il codice qui sotto."; "auth_msisdn_validation_error" = "Impossibile verificare il numero di telefono."; -"auth_reset_password_message" = "Per ripristinare la password inserisci l'indirizzo email associato al tuo account:"; +"auth_reset_password_message" = "Per ripristinare la password dell'account Matrix inserisci l'indirizzo email associato al tuo account:"; // String for App Store "store_short_description" = "Conversazioni sicure e decentralizzate"; // Actions @@ -90,7 +90,7 @@ "auth_reset_password_next_step_button" = "Ho verificato il mio indirizzo email"; "auth_reset_password_error_unauthorized" = "Verifica indirizzo email fallita: assicurati di aver cliccato sul link contenuto nell'email"; "auth_reset_password_error_not_found" = "Il tuo indirizzo email non sembra associato a nessun ID utente registrato su questo Home Server."; -"auth_reset_password_success_message" = "La tua password è stata ripristinata.\n\nSei stato disconnesso da tutte le sessioni e non riceverai più alcuna notifica. Per riabilitare le notifiche, riconnettiti su ciascun dispositivo."; +"auth_reset_password_success_message" = "La password del tuo account Matrix è stata ripristinata.\n\nSei stato disconnesso da tutte le sessioni e non riceverai più alcuna notifica. Per riabilitare le notifiche, riconnettiti su ciascun dispositivo."; "auth_add_email_and_phone_warning" = "Al momento non è ancora possibile registrarsi contemporaneamente con indirizzo email e numero di telefono e quindi verrà utilizzato solo il numero di telefono. Puoi aggiungere la tua email al tuo profilo dall menù Impostazioni."; "auth_accept_policies" = "Per favore, rileggi e accetta i termini di servizio di questo Home Server:"; "auth_autodiscover_invalid_response" = "Risposta Home Server non valida"; @@ -230,7 +230,7 @@ "room_event_action_redact" = "Rimuovi"; "room_event_action_more" = "Altro"; "room_event_action_share" = "Condividi"; -"room_event_action_permalink" = "Permalink"; +"room_event_action_permalink" = "Copia link nel messaggio"; "room_event_action_view_source" = "Vedi il codice sorgente"; "room_event_action_view_decrypted_source" = "Vedi il codice sorgente decifrato"; "room_event_action_report" = "Segnala il contenuto"; @@ -319,7 +319,7 @@ "settings_add_email_address" = "Aggiungi indirizzo email"; "settings_phone_number" = "Telefono"; "settings_add_phone_number" = "Aggiungi numero di telefono"; -"settings_change_password" = "Cambia password"; +"settings_change_password" = "Cambia password dell'account Matrix"; "settings_night_mode" = "Modalità notte"; "settings_fail_to_update_profile" = "Errore nell'aggiornamento del profilo"; "settings_enable_push_notif" = "Notifiche per questo dispositivo"; @@ -356,8 +356,8 @@ "settings_old_password" = "vecchia password"; "settings_new_password" = "nuova password"; "settings_confirm_password" = "conferma password"; -"settings_fail_to_update_password" = "Aggiornamento password fallito"; -"settings_password_updated" = "La tua password è stata aggiornata"; +"settings_fail_to_update_password" = "Aggiornamento password dell'account Matrix fallito"; +"settings_password_updated" = "La password del tuo account Matrix è stata aggiornata"; "settings_crypto_device_name" = "Nome sessione: "; "settings_crypto_device_id" = "\nID sessione: "; "settings_crypto_device_key" = "\nChiave sessione:\n"; @@ -580,7 +580,7 @@ "deactivate_account_forget_messages_information_part3" = ": gli utenti futuri vedranno un elenco incompleto di conversazioni)"; "deactivate_account_validate_action" = "Disattiva account"; "deactivate_account_password_alert_title" = "Disattiva account"; -"deactivate_account_password_alert_message" = "Per proseguire, inserisci la tua password"; +"deactivate_account_password_alert_message" = "Per continuare, inserisci la password del tuo account Matrix"; // Re-request confirmation dialog "rerequest_keys_alert_title" = "Richiesta inviata"; "rerequest_keys_alert_message" = "Avvia %@ su un altro dispositivo che possa decifrare il messaggio, in modo da poter inviare le chiavi a questa sessione."; @@ -594,7 +594,7 @@ "key_backup_setup_intro_manual_export_info" = "(Avanzato)"; "key_backup_setup_intro_manual_export_action" = "Esporta manualmente le chiavi"; "key_backup_setup_passphrase_title" = "Proteggi il tuo backup con una frase di sicurezza"; -"key_backup_setup_passphrase_info" = "Sul tuo Home Server verrà effettuato un Backup cifrato delle tue chiavi crittograficher. Proteggi il Backup con una frase di sicurezza perchè sia al sicuro.\n \nPer una massima sicurezza, la password del Backup dovrebbe essere diversa dalla password del tuo account."; +"key_backup_setup_passphrase_info" = "Conserveremo una copia cifrata delle tue chiavi sul nostro server. Proteggi il tuo backup con una frase di sicurezza per tenerlo sicuro.\n \nPer la massima sicurezza, dovrebbe essere diversa dalla password del tuo account Matrix."; "key_backup_setup_passphrase_passphrase_title" = "Inserisci"; "key_backup_setup_passphrase_passphrase_placeholder" = "Inserisci frase"; "key_backup_setup_passphrase_passphrase_valid" = "Bene!"; @@ -651,7 +651,7 @@ "sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Non voglio i miei messaggi cifrati"; "sign_out_key_backup_in_progress_alert_cancel_action" = "Attendo"; "close" = "Chiudi"; -"auth_forgot_password_error_no_configured_identity_server" = "Non è stato configurato alcun server d'identità: aggiungine uno per ripristinare la password."; +"auth_forgot_password_error_no_configured_identity_server" = "Non è stato configurato alcun server d'identità: aggiungine uno per ripristinare la password dell'account Matrix."; "auth_softlogout_signed_out" = "Sei uscito"; "auth_softlogout_sign_in" = "Accedi"; "auth_softlogout_reason" = "L'amministratore dell'Home Server (%1$@) ti ha disconnesso dal tuo account %2$@ (%3$@)."; @@ -820,9 +820,9 @@ "auth_add_email_message_2" = "Imposta un'email per il ripristino dell'account in caso di problemi e, se vuoi, anche per farti trovare da chi conosce quell'indirizzo email."; "auth_add_phone_message_2" = "Aggiungi un numero di telefono se vuoi farti trovare da chi lo conosce."; "auth_add_email_phone_message_2" = "Imposta un'email per il ripristino dell'account in caso di problemi. Email e telefono potranno essere usati anche per farti trovare dagli altri utenti."; -"auth_email_is_required" = "Non è stato configurato alcun server d'identità, perciò non puoi aggiungere un indirizzo email per ripristinare la tua password in futuro."; -"auth_phone_is_required" = "Non è stato configurato alcun server d'identità, perciò non puoi aggiungere un numero di telefono per ripristinare la tua password in futuro."; -"auth_reset_password_error_is_required" = "Non è stato configurato alcun server d'identità: aggiungine uno nelle opzioni server per ripristinare la password."; +"auth_email_is_required" = "Non è stato configurato alcun server d'identità, perciò non puoi aggiungere un indirizzo email per ripristinare la tua password dell'account Matrix in futuro."; +"auth_phone_is_required" = "Non è stato configurato alcun server d'identità, perciò non puoi aggiungere un numero di telefono per ripristinare la tua password dell'account Matrix in futuro."; +"auth_reset_password_error_is_required" = "Non è stato configurato alcun server d'identità: aggiungine uno nelle opzioni server per ripristinare la password dell'account Matrix."; "contacts_address_book_no_identity_server" = "Nessun server d'identità configurato"; "settings_discovery_settings" = "SCOPRI"; "settings_identity_server_settings" = "SERVER D'IDENTITÀ"; @@ -883,7 +883,7 @@ "error_invite_3pid_with_no_identity_server" = "Aggiungi un server d'identità nelle impostazioni per poter invitare utenti tramite email."; "settings_add_3pid_password_title_email" = "Aggiungi indirizzo email"; "settings_add_3pid_password_title_msidsn" = "Aggiungi numero di telefono"; -"settings_add_3pid_password_message" = "Per continuare, inserisci la tua password"; +"settings_add_3pid_password_message" = "Per continuare, inserisci la password del tuo account Matrix"; "settings_add_3pid_invalid_password_message" = "Credenziali non valide"; "error_not_supported_on_mobile" = "Non puoi farlo da %@ mobile."; "widget_menu_refresh" = "Ricarica"; @@ -1104,7 +1104,7 @@ "secrets_setup_recovery_key_storage_alert_message" = "✓ Stampala e conservala in un posto sicuro\n✓ Salvala in una chiave USB o disco di backup\n✓ Copiala nel tuo archivio cloud pesonale"; "secrets_setup_recovery_passphrase_title" = "Imposta una frase di sicurezza"; "secrets_setup_recovery_passphrase_information" = "Inserisci una frase di sicurezza che conosci solo tu, usata per proteggere i segreti nel tuo server."; -"secrets_setup_recovery_passphrase_additional_information" = "Non usare la password del tuo account."; +"secrets_setup_recovery_passphrase_additional_information" = "Non usare la password del tuo account Matrix."; "secrets_setup_recovery_passphrase_validate_action" = "Fatto"; "secrets_setup_recovery_passphrase_confirm_information" = "Inserisci la frase di sicurezza di nuovo per confermarla."; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Conferma"; @@ -1113,13 +1113,13 @@ "authenticated_session_flow_not_supported" = "Questa app non supporta il metodo di autenticazione del tuo homeserver."; "secure_backup_setup_banner_title" = "Backup Sicuro"; "secure_backup_setup_banner_subtitle" = "Proteggiti dalla perdita dei messaggi e dati crittografati"; -"security_settings_crypto_sessions_description_2" = "Se non riconosci un accesso, modifica la password e reimposta il Backup Sicuro."; +"security_settings_crypto_sessions_description_2" = "Se non riconosci un accesso, modifica la password dell'account Matrix e reimposta il Backup Sicuro."; "security_settings_secure_backup" = "BACKUP SICURO"; "security_settings_secure_backup_description" = "Fai un backup delle chiavi crittografiche con i dati del tuo account in caso tu perda l'accesso alle tue sessioni. Le chiavi saranno protette con una chiave di sicurezza univoca."; "security_settings_secure_backup_setup" = "Configura"; "security_settings_secure_backup_synchronise" = "Sincronizza"; "security_settings_secure_backup_delete" = "Elimina backup"; -"security_settings_user_password_description" = "Conferma la tua identità inserendo la password del tuo account"; +"security_settings_user_password_description" = "Conferma la tua identità inserendo la password dell'account Matrix"; // Events formatter with you "event_formatter_widget_added_by_you" = "Hai aggiunto il widget: %@"; "event_formatter_widget_removed_by_you" = "Hai rimosso il widget: %@"; @@ -1215,7 +1215,7 @@ "room_details_advanced_e2e_encryption_enabled_for_dm" = "La crittografia è attiva"; "room_details_advanced_e2e_encryption_disabled_for_dm" = "La crittografia non è attiva."; "pin_protection_kick_user_alert_message" = "Troppi errori, sei stato disconnesso"; -"secrets_reset_authentication_message" = "Inserisci la password del tuo account per confermare"; +"secrets_reset_authentication_message" = "Inserisci la password del tuo account Matrix per confermare"; "secrets_reset_reset_action" = "Reimposta"; "secrets_reset_warning_message" = "Ricomincerai senza cronologia, messaggi, dispositivi o utenti fidati."; "secrets_reset_warning_title" = "Se reimposti tutto"; @@ -1597,3 +1597,35 @@ "poll_edit_form_update_failure_subtitle" = "Riprova"; "poll_edit_form_update_failure_title" = "Aggiornamento del sondaggio fallito"; "poll_edit_form_poll_type" = "Tipo sondaggio"; +"location_sharing_post_failure_subtitle" = "%@ non ha potuto rilevare la tua posizione. Riprova più tardi."; +"location_sharing_post_failure_title" = "Non siamo riusciti ad inviare la tua posizione"; +"home_context_menu_leave" = "Esci"; +"home_context_menu_normal_priority" = "Priorità normale"; +"home_context_menu_low_priority" = "Bassa priorità"; +"home_context_menu_unfavourite" = "Rimuovi dai preferiti"; +"home_context_menu_favourite" = "Preferito"; +"home_context_menu_unmute" = "Riattiva audio"; +"home_context_menu_mute" = "Silenzioso"; +"home_context_menu_notifications" = "Notifiche"; +"home_context_menu_make_room" = "Sposta in Stanze"; +"home_context_menu_make_dm" = "Sposta in Persone"; +"event_formatter_message_deleted" = "Messaggio eliminato"; +"settings_labs_enable_threads" = "Messaggi in conversazioni"; +"message_from_a_thread" = "Da una conversazione"; +"threads_empty_show_all_threads" = "Mostra tutte le conversazioni"; +"threads_empty_tip" = "Consiglio: tocca un messaggio e usa “Conversazione” per iniziarne una."; +"threads_empty_info_my" = "Rispondi ad una conversazione in corso o tocca un messaggio e usa “Conversazione” per iniziarne una nuova."; +"threads_empty_info_all" = "Le conversazioni ti aiutano a tenere le tue discussioni in tema e rintracciabili."; +"threads_empty_title" = "Tieni le discussioni organizzate in conversazioni"; +"threads_action_my_threads" = "Le mie conversazioni"; +"threads_action_all_threads" = "Tutte le conversazioni"; +"threads_title" = "Conversazioni"; +"thread_copy_link_to_thread" = "Copia link nella conversazione"; + +// MARK: Threads +"room_thread_title" = "Conversazione"; +"room_accessibility_thread_more" = "Altro"; +"room_accessibility_threads" = "Conversazioni"; +"room_event_copy_link_info" = "Link copiato negli appunti."; +"room_event_action_reply_in_thread" = "Conversazione"; +"room_event_action_view_in_room" = "Vedi nella stanza"; diff --git a/Riot/Assets/ja.lproj/InfoPlist.strings b/Riot/Assets/ja.lproj/InfoPlist.strings index 7b9dfeb15..29532a6d2 100644 --- a/Riot/Assets/ja.lproj/InfoPlist.strings +++ b/Riot/Assets/ja.lproj/InfoPlist.strings @@ -1,5 +1,8 @@ // Permissions usage explanations -"NSCameraUsageDescription" = "ビデオ通話や写真撮影、動画撮影などを行うときにカメラを使用します。"; +"NSCameraUsageDescription" = "カメラは、ビデオ通話や写真撮影、動画撮影に使用されます。"; "NSPhotoLibraryUsageDescription" = "フォトライブラリは、写真や動画の送信に使用されます。"; -"NSMicrophoneUsageDescription" = "マイクは動画撮影や通話に使用されます。"; -"NSContactsUsageDescription" = "連絡先のどれがElementまたはMatrixを既に使用しているかを表示するために、あなたのアドレス帳にあるメールアドレスと電話番号をMatrix Identity Serverに送信することができます。 New Vectorはこのデータを保存したり、他の目的に使用したりしません。 詳細については、アプリケーション設定のプライバシーポリシーページを参照してください。"; +"NSMicrophoneUsageDescription" = "Elementは通話、動画撮影、ボイスメッセージの録音にマイクへのアクセスを必要としています。"; +"NSContactsUsageDescription" = "Elementは、あなたが連絡先をチャットに招待できるように、連絡先を表示します。"; +"NSCalendarsUsageDescription" = "予定されているミーティングをアプリで確認することができます。"; +"NSFaceIDUsageDescription" = "Face IDはアプリへのアクセスに使用されます。"; +"NSLocationWhenInUseUsageDescription" = "位置情報を共有する際には、地図を表示するためのアクセスをElementに付与する必要があります。"; diff --git a/Riot/Assets/ja.lproj/Localizable.strings b/Riot/Assets/ja.lproj/Localizable.strings index a2cbb08e6..b47601f88 100644 --- a/Riot/Assets/ja.lproj/Localizable.strings +++ b/Riot/Assets/ja.lproj/Localizable.strings @@ -31,9 +31,9 @@ /* A user has invited you to a chat */ "USER_INVITE_TO_CHAT" = "%@ さんがあなたを対話に招待しました"; /* A user has invited you to an (unamed) group chat */ -"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ さんがあなたを部屋へ招待しました"; +"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ さんがあなたをルームへ招待しました"; /* A user has invited you to a named room */ -"USER_INVITE_TO_NAMED_ROOM" = "%@ さんが部屋 %@ へ招待しました"; +"USER_INVITE_TO_NAMED_ROOM" = "%@ さんがルーム %@ へ招待しました"; /* Incoming one-to-one voice call */ "VOICE_CALL_FROM_USER" = "%@ さんから通話着信"; /* Incoming one-to-one video call */ @@ -53,7 +53,7 @@ /** Key verification **/ -"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@は検証したい"; +"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@は検証を求めています"; /* New message indicator on a room */ "MESSAGE_IN_X" = "%@ 内のメッセージ"; @@ -74,3 +74,6 @@ /* New message indicator on unknown room */ "MESSAGE" = "メッセージ"; +/** General **/ + +"NOTIFICATION" = "通知"; diff --git a/Riot/Assets/ja.lproj/Vector.strings b/Riot/Assets/ja.lproj/Vector.strings index 6ee67bd59..6e9584a13 100644 --- a/Riot/Assets/ja.lproj/Vector.strings +++ b/Riot/Assets/ja.lproj/Vector.strings @@ -1,17 +1,17 @@ // Titles "title_home" = "ホーム"; "title_favourites" = "お気に入り"; -"title_people" = "対話"; +"title_people" = "連絡先"; "title_rooms" = "ルーム"; "warning" = "警告"; // Actions "view" = "表示"; "next" = "次へ"; "back" = "戻る"; -"continue" = "続く"; +"continue" = "続ける"; "create" = "作成"; "start" = "開始"; -"leave" = "保存しない"; +"leave" = "退出"; "remove" = "削除"; "invite" = "招待"; "retry" = "再試行"; @@ -22,16 +22,16 @@ "join" = "参加"; "decline" = "断る"; "accept" = "受諾"; -"preview" = "下見"; +"preview" = "プレビュー"; "camera" = "カメラ"; "voice" = "音声"; "video" = "映像"; "active_call" = "通話開始"; -"active_call_details" = "通話開始 (%@)"; +"active_call_details" = "通話開始(%@)"; "later" = "後で"; "rename" = "名前変更"; "collapse" = "たたむ"; -"send_to" = "%@ さんへ送信"; +"send_to" = "%@さんへ送信"; "sending" = "送信中"; // Authentication "auth_login" = "ログイン"; @@ -40,104 +40,104 @@ "auth_skip" = "省く"; "auth_send_reset_email" = "初期化メール送信"; "auth_return_to_login" = "ログイン画面へ戻る"; -"auth_user_id_placeholder" = "ユーザ名または電子メール"; +"auth_user_id_placeholder" = "ユーザー名または電子メール"; "auth_password_placeholder" = "パスワード"; "auth_new_password_placeholder" = "新しいパスワード"; -"auth_user_name_placeholder" = "ユーザ名"; -"auth_optional_email_placeholder" = "電子メールアドレス (任意で)"; -"auth_email_placeholder" = "電子メールアドレス"; -"auth_optional_phone_placeholder" = "電話番号 (任意で)"; +"auth_user_name_placeholder" = "ユーザー名"; +"auth_optional_email_placeholder" = "メールアドレス(任意)"; +"auth_email_placeholder" = "メールアドレス"; +"auth_optional_phone_placeholder" = "電話番号(任意)"; "auth_phone_placeholder" = "電話番号"; "auth_repeat_password_placeholder" = "パスワード再確認"; "auth_repeat_new_password_placeholder" = "新しいパスワードを再確認"; "auth_home_server_placeholder" = "URL (例 https://matrix.org)"; "auth_identity_server_placeholder" = "URL (例 https://vector.im)"; -"auth_invalid_login_param" = "ユーザ名かパスワードが正しくありません"; -"auth_invalid_user_name" = "ユーザ名は半角英数字、ドット、ハイフン、アンダスコアのみで記して下さい"; +"auth_invalid_login_param" = "ユーザー名かパスワードが正しくありません"; +"auth_invalid_user_name" = "ユーザー名は半角英数字、ドット、ハイフン、アンダスコアのみで記して下さい"; "auth_invalid_password" = "パスワードが短すぎます(最小6文字)"; -"auth_invalid_email" = "正しくない電子メールアドレスのようです"; +"auth_invalid_email" = "メールアドレスの形式が正しくありません"; "auth_invalid_phone" = "正しくない電話番号のようです"; -"auth_missing_password" = "パスワードが違います"; +"auth_missing_password" = "パスワードが入力されていません"; "auth_add_email_message" = "電子メールアドレスを登録すると, 誰かがあなたを検索をしたり, パスワード紛失時に初期化のメールを送ることができます."; "auth_add_phone_message" = "電話番号を登録すると, 誰かがあなたを電話番号で検索できるようになります."; "auth_add_email_phone_message" = "電子メールアドレスや電話番号を登録すると, それらを使用して誰かがあなたを検索できるようになります. 電子メールアドレスは, パスワード紛失時に初期化の連絡をするためにも使用されます."; "auth_add_email_and_phone_message" = "電子メールアドレスや電話番号を登録すると, それらを使用して誰かがあなたを検索できるようになります. 電子メールアドレスは, パスワード紛失時に初期化の連絡をするためにも使用されます."; -"auth_missing_email" = "電子メールアドレスが違います"; -"auth_missing_phone" = "電話番号が違います"; -"auth_missing_email_or_phone" = "電子メールアドレスまたは電話番号が違います"; -"auth_email_in_use" = "この電子メールアドレスは既に使われています"; +"auth_missing_email" = "メールアドレスが入力されていません"; +"auth_missing_phone" = "電話番号が入力されていません"; +"auth_missing_email_or_phone" = "メールアドレスまたは電話番号が入力されていません"; +"auth_email_in_use" = "このメールアドレスは既に使われています"; "auth_phone_in_use" = "この電話番号は既に使用されています"; -"auth_untrusted_id_server" = "この認証サーバは信用されていません"; +"auth_untrusted_id_server" = "この認証サーバーは信用されていません"; "auth_password_dont_match" = "パスワードが一致しません"; -"auth_username_in_use" = "ユーザ名はすでに使用されています"; +"auth_username_in_use" = "ユーザー名は既に使用されています"; "auth_forgot_password" = "パスワードを忘れましたか?"; -"auth_email_not_found" = "電子メールの送信に失敗しました: メールアドレスが見つかりません"; -"auth_use_server_options" = "接続先サーバを指定する(追加設定)"; +"auth_email_not_found" = "電子メールの送信に失敗しました:メールアドレスが見つかりません"; +"auth_use_server_options" = "接続先サーバーを指定する(追加設定)"; "auth_email_validation_message" = "登録を続行するには電子メールを確認して下さい"; "auth_msisdn_validation_title" = "認証を確認中"; -"auth_msisdn_validation_message" = "SMSで認証番号を送りました. 以下にその番号を入力してください."; +"auth_msisdn_validation_message" = "SMSで認証番号を送りました。以下にその番号を入力してください。"; "auth_msisdn_validation_error" = "電話番号を認証できません。"; -"auth_recaptcha_message" = "接続先サーバ側が、機械による自動登録ではなく、あなたが人間であることを確認したいとのことです"; -"auth_reset_password_message" = "パスワードを初期化するには, アカウントに登録されている電子メールアドレスを入力してください:"; -"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あなたはすべてのセッションから切断しており、プッシュ通知を受け取ることはありません。通知を再度有効にするには、各端末に再度ログインします。"; -"auth_add_email_and_phone_warning" = "電子メールと電話番号の同時登録は, まだシステムが対応できません. 電話番号だけの登録は可能です. お手数おかけしますが, 後ほど個人情報設定から電子メールアドレスを登録してください."; +"auth_recaptcha_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あなたは全てのセッションから切断しており、プッシュ通知を受け取ることはありません。通知を再度有効にするには、各端末に再度ログインします。"; +"auth_add_email_and_phone_warning" = "電子メールと電話番号の同時登録は、まだシステムが対応できません。電話番号だけの登録は可能です。お手数おかけしますが、後ほど個人情報設定からメールアドレスを登録してください。"; // Chat creation -"room_creation_title" = "新しい対話"; +"room_creation_title" = "チャットを開始"; "room_creation_account" = "アカウント"; "room_creation_appearance" = "外観"; "room_creation_appearance_name" = "名前"; -"room_creation_appearance_picture" = "チャット画像 (任意)"; +"room_creation_appearance_picture" = "チャット画像(任意)"; "room_creation_privacy" = "個人情報保護"; "room_creation_private_room" = "この会話は非公開です"; "room_creation_public_room" = "この会話は公開されます"; -"room_creation_make_public" = "パブリックにする"; -"room_creation_make_public_prompt_title" = "このチャットをパブリックしますか?"; -"room_creation_make_public_prompt_msg" = "このチャットをパブリックしてもよろしいですか? 誰でもあなたのメッセージを読んでチャットに参加できます。"; -"room_creation_keep_private" = "プライベートに保つ"; -"room_creation_make_private" = "プライベートにする"; -"room_creation_wait_for_creation" = "ルームはすでに作成されています。 お待ちください。"; -"room_creation_invite_another_user" = "ユーザID, 表示名, 電子メールで検索と招待"; +"room_creation_make_public" = "公開"; +"room_creation_make_public_prompt_title" = "このチャットを公開しますか?"; +"room_creation_make_public_prompt_msg" = "このチャットを公開してもよろしいですか?誰でもあなたのメッセージを読んでチャットに参加できます。"; +"room_creation_keep_private" = "非公開に保つ"; +"room_creation_make_private" = "非公開にする"; +"room_creation_wait_for_creation" = "ルームは既に作成されています。お待ちください。"; +"room_creation_invite_another_user" = "ユーザーID、表示名、電子メールで検索と招待"; // Room recents "room_recents_directory_section" = "ルーム一覧"; "room_recents_favourites_section" = "お気に入り"; -"room_recents_people_section" = "対話"; +"room_recents_people_section" = "連絡先"; "room_recents_conversations_section" = "ルーム"; "room_recents_no_conversation" = "ルームがありません"; "room_recents_low_priority_section" = "低優先度"; "room_recents_invites_section" = "招待中"; -"room_recents_start_chat_with" = "対話を開始"; +"room_recents_start_chat_with" = "チャットを開始"; "room_recents_create_empty_room" = "ルームを作成"; "room_recents_join_room" = "ルームへ参加"; "room_recents_join_room_title" = "ルームへ参加"; -"room_recents_join_room_prompt" = "ルームIDまたはルームのエイリアスを入力します"; +"room_recents_join_room_prompt" = "ルームIDまたはルームのエイリアスを入力"; // People tab "people_invites_section" = "招待中"; "people_conversation_section" = "会話"; "people_no_conversation" = "会話なし"; // Rooms tab -"room_directory_no_public_room" = "利用可能なパブリックのルームはありません"; +"room_directory_no_public_room" = "利用可能な公開ルームはありません"; // Search "search_rooms" = "ルーム"; -"search_messages" = "発言"; -"search_people" = "対話"; +"search_messages" = "メッセージ"; +"search_people" = "連絡先"; "search_files" = "添付ファイル"; "search_default_placeholder" = "検索"; -"search_people_placeholder" = "ユーザID, 表示名, 電子メールで検索"; +"search_people_placeholder" = "ユーザーID、表示名、電子メールで検索"; "search_no_result" = "結果なし"; -"search_in_progress" = "検索中…"; +"search_in_progress" = "検索しています…"; // Directory "directory_cell_title" = "ルーム一覧を見る"; "directory_cell_description" = "%tu ルーム"; "directory_search_results_title" = "ルーム一覧検索結果"; -"directory_search_results" = "%tu 件の検索結果 for %@"; -"directory_search_results_more_than" = ">%tu 件の検索結果 for %@"; -"directory_searching_title" = "ルーム一覧を検索中…"; +"directory_search_results" = "%@の検索結果%tu件"; +"directory_search_results_more_than" = ">%@の検索結果%tu件"; +"directory_searching_title" = "ルーム一覧を検索しています…"; "directory_search_fail" = "一覧を取得できませんでした"; // Contacts "contacts_address_book_section" = "端末の電話帳"; @@ -146,117 +146,117 @@ "contacts_address_book_permission_required" = "端末内電話帳へのアクセス権限が必要です"; "contacts_address_book_permission_denied" = "端末の電話帳をElementアプリが読み取ることは許可されていません"; "contacts_user_directory_section" = "利用者一覧"; -"contacts_user_directory_offline_section" = "利用者一覧 (切断中)"; +"contacts_user_directory_offline_section" = "利用者一覧(オフライン)"; // Chat participants "room_participants_title" = "参加者"; "room_participants_add_participant" = "参加者を追加"; -"room_participants_one_participant" = "参加者 1名"; -"room_participants_multi_participants" = "参加者 %d名"; -"room_participants_leave_prompt_title" = "ルームを出る"; -"room_participants_leave_prompt_msg" = "ルームを退室して本当によろしいですか?"; +"room_participants_one_participant" = "参加者1名"; +"room_participants_multi_participants" = "参加者%d名"; +"room_participants_leave_prompt_title" = "ルームを退出"; +"room_participants_leave_prompt_msg" = "ルームを退出してよろしいですか?"; "room_participants_remove_prompt_title" = "確認"; -"room_participants_remove_prompt_msg" = "本当に %@ さんをチャットから退去させますか?"; +"room_participants_remove_prompt_msg" = "本当に%@をチャットから退去させますか?"; "room_participants_remove_third_party_invite_msg" = "サードパーティの招待を削除することは、APIが存在するまでサポートされていません"; "room_participants_invite_prompt_title" = "確認"; -"room_participants_invite_prompt_msg" = "本当に %@ さんをチャットへ招待しますか?"; -"room_participants_filter_room_members" = "参加者を検索"; -"room_participants_invite_another_user" = "ユーザID, 表示名, 電子メールで検索と招待"; +"room_participants_invite_prompt_msg" = "%@をチャットに招待してよろしいですか?"; +"room_participants_filter_room_members" = "ルームのメンバーを絞り込む"; +"room_participants_invite_another_user" = "ユーザーID、名前、電子メールで検索、招待"; "room_participants_invite_malformed_id_title" = "招待エラー"; -"room_participants_invite_malformed_id" = "不正なIDです。電子メールアドレスを用いるか、'@localpart:domain' のような Matrix IDを使用してください"; +"room_participants_invite_malformed_id" = "不正なIDです。メールアドレスを用いるか、'@localpart:domain'のようなMatrix IDを使用してください"; "room_participants_invited_section" = "招待中"; -"room_participants_online" = "接続中"; -"room_participants_offline" = "切断中"; -"room_participants_unknown" = "状態不明"; -"room_participants_idle" = "待機中"; +"room_participants_online" = "オンライン"; +"room_participants_offline" = "オフライン"; +"room_participants_unknown" = "不明"; +"room_participants_idle" = "アイドル"; "room_participants_now" = "現在"; "room_participants_ago" = "前"; "room_participants_action_section_admin_tools" = "管理者権限操作"; -"room_participants_action_section_direct_chats" = "対話"; -"room_participants_action_section_devices" = "セッション"; +"room_participants_action_section_direct_chats" = "非公開のチャット"; +"room_participants_action_section_devices" = "セッション一覧"; "room_participants_action_section_other" = "オプション"; "room_participants_action_invite" = "招待"; "room_participants_action_leave" = "このルームを出る"; -"room_participants_action_remove" = "このルームから退室させる"; -"room_participants_action_ban" = "このルームからブロックする"; -"room_participants_action_unban" = "ブロック解除"; -"room_participants_action_ignore" = "この参加者の発言を全て非表示にする"; -"room_participants_action_unignore" = "この参加者の発言を全て表示する"; -"room_participants_action_set_default_power_level" = "権限を一般参加者へ変更"; -"room_participants_action_set_moderator" = "権限を司会者へ変更"; +"room_participants_action_remove" = "このルームから削除"; +"room_participants_action_ban" = "このルームからブロック"; +"room_participants_action_unban" = "ブロックを解除"; +"room_participants_action_ignore" = "このユーザーの発言を全て非表示にする"; +"room_participants_action_unignore" = "このユーザーの発言を全て表示"; +"room_participants_action_set_default_power_level" = "権限を一般ユーザーへ変更"; +"room_participants_action_set_moderator" = "権限をモデレーターへ変更"; "room_participants_action_set_admin" = "権限を管理者へ変更"; -"room_participants_action_start_new_chat" = "対話を開始"; +"room_participants_action_start_new_chat" = "チャットを開始"; "room_participants_action_start_voice_call" = "音声通話を開始"; "room_participants_action_start_video_call" = "映像付き音声通話を開始"; -"room_participants_action_mention" = "指名して呼掛け"; +"room_participants_action_mention" = "メンション"; // Chat "room_jump_to_first_unread" = "最初の未読位置へ移動"; -"room_new_message_notification" = "%d 新しい発言"; -"room_new_messages_notification" = "%d 新しい発言"; -"room_one_user_is_typing" = "%@ さんが文字入力中…"; -"room_two_users_are_typing" = "%@ さん、 %@ さんが文字入力中…"; -"room_many_users_are_typing" = "%@ さん、 %@ さん他が文字入力中…"; -"room_message_placeholder" = "ここに送信文を入力 (暗号なし)…"; +"room_new_message_notification" = "%d件の新しい発言"; +"room_new_messages_notification" = "%d件の新しい発言"; +"room_one_user_is_typing" = "%@さんが入力しています…"; +"room_two_users_are_typing" = "%@さん、%@さんが入力しています…"; +"room_many_users_are_typing" = "%@さん、%@さん他が入力しています…"; +"room_message_placeholder" = "返信を送る(未暗号化)…"; "encrypted_room_message_placeholder" = "暗号文を送信…"; "room_message_short_placeholder" = "ここに送信文を入力…"; -"room_offline_notification" = "サーバとの接続が失われました."; -"room_unsent_messages_notification" = "文章が送信できませんでした."; -"room_unsent_messages_unknown_devices_notification" = "未知のセッションが存在するために文章が送信されませんでした."; -"room_ongoing_conference_call" = "会議通話実施中。 %@ または %@で参加してください。"; -"room_ongoing_conference_call_with_close" = "会議通話実施中。%@または%@で参加してください。 %@。"; +"room_offline_notification" = "サーバーとの接続が失われました。"; +"room_unsent_messages_notification" = "メッセージを送信できませんでした。"; +"room_unsent_messages_unknown_devices_notification" = "未知のセッションが存在するために文章が送信されませんでした。"; +"room_ongoing_conference_call" = "会議通話実施中。%@または%@で参加してください。"; +"room_ongoing_conference_call_with_close" = "会議通話実施中。%@または%@で参加してください。%@。"; "room_ongoing_conference_call_close" = "閉じる"; "room_conference_call_no_power" = "このルームで会議通話を管理する権限が必要です"; "room_prompt_resend" = "全て再送信"; "room_prompt_cancel" = "全て中止"; "room_resend_unsent_messages" = "未送信の文を再送信"; "room_delete_unsent_messages" = "未送信の文を削除"; -"room_event_action_copy" = "複写"; +"room_event_action_copy" = "コピー"; "room_event_action_quote" = "引用"; "room_event_action_redact" = "移動して削除"; "room_event_action_more" = "さらに"; "room_event_action_share" = "共有"; -"room_event_action_permalink" = "直リンク"; -"room_event_action_view_source" = "ソースコードを表示"; +"room_event_action_permalink" = "メッセージへのリンクをコピー"; +"room_event_action_view_source" = "ソースを表示"; "room_event_action_report" = "発言を報告"; "room_event_action_report_prompt_reason" = "この発言を報告する理由"; -"room_event_action_report_prompt_ignore_user" = "このユーザからのすべての発言を非表示にしますか?"; +"room_event_action_report_prompt_ignore_user" = "このユーザーからの全ての発言を非表示にしますか?"; "room_event_action_save" = "保存"; "room_event_action_resend" = "再送信"; "room_event_action_delete" = "削除"; "room_event_action_cancel_send" = "送信中止"; "room_event_action_cancel_download" = "ダウンロード中止"; "room_event_action_view_encryption" = "暗号についての情報"; -"room_warning_about_encryption" = "End-to-end 暗号化はベータ版であり、信頼性が低い場合があります。\n\n発言を保護するためにはまだ信用すべきではありません。\n\n端末が参加するより前の発言履歴を復号化することはまだできません。\n\n暗号化された発言は、まだ暗号化を実装していないクライアントでは表示されません。"; +"room_warning_about_encryption" = "エンドツーエンド暗号化はベータ版であり、信頼性が低い場合があります。\n\n発言を保護するためにはまだ信用すべきではありません。\n\n端末が参加するより前の発言履歴を復号化することはまだできません。\n\n暗号化された発言は、まだ暗号化を実装していないクライアントでは表示されません。"; "room_event_failed_to_send" = "送信失敗"; // Unknown devices -"unknown_devices_alert_title" = "部屋に未知のセッションが存在します"; -"unknown_devices_alert" = "この部屋には、確認されていない未知のセッションが含まれています。\nすなわち、セッションがをユーザ本人が所有しているという保証はありません。\n続ける前に各セッションの確認を行うことをおすすめしますが、確認することなく発言を再送信することができます。"; +"unknown_devices_alert_title" = "ルームに未知のセッションが存在します"; +"unknown_devices_alert" = "このルームには、確認されていない未知のセッションが含まれています。\nすなわち、セッションがをユーザー本人が所有しているという保証はありません。\n続ける前に各セッションの確認を行うことをおすすめしますが、確認することなく発言を再送信することができます。"; "unknown_devices_send_anyway" = "とにかく送る"; -"unknown_devices_call_anyway" = "とにかく通話する"; -"unknown_devices_answer_anyway" = "とにかく応答する"; +"unknown_devices_call_anyway" = "とにかく通話"; +"unknown_devices_answer_anyway" = "とにかく応答"; "unknown_devices_verify" = "確認…"; "unknown_devices_title" = "未知のセッション"; // Room Title -"room_title_new_room" = "新しい部屋"; -"room_title_multiple_active_members" = "全%@人中 %@人が回線接続"; -"room_title_one_active_member" = "全%@人中 %@人が回線接続"; +"room_title_new_room" = "新しいルーム"; +"room_title_multiple_active_members" = "全%@人中%@人が回線接続"; +"room_title_one_active_member" = "全%@人中%@人が回線接続"; "room_title_invite_members" = "招待中"; -"room_title_members" = "%@名の参加者"; -"room_title_one_member" = "1名の参加者"; +"room_title_members" = "%@名のメンバー"; +"room_title_one_member" = "1名のメンバー"; // Room Preview -"room_preview_invitation_format" = "あなたは %@ さんに呼ばれてこの部屋へ参加しました"; -"room_preview_subtitle" = "これは部屋の下見です。発言があっても部屋は更新されません。"; -"room_preview_unlinked_email_warning" = "このアカウントに関連付けられていない %@ 宛に招待が送信されました。別のアカウントでログインするか、メールアドレスをこのアカウントに追加することができます。"; -"room_preview_try_join_an_unknown_room" = "あなたは %@ へ接続しようとしています。この会議に参加しますか?"; -"room_preview_try_join_an_unknown_room_default" = "部屋"; +"room_preview_invitation_format" = "あなたは%@さんに呼ばれてこのルームへ参加しました"; +"room_preview_subtitle" = "これはルームのプレビューです。ルームでのやり取りは無効化されています。"; +"room_preview_unlinked_email_warning" = "このアカウントに関連付けられていない%@宛に招待が送信されました。別のアカウントでログインするか、メールアドレスをこのアカウントに追加することができます。"; +"room_preview_try_join_an_unknown_room" = "あなたは%@へ接続しようとしています。この会議に参加しますか?"; +"room_preview_try_join_an_unknown_room_default" = "ルーム"; // Settings "settings_title" = "設定"; -"account_logout_all" = "すべてのアカウントを回線切断する"; +"account_logout_all" = "全てのアカウントを回線切断"; "settings_config_no_build_info" = "ビルド情報がありません"; "settings_mark_all_as_read" = "全ての発言を既読にする"; -"settings_report_bug" = "不具合報告"; -"settings_config_home_server" = "接続先サーバは %@"; +"settings_report_bug" = "バグレポート"; +"settings_config_home_server" = "接続先サーバーは %@"; "settings_config_identity_server" = "認証サーバは %@"; -"settings_config_user_id" = "%@ でログインしています"; +"settings_config_user_id" = "%@でログインしています"; "settings_user_settings" = "利用者設定"; "settings_notifications_settings" = "通知設定"; "settings_calls_settings" = "通話"; @@ -270,26 +270,26 @@ "settings_cryptography" = "暗号化"; "settings_sign_out" = "通信完全切断"; "settings_sign_out_confirmation" = "本当によろしいですか?"; -"settings_sign_out_e2e_warn" = "あなたはend-to-end暗号鍵を紛失してしまったようです。すなわち、この端末で暗号化された部屋の昔の発言を読むことができなくなります。"; +"settings_sign_out_e2e_warn" = "あなたはエンドツーエンド暗号鍵を失ってしまいます。この端末で暗号化されたルームの昔の発言を読むことができなくなります。"; "settings_profile_picture" = "自分のアイコン画像"; "settings_display_name" = "表示名"; "settings_first_name" = "名"; "settings_surname" = "姓"; "settings_remove_prompt_title" = "確認"; -"settings_remove_email_prompt_msg" = "電子メールアドレス %@ を本当に削除してよろしいですか?"; +"settings_remove_email_prompt_msg" = "メールアドレス %@ を本当に削除してよろしいですか?"; "settings_remove_phone_prompt_msg" = "電話番号 %@ を本当に削除してよろしいですか?"; "settings_email_address" = "電子メール"; -"settings_email_address_placeholder" = "あなたの電子メールアドレスを入力してください"; -"settings_add_email_address" = "電子メールアドレスを追加"; +"settings_email_address_placeholder" = "あなたのメールアドレスを入力してください"; +"settings_add_email_address" = "メールアドレスを追加"; "settings_phone_number" = "電話番号"; "settings_add_phone_number" = "電話番号を追加"; "settings_night_mode" = "夜間おやすみモード"; "settings_fail_to_update_profile" = "自己紹介設定の更新に失敗しました"; "settings_enable_push_notif" = "この端末での通知"; "settings_show_decrypted_content" = "復号化された文章を表示"; -"settings_global_settings_info" = "あなたの %@ webクライアント上で、全体の通知設定が可能です"; -"settings_pin_rooms_with_missed_notif" = "通知の届かなかった部屋をピン止めする"; -"settings_callkit_info" = "画面がロックされているときに着信がありました。Elementの着信はシステムの通話履歴で確認できます。 iCloudが有効になっている場合、この通話履歴はAppleと共有されます。"; +"settings_global_settings_info" = "あなたの%@ webクライアント上で、全体の通知設定が可能です"; +"settings_pin_rooms_with_missed_notif" = "逃した通知があるルームを固定"; +"settings_callkit_info" = "画面がロックされているときに着信がありました。Elementの着信はシステムの通話履歴で確認できます。iCloudが有効になっている場合、この通話履歴はAppleと共有されます。"; "settings_ui_language" = "言語"; "settings_ui_theme" = "外観"; "settings_ui_theme_auto" = "自動"; @@ -300,18 +300,18 @@ "settings_unignore_user" = "%@さんからのメッセージを見ますか?"; "settings_contacts_discover_matrix_users" = "電子メールと電話番号をユーザの検索に使用"; "settings_contacts_phonebook_country" = "電話帳の国番号"; -"settings_labs_e2e_encryption" = "End-to-End暗号"; +"settings_labs_e2e_encryption" = "エンドツーエンド暗号化"; "settings_labs_e2e_encryption_prompt_message" = "暗号化の設定を完了するためには再度ログインしてください。"; "settings_labs_matrix_apps" = "Matrixアプリ"; -"settings_labs_create_conference_with_jitsi" = "jitsiの会議通話を作成する"; -"settings_version" = "Version %@"; -"settings_olm_version" = "Olm バージョン %@"; +"settings_labs_create_conference_with_jitsi" = "jitsiの会議通話を作成"; +"settings_version" = "バージョン %@"; +"settings_olm_version" = "Olmバージョン %@"; "settings_copyright" = "著作権"; "settings_term_conditions" = "利用規約"; "settings_privacy_policy" = "個人情報保護方針"; "settings_third_party_notices" = "外部ライブラリの規約"; "settings_send_crash_report" = "匿名利用状況と誤動作情報を送信"; -"settings_enable_rageshake" = "バグ報告のため端末を揺さぶる"; +"settings_enable_rageshake" = "バグレポートのため端末を振る"; "settings_clear_cache" = "一時保存を消去"; "settings_change_password" = "パスワード変更"; "settings_old_password" = "今までのパスワード"; @@ -322,96 +322,96 @@ "settings_crypto_device_name" = "セッション名: "; "settings_crypto_device_id" = "\nセッションID: "; "settings_crypto_device_key" = "\nセッションキー:\n"; -"settings_crypto_export" = "暗号鍵を外部へ保存"; +"settings_crypto_export" = "鍵をエクスポート"; "settings_crypto_blacklist_unverified_devices" = "検証されたセッションのみで暗号化"; // Room Details -"room_details_title" = "部屋の詳細"; -"room_details_people" = "参加者"; +"room_details_title" = "ルームの詳細"; +"room_details_people" = "メンバー"; "room_details_files" = "アップロード"; "room_details_settings" = "設定"; -"room_details_photo" = "部屋の画像アイコン"; -"room_details_room_name" = "部屋名"; +"room_details_photo" = "ルームの画像アイコン"; +"room_details_room_name" = "ルーム名"; "room_details_topic" = "トピック"; "room_details_favourite_tag" = "お気に入り"; "room_details_low_priority_tag" = "低優先度"; "room_details_mute_notifs" = "発言があっても通知しない"; "room_details_direct_chat" = "対話"; -"room_details_access_section" = "誰を部屋に入室可能としますか?"; -"room_details_access_section_invited_only" = "この部屋に招待された人のみ参加可能"; -"room_details_access_section_anyone_apart_from_guest" = "誰でも部屋に参加できる (ゲストユーザは不可)"; -"room_details_access_section_anyone" = "誰でも部屋に参加できる (ゲストユーザも許可)"; -"room_details_access_section_no_address_warning" = "この部屋へのリンクを作成するには、部屋の住所表記が必要です"; -"room_details_access_section_directory_toggle" = "部屋一覧へ公開する"; -"room_details_history_section" = "誰をこの部屋の発言履歴を読めるようにしますか?"; +"room_details_access_section" = "誰がルームにアクセスできますか?"; +"room_details_access_section_invited_only" = "招待された人のみ"; +"room_details_access_section_anyone_apart_from_guest" = "ルームのリンクを知っている人なら誰でも(ゲストユーザーを除く)"; +"room_details_access_section_anyone" = "ルームのリンクを知っている人なら誰でも(ゲストユーザーを含む)"; +"room_details_access_section_no_address_warning" = "このルームへのリンクを作成するには、ルームのアドレスが必要です"; +"room_details_access_section_directory_toggle" = "ルーム一覧へ公開"; +"room_details_history_section" = "誰をこのルームの発言履歴を読めるようにしますか?"; "room_details_history_section_anyone" = "誰でも"; -"room_details_history_section_members_only" = "参加者のみ (この設定を選択した時点から)"; -"room_details_history_section_members_only_since_invited" = "参加者のみ (招待を送った時点から)"; -"room_details_history_section_members_only_since_joined" = "参加者のみ (参加した時点から)"; +"room_details_history_section_members_only" = "メンバーのみ (この設定を選択した時点から)"; +"room_details_history_section_members_only_since_invited" = "メンバーのみ(招待を送った時点から)"; +"room_details_history_section_members_only_since_joined" = "メンバーのみ (参加した時点から)"; "room_details_history_section_prompt_title" = "個人情報の警告"; "room_details_history_section_prompt_msg" = "発言履歴を読むことができる人の変更は、以後の発言にのみ適用されます。既存の発言履歴の可視性は変更されません。"; -"room_details_addresses_section" = "住所表記"; -"room_details_no_local_addresses" = "この部屋はサーバ内住所表記がありません"; -"room_details_new_address" = "新しい住所表記を追加"; +"room_details_addresses_section" = "アドレス"; +"room_details_no_local_addresses" = "このルームにはローカルアドレスがありません"; +"room_details_new_address" = "新しいアドレスを追加"; "room_details_new_address_placeholder" = "新しいアドレスを追加(例 #foo%@)"; -"room_details_addresses_invalid_address_prompt_title" = "住所表記が正しくありません"; -"room_details_addresses_invalid_address_prompt_msg" = "%@ は正しくない形式の住所表記です"; -"room_details_addresses_disable_main_address_prompt_title" = "代表住所表記の警告"; -"room_details_addresses_disable_main_address_prompt_msg" = "代表住所表記が設定されていません. この部屋の代表住所表記は無作為に選択、設定されます"; +"room_details_addresses_invalid_address_prompt_title" = "不正なエイリアスのフォーマット"; +"room_details_addresses_invalid_address_prompt_msg" = "%@はエイリアスの正しいフォーマットではありません"; +"room_details_addresses_disable_main_address_prompt_title" = "メインアドレスの警告"; +"room_details_addresses_disable_main_address_prompt_msg" = "メインアドレスが設定されていません。このルームのメインアドレスは無作為に選択、設定されます"; "room_details_banned_users_section" = "ブロックされたユーザー"; "room_details_advanced_section" = "拡張設定"; -"room_details_advanced_room_id" = "部屋の固有ID:"; -"room_details_advanced_enable_e2e_encryption" = "暗号化を開始(警告: 部屋の暗号を中止することはできません!)"; -"room_details_advanced_e2e_encryption_enabled" = "この部屋の発言は暗号化されています"; -"room_details_advanced_e2e_encryption_disabled" = "この部屋の発言は暗号化されていません。"; +"room_details_advanced_room_id" = "ルームの固有ID:"; +"room_details_advanced_enable_e2e_encryption" = "暗号化を有効にする(警告: 有効後にこれを無効にすることはできません!)"; +"room_details_advanced_e2e_encryption_enabled" = "このルームの発言は暗号化されています"; +"room_details_advanced_e2e_encryption_disabled" = "このルームの発言は暗号化されていません。"; "room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "検証されたセッションのみで暗号化"; -"room_details_fail_to_update_avatar" = "部屋のアイコン画像の更新に失敗しました"; -"room_details_fail_to_update_room_name" = "部屋名の更新に失敗しました"; -"room_details_fail_to_update_topic" = "部屋の説明の更新に失敗しました"; -"room_details_fail_to_update_room_guest_access" = "部屋のゲスト参加者設定の更新に失敗しました"; -"room_details_fail_to_update_room_join_rule" = "入室規則の更新に失敗しました"; -"room_details_fail_to_update_room_directory_visibility" = "部屋一覧の可視設定の更新に失敗しました"; -"room_details_fail_to_update_history_visibility" = "発言履歴の可視範囲の設定更新に失敗しました"; -"room_details_fail_to_add_room_aliases" = "部屋住所の新規追加に失敗しました"; -"room_details_fail_to_remove_room_aliases" = "部屋住所の削除に失敗しました"; -"room_details_fail_to_update_room_canonical_alias" = "代表部屋住所の更新に失敗しました"; -"room_details_fail_to_update_room_direct" = "部屋の対話タグの変更に失敗しました"; -"room_details_fail_to_enable_encryption" = "部屋の暗号化の開始に失敗しました"; +"room_details_fail_to_update_avatar" = "ルームのアイコン画像の更新に失敗"; +"room_details_fail_to_update_room_name" = "ルーム名の更新に失敗"; +"room_details_fail_to_update_topic" = "ルームの説明の更新に失敗"; +"room_details_fail_to_update_room_guest_access" = "ゲストによるルームへのアクセスの設定更新に失敗"; +"room_details_fail_to_update_room_join_rule" = "参加ルールの更新に失敗"; +"room_details_fail_to_update_room_directory_visibility" = "ルーム一覧の可視設定の更新に失敗"; +"room_details_fail_to_update_history_visibility" = "発言履歴の可視範囲の設定更新に失敗"; +"room_details_fail_to_add_room_aliases" = "新しいルームアドレスの追加に失敗"; +"room_details_fail_to_remove_room_aliases" = "ルームアドレスの削除に失敗"; +"room_details_fail_to_update_room_canonical_alias" = "メインアドレスの更新に失敗"; +"room_details_fail_to_update_room_direct" = "ルームの対話タグの変更に失敗"; +"room_details_fail_to_enable_encryption" = "ルームの暗号化の開始に失敗"; "room_details_save_changes_prompt" = "変更を保存しますか?"; -"room_details_set_main_address" = "代表住所表記を設定"; -"room_details_unset_main_address" = "代表住所表記を設定解除"; -"room_details_copy_room_id" = "部屋固有IDをコピー"; -"room_details_copy_room_address" = "部屋の住所表記をコピー"; -"room_details_copy_room_url" = "部屋のURLをコピー"; +"room_details_set_main_address" = "メインアドレスを設定"; +"room_details_unset_main_address" = "メインアドレスの設定を解除"; +"room_details_copy_room_id" = "ルーム固有IDをコピー"; +"room_details_copy_room_address" = "ルームのアドレスをコピー"; +"room_details_copy_room_url" = "ルームのURLをコピー"; // Read Receipts "read_receipts_list" = "既読一覧を見る"; -"receipt_status_read" = "既読状況: "; +"receipt_status_read" = "既読状況: "; // Media picker "media_picker_library" = "ライブラリ"; "media_picker_select" = "選択"; // Directory -"directory_title" = "部屋一覧"; -"directory_server_picker_title" = "部屋一覧を選択"; -"directory_server_all_rooms" = "%@ サーバ上の全ての部屋"; -"directory_server_all_native_rooms" = "全てのMatrix連携部屋"; -"directory_server_type_homeserver" = "公開部屋一覧を表示するための接続サーバを入力してください"; +"directory_title" = "ルーム一覧"; +"directory_server_picker_title" = "ルーム一覧を選択"; +"directory_server_all_rooms" = "%@ サーバー上の全てのルーム"; +"directory_server_all_native_rooms" = "全てのMatrix連携ルーム"; +"directory_server_type_homeserver" = "公開ルーム一覧を表示するための接続サーバーを入力してください"; "directory_server_placeholder" = "matrix.org"; // Events formatter -"event_formatter_member_updates" = "%tu 権限が変更されました"; -"event_formatter_widget_added" = "%@ ウィジェットが %@ さんにより追加されました"; -"event_formatter_widget_removed" = "%@ ウィジェットが %@ さんにより削除されました"; -"event_formatter_jitsi_widget_added" = "音声会議が %@ さんにより追加されました"; -"event_formatter_jitsi_widget_removed" = "音声会議が %@ さんにより削除されました"; +"event_formatter_member_updates" = "%tu権限が変更されました"; +"event_formatter_widget_added" = "%@ウィジェットが %@ さんにより追加されました"; +"event_formatter_widget_removed" = "%@ウィジェットが %@ さんにより削除されました"; +"event_formatter_jitsi_widget_added" = "音声会議が%@ さんにより追加されました"; +"event_formatter_jitsi_widget_removed" = "音声会議が%@ さんにより削除されました"; // Others "or" = "または"; "you" = "あなた"; "today" = "今日"; "yesterday" = "昨日"; "network_offline_prompt" = "インターネットへの接続が切れているようです。"; -"public_room_section_title" = "公開部屋 (at %@):"; -"bug_report_prompt" = "前回アプリが異常終了しました。不具合報告を送信しますか?"; -"rage_shake_prompt" = "あなたは不満があって端末を揺らしているようです。不具合報告をしますか?"; -"do_not_ask_again" = "再度質問しない"; -"camera_access_not_granted" = "%@ はカメラを使用する権限を持っていません。個人情報保護設定の変更をお願いします"; +"public_room_section_title" = "公開ルーム(%@ にて):"; +"bug_report_prompt" = "前回アプリが異常終了しました。バグレポートを送信しますか?"; +"rage_shake_prompt" = "あなたは不満があって端末を揺らしているようです。バグレポートをしますか?"; +"do_not_ask_again" = "再び表示しない"; +"camera_access_not_granted" = "%@はカメラを使用する権限を持っていません。個人情報保護設定の変更をお願いします"; "large_badge_value_k_format" = "%.1fK"; // room display name "room_displayname_invite_from" = "%@ から招待されました"; @@ -422,67 +422,67 @@ // Call "call_incoming_voice_prompt" = "%@ さんから通話の着信中"; "call_incoming_video_prompt" = "%@ さんから映像つき通話の着信中"; -"call_incoming_voice" = "通話着信中…"; +"call_incoming_voice" = "着信中…"; "call_incoming_video" = "ビデオ通話の着信中…"; -"call_already_displayed" = "すでに通話中です。"; +"call_already_displayed" = "既に通話中です。"; "call_jitsi_error" = "会議通話への参加に失敗しました。"; // No VoIP support "no_voip_title" = "通話着信中"; -"no_voip" = "%@ さんから通話の着信がありましたが、 %@ は通話をまだサポートしていません。\nこの通知を無視して、別の端末から着信に応答することも、拒否することもできます。"; +"no_voip" = "%@さんから通話の着信がありましたが、%@は通話をまだサポートしていません。\nこの通知を無視して、別の端末から着信に応答することも、拒否することもできます。"; // Crash report "google_analytics_use_prompt" = "匿名の誤動作報告と使用状況データを自動的に報告して%@の改善に役立てますか?"; // Crypto "e2e_enabling_on_app_update" = "Elementはエンドツーエンド暗号化をサポートするようになりましたが、有効にするには再びログインする必要があります。\n\nアプリの設定から再ログインできます。今すぐ、または後からでも構いません。"; -"e2e_need_log_in_again" = "このセッションのエンドツーエンド暗号化キーを生成し、公開キーをホームサーバーに送信するには、再度ログインする必要があります。\nこれは一度だけです。 ご不便おかけしてすみません。"; +"e2e_need_log_in_again" = "再度ログインして、このセッションのエンドツーエンド暗号鍵を生成し、公開鍵をホームサーバーに送信する必要があります。\nご迷惑をおかけしますが、ご了承ください。"; // Bug report -"bug_report_title" = "不具合報告"; +"bug_report_title" = "バグレポート"; "bug_report_description" = "誤動作の内容と状況の説明をお願い致します。あなたは何をしましたか?何が起こると思いますか?実際何が起こったのですか?"; "bug_crash_report_title" = "異常終了報告"; "bug_crash_report_description" = "異常停止する前にあなたがしていたことを記してください:"; -"bug_report_logs_description" = "開発者が問題を診断するために、このElementの動作記録が不具合報告と一緒に送信されます。上記文章のみを送信したい場合は以下のチェックを解除してください:"; -"bug_report_send_logs" = "動作記録を送信"; +"bug_report_logs_description" = "開発者が問題を診断するために、このElementのログがバグレポートと一緒に送信されます。上記文章のみを送信したい場合は以下のチェックを解除してください:"; +"bug_report_send_logs" = "ログを送信"; "bug_report_send_screenshot" = "画面のスクリーンショット画像を送信"; -"bug_report_progress_zipping" = "動作記録を収集する"; +"bug_report_progress_zipping" = "ログを収集"; "bug_report_progress_uploading" = "報告を送信しています"; "bug_report_send" = "送信"; // Widget -"widget_no_power_to_manage" = "あなたがこの部屋でウィジェットを管理するための権限が必要です"; +"widget_no_power_to_manage" = "あなたがこのルームでウィジェットを管理するための権限が必要です"; "widget_creation_failure" = "ウィジェットの作成に失敗しました"; // Widget Integration Manager -"widget_integration_need_to_be_able_to_invite" = "それを行うにはユーザを招待する権限が必要です。"; +"widget_integration_need_to_be_able_to_invite" = "それを行うにはユーザーを招待する権限が必要です。"; "widget_integration_unable_to_create" = "ウィジェットを作成できません。"; "widget_integration_failed_to_send_request" = "リクエストの送信に失敗しました。"; -"widget_integration_room_not_recognised" = "この部屋では認められません。"; +"widget_integration_room_not_recognised" = "このルームでは認められません。"; "widget_integration_positive_power_level" = "権限の数値は正の整数で入力してください。"; -"widget_integration_must_be_in_room" = "あなたはこの部屋に所属していません。"; -"widget_integration_no_permission_in_room" = "あなたはこの部屋で権限がありません。"; -"widget_integration_missing_room_id" = "部屋固有IDの要求に失敗しました。"; -"widget_integration_missing_user_id" = "ユーザ固有IDの要求に失敗しました。"; -"widget_integration_room_not_visible" = "部屋 %@ は見えません。"; +"widget_integration_must_be_in_room" = "あなたはこのルームに所属していません。"; +"widget_integration_no_permission_in_room" = "あなたはこのルームで権限がありません。"; +"widget_integration_missing_room_id" = "ルーム固有IDの要求に失敗しました。"; +"widget_integration_missing_user_id" = "ユーザー固有IDの要求に失敗しました。"; +"widget_integration_room_not_visible" = "ルーム %@ は見えません。"; // Share extension -"share_extension_auth_prompt" = "コンテンツを共有するためにメインのアプリにログインする"; -"share_extension_failed_to_encrypt" = "送信に失敗しました。この部屋の暗号設定をメインの端末で確認して下さい"; +"share_extension_auth_prompt" = "メインのアプリにログインしてコンテンツを共有"; +"share_extension_failed_to_encrypt" = "送信に失敗しました。このルームの暗号設定をメインの端末で確認して下さい"; "room_details_advanced_e2e_encryption_prompt_message" = "End-to-end暗号化は実験的なものであり、信頼性が低い場合があります。\n\n発言を保護するためにはまだそれを信用すべきではありません。\n\n端末は、まだ参加する前の発言履歴を復号化することはできません。\n\n部屋の暗号化が今から有効になったら、もう無効にすることはできません。\n\n暗号化された発言は、まだ暗号化を実装していないアプリでは表示されません。"; "settings_enable_callkit" = "呼び出しの統合"; -"settings_pin_rooms_with_unread" = "未読のある部屋をピン止めする"; -"title_groups" = "コミュニティ"; +"settings_pin_rooms_with_unread" = "未読のあるルームを固定"; +"title_groups" = "コミュニティー"; "room_recents_server_notice_section" = "システムアラート"; // Groups tab "group_invite_section" = "招待"; -"group_section" = "コミュニティ"; -"room_message_reply_to_placeholder" = "返信を送る (暗号化されていない)…"; +"group_section" = "コミュニティー"; +"room_message_reply_to_placeholder" = "返信を送る(暗号化されていない)…"; "room_do_not_have_permission_to_post" = "このルームに投稿する権限がありません"; -"encrypted_room_message_reply_to_placeholder" = "暗号化された返信を送る…"; +"encrypted_room_message_reply_to_placeholder" = "暗号化された返信を送信…"; "room_message_reply_to_short_placeholder" = "返信を送る…"; "room_event_action_view_decrypted_source" = "復号化されたソースを見る"; "room_event_action_kick_prompt_reason" = "このユーザーを追放する理由"; "room_action_send_photo_or_video" = "写真か動画を送る"; "room_action_send_sticker" = "スタンプ送信"; -"room_replacement_information" = "このルームは交換されており、もうアクティブではありません。"; -"room_replacement_link" = "会話はここで続けられます。"; -"room_predecessor_information" = "この部屋は別の会話の続きです。"; -"room_predecessor_link" = "より古いメッセージを見るにはここをタップしてください。"; -"room_resource_limit_exceeded_message_contact_2_link" = "サービス管理者に連絡する"; +"room_replacement_information" = "このルームは交換されており、使用されていません。"; +"room_replacement_link" = "こちらから継続中の会話を確認する。"; +"room_predecessor_information" = "このルームは別の会話の続きです。"; +"room_predecessor_link" = "以前のメッセージを見るには、ここをタップしてください。"; +"room_resource_limit_exceeded_message_contact_2_link" = "サービス管理者に連絡"; "room_resource_limit_exceeded_message_contact_3" = " このサービスの使用を継続するには。"; "room_resource_usage_limit_reached_message_1_default" = "このホームサーバーはリソース制限の1つを超えています "; "room_resource_usage_limit_reached_message_1_monthly_active_user" = "このホームサーバーは月間アクティブユーザー数制限を超えています "; @@ -492,46 +492,46 @@ "settings_labs_room_members_lazy_loading" = "遅延ロードルームのメンバー"; "settings_labs_room_members_lazy_loading_error_message" = "あなたのホームサーバーはまだルームメンバーの遅延ロードをサポートしていません。 後で試してください。"; "settings_deactivate_my_account" = "アカウントを無効にします"; -"room_details_flair_section" = "コミュニティの特色を表示"; -"room_details_new_flair_placeholder" = "新しいコミュニティIDを追加 (例 +foo%@)"; +"room_details_flair_section" = "コミュニティーの特色を表示"; +"room_details_new_flair_placeholder" = "新しいコミュニティーIDを追加(例 +foo%@)"; "room_details_flair_invalid_id_prompt_title" = "無効な形式"; -"room_details_flair_invalid_id_prompt_msg" = "%@はコミュニティの有効な識別子ではありません"; -"room_details_fail_to_update_room_communities" = "関連コミュニティを更新できない"; +"room_details_flair_invalid_id_prompt_msg" = "%@はコミュニティーの有効な識別子ではありません"; +"room_details_fail_to_update_room_communities" = "関連するコミュニティーの更新に失敗"; // Group Details -"group_details_title" = "コミュニティの詳細"; +"group_details_title" = "コミュニティーの詳細"; "group_details_home" = "ホーム"; "group_details_people" = "人々"; -"group_details_rooms" = "部屋"; +"group_details_rooms" = "ルーム"; // Group Home -"group_home_one_member_format" = "1 メンバー"; -"group_home_multi_members_format" = "%tu メンバー"; -"group_home_one_room_format" = "1 部屋"; -"group_home_multi_rooms_format" = "%tu 部屋"; -"group_invitation_format" = "%@がこのコミュニティにあなたを招待しました"; +"group_home_one_member_format" = "1メンバー"; +"group_home_multi_members_format" = "%tuメンバー"; +"group_home_one_room_format" = "1ルーム"; +"group_home_multi_rooms_format" = "%tuルーム"; +"group_invitation_format" = "%@がこのコミュニティーにあなたを招待しました"; // Group participants "group_participants_add_participant" = "参加者を追加"; "group_participants_leave_prompt_title" = "グループを退出"; -"group_participants_leave_prompt_msg" = "本当にグループを退出しますか?"; +"group_participants_leave_prompt_msg" = "グループを退出してよろしいですか?"; "group_participants_remove_prompt_title" = "確認"; -"group_participants_remove_prompt_msg" = "本当にこのグループから%@を削除しますか?"; +"group_participants_remove_prompt_msg" = "このグループから%@を削除してよろしいですか?"; "group_participants_invite_prompt_title" = "確認"; -"group_participants_invite_prompt_msg" = "本当にこのグループに%@を招待しますか?"; -"group_participants_filter_members" = "コミュニティメンバーをフィルタリング"; +"group_participants_invite_prompt_msg" = "このグループに%@を招待してよろしいですか?"; +"group_participants_filter_members" = "コミュニティーのメンバーを絞り込む"; "group_participants_invite_another_user" = "ユーザーIDまたは名前による検索/招待"; "group_participants_invite_malformed_id_title" = "招待エラー"; -"group_participants_invite_malformed_id" = "不正なID。 '@localpart:domain' のようなMatrix IDでなければなりません"; -"group_participants_invited_section" = "招待した"; +"group_participants_invite_malformed_id" = "不正なID。'@localpart:domain'のようなMatrix IDでなければなりません"; +"group_participants_invited_section" = "招待中"; // Group rooms -"group_rooms_filter_rooms" = "コミュニティルームをフィルタリング"; -"event_formatter_rerequest_keys_part1_link" = "暗号鍵の再要求"; -"event_formatter_rerequest_keys_part2" = " あなたの他のセッションから。"; +"group_rooms_filter_rooms" = "コミュニティールームをフィルタリング"; +"event_formatter_rerequest_keys_part1_link" = "暗号鍵を再要求"; +"event_formatter_rerequest_keys_part2" = " あなたの他のセッションに。"; "homeserver_connection_lost" = "ホームサーバーに接続できませんでした。"; "widget_sticker_picker_no_stickerpacks_alert" = "現在、ステッカーパックを有効にしていません。"; "widget_sticker_picker_no_stickerpacks_alert_add_now" = "今すぐ追加しますか?"; // Room key request dialog -"e2e_room_key_request_title" = "暗号化キー要求"; -"e2e_room_key_request_message_new_device" = "暗号化キーを要求している新しい端末 '%@'を追加しました。"; -"e2e_room_key_request_message" = "検証されていない端末 '%@'が暗号化キーを要求しています。"; +"e2e_room_key_request_title" = "暗号鍵の要求"; +"e2e_room_key_request_message_new_device" = "暗号鍵を要求している新しい端末 '%@' を追加しました。"; +"e2e_room_key_request_message" = "検証されていない端末 '%@' が暗号鍵を要求しています。"; "e2e_room_key_request_start_verification" = "検証を始めます…"; "e2e_room_key_request_share_without_verifying" = "検証せずに共有"; "e2e_room_key_request_ignore_request" = "要求を無視"; @@ -539,24 +539,24 @@ "gdpr_consent_not_given_alert_message" = "%@ホームサーバーを引き続き使用するには、利用規約を確認して同意する必要があります。"; "gdpr_consent_not_given_alert_review_now_action" = "今レビュー"; "deactivate_account_title" = "無効なアカウント"; -"deactivate_account_informations_part1" = "これにより、アカウントは永久に使用できなくなります。 ログインすることはできず、誰も同じユーザーIDを再登録することはできません。 これにより、あなたのアカウントは参加しているすべての部屋から退去し、あなたのIDサーバーからアカウントの詳細が削除されます。 "; +"deactivate_account_informations_part1" = "これにより、アカウントは永久に使用できなくなります。ログインすることはできず、誰も同じユーザーIDを再登録することはできません。これにより、あなたのアカウントは参加している全てのルームから退去し、あなたのIDサーバーからアカウントの詳細が削除されます。 "; "deactivate_account_informations_part2_emphasize" = "この動作は元に戻せません。"; "deactivate_account_informations_part3" = "\n\nアカウントの無効化 "; "deactivate_account_informations_part4_emphasize" = "デフォルトではあなたが送信したメッセージを忘れることはありません。 "; -"deactivate_account_informations_part5" = "あなたのメッセージを忘れたければ、下のボックスにチェックを入れてください\n\nMatrixのメッセージの可視性はEメールと似ています。 forgettingメッセージは、送信したメッセージは新規または未登録のユーザーと共有されませんが、既にこれらのメッセージにアクセスしている登録ユーザーは引き続き自分のコピーにアクセスできます。"; -"deactivate_account_forget_messages_information_part1" = "アカウントが無効になったときに送信したすべてのメッセージを忘れてください ("; +"deactivate_account_informations_part5" = "メッセージの履歴の消去を望む場合は、以下のボックスにチェックを入れてください。\n\nMatrixのメッセージの見え方は、電子メールと同様のものです。メッセージの履歴を消去すると、あなたが送信したメッセージは、新規または未登録のユーザーに共有されることはありませんが、既にメッセージを取得している登録ユーザーは、今後もそのコピーにアクセスできます。"; +"deactivate_account_forget_messages_information_part1" = "アカウントが無効になったときに送信した全てのメッセージを忘れてください ("; "deactivate_account_forget_messages_information_part2_emphasize" = "警告"; "deactivate_account_forget_messages_information_part3" = ":これは将来のユーザーに会話の不完全なビューが表示される)"; "deactivate_account_validate_action" = "無効なアカウント"; "deactivate_account_password_alert_title" = "無効なアカウント"; "deactivate_account_password_alert_message" = "続行するには、パスワードを入力してください"; // Re-request confirmation dialog -"rerequest_keys_alert_title" = "リクエスト送信"; -"rerequest_keys_alert_message" = "この端末にキーを送信できるように、メッセージを復号化できる別の端末でElementを起動してください。"; +"rerequest_keys_alert_title" = "要求が送信されました"; +"rerequest_keys_alert_message" = "鍵をこのセッションに送信できるように、メッセージを復号化できる他の端末で%@を起動してください。"; "room_event_action_ban_prompt_reason" = "このユーザーをブロックする理由"; "room_resource_limit_exceeded_message_contact_1" = " お願い "; "settings_ui_theme_black" = "Black"; -"settings_flair" = "特色を表示する"; +"settings_flair" = "特色を表示"; // String for App Store "store_short_description" = "セキュアな分散型チャット/VoIP"; "close" = "閉じる"; @@ -567,44 +567,44 @@ "room_message_unable_open_link_error_message" = "リンクを開くことができません。"; "user_verification_session_details_verify_action_other_user" = "手動で確認"; "room_info_list_section_other" = "その他"; -"room_info_list_several_members" = "%@ 人のメンバー"; +"room_info_list_several_members" = "%@人のメンバー"; // MARK: - Room Info -"room_info_list_one_member" = "1 人のメンバー"; +"room_info_list_one_member" = "1人のメンバー"; "create_room_placeholder_address" = "#testroom:matrix.org"; -"create_room_section_header_address" = "部屋のアドレス"; -"create_room_show_in_directory" = "この部屋をディレクトリに掲載する"; -"create_room_section_footer_type" = "プライベートな部屋は、部屋に招待された人のみ参加できます。"; -"create_room_type_public" = "公開された部屋"; -"create_room_type_private" = "プライベートな部屋"; -"create_room_section_header_type" = "部屋の種類"; +"create_room_section_header_address" = "ルームのアドレス"; +"create_room_show_in_directory" = "このルームを一覧に掲載"; +"create_room_section_footer_type" = "非公開のルームは、ルームに招待された人のみ参加できます。"; +"create_room_type_public" = "公開されたルーム"; +"create_room_type_private" = "非公開のルーム"; +"create_room_section_header_type" = "ルームの種類"; "create_room_section_footer_encryption" = "暗号化はあとから無効にすることはできません。"; -"create_room_section_header_encryption" = "部屋の暗号化"; +"create_room_section_header_encryption" = "ルームの暗号化"; "create_room_placeholder_topic" = "トピック"; -"create_room_section_header_topic" = "部屋のトピック (任意)"; +"create_room_section_header_topic" = "ルームのトピック(任意)"; "create_room_placeholder_name" = "名前"; -"create_room_section_header_name" = "部屋の名前"; +"create_room_section_header_name" = "ルーム名"; // MARK: - Create Room -"create_room_title" = "新しい部屋"; +"create_room_title" = "新しいルーム"; "create_room_enable_encryption" = "暗号化を有効にする"; "room_details_room_name_for_dm" = "名前"; -"room_participants_security_information_room_encrypted_for_dm" = "ここで送受信されるメッセージはエンドツーエンド暗号化されます。\n\nメッセージは安全に保護されており、あなたと宛先のみがメッセージの閲覧に必要な鍵を所持します。"; -"room_participants_security_information_room_not_encrypted_for_dm" = "ここで送受信されるメッセージはエンドツーエンド暗号化されません。"; +"room_participants_security_information_room_encrypted_for_dm" = "ここで送受信されるメッセージはエンドツーエンド暗号化されています。\n\nメッセージは安全に保護されており、メッセージのロックを解除するための固有の鍵は、あなたと受信者だけが持っています。"; +"room_participants_security_information_room_not_encrypted_for_dm" = "ここでのメッセージはエンドツーエンド暗号化されていません。"; // Mark: - Room creation introduction cell "room_intro_cell_add_participants_action" = "参加者を追加"; -"room_participants_security_information_room_encrypted" = "このルームで送受信されるメッセージはエンドツーエンド暗号化されます。\n\nメッセージは安全に保護されており、このルームの参加者のみがメッセージの閲覧に必要な鍵を所持します。"; +"room_participants_security_information_room_encrypted" = "このルームのメッセージはエンドツーエンド暗号化されています。\n\nメッセージは安全に保護されており、メッセージのロックを解除するための固有の鍵は、あなたと受信者だけが持っています。"; "room_participants_security_information_room_not_encrypted" = "このルームのメッセージはエンドツーエンド暗号化されていません。"; "room_intro_cell_information_dm_sentence1_part3" = ". "; -"callbar_active_and_single_paused" = "ひとつのアクティブな通話 (%@) · ひとつの一時停止された通話"; +"callbar_active_and_single_paused" = "1つのアクティブな通話(%@)· 1つの一時停止された通話"; // Call Bar -"callbar_only_single_active" = "アクティブな通話(%@)"; -"settings_add_3pid_password_title_msidsn" = "電話番号の追加する"; +"callbar_only_single_active" = "タップして通話(%@)に戻る"; +"settings_add_3pid_password_title_msidsn" = "電話番号を追加"; "device_verification_emoji_scissors" = "ハサミ"; "device_verification_emoji_paperclip" = "ペーパークリップ"; "device_verification_emoji_pencil" = "鉛筆"; @@ -632,21 +632,21 @@ // Widget Picker "widget_picker_title" = "インテグレーションマネージャー"; "widget_integration_manager_disabled" = "設定でインテグレーションマネージャーを有効にする必要があります"; -"widget_menu_remove" = "すべて取り除く"; +"widget_menu_remove" = "全て取り除く"; "widget_menu_revoke_permission" = "アクセスを取り消す"; -"widget_menu_open_outside" = "ブラウザを開く"; +"widget_menu_open_outside" = "ブラウザーで開く"; "widget_menu_refresh" = "リフレッシュ"; -"widget_integrations_server_failed_to_connect" = "インテグレーションサーバーへの説を句が失敗しました"; +"widget_integrations_server_failed_to_connect" = "インテグレーションサーバーへの接続が失敗しました"; // Widget "widget_no_integrations_server_configured" = "インテグレーションサーバーが設定されていません"; -"bug_report_background_mode" = "バックグラウンドで継続する"; +"bug_report_background_mode" = "バックグラウンドで継続"; "e2e_key_backup_wrong_version_button_wasme" = "これはわたしです"; "e2e_key_backup_wrong_version_button_settings" = "設定"; -"e2e_key_backup_wrong_version" = "新しいメッセージキーのバックアップが検出されました。\n\nこれがあなたによるものでばい場合は、設定から新しいパスフレーズを設定してください。"; +"e2e_key_backup_wrong_version" = "メッセージの鍵の新しい安全なバックアップが検出されました。\n\nこれがあなたによるものではない場合は、設定から新しいパスフレーズを設定してください。"; // Key backup wrong version -"e2e_key_backup_wrong_version_title" = "新しいキーのバックアップ"; +"e2e_key_backup_wrong_version_title" = "新しい鍵のバックアップ"; "call_no_stun_server_error_use_fallback_button" = "%@を使ってみてください"; "call_actions_unhold" = "やり直す"; "call_no_stun_server_error_message_2" = "代わりに、%@のパブリックサーバーを使用することもできますが、これは信頼性が低くあなたのIPアドレスがそのサーバーと共有されてしまいます。これは、設定から管理することができます"; @@ -655,19 +655,19 @@ "room_does_not_exist" = "%@は存在しません"; "photo_library_access_not_granted" = "%@はフォトライブラリにアクセスする権限がありません"; "camera_unavailable" = "お使いの端末ではカメラを利用できません"; -"event_formatter_widget_removed_by_you" = "ウィジェットを削除しました: %@"; +"event_formatter_widget_removed_by_you" = "ウィジェットを削除しました:%@"; "event_formatter_jitsi_widget_removed_by_you" = "VoIPカンファレンスを削除しました"; "event_formatter_jitsi_widget_added_by_you" = "VoIPカンファレンスを追加しました"; // Events formatter with you -"event_formatter_widget_added_by_you" = "ウィジェットを追加しました: %@"; +"event_formatter_widget_added_by_you" = "ウィジェットを追加しました:%@"; "event_formatter_call_back" = "かけ直す"; "event_formatter_call_you_declined" = "通話を拒否しました"; "event_formatter_call_you_currently_in" = "通話中です"; "event_formatter_call_has_ended" = "通話は有効です"; "event_formatter_call_video" = "ビデオ通話"; "event_formatter_call_voice" = "音声通話"; -"event_formatter_message_edited_mention" = "(編集済み)"; +"event_formatter_message_edited_mention" = "(編集済)"; "image_picker_action_library" = "ライブラリを選ぶ"; // Image picker @@ -679,55 +679,55 @@ "room_details_advanced_e2e_encryption_enabled_for_dm" = "ここは暗号化が有効です"; "room_details_advanced_room_id_for_dm" = "ID:"; "room_details_no_local_addresses_for_dm" = "ここにはローカルアドレスがありません"; -"room_details_access_section_directory_toggle_for_dm" = "ルームディレクトリに掲載する"; -"room_details_access_section_anyone_apart_from_guest_for_dm" = "ゲスト以外のリンクを知っている人"; -"room_details_access_section_anyone_for_dm" = "ゲストを含め、リンクを知っている人なら誰でも"; -"room_details_access_section_for_dm" = "誰がアクセスできるのか?"; +"room_details_access_section_directory_toggle_for_dm" = "ルーム一覧に掲載"; +"room_details_access_section_anyone_apart_from_guest_for_dm" = "リンクを知っている人なら誰でも(ゲストユーザーを除く)"; +"room_details_access_section_anyone_for_dm" = "リンクを知っている人なら誰でも(ゲストユーザーを含む)"; +"room_details_access_section_for_dm" = "誰がアクセスできますか?"; "room_details_photo_for_dm" = "写真"; "room_details_integrations" = "インテグレーション"; -"room_details_search" = "部屋(ルーム)を探す"; +"room_details_search" = "ルーム(部屋)を探す"; "room_details_title_for_dm" = "詳細"; "identity_server_settings_alert_error_invalid_identity_server" = "%@は有効なIDサーバーではありません。"; "identity_server_settings_alert_error_terms_not_accepted" = "IDサーバーとして設定するには%@の条件を受け入れる必要があります。"; -"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "とにかく切断する"; +"identity_server_settings_alert_disconnect_still_sharing_3pid_button" = "無視して切断"; "identity_server_settings_alert_disconnect_still_sharing_3pid" = "あなたはまだIDサーバー%@で個人データを共有しています。\n\n切断する前にメールアドレスと電話番号をIDサーバーから削除することをお勧めします。"; "identity_server_settings_alert_disconnect_button" = "接続を解除"; "identity_server_settings_alert_disconnect" = "IDサーバー%@を接続解除しますか?"; "identity_server_settings_alert_disconnect_title" = "IDサーバーを接続解除"; -"identity_server_settings_alert_change" = "IDサーバー%1$@を切断し、代わりに %2$@に接続しますか?"; -"identity_server_settings_alert_change_title" = "IDサーバーを変更する"; +"identity_server_settings_alert_change" = "IDサーバー%1$@を切断し、代わりに%2$@に接続しますか?"; +"identity_server_settings_alert_change_title" = "IDサーバーを変更"; "identity_server_settings_alert_no_terms" = "選択したIDサーバーには利用規約がありません。そのサーバーの所有者を信頼できる場合にのみ続行してください。"; "identity_server_settings_alert_no_terms_title" = "IDサーバーには利用規約がありません"; "identity_server_settings_disconnect" = "接続を解除"; "identity_server_settings_disconnect_info" = "IDサーバーとの接続を解除すると、他のユーザーから発見されなくなり、メールや電話で他のユーザーを招待することができるようになります。"; -"identity_server_settings_change" = "変更する"; -"identity_server_settings_add" = "追加する"; -"identity_server_settings_place_holder" = "IDサーバーを入力する"; -"identity_server_settings_no_is_description" = "現在、ID サーバーを使用していません。知り合いを発見したり、発見できるようにするには、上記に追加してください。"; +"identity_server_settings_change" = "変更"; +"identity_server_settings_add" = "追加"; +"identity_server_settings_place_holder" = "IDサーバーを入力"; +"identity_server_settings_no_is_description" = "現在、IDサーバーを使用していません。あなたの知っている連絡先を発見したり、その連絡先から発見されるようにするには、以上でIDサーバーを追加してください。"; "identity_server_settings_description" = "あなたは%@を使って、あなたの知り合いを発見し、また向こうから発見できるようにしています。"; -"security_settings_complete_security_alert_title" = "セキュリティを完了します"; -"security_settings_crosssigning_complete_security" = "セキュリティーを完了する"; -"security_settings_crosssigning_bootstrap" = "独立したクロス署名"; -"settings_devices_description" = "セッションのパブリックネームはコミュニケーションをとる相手に表示されます"; +"security_settings_complete_security_alert_title" = "セキュリティーを確認"; +"security_settings_crosssigning_complete_security" = "セキュリティーを確認"; +"security_settings_crosssigning_bootstrap" = "設定"; +"settings_devices_description" = "セッションの公開名は、あなたとやり取りする人々に対して表示されます"; "settings_key_backup_delete_confirmation_prompt_title" = "バックアップの削除"; -"settings_key_backup_info_valid" = "このセッションは鍵のバックアップをされています。"; -"settings_key_backup_info_algorithm" = "アルゴリズム: %@"; -"settings_key_backup_info_version" = "キーのバックアップバージョン: %@"; -"settings_key_backup_info_none" = "あなたのキーはこのセッションからバックアップされていません。"; -"settings_key_backup_info_checking" = "確認中…"; +"settings_key_backup_info_valid" = "このセッションは鍵をバックアップしています。"; +"settings_key_backup_info_algorithm" = "アルゴリズム:%@"; +"settings_key_backup_info_version" = "鍵のバックアップのバージョン:%@"; +"settings_key_backup_info_none" = "あなたの鍵は、このセッションからバックアップされていません。"; +"settings_key_backup_info_checking" = "確認しています…"; "settings_add_3pid_password_message" = "続けるにはパスワードを入力してください"; "settings_add_3pid_invalid_password_message" = "無効な認証情報"; -"settings_add_3pid_password_title_email" = "メールアドレスを追加する"; +"settings_add_3pid_password_title_email" = "メールアドレスを追加"; "settings_integrations_allow_description" = "インテグレーションマネージャー(%@)を使用して、ボット、ブリッジ、ウィジェット、ステッカーパックを管理します。\n\n設定データを受け取り、お客様に代わってウィジェットの変更、ルーム招待の送信、権限の設定を行うことができます。"; "settings_integrations_allow_button" = "インテグレーションを管理"; -"settings_calls_stun_server_fallback_button" = "フォールバックコールアシストサーバを許可する"; -"settings_key_backup" = "キーのバックアップ"; +"settings_calls_stun_server_fallback_button" = "フォールバックコールアシストサーバーを許可"; +"settings_key_backup" = "鍵のバックアップ"; "settings_integrations" = "インテグレーション"; "settings_discovery_settings" = "ディスカバリー"; "room_multiple_typing_notification" = "%@とその他のユーザーが入力中です"; -"external_link_confirmation_message" = "リンク%@は別のサイトに移動します: %@\n\n本当に続けますか?"; +"external_link_confirmation_message" = "リンク%@は別のサイトに移動します:%@\n\n本当に続けますか?"; "room_event_action_delete_confirmation_title" = "未送信メッセージを削除"; -"room_unsent_messages_cancel_message" = "このルームにある未送信のメッセージをすべて削除してもよろしいですか?"; +"room_unsent_messages_cancel_message" = "このルームにある未送信のメッセージを全て削除してもよろしいですか?"; "room_unsent_messages_cancel_title" = "未送信メッセージを削除"; "room_message_replying_to" = "%@に返信中"; "room_message_editing" = "編集中"; @@ -738,23 +738,23 @@ "room_member_power_level_short_admin" = "管理者"; "room_member_power_level_moderator_in" = "モデレーターは%@"; "room_member_power_level_admin_in" = "管理者は%@"; -"room_participants_security_loading" = "読み込み中…"; -"room_participants_action_security_status_loading" = "読み込み中…"; +"room_participants_security_loading" = "読み込んでいます…"; +"room_participants_action_security_status_loading" = "読み込んでいます…"; "room_participants_action_security_status_warning" = "警告"; -"room_participants_action_security_status_complete_security" = "セキュリティーを完了する"; +"room_participants_action_security_status_complete_security" = "セキュリティーを確認"; "room_participants_action_security_status_verify" = "検証"; -"room_participants_action_security_status_verified" = "検証済み"; -"room_participants_action_section_security" = "セキュリティ"; +"room_participants_action_security_status_verified" = "検証済"; +"room_participants_action_section_security" = "セキュリティー"; "room_participants_start_new_chat_error_using_user_email_without_identity_server" = "IDサーバーが設定されていないため、メールアドレスを使って連絡先とチャットを開始することができません。"; -"room_participants_filter_room_members_for_dm" = "メンバーをフィルター"; +"room_participants_filter_room_members_for_dm" = "メンバーを絞り込む"; "room_participants_remove_third_party_invite_prompt_msg" = "招待を取り消してよろしいですか?"; "room_participants_leave_prompt_msg_for_dm" = "退出してよろしいですか?"; -"room_participants_leave_prompt_title_for_dm" = "退出する"; +"room_participants_leave_prompt_title_for_dm" = "退出"; "contacts_address_book_no_identity_server" = "IDサーバーが設定されていません"; -"rooms_empty_view_information" = "ルームはプライベートでもパブリックでも、あらゆるグループチャットに最適です。+をタップすると、既にあるルームを見つけたり、新しいルームを作ることができます。"; +"rooms_empty_view_information" = "ルームは非公開でも公開でも、あらゆるグループチャットに最適です。+をタップすると、既にあるルームを見つけたり、新しいルームを作ったりすることができます。"; "rooms_empty_view_title" = "ルーム"; -"people_empty_view_information" = "誰とでも安全にチャットできます。+をタップすると会話相手を追加できます。"; -"people_empty_view_title" = "参加者"; +"people_empty_view_information" = "誰とでも安全にチャットできます。+をタップすると連絡先を追加できます。"; +"people_empty_view_title" = "連絡先"; "room_creation_error_invite_user_by_email_without_identity_server" = "IDサーバーが設定されていないため、メールで参加者を追加することができません。"; // Errors @@ -768,34 +768,34 @@ // Social login "social_login_list_title_continue" = "続きはこちら"; -"auth_softlogout_clear_data_sign_out_msg" = "このデバイスに現在保存されているすべてのデータを消去してよろしいですか?再びサインインするとアカウントデータやメッセージにアクセスできます。"; -"auth_softlogout_clear_data_sign_out_title" = "本当によろしいですか?"; -"auth_softlogout_clear_data_button" = "すべてのデータをクリアする"; -"auth_softlogout_clear_data_message_2" = "このデバイスの使用を終了する場合や、別のアカウントにサインインしたい場合は、クリアしてください。"; -"auth_softlogout_clear_data_message_1" = "警告: 個人データ(暗号化キーを含む)がこのデバイスにまだ保存されています。"; +"auth_softlogout_clear_data_sign_out_msg" = "この端末に現在保存されている全てのデータを消去してよろしいですか?再びサインインするとアカウントデータやメッセージにアクセスできます。"; +"auth_softlogout_clear_data_sign_out_title" = "続行してよろしいですか?"; +"auth_softlogout_clear_data_button" = "全てのデータをクリア"; +"auth_softlogout_clear_data_message_2" = "この端末の使用を終了する場合や、別のアカウントにサインインしたい場合は、クリアしてください。"; +"auth_softlogout_clear_data_message_1" = "警告:個人データ(暗号鍵を含む)がこの端末にまだ保存されています。"; "callbar_return" = "かけ直す"; -"callbar_active_and_multiple_paused" = "アクティブな通話 (%@) · %@ の一時停止された通話"; +"callbar_active_and_multiple_paused" = "アクティブな通話(%@)· %@の一時停止された通話"; "callbar_only_multiple_paused" = "一時停止した%@の通話"; "callbar_only_single_paused" = "通話の一時停止"; "store_promotional_text" = "オープンネットワーク上でプライバシーを保護したチャットアプリ。あなた自身でコントロールできるように非中央集権化(分散化)されています。データマイニング、バックドア、サードパーティによるアクセスはありません。"; -"auth_softlogout_clear_data" = "個人データをクリアする"; -"auth_softlogout_recover_encryption_keys" = "サインインして、このデバイスにのみ保存されている暗号化キーを復元します。どのデバイスでも、あなたのセキュアなメッセージをすべて読むために必要です。"; +"auth_softlogout_clear_data" = "個人データをクリア"; +"auth_softlogout_recover_encryption_keys" = "暗号化されたメッセージがどの端末でも読めるように、サインインしてこの端末にのみ保存されている暗号鍵を取り戻してください。"; "auth_softlogout_reason" = "ホームサーバー(%1$@)の管理者が%2$@(%3$@)からサインアウトさせました。"; "auth_softlogout_sign_in" = "サインイン"; "auth_softlogout_signed_out" = "サインアウトしました"; "auth_autodiscover_invalid_response" = "無効なホームサーバー発見レスポンス"; "auth_accept_policies" = "このホームサーバーのポリシーを確認して同意してください:"; -"auth_reset_password_error_is_required" = "IDサーバーが設定されていません: パスワードをリセットするためにサーバーオプションに追加してください。"; -"auth_forgot_password_error_no_configured_identity_server" = "IDサーバーが設定されていません: パスワードをリセットするためにIDサーバーを追加してください。"; +"auth_reset_password_error_is_required" = "IDサーバーが設定されていません:パスワードをリセットするためにサーバーオプションに追加してください。"; +"auth_forgot_password_error_no_configured_identity_server" = "IDサーバーが設定されていません:パスワードをリセットするためにIDサーバーを追加してください。"; "auth_phone_is_required" = "IDサーバーが設定されていないので、パスワードをリセットするために電話番号を追加することはできません。"; "auth_email_is_required" = "IDサーバーが設定されていないので、パスワードをリセットするためにメールアドレスを追加することはできません。"; -"auth_add_email_phone_message_2" = "アカウント回復のためにメールを設定します。これを行うとメールアドレスや電話番号を使って、あなたのことを知っている人が発見できるようになります。"; -"auth_add_phone_message_2" = "電話番号を設定し、オプションとして自分を知っている人が発見できるようにします。"; -"auth_add_email_message_2" = "アカウント回復のためにメールを設定し、オプションとして自分のことを知っている人が発見できるようにします。"; +"auth_add_email_phone_message_2" = "アカウント復旧用のメールアドレスを設定します。後からオプションでメールアドレスや電話番号を使用して知人に見つけてもらえるようにできます。"; +"auth_add_phone_message_2" = "電話番号を設定します。後からオプションで知人に見つけてもらえるようにできます。"; +"auth_add_email_message_2" = "アカウント復旧用のメールアドレスを設定します。後からオプションで知人に見つけてもらえるようにできます。"; "less" = "たたむ"; "more" = "もっと"; "switch" = "切り替え"; -"joined" = "参加済み"; +"joined" = "参加済"; "skip" = "スキップ"; // Identity server settings @@ -804,116 +804,116 @@ // AuthenticatedSessionViewControllerFactory "authenticated_session_flow_not_supported" = "このアプリは、ホームサーバーの認証機構をサポートしていません。"; "manage_session_sign_out" = "セッションからサインアウト"; -"manage_session_not_trusted" = "信頼できない"; -"manage_session_trusted" = "信頼済み"; +"manage_session_not_trusted" = "信頼されていません"; +"manage_session_trusted" = "信頼済"; "manage_session_name" = "セッション名"; "manage_session_info" = "セッションの情報"; // Manage session "manage_session_title" = "セッションを管理"; -"security_settings_user_password_description" = "アカウントのパスワードを入力して本人確認を行う"; +"security_settings_user_password_description" = "アカウントのパスワードを入力して本人確認を行ってください"; "security_settings_coming_soon" = "申し訳ありません。このアクションはElement iOSではまだ利用できません。他のMatrixクライアントを使って設定してください。将来的にはElement iOSでも実装される予定です。"; -"security_settings_complete_security_alert_message" = "現在のセッションのセキュリティを完了させる必要があります。"; -"security_settings_blacklist_unverified_devices_description" = "すべてのセッションを検証して、信頼できるものとしてマークしメッセージを送信します。"; +"security_settings_complete_security_alert_message" = "現在のセッションのセキュリティーを完了させる必要があります。"; +"security_settings_blacklist_unverified_devices_description" = "全てのセッションを検証して、信頼できるものとしてマークしメッセージを送信します。"; "security_settings_blacklist_unverified_devices" = "信頼していないセッションにはメッセージを送信しない"; "security_settings_advanced" = "上級者向け"; -"security_settings_export_keys_manually" = "手動でキーをエクスポートする"; +"security_settings_export_keys_manually" = "手動で鍵をエクスポート"; "security_settings_cryptography" = "暗号技術"; "security_settings_crosssigning_reset" = "クロス署名をリセット"; "security_settings_crosssigning_info_ok" = "クロス署名が有効です。"; -"security_settings_crosssigning_info_trusted" = "クロス署名が有効になっています。クロス署名に基づいて他のユーザーや自分の他のセッションを信頼することはできますが、このセッションにはクロス署名用の秘密鍵がないため、このセッションからクロス署名を行うことはできません。このセッションのセキュリティを完了してください。"; -"security_settings_crosssigning_info_exists" = "アカウントにはクロス署名IDがありますが、このセッションはまだ信頼されていません。 このセッションのセキュリティを完了してください。"; +"security_settings_crosssigning_info_trusted" = "クロス署名が有効になっています。クロス署名に基づいて他のユーザーや自分の他のセッションを信頼することはできますが、このセッションにはクロス署名用の秘密鍵がないため、このセッションからクロス署名を行うことはできません。このセッションのセキュリティーを完了してください。"; +"security_settings_crosssigning_info_exists" = "アカウントにはクロス署名IDがありますが、このセッションはまだ信頼されていません。このセッションのセキュリティーを完了してください。"; "security_settings_crosssigning_info_not_bootstrapped" = "クロス署名がまだ行われていません。"; "security_settings_crosssigning" = "クロス署名"; "security_settings_backup" = "メッセージのバックアップ"; -"security_settings_secure_backup_delete" = "削除"; +"security_settings_secure_backup_delete" = "バックアップの削除"; "security_settings_secure_backup_synchronise" = "同期"; -"security_settings_secure_backup_setup" = "セットアップ"; -"security_settings_secure_backup_description" = "暗号化キーをサーバーにバックアップすることにより、暗号化されたメッセージとデータへのアクセスが失われるのを防ぎます。"; +"security_settings_secure_backup_setup" = "設定"; +"security_settings_secure_backup_description" = "セッションにアクセスできなくなる場合に備えて、アカウントデータと暗号鍵をバックアップします。鍵は一意のセキュリティーキーで保護されます。"; "security_settings_secure_backup" = "安全なバックアップ"; -"security_settings_crypto_sessions_description_2" = "ログインが認識されない場合は、パスワードを変更してバックアップをリセットしてください。"; -"security_settings_crypto_sessions_loading" = "セッションを読み込み中…"; +"security_settings_crypto_sessions_description_2" = "見覚えのないログインがある場合は、Matrixアカウントのパスワードを変更し、バックアップをリセットしてください。"; +"security_settings_crypto_sessions_loading" = "セッションを読み込んでいます…"; "security_settings_crypto_sessions" = "セッション"; // Security settings -"security_settings_title" = "セキュリティ"; -"settings_show_NSFW_public_rooms" = "NSFWパブリックルームを表示する"; -"settings_identity_server_no_is_description" = "現在、ID サーバーを使用していません。知り合いを発見したり発見されるようにするには1つ連絡先を追加します。"; +"security_settings_title" = "セキュリティー"; +"settings_show_NSFW_public_rooms" = "NSFWパブリックルームを表示"; +"settings_identity_server_no_is_description" = "現在、IDサーバーを使用していません。あなたの知っている連絡先を発見したり、その連絡先から発見されるようにするには、以上でIDサーバーを追加してください。"; "settings_identity_server_no_is" = "IDサーバーが設定されていません"; "settings_identity_server_description" = "上記で設定したIDサーバーを使って、自分の知り合いを発見したり、発見されたりすることができます。"; -"settings_discovery_three_pid_details_enter_sms_code_action" = "SMSアクティベーションコードを入力する"; +"settings_discovery_three_pid_details_enter_sms_code_action" = "SMSアクティベーションコードを入力"; "settings_discovery_three_pid_details_cancel_email_validation_action" = "メールの検証をキャンセル"; "settings_discovery_three_pid_details_revoke_action" = "取り消し"; "settings_discovery_three_pid_details_share_action" = "共有"; -"settings_discovery_three_pid_details_title_email" = "メールアドレスを管理する"; -"settings_discovery_three_pid_details_title_phone_number" = "電話番号を管理する"; +"settings_discovery_three_pid_details_title_email" = "メールアドレスを管理"; +"settings_discovery_three_pid_details_title_phone_number" = "電話番号を管理"; "settings_discovery_three_pid_details_information_phone_number" = "他のユーザーがあなたを発見したり、ルームに招待する際に使用できる電話番号の設定を管理します。アカウントへ電話番号の追加や削除ができます。"; "settings_discovery_three_pid_details_information_email" = "他のユーザーがあなたを発見したり、ルームに招待する際に使用できるメールアドレスの設定を管理します。アカウントへメールアドレスの追加や削除ができます。"; "settings_discovery_error_message" = "エラーが発生しました。再試行してください。"; "settings_discovery_three_pids_management_information_part3" = "。"; "settings_discovery_three_pids_management_information_part2" = "ユーザー設定"; -"settings_discovery_three_pids_management_information_part1" = "他のユーザーがあなたを発見したり、部屋に招待する際に使用するメールアドレスや電話番号を管理できます。このリストにメールアドレスや電話番号を追加したり、削除したりすることができます。 "; -"settings_discovery_terms_not_signed" = "メールアドレスや電話番号で自分を発見できるようにするには、IDサーバー(%@)の利用規約に同意する必要があります。"; -"settings_discovery_no_identity_server" = "現在、IDサーバーを使用していません。知り合いがあなたを発見できるようにするには、IDサーバーを追加してください。"; +"settings_discovery_three_pids_management_information_part1" = "他のユーザーがあなたを発見したり、ルームに招待する際に使用するメールアドレスや電話番号を管理できます。このリストにメールアドレスや電話番号を追加したり、削除したりすることができます。 "; +"settings_discovery_terms_not_signed" = "メールアドレスか電話番号でアカウントを見つけてもらえるようにするには、IDサーバー(%@)の利用規約への同意が必要です。"; +"settings_discovery_no_identity_server" = "現在、IDサーバーを使用していません。あなたの知っている連絡先から発見されるようにするには、IDサーバーを追加してください。"; "settings_key_backup_delete_confirmation_prompt_msg" = "よろしいですか?鍵が適切にバックアップされていないと、暗号化されたメッセージを失うことがあります。"; -"settings_key_backup_button_connect" = "このセッションをキーバックアップに接続する"; -"settings_key_backup_button_delete" = "バックアップの消去"; -"settings_key_backup_button_restore" = "バックアップからの復旧"; -"settings_key_backup_button_create" = "キーバックアップをする"; -"settings_key_backup_info_trust_signature_invalid_device_unverified" = "バックアップには%@からの無効な署名があります"; -"settings_key_backup_info_trust_signature_invalid_device_verified" = "バックアップには%@からの無効な署名があります"; -"settings_key_backup_info_trust_signature_valid_device_unverified" = "バックアップには%@からの署名があります"; +"settings_key_backup_button_connect" = "このセッションを鍵のバックアップに接続"; +"settings_key_backup_button_delete" = "バックアップの削除"; +"settings_key_backup_button_restore" = "バックアップによる復元"; +"settings_key_backup_button_create" = "鍵のバックアップを使用開始"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "バックアップには%@による無効な署名があります"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "バックアップには%@による無効な署名があります"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "バックアップには%@による署名があります"; "settings_key_backup_info_trust_signature_valid_device_verified" = "バックアップには%@による有効な署名があります"; "settings_key_backup_info_trust_signature_valid" = "バックアップにはこのセッションの有効な署名があります"; -"settings_key_backup_info_trust_signature_unknown" = "バックアップにはID: %@によるセッションの署名があります"; -"settings_key_backup_info_progress_done" = "キーのバックアップが完了しました"; -"settings_key_backup_info_progress" = "%@のキーをバックアップしています…"; -"settings_key_backup_info_not_valid" = "このセッションではキーをバックアップしていませんが、復元して今後追加できる既存のバックアップがあります。"; -"settings_key_backup_info_signout_warning" = "このデバイスにのみ存在するキーの消失を防ぐために、サインアウトする前にこのセッションをキーバックアップに接続してください。"; -"settings_key_backup_info" = "暗号化されたメッセージはエンドツーエンドで暗号化されています。送信者と受信者だけがこのメッセージを読むための鍵を持っています。"; -"settings_labs_message_reaction" = "絵文字でメッセージに反応する"; +"settings_key_backup_info_trust_signature_unknown" = "バックアップにはID:%@によるセッションの署名があります"; +"settings_key_backup_info_progress_done" = "全ての鍵がバックアップされています"; +"settings_key_backup_info_progress" = "%@の鍵をバックアップしています…"; +"settings_key_backup_info_not_valid" = "このセッションでは鍵をバックアップしていませんが、復元に使用したり、今後鍵を追加したりできるバックアップを持っています。"; +"settings_key_backup_info_signout_warning" = "鍵を失くさないよう、サインアウトする前にバックアップしてください。"; +"settings_key_backup_info" = "暗号化されたメッセージは、エンドツーエンドの暗号化によって保護されています。これらの暗号化されたメッセージを読むための鍵を持っているのは、あなたと受信者だけです。"; +"settings_labs_message_reaction" = "絵文字でメッセージに反応"; "settings_calls_stun_server_fallback_description" = "ホームサーバーがフォールバックコールアシストサーバーを提供していない場合は%@を許可します(IPアドレスは通話中に共有されます)。"; -"settings_security" = "セキュリティ"; +"settings_security" = "セキュリティー"; "settings_three_pids_management_information_part3" = ""; "settings_three_pids_management_information_part2" = "ディスカバリー"; -"store_full_description" = "Elementはまったく新しいメッセンジャーアプリです。\n\n1. あなた自身がプライバシーをコントロールすることを可能にします。\n2. Matrixネットワークにいる誰とでも通信できることはもちろん、Slackなどのアプリとの連携によって他のネットワークとも通信ができます。\n3. 広告、データ収集、バックドア、ユーザーの囲い込みから逃れることができます。\n4. エンドツーエンド暗号化とクロス署名によってあなたを保護します。\n\nElementは分散型(非中央集権型)でオープンソースであるため、他のメッセンジャーアプリと完全に異なっています。\n\nElementはあなた自身でサーバーをホストすることも、サーバーを選ぶこともできます。これによってあなたのデータと会話に関するプライバシーや所有権はあなた自身で管理できるようになります。さらに、あなたは他のElementユーザーと話せるだけでなくオープンネットワークへのアクセスも可能です。\n\nElementは、オープンな分散型通信の標準規格であるMatrixで動作するため、これらすべてを実現することができています。\n\nどのサーバーがホストするか決めることができます。さまざまな方法で選択できます。\n\n1. 開発者がホストする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は開かれたネットワークにアクセスするので、Elementのユーザー以外とも話すことができます。しかもきわめて安全です。\n\nElementはMatrixーーオープンな分散型通信の標準規格ーーで動作するため、これら全てを実現することができています。\n\nElementでは、どのサーバーを使用するかを、ご自身でElementのアプリから決めることができます。\n\n1. 開発者がホストする matrix.org のパブリックサーバーで無料アカウントを取得する。\n2. あなた自身がサーバーを運営し、アカウントを管理する。\n3. Element Matrix Servicesのホスティングプラットフォームに加入し、カスタムサーバー上でアカウントを作る。\n\nなぜElementを選ぶべきなのか?\n\n自分のデータを、自分で所有: データやメッセージを保管する場所を自分で決めることができます。データを所有しコントロールするのは、あなた自身です。データを解析したり第三者にデータを渡したりする巨大IT企業ではありません。\n\nオープンなメッセージングとコラボレーション: Matrixネットワーク上の誰とでも、相手がElementや他のMatrixアプリを使っているか、さらにはSlack、IRC、XMPPのような他のメッセージングシステムを使っているかに関わらず、チャットをすることができます。\n\n非常に安全: 本物のエンド・ツー・エンドの暗号化(会話に参加している人だけがメッセージを復号化できる)と、会話参加者の真正性を確認するための相互署名を行います。\n\n包括的なコミュニケーション: メッセージング、音声およびビデオ通話、ファイル共有、画面共有、その他多くのインテグレーション、ボット、ウィジェットを提供します。ルームやコミュニティーを立ち上げて連絡を取り合い、物事をスムーズに成し遂げることができます。\n\nいつでも、どこにいても: 全ての端末とウェブ(https://app.element.io)でメッセージの履歴が同期されるため、どこにいても連絡を取ることができます。"; "user_verification_session_details_additional_information_untrusted_other_user" = "ユーザーがこのセッションを信頼するまでは、セッションとの間で送受信されるメッセージには警告が表示されます。また、手動で検証することもできます。"; "user_verification_session_details_information_untrusted_other_user" = " 新しいセッションを使ってサインインしました:"; "user_verification_session_details_information_untrusted_current_user" = "このセッションを検証することで、信頼できるものとしてマークし、暗号化されたメッセージへのアクセスを許可します。"; -"user_verification_session_details_information_trusted_other_user_part2" = " 検証済み:"; +"user_verification_session_details_information_trusted_other_user_part2" = " 検証済:"; "user_verification_session_details_information_trusted_other_user_part1" = "このセッションは安全なものとして信頼されています。なぜなら "; "user_verification_session_details_information_trusted_current_user" = "このセッションは、検証されたため安全なものとして信頼されています。"; -"user_verification_session_details_untrusted_title" = "信頼できない"; +"user_verification_session_details_untrusted_title" = "信頼されていません"; // Session details -"user_verification_session_details_trusted_title" = "信頼済み"; -"user_verification_sessions_list_session_untrusted" = "信頼できない"; -"user_verification_sessions_list_session_trusted" = "信頼済み"; +"user_verification_session_details_trusted_title" = "信頼済"; +"user_verification_sessions_list_session_untrusted" = "信頼されていません"; +"user_verification_sessions_list_session_trusted" = "信頼済"; "user_verification_sessions_list_table_title" = "セッション一覧"; -"user_verification_sessions_list_information" = "この部屋にいるこのユーザーとのメッセージはエンドツーエンドで暗号化されており第三者が読み取ることはできません。"; +"user_verification_sessions_list_information" = "このルームにいるこのユーザーとのメッセージはエンドツーエンドで暗号化されており第三者が読み取ることはできません。"; "user_verification_sessions_list_user_trust_level_unknown_title" = "未知"; "user_verification_sessions_list_user_trust_level_warning_title" = "警告"; // Sessions list -"user_verification_sessions_list_user_trust_level_trusted_title" = "信頼済み"; +"user_verification_sessions_list_user_trust_level_trusted_title" = "信頼済"; "user_verification_start_additional_information" = "安心してご利用いただくために、直接お会いするか、別の方法でご連絡ください。"; "user_verification_start_waiting_partner" = "%@を待っています…"; -"user_verification_start_information_part2" = " 両方のデバイスでワンタイムコードを確認します。"; -"user_verification_start_information_part1" = "セキュリティを高めるために "; +"user_verification_start_information_part2" = " 両方の端末でワンタイムコードを確認します。"; +"user_verification_start_information_part1" = "セキュリティーを高めるために "; // MARK: - User verification // Start -"user_verification_start_verify_action" = "検証を開始する"; -"key_verification_scan_confirmation_scanned_device_information" = "もう一方のデバイスにも同じシールドが表示されていますか?"; +"user_verification_start_verify_action" = "検証を開始"; +"key_verification_scan_confirmation_scanned_device_information" = "もう一方の端末にも同じシールドが表示されていますか?"; "key_verification_scan_confirmation_scanned_user_information" = "%@は同じシールドを表示していますか?"; // Scanned "key_verification_scan_confirmation_scanned_title" = "まもなくです!"; -"key_verification_scan_confirmation_scanning_device_waiting_other" = "他のデバイスを待っています…"; +"key_verification_scan_confirmation_scanning_device_waiting_other" = "他の端末を待っています…"; // MARK: Scan confirmation @@ -925,20 +925,20 @@ "key_verification_verify_qr_code_other_scan_my_code_title" = "相手がQRコードを読み取ってくれましたか?"; "key_verification_verify_qr_code_start_emoji_action" = "絵文字による検証"; "key_verification_verify_qr_code_cannot_scan_action" = "スキャンできませんか?"; -"key_verification_verify_qr_code_scan_code_action" = "コードを読み取る"; -"key_verification_verify_qr_code_emoji_information" = "ユニークな絵文字を比較して検証します。"; +"key_verification_verify_qr_code_scan_code_action" = "コードをスキャン"; +"key_verification_verify_qr_code_emoji_information" = "絵文字の並びを比較して検証。"; "key_verification_verify_qr_code_information_other_device" = "以下のコードをスキャンして確認してください:"; "key_verification_verify_qr_code_information" = "コードをスキャンして、お互いをしっかりと確認します。"; // MARK: QR code -"key_verification_verify_qr_code_title" = "スキャンして確認する"; +"key_verification_verify_qr_code_title" = "スキャンして検証"; // Incoming key verification request "key_verification_incoming_request_incoming_alert_message" = "%@は検証を求めています"; "key_verification_tile_conclusion_warning_title" = "信頼されていないサインイン"; -"key_verification_tile_conclusion_done_title" = "検証済み"; +"key_verification_tile_conclusion_done_title" = "認証済"; "key_verification_tile_request_incoming_approval_decline" = "却下"; "key_verification_tile_request_incoming_approval_accept" = "承認"; "key_verification_tile_request_status_accepted" = "あなたは承認しました"; @@ -947,11 +947,11 @@ "key_verification_tile_request_status_expired" = "期限切れ"; "key_verification_tile_request_status_waiting" = "お待ちください…"; "key_verification_tile_request_status_data_loading" = "日時を読み込み…"; -"key_verification_tile_request_outgoing_title" = "検証の送信"; +"key_verification_tile_request_outgoing_title" = "認証を送信済"; // Tiles -"key_verification_tile_request_incoming_title" = "検証リクエスト"; +"key_verification_tile_request_incoming_title" = "認証の要求"; "key_verification_bootstrap_not_setup_message" = "まずはクロス署名を行う必要があります。"; // MARK: - Key Verification @@ -1002,7 +1002,7 @@ "external_link_confirmation_title" = "このリンクを再確認してください"; "media_type_accessibility_sticker" = "スティッカー"; "media_type_accessibility_file" = "ファイル"; -"media_type_accessibility_location" = "場所"; +"media_type_accessibility_location" = "位置情報"; "media_type_accessibility_video" = "動画"; "media_type_accessibility_audio" = "音声"; "media_type_accessibility_image" = "画像"; @@ -1021,38 +1021,38 @@ "room_action_camera" = "写真やビデオの撮影"; "room_event_action_reaction_history" = "反応の履歴"; "room_event_action_reaction_show_less" = "表示しない"; -"room_event_action_reaction_show_all" = "すべてを見る"; +"room_event_action_reaction_show_all" = "全てを見る"; "room_event_action_edit" = "編集"; "room_event_action_reply" = "返信"; "device_verification_security_advice_emoji" = "絵文字の順番はもう一方のログインと一致しますか?"; -"key_verification_verify_sas_validate_action" = "一致します"; +"key_verification_verify_sas_validate_action" = "一致しています"; "key_verification_verify_sas_cancel_action" = "一致しません"; // MARK: Verify "key_verification_verify_sas_title_emoji" = "絵文字の比較"; "device_verification_self_verify_alert_validate_action" = "検証"; -"device_verification_self_verify_alert_message" = "ログインを検証してください: %@"; +"device_verification_self_verify_alert_message" = "ログインを検証してください:%@"; // MARK: Self verification start // New login "device_verification_self_verify_alert_title" = "ログインしましたか?"; -"room_recents_suggested_rooms_section" = "おすすめの部屋"; -"settings_show_url_previews_description" = "プレビューは暗号化されていない部屋でのみ表示されます。"; +"room_recents_suggested_rooms_section" = "おすすめのルーム"; +"settings_show_url_previews_description" = "プレビューは暗号化されていないルームでのみ表示されます。"; "settings_show_url_previews" = "ウェブサイトプレビューを表示"; -"biometrics_setup_enable_button_title_x" = "%@ を有効にする"; -"biometrics_setup_title_x" = "%@ を有効にする"; -"biometrics_settings_enable_x" = "%@ を有効にする"; +"biometrics_setup_enable_button_title_x" = "%@を有効にする"; +"biometrics_setup_title_x" = "%@を有効にする"; +"biometrics_settings_enable_x" = "%@を有効にする"; "biometrics_mode_face_id" = "Face ID"; // MARK: - Biometrics Protection "biometrics_mode_touch_id" = "Touch ID"; -"pin_protection_settings_enable_pin" = "PIN を有効にする"; -"pin_protection_settings_section_header_with_biometrics" = "PIN と %@"; +"pin_protection_settings_enable_pin" = "PINを有効にする"; +"pin_protection_settings_section_header_with_biometrics" = "PINと%@"; "pin_protection_settings_section_header" = "PIN"; -"settings_mentions_and_keywords_encryption_notice" = "モバイルでは、暗号化された部屋でのメンションとキーワードの通知は受信できません。"; +"settings_mentions_and_keywords_encryption_notice" = "携帯端末では、暗号化されたルームでのメンションとキーワードの通知は受信できません。"; "settings_new_keyword" = "キーワードを追加"; "settings_your_keywords" = "以下でキーワードを指定できます"; "settings_mentions_and_keywords" = "メンションとキーワード"; @@ -1064,7 +1064,7 @@ "settings_group_messages" = "グループメッセージ"; "settings_encrypted_direct_messages" = "暗号化されたダイレクトメッセージ"; "settings_direct_messages" = "ダイレクトメッセージ"; -"settings_notify_me_for" = "以下がメッセージ含まれる場合通知します"; +"settings_notify_me_for" = "以下がメッセージに含まれる場合に通知"; "settings_phone_contacts" = "端末の連絡先"; "settings_notifications" = "通知"; "settings_links" = "リンク"; @@ -1074,3 +1074,78 @@ "spaces_home_space_title" = "ホーム"; "spaces_left_panel_title" = "スペース"; "spaces_suggested_room" = "おすすめ"; + +// MARK: Self verification wait + +"device_verification_self_verify_wait_title" = "セキュリティーを確認"; +"location_sharing_open_google_maps" = "Googleマップで開く"; +"location_sharing_settings_toggle_title" = "位置情報の共有を有効にする"; +"threads_empty_show_all_threads" = "全てのスレッドを表示"; +"room_join_group_call" = "参加"; +"room_accessibility_threads" = "スレッド"; +"threads_action_my_threads" = "自分のスレッド"; +"threads_action_all_threads" = "全てのスレッド"; +"threads_title" = "スレッド"; + +// MARK: Threads +"room_thread_title" = "スレッド"; +"thread_copy_link_to_thread" = "スレッドへのリンクをコピー"; +"room_event_action_reply_in_thread" = "スレッド"; +"settings_room_upgrades" = "ルームのアップグレード"; +"settings_messages_by_a_bot" = "ボットによるメッセージ"; +"settings_call_invitations" = "通話への招待"; +"settings_room_invitations" = "ルームへの招待"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "コピーしました"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "コピーする"; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "セキュリティーキー"; + +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "あなたの鍵はバックアップされています。\n\nこのセキュリティーキーをコピーして、安全なところに保管してください。"; +"key_backup_setup_success_from_passphrase_done_action" = "完了"; +"room_notifs_settings_cancel_action" = "キャンセル"; +"room_notifs_settings_done_action" = "完了"; +"key_backup_recover_done_action" = "完了"; +"done" = "完了"; +"open" = "開く"; +"enable" = "有効にする"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "パスフレーズを入力"; +"key_backup_recover_from_passphrase_passphrase_title" = "入力"; +"analytics_prompt_terms_link_new_user" = "ここ"; +"secrets_setup_recovery_key_done_action" = "完了"; +"secrets_setup_recovery_passphrase_validate_action" = "完了"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "バックアップ"; +"room_event_action_forward" = "転送"; +"room_event_action_view_in_room" = "ルームに表示"; +"room_notifs_settings_encrypted_room_notice" = "暗号化されたルームでのメンションとキーワードによる通知は、携帯端末では利用できません。"; +"room_notifs_settings_mentions_and_keywords" = "メンションとキーワードのみ"; +"security_settings_secure_backup_info_valid" = "このセッションは鍵をバックアップしています。"; +"key_backup_setup_intro_setup_action_without_existing_backup" = "鍵のバックアップを使用開始"; +"space_participants_action_ban" = "このスペースからブロック"; +"space_participants_action_remove" = "このスペースから削除"; +"accessibility_button_label" = "ボタン"; +"ok" = "OK"; +"spaces_empty_space_detail" = "非公開で招待が必要なルームは表示されていません。"; +"spaces_empty_space_title" = "このスペースには(まだ)ルームがありません"; + +// MARK: - Favourites + +"favourites_empty_view_title" = "お気に入りのルームと連絡先"; +"call_transfer_dialpad" = "ダイヤルパッド"; + +// MARK: - Home + +"home_empty_view_title" = "%@へようこそ、\n%@"; +"threads_empty_tip" = "ヒント:メッセージをタップして「スレッド」を選択し、開始。"; +"threads_empty_info_all" = "スレッドを用いると、会話のテーマを保ったり、会話を追跡したりするのが容易になります。"; +"threads_empty_title" = "スレッドでディスカッションを整理して管理"; +"secure_key_backup_setup_intro_use_security_key_title" = "セキュリティーキーを使用"; + +// MARK: Secure backup setup + +// Intro + +"secure_key_backup_setup_intro_title" = "セキュアバックアップ"; +"spaces_explore_rooms" = "ルームを探索"; +"secure_key_backup_setup_intro_use_security_key_info" = "セキュリティーキーを生成します。パスワードマネージャーもしくは金庫のような安全な場所で保管してください。"; +"secure_key_backup_setup_intro_info" = "サーバー上の暗号鍵をバックアップして、暗号化されたメッセージとデータへのアクセスが失われるのを防ぎましょう。"; +"secure_backup_setup_banner_subtitle" = "暗号化されたメッセージとデータへのアクセスが失われるのを防ぎましょう"; diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index 0152ef38c..ffe224e84 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -62,7 +62,7 @@ "auth_optional_phone_placeholder" = "Telefoonnummer (optioneel)"; "auth_phone_placeholder" = "Telefoonnummer"; "auth_repeat_password_placeholder" = "Wachtwoord herhalen"; -"auth_repeat_new_password_placeholder" = "Bevestig uw nieuwe wachtwoord"; +"auth_repeat_new_password_placeholder" = "Bevestig uw nieuwe Matrix account wachtwoord"; "auth_invalid_login_param" = "Onjuiste gebruikersnaam en/of wachtwoord"; "auth_invalid_user_name" = "Inlognamen mogen alleen letters, cijfers, punten, koppeltekens en onderstrepingstekens bevatten"; "auth_invalid_password" = "Het wachtwoord is te kort (min 6)"; @@ -78,21 +78,21 @@ "auth_missing_email_or_phone" = "E-mailadres of telefoonnummer ontbreekt"; "auth_password_dont_match" = "De wachtwoorden komen niet overeen"; "auth_username_in_use" = "Inlognaam al in gebruik"; -"auth_forgot_password" = "Wachtwoord vergeten?"; +"auth_forgot_password" = "Matrix account wachtwoord vergeten?"; "auth_use_server_options" = "Aangepaste serverinstellingen gebruiken (geavanceerd)"; "auth_email_validation_message" = "Bekijk uw e-mail om verder te gaan met de registratie"; "auth_msisdn_validation_title" = "Verificatie in afwachting"; "auth_msisdn_validation_message" = "We hebben een sms met een activatiecode gestuurd. Voer deze code hieronder in."; "auth_msisdn_validation_error" = "Kan het telefoonnummer niet verifiëren."; "auth_recaptcha_message" = "Deze server wil er graag zeker van zijn dat u geen robot bent"; -"auth_reset_password_message" = "Voer het e-mailadres dat met uw account verbonden is in om uw wachtwoord opnieuw in te stellen:"; +"auth_reset_password_message" = "Voer het e-mailadres dat met uw account verbonden is in om uw Matrix account wachtwoord opnieuw in te stellen:"; "auth_reset_password_missing_email" = "Het e-mailadres dat met uw account verbonden is moet ingevoerd worden."; "auth_reset_password_missing_password" = "Er moet een nieuw wachtwoord ingevoerd worden."; "auth_reset_password_email_validation_message" = "Er is een e-mail naar %@ gestuurd. Klik hieronder zodra u de koppeling erin hebt gevolgd."; "auth_reset_password_next_step_button" = "Ik heb mijn e-mailadres geverifieerd"; "auth_reset_password_error_unauthorized" = "Verifiëren van e-mailadres is mislukt: wees er zeker van dat u op de koppeling in de e-mail hebt getikt"; "auth_reset_password_error_not_found" = "Het ziet er niet naar uit dat uw e-mailadres met een Matrix-ID is verbonden op deze server."; -"auth_reset_password_success_message" = "Uw wachtwoord is opnieuw ingesteld.\n\nU bent op alle apparaten afgemeld en u zult geen pushmeldingen meer ontvangen. Om meldingen weer in te schakelen, meldt u zich op elk apparaat opnieuw aan."; +"auth_reset_password_success_message" = "Uw Matrix account wachtwoord is opnieuw ingesteld.\n\nU bent op alle apparaten afgemeld en u zult geen pushmeldingen meer ontvangen. Om meldingen weer in te schakelen, meldt u zich op elk apparaat opnieuw aan."; "auth_add_email_and_phone_warning" = "Registratie met e-mailadres en telefoonnummer tegelijkertijd wordt, totdat de API bestaat, nog niet ondersteund. Alleen het telefoonnummer zal worden gebruikt. U kunt uw e-mailadres later aan uw profiel in de instellingen toevoegen."; // Chat creation "room_creation_title" = "Nieuwe chat"; @@ -215,7 +215,7 @@ "room_event_action_redact" = "Verwijderen"; "room_event_action_more" = "Meer"; "room_event_action_share" = "Delen"; -"room_event_action_permalink" = "Permalink"; +"room_event_action_permalink" = "Kopieer link naar bericht"; "room_event_action_view_source" = "Bron weergeven"; "room_event_action_report" = "Inhoud melden"; "room_event_action_report_prompt_reason" = "Reden voor het melden van deze inhoud"; @@ -310,12 +310,12 @@ "settings_third_party_notices" = "Derdepartijmeldingen"; "settings_send_crash_report" = "Anonieme crash- en gebruiksgegevens versturen"; "settings_clear_cache" = "Cache verwijderen"; -"settings_change_password" = "Wachtwoord veranderen"; +"settings_change_password" = "Matrix account wachtwoord veranderen"; "settings_old_password" = "oud wachtwoord"; "settings_new_password" = "nieuw wachtwoord"; "settings_confirm_password" = "wachtwoord bevestigen"; -"settings_fail_to_update_password" = "Bijwerken van wachtwoord is mislukt"; -"settings_password_updated" = "Uw wachtwoord is bijgewerkt"; +"settings_fail_to_update_password" = "Bijwerken van Matrix account wachtwoord is mislukt"; +"settings_password_updated" = "Uw Matrix account wachtwoord is bijgewerkt"; "settings_crypto_device_name" = "Apparaatnaam: "; "settings_crypto_device_id" = "\nApparaats-ID: "; "settings_crypto_device_key" = "\nApparaatssleutel:\n"; @@ -557,7 +557,7 @@ "deactivate_account_forget_messages_information_part3" = ": dit zal er voor zorgen dat toekomstige gebruikers een onvolledig beeld krijgen van gesprekken)"; "deactivate_account_validate_action" = "Account deactiveren"; "deactivate_account_password_alert_title" = "Account deactiveren"; -"deactivate_account_password_alert_message" = "Voer uw wachtwoord in om verder te gaan"; +"deactivate_account_password_alert_message" = "Voer uw Matrix account wachtwoord in om verder te gaan"; "room_event_action_view_decrypted_source" = "Ontsleutelde bron weergeven"; "room_message_reply_to_placeholder" = "Stuur een antwoord (onversleuteld)…"; "encrypted_room_message_reply_to_placeholder" = "Stuur een versleuteld antwoord…"; @@ -622,7 +622,7 @@ "key_backup_setup_intro_manual_export_info" = "(Geavanceerd)"; "key_backup_setup_intro_manual_export_action" = "Sleutels handmatig exporteren"; "key_backup_setup_passphrase_title" = "Beveilig uw back-up met een veiligheidswachtwoord"; -"key_backup_setup_passphrase_info" = "We bewaren een versleutelde kopie van uw sleutels op onze server. Bescherm uw back-up met een veiligheidswachtwoord om deze veilig te houden.\n\nVoor maximale beveiliging zou dit moeten verschillen van uw accountwachtwoord."; +"key_backup_setup_passphrase_info" = "We bewaren een versleutelde kopie van uw sleutels op onze server. Bescherm uw back-up met een veiligheidswachtwoord om deze veilig te houden.\n\nVoor maximale beveiliging zou dit moeten verschillen van uw Matrix account wachtwoord."; "key_backup_setup_passphrase_passphrase_title" = "Invoeren"; "key_backup_setup_passphrase_passphrase_placeholder" = "Wachtwoord invoeren"; "key_backup_setup_passphrase_passphrase_valid" = "Top!"; @@ -824,7 +824,7 @@ "emoji_picker_flags_category" = "Vlaggen"; // MARK: Reaction history "reaction_history_title" = "Reacties"; -"auth_forgot_password_error_no_configured_identity_server" = "Er is geen identiteitsserver geconfigureerd: voeg er een toe om uw wachtwoord opnieuw in te stellen."; +"auth_forgot_password_error_no_configured_identity_server" = "Er is geen identiteitsserver geconfigureerd: voeg er een toe om uw Matrix account wachtwoord opnieuw in te stellen."; "room_creation_error_invite_user_by_email_without_identity_server" = "Er is geen identiteitsserver geconfigureerd, dus u kunt geen deelnemers toevoegen via e-mail."; "room_participants_start_new_chat_error_using_user_email_without_identity_server" = "Er is geen identiteitsserver geconfigureerd, dus u kunt geen gesprek beginnen met een contact via e-mail."; // Service terms @@ -856,9 +856,9 @@ "auth_add_email_message_2" = "Stel een e-mailadres in voor accountherstel en om later optioneel vindbaar te zijn voor mensen die u kennen."; "auth_add_phone_message_2" = "Stel een telefoon in om later optioneel vindbaar te zijn voor mensen die u kennen."; "auth_add_email_phone_message_2" = "Stel een e-mailadres in voor accountherstel. Gebruik later e-mail of telefoon om optioneel vindbaar te zijn voor mensen die u kennen."; -"auth_email_is_required" = "Er is geen identiteitsserver geconfigureerd, dus u kunt geen e-mailadres toevoegen om uw wachtwoord in de toekomst opnieuw in te stellen."; -"auth_phone_is_required" = "Er is geen identiteitsserver geconfigureerd, dus u kunt geen telefoonnummer toevoegen om uw wachtwoord in de toekomst opnieuw in te stellen."; -"auth_reset_password_error_is_required" = "Geen identiteitsserver ingesteld: voeg er één toe bij server opties om uw wachtwoord te wijzigen."; +"auth_email_is_required" = "Er is geen identiteitsserver geconfigureerd, dus u kunt geen e-mailadres toevoegen om uw Matrix account wachtwoord in de toekomst opnieuw in te stellen."; +"auth_phone_is_required" = "Er is geen identiteitsserver geconfigureerd, dus u kunt geen telefoonnummer toevoegen om uw Matrix account wachtwoord in de toekomst opnieuw in te stellen."; +"auth_reset_password_error_is_required" = "Geen identiteitsserver ingesteld: voeg er één toe bij server opties om uw Matrix account wachtwoord te wijzigen."; // MARK: - Secrets set up @@ -1009,7 +1009,7 @@ // Banner "cross_signing_setup_banner_title" = "Versleuteling instellen"; -"secrets_reset_authentication_message" = "Geef het wachtwoord van uw account om te bevestigen"; +"secrets_reset_authentication_message" = "Geef het Matrix account wachtwoord in om te bevestigen"; "secrets_reset_reset_action" = "Opnieuw instellen"; "secrets_reset_warning_message" = "U zult opnieuw starten zonder geschiedenis, berichten, vertrouwde apparaten en vertrouwde gebruikers."; "secrets_reset_warning_title" = "Als u alles terugzet"; @@ -1021,7 +1021,7 @@ "secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Wachtwoord bevestigen"; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Bevestigen"; "secrets_setup_recovery_passphrase_validate_action" = "Klaar"; -"secrets_setup_recovery_passphrase_additional_information" = "Gebruik niet het wachtwoord van uw account."; +"secrets_setup_recovery_passphrase_additional_information" = "Gebruik niet het Matrix account wachtwoord."; "secrets_setup_recovery_key_storage_alert_title" = "Bewaar hem op een veilige plaats"; "secrets_setup_recovery_key_done_action" = "Klaar"; "secrets_setup_recovery_key_export_action" = "Opslaan"; @@ -1326,7 +1326,7 @@ // Manage session "manage_session_title" = "Beheer sessie"; -"security_settings_user_password_description" = "Bevestig uw identiteit door uw accountwachtwoord in te voeren"; +"security_settings_user_password_description" = "Bevestig uw identiteit door uw Matrix account wachtwoord in te voeren"; "security_settings_coming_soon" = "Sorry. Deze actie is nog niet beschikbaar in %@ iOS. Gebruik een andere Matrix-app om het in te stellen. %@ iOS zal het dan wel gebruiken."; "security_settings_complete_security_alert_message" = "U moet de beveiliging van uw huidige sessie nog afronden."; "security_settings_complete_security_alert_title" = "Beveiliging afronden"; @@ -1349,7 +1349,7 @@ "security_settings_secure_backup_setup" = "Instellen"; "security_settings_secure_backup_description" = "Maak een back-up van uw versleutelingssleutel bij uw account data voor het geval u toegang verliest tot uw sessies. Uw sleutels zullen worden beveiligd met een unieke veiligheidssleutel."; "security_settings_secure_backup" = "VEILIGE BACK-UP"; -"security_settings_crypto_sessions_description_2" = "Als u deze inlog niet herkent, verander uw wachtwoord en reset uw Veilige Back-up."; +"security_settings_crypto_sessions_description_2" = "Als u deze inlog niet herkent, verander uw Matrix account wachtwoord en reset uw Veilige Back-up."; "security_settings_crypto_sessions_loading" = "Sessies laden…"; "security_settings_crypto_sessions" = "MIJN SESSIES"; @@ -1375,7 +1375,7 @@ "settings_discovery_no_identity_server" = "U gebruikt momenteel geen identiteitsserver. Om door de u bekende contacten vindbaar te zijn, voeg er een toe."; "settings_devices_description" = "De publieke naam van een sessie is zichtbaar voor de personen waarmee u communiceert"; "settings_add_3pid_invalid_password_message" = "Ongeldig wachtwoord"; -"settings_add_3pid_password_message" = "Geef uw wachtwoord om verder te gaan"; +"settings_add_3pid_password_message" = "Geef uw Matrix account wachtwoord om verder te gaan"; "settings_add_3pid_password_title_msidsn" = "Telefoonnummer toevoegen"; "settings_add_3pid_password_title_email" = "E-mailadres toevoegen"; "settings_integrations_allow_description" = "Gebruik een integratiebeheerder om bots, bruggen, widgets en stickerpakketten te beheren.\n\nIntegratiebeheerders ontvangen configuratiedata en kunnen widgets aanpassen, kameruitnodigingen versturen en bestuursniveaus instellen namens u."; @@ -1726,3 +1726,35 @@ "poll_edit_form_update_failure_subtitle" = "Probeer het opnieuw"; "poll_edit_form_update_failure_title" = "Kan poll niet bijwerken"; "poll_edit_form_poll_type" = "Poll type"; +"location_sharing_post_failure_subtitle" = "%@ kan uw locatie niet verzenden. Probeer het later opnieuw."; +"location_sharing_post_failure_title" = "We konden uw locatie niet verzenden"; +"home_context_menu_leave" = "Verlaten"; +"home_context_menu_normal_priority" = "Normale prioriteit"; +"home_context_menu_low_priority" = "Lage prioriteit"; +"home_context_menu_unfavourite" = "Verwijder uit favorieten"; +"home_context_menu_favourite" = "Favoriet"; +"home_context_menu_unmute" = "Dempen opheffen"; +"home_context_menu_mute" = "Dempen"; +"home_context_menu_notifications" = "Meldingen"; +"home_context_menu_make_room" = "Verplaatsen naar kamers"; +"home_context_menu_make_dm" = "Verplaatsen naar personen"; +"event_formatter_message_deleted" = "Bericht verwijderd"; +"settings_labs_enable_threads" = "Berichten met discussielijnen"; +"message_from_a_thread" = "Van een discussie"; +"threads_empty_show_all_threads" = "Toon alle discussies"; +"threads_empty_tip" = "Tip: Tik op een bericht en gebruik “Discussie” om er een te starten."; +"threads_empty_info_my" = "Reageer op een lopende discussie of tik op een bericht en gebruik “Discussie” om een nieuwe te starten."; +"threads_empty_info_all" = "Discussies helpen u gesprekken on-topic te houden en gemakkelijk bij te houden."; +"threads_empty_title" = "Houd discussies georganiseerd met discussielijnen"; +"threads_action_my_threads" = "Mijn discussies"; +"threads_action_all_threads" = "Alle discussies"; +"threads_title" = "Discussies"; +"thread_copy_link_to_thread" = "Kopieer link naar discussie"; + +// MARK: Threads +"room_thread_title" = "Discussies"; +"room_accessibility_thread_more" = "Meer"; +"room_accessibility_threads" = "Discussies"; +"room_event_copy_link_info" = "Link gekopieerd naar klembord."; +"room_event_action_reply_in_thread" = "Discussie"; +"room_event_action_view_in_room" = "Bekijk in kamer"; diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index bc625d918..ac178a35a 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -50,7 +50,7 @@ "auth_optional_phone_placeholder" = "Número de telefone (opcional)"; "auth_phone_placeholder" = "Número de telefone"; "auth_repeat_password_placeholder" = "Repetir senha"; -"auth_repeat_new_password_placeholder" = "Confirmar sua nova senha"; +"auth_repeat_new_password_placeholder" = "Confirme a nova senha de sua conta Matrix"; "auth_home_server_placeholder" = "URL (e.g. https://matrix.org)"; "auth_identity_server_placeholder" = "URL (e.g. https://vector.im)"; "auth_invalid_login_param" = "Nome de usuária(o) e/ou senha incorretos"; @@ -71,7 +71,7 @@ "auth_untrusted_id_server" = "O servidor de identidade não é confiado"; "auth_password_dont_match" = "Senhas não correspondem"; "auth_username_in_use" = "Nome de usuária(o) em uso"; -"auth_forgot_password" = "Esqueceu senha?"; +"auth_forgot_password" = "Esqueceu senha de conta Matrix?"; "auth_email_not_found" = "Falha para enviar email: Este endereço de email não foi encontrado"; "auth_use_server_options" = "Usar opções de servidor personalizadas (avançado)"; "auth_email_validation_message" = "Por favor cheque seu email para continuar registro"; @@ -79,14 +79,14 @@ "auth_msisdn_validation_message" = "Nós temos enviado um SMS com um código de ativação. Por favor entre este código abaixo."; "auth_msisdn_validation_error" = "Incapaz de verificar número de telefone."; "auth_recaptcha_message" = "Este servidorcasa gostaria de assegurar que você não é um robô"; -"auth_reset_password_message" = "Para resettar sua senha, entre o endereço de email linkado a sua conta:"; +"auth_reset_password_message" = "Para resettar a senha de sua conta Matrix, entre o endereço de email linkado a sua conta:"; "auth_reset_password_missing_email" = "O email linkado à sua conta deve ser entrado."; "auth_reset_password_missing_password" = "Uma nova senha deve ser entrada."; "auth_reset_password_email_validation_message" = "Um email tem sido enviado para %@. Uma vez que você tenha seguido o link que ele contém, clique abaixo."; "auth_reset_password_next_step_button" = "Eu tenho verificado meu endereço de email"; "auth_reset_password_error_unauthorized" = "Falha para verificar endereço de email: assegure que você clicou no link no email"; "auth_reset_password_error_not_found" = "Seu endereço de email não parece estar associado com uma ID Matrix neste servidorcasa."; -"auth_reset_password_success_message" = "Sua senha tem sido resettada.\n\nVocê tem sido feito logout de todas as sessões e não vai mais receber notificações push. Para re-habilitar notificações, refaça login em cada dispositivo."; +"auth_reset_password_success_message" = "A senha de sua conta Matrix tem sido resettada.\n\nVocê tem sido feito logout de todas as sessões e não vai mais receber notificações push. Para re-habilitar notificações, refaça login em cada dispositivo."; "auth_add_email_and_phone_warning" = "Registro com email e número de telefone ao mesmo tempo não é suportado ainda até que a api exista. Somente o número de telefone vai ser levado em conta. Você pode adicionar seu email a seu perfil em ajustes."; "room_creation_appearance" = "Aparência"; "room_creation_appearance_name" = "Nome"; @@ -105,7 +105,7 @@ "room_recents_people_section" = "PESSOAS"; "room_recents_conversations_section" = "SALAS"; "room_recents_no_conversation" = "Nenhuma sala"; -"room_recents_low_priority_section" = "BAIXA PRIORIDADE"; +"room_recents_low_priority_section" = "PRIORIDADE BAIXA"; "room_recents_invites_section" = "CONVITES"; "room_recents_start_chat_with" = "Começar chat"; "room_recents_create_empty_room" = "Criar sala"; @@ -224,7 +224,7 @@ "room_event_action_redact" = "Remover"; "room_event_action_more" = "Mais"; "room_event_action_share" = "Compartilhar"; -"room_event_action_permalink" = "Permalink"; +"room_event_action_permalink" = "Copiar link para mensagem"; "room_event_action_view_source" = "Visualizar Fonte"; "room_event_action_view_decrypted_source" = "Visualizar Fonte Decriptada"; "room_event_action_report" = "Reportar conteúdo"; @@ -347,12 +347,12 @@ "settings_send_crash_report" = "Enviar dados de cash & uso anon"; "settings_enable_rageshake" = "Agitar com raiva para reportar bug"; "settings_clear_cache" = "Limpar cache"; -"settings_change_password" = "Mudar senha"; +"settings_change_password" = "Mudar senha de conta Matrix"; "settings_old_password" = "senha antiga"; "settings_new_password" = "senha nova"; "settings_confirm_password" = "confirmar senha"; -"settings_fail_to_update_password" = "Falha para atualizar senha"; -"settings_password_updated" = "Sua senha tem sido atualizada"; +"settings_fail_to_update_password" = "Falha para atualizar senha de conta Matrix"; +"settings_password_updated" = "A senha de sua conta Matrix tem sido atualizada"; "settings_crypto_device_name" = "Nome de sessão: "; "settings_crypto_device_id" = "\nID de sessão: "; "settings_crypto_device_key" = "\nChave de sessão:\n"; @@ -366,7 +366,7 @@ "room_details_room_name" = "Nome de Sala"; "room_details_topic" = "Tópico"; "room_details_favourite_tag" = "Favoritar"; -"room_details_low_priority_tag" = "Baixa prioridade"; +"room_details_low_priority_tag" = "Prioridade baixa"; "room_details_mute_notifs" = "Mutar notificações"; "room_details_direct_chat" = "Chat Direto"; "room_details_access_section" = "Quem pode acessar esta sala?"; @@ -550,7 +550,7 @@ "deactivate_account_forget_messages_information_part3" = ": isto vai causar usuárias/os futuras/os terem uma visualização incompleta de conversas)"; "deactivate_account_validate_action" = "Desativar conta"; "deactivate_account_password_alert_title" = "Desativar Conta"; -"deactivate_account_password_alert_message" = "Para continuar, por favor entre sua senha"; +"deactivate_account_password_alert_message" = "Para continuar, por favor entre a senha de sua conta Matrix"; // Re-request confirmation dialog "rerequest_keys_alert_title" = "Requisição Enviada"; "rerequest_keys_alert_message" = "Por favor lance %@ num outro dispositivo que possa decriptar a mensagem para que ele possa enviar as chaves para esta sessão."; @@ -600,7 +600,7 @@ "key_backup_setup_intro_manual_export_info" = "(Avançada)"; "key_backup_setup_intro_manual_export_action" = "Exportar chaves manualmente"; "key_backup_setup_passphrase_title" = "Assegure seu backup com uma Frase de Segurança"; -"key_backup_setup_passphrase_info" = "Nós vamos armazenar uma cópia encriptada de suas chaves em nosso servidor. Proteja seu backup com uma frase para mantê-lo seguro.\n\nPara segurança máxima, esta deveria ser diferente da senha de sua conta."; +"key_backup_setup_passphrase_info" = "Nós vamos armazenar uma cópia encriptada de suas chaves em nosso servidor. Proteja seu backup com uma frase para mantê-lo seguro.\n\nPara segurança máxima, esta deveria ser diferente da senha de sua conta Matrix."; "key_backup_setup_passphrase_passphrase_title" = "Entrar"; "key_backup_setup_passphrase_passphrase_placeholder" = "Entrar frase"; "key_backup_setup_passphrase_passphrase_valid" = "Ótimo!"; @@ -667,7 +667,7 @@ "settings_calls_stun_server_fallback_description" = "Permitir servidor fallback de assistência de chamadas %@ quando seu servidorcasa não oferece um (seu endereço de IP seria compartilhado durante uma chamada)."; "security_settings_blacklist_unverified_devices" = "Nunca enviar mensagens para sessões não-confiadas"; "security_settings_blacklist_unverified_devices_description" = "Verificar todas as sessões de um/uma usuário(a) para marcá-las como confiadas e enviar mensagens para ele/ela."; -"security_settings_user_password_description" = "Confirme sua identidade ao entrar a senha de sua conta"; +"security_settings_user_password_description" = "Confirme sua identidade ao entrar a senha de sua conta Matrix"; // Manage session "manage_session_title" = "Gerenciar sessão"; "manage_session_name" = "Nome de sessão"; @@ -740,7 +740,7 @@ "key_verification_scan_confirmation_scanning_device_waiting_other" = "Esperando por outro dispositivo…"; "user_verification_start_waiting_partner" = "Esperando por %@…"; "settings_devices_description" = "O nome público de uma sessão é visível para pessoas com quem você se comunica"; -"security_settings_crypto_sessions_description_2" = "Se você não reconhece um login, mude sua senha e resette Backup Seguro."; +"security_settings_crypto_sessions_description_2" = "Se você não reconhece um login, mude a senha de sua conta Matrix e resette Backup Seguro."; "security_settings_export_keys_manually" = "Exportar chaves manualmente"; "identity_server_settings_change" = "Mudar"; "identity_server_settings_alert_change_title" = "Mudar servidor de identidade"; @@ -771,7 +771,7 @@ "user_verification_session_details_verify_action_current_user_manually" = "Verificar Manualmente por Texto"; "user_verification_session_details_verify_action_other_user" = "Verificar manualmente"; "auth_add_email_phone_message_2" = "Defina um email para recuperação de conta. Use depois email ou telefone para ser opcionalmente descobertável por pessoas que conhecem você."; -"auth_email_is_required" = "Nenhum servidor de identidade está configurado então você não pode adicionar um endereço de email a fim de resettar sua senha no futuro."; +"auth_email_is_required" = "Nenhum servidor de identidade está configurado então você não pode adicionar um endereço de email a fim de resettar a senha de sua conta Matrix no futuro."; "auth_softlogout_clear_data_message_1" = "Aviso: Seus dados pessoais (incluindo chaves de encriptação) ainda estão armazenados neste dispositivo."; "auth_softlogout_clear_data_sign_out" = "Fazer signout"; "room_creation_error_invite_user_by_email_without_identity_server" = "Nenhum servidor de identidade está configurado então você não pode adicionar um/uma participante com um email."; @@ -818,9 +818,9 @@ // Accessibility "accessibility_checkbox_label" = "checkbox"; "auth_add_phone_message_2" = "Defina um número de telefone, e depois para ser opcionalmente descobertável por pessoas que conhecem você."; -"auth_phone_is_required" = "Nenhum servidor de identidade está configurado então você não pode adicionar um número de telefone a fim de resettar sua senha no futuro."; -"auth_forgot_password_error_no_configured_identity_server" = "Nenhum servidor de identidade está configurado: adicione um para resettar sua senha."; -"auth_reset_password_error_is_required" = "Nenhum servidor de identidade está configurado: adicione um em opções de servidor para resettar sua senha."; +"auth_phone_is_required" = "Nenhum servidor de identidade está configurado então você não pode adicionar um número de telefone a fim de resettar a senha de sua conta Matrix no futuro."; +"auth_forgot_password_error_no_configured_identity_server" = "Nenhum servidor de identidade está configurado: adicione um para resettar a senha de sua conta Matrix."; +"auth_reset_password_error_is_required" = "Nenhum servidor de identidade está configurado: adicione um em opções de servidor para resettar a senha de sua conta Matrix."; "auth_softlogout_signed_out" = "Você está com signout feito"; "auth_softlogout_sign_in" = "Fazer Signin"; "auth_softlogout_recover_encryption_keys" = "Faça signin para recuperar chaves de encriptação armazenadas exclusivamente neste dispositivo. Você precisa delas para ler todas suas mensagens seguras em qualquer dispositivo."; @@ -853,7 +853,7 @@ "settings_three_pids_management_information_part2" = "Descoberta"; "settings_labs_message_reaction" = "Reagir a mensagens com emoji"; "settings_add_3pid_password_title_msidsn" = "Adicionar número de telefone"; -"settings_add_3pid_password_message" = "Para continuar, por favor entre sua senha"; +"settings_add_3pid_password_message" = "Para continuar, por favor entre a senha de sua conta Matrix"; "settings_add_3pid_invalid_password_message" = "Credenciais inválidas"; "settings_key_backup_button_connect" = "Conectar esta sessão a Backup de Chave"; "settings_discovery_no_identity_server" = "Você não está atualmente usando um servidor de identidade. Para ser descobertável por contatos existentes, adicione um."; @@ -1121,7 +1121,7 @@ "secrets_setup_recovery_key_storage_alert_message" = "✓ Imprima-a e armazene-a em algum lugar seguro\n✓ Salve-a em uma chave USB ou drive de backup \n✓ Copie-a para seu armazenamento nuvem pessoal"; "secrets_setup_recovery_passphrase_title" = "Definir uma Frase de Segurança"; "secrets_setup_recovery_passphrase_information" = "Entre uma frase de segurança que só você conheça, usada para assegurar segredos em seu servidor."; -"secrets_setup_recovery_passphrase_additional_information" = "Não use a senha de sua conta."; +"secrets_setup_recovery_passphrase_additional_information" = "Não use a senha de sua conta Matrix."; "secrets_setup_recovery_passphrase_confirm_information" = "Entre sua Frase de Segurança de novo para confirmá-la."; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Confirmar"; "secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Confirmar frase"; @@ -1212,7 +1212,7 @@ "more" = "Mais"; "switch" = "Trocar"; "joined" = "Juntou-Se"; -"secrets_reset_authentication_message" = "Entre a senha de sua conta para confirmar"; +"secrets_reset_authentication_message" = "Entre a senha de sua conta Matrix para confirmar"; "secrets_reset_reset_action" = "Resettar"; "secrets_reset_warning_message" = "Você vai recomeçar com nada de histórico, mensagens, dispositivos confiados ou usuárias(os) confiadas(os)."; "secrets_reset_warning_title" = "Se você resettar tudo"; @@ -1594,3 +1594,35 @@ "poll_edit_form_update_failure_subtitle" = "Por favor tente de novo"; "poll_edit_form_update_failure_title" = "Falha para atualizar sondagem"; "poll_edit_form_poll_type" = "Tipo de sondagem"; +"location_sharing_post_failure_subtitle" = "%@ não conseguiu enviar sua localização. Por favor tente de novo mais tarde."; +"location_sharing_post_failure_title" = "Nós não conseguimos enviar sua localização"; +"home_context_menu_leave" = "Sair"; +"home_context_menu_normal_priority" = "Prioridade normal"; +"home_context_menu_low_priority" = "Prioridade baixa"; +"home_context_menu_unfavourite" = "Remover de Favoritos"; +"home_context_menu_favourite" = "Favoritar"; +"home_context_menu_unmute" = "Desmutar"; +"home_context_menu_mute" = "Mutar"; +"home_context_menu_notifications" = "Notificações"; +"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"; +"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."; +"threads_empty_info_my" = "Responda a uma thread acontecendo ou toque numa mensagem e use “Thread” para começar uma nova."; +"threads_empty_info_all" = "Threads ajudam manter suas conversas em-tópico e fáceis de rastrear."; +"threads_empty_title" = "Mantenha discussões organizadas com threads"; +"threads_action_my_threads" = "Minhas threads"; +"threads_action_all_threads" = "Todas as threads"; +"threads_title" = "Threads"; +"thread_copy_link_to_thread" = "Copiar link para thread"; + +// MARK: Threads +"room_thread_title" = "Thread"; +"room_accessibility_thread_more" = "Mais"; +"room_accessibility_threads" = "Threads"; +"room_event_copy_link_info" = "Link copiado para clipboard."; +"room_event_action_reply_in_thread" = "Thread"; +"room_event_action_view_in_room" = "Visualizar em sala"; diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 99bee79d5..227ca56c1 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -112,15 +112,15 @@ "auth_reset_password_email_validation_message" = "Na adresu %s bola odoslaná správa. Po kliknutí na odkaz, ktorý obsahuje, kliknite nižšie."; "auth_reset_password_missing_password" = "Musíte zadať nové heslo."; "auth_reset_password_missing_email" = "Musíte zadať emailovú adresu prepojenú s vašim účtom."; -"auth_reset_password_message" = "Ak chcete obnoviť vaše heslo, zadajte emailovú adresu prepojenú s vašim účtom:"; +"auth_reset_password_message" = "Ak chcete obnoviť vaše heslo k účtu Matrix, zadajte emailovú adresu prepojenú s vašim účtom:"; "auth_recaptcha_message" = "Tento domovský server by sa rád uistil, že nie ste robot"; "auth_msisdn_validation_error" = "Nie je možné overiť telefónne číslo."; "auth_msisdn_validation_message" = "Odoslali sme vám SMS správu, ktorá obsahuje overovací kód. Prosím, zadajte ho nižšie."; "auth_msisdn_validation_title" = "Čaká sa na overenie"; "auth_use_server_options" = "Použiť vlastné možnosti servera (pre pokročilých)"; "auth_email_not_found" = "Nepodarilo sa odoslať e-mail: Táto e-mailová adresa nebola nájdená"; -"auth_forgot_password_error_no_configured_identity_server" = "Nie je nastavený žiadny server identity: pridajte ho na obnovenie hesla."; -"auth_forgot_password" = "Zabudli ste heslo?"; +"auth_forgot_password_error_no_configured_identity_server" = "Nie je nastavený žiadny server identity: pridajte ho na obnovenie hesla k účtu Matrix."; +"auth_forgot_password" = "Zabudli ste heslo k účtu Matrix?"; "auth_username_in_use" = "Používateľské meno sa už používa"; "auth_password_dont_match" = "Heslá sa nezhodujú"; "auth_phone_in_use" = "Toto telefónne číslo sa už používa"; @@ -138,7 +138,7 @@ "auth_invalid_login_param" = "Nesprávne používateľské meno a/alebo heslo"; "auth_identity_server_placeholder" = "URL (napr. https://vector.im)"; "auth_home_server_placeholder" = "URL (napr. https://matrix.org)"; -"auth_repeat_new_password_placeholder" = "Potvrďte svoje nové heslo"; +"auth_repeat_new_password_placeholder" = "Potvrďte svoje nové heslo k účtu Matrix"; "auth_repeat_password_placeholder" = "Zopakovať heslo"; "auth_phone_placeholder" = "Telefónne číslo"; "auth_optional_phone_placeholder" = "Telefónne číslo (nepovinné)"; @@ -189,7 +189,7 @@ "settings_notifications_disabled_alert_title" = "Oznámenia vypnuté"; "settings_device_notifications" = "Oznámenia zariadenia"; "settings_night_mode" = "Nočný režim"; -"settings_change_password" = "Zmeniť heslo"; +"settings_change_password" = "Zmeniť heslo k účtu Matrix"; "settings_first_name" = "Krstné meno"; "settings_display_name" = "Zobrazované meno"; "settings_profile_picture" = "Profilový obrázok"; @@ -506,7 +506,7 @@ "room_event_action_delete" = "Vymazať"; "room_event_action_resend" = "Znovu odoslať"; "room_event_action_save" = "Uložiť"; -"room_event_action_permalink" = "Trvalý odkaz"; +"room_event_action_permalink" = "Kopírovať odkaz do správy"; "room_event_action_forward" = "Preposlať"; "room_event_action_share" = "Zdieľať"; "room_event_action_more" = "Viac"; @@ -533,10 +533,10 @@ "room_member_power_level_custom_in" = "Vlastné (%@) v %@"; "room_member_power_level_moderator_in" = "Moderátor v %@"; "room_member_power_level_admin_in" = "Správca v %@"; -"room_participants_security_information_room_encrypted_for_dm" = "Správy sú tu šifrované end-to-end.\n\nVaše správy sú zabezpečené zámkami a jedinečné kľúče na ich odomknutie máte len vy a príjemca."; -"room_participants_security_information_room_encrypted" = "Správy v tejto miestnosti sú šifrované end-to-end.\n\nVaše správy sú zabezpečené zámkami a jedinečné kľúče na ich odomknutie máte len vy a príjemca."; -"room_participants_security_information_room_not_encrypted" = "Správy v tejto miestnosti nie sú šifrované end-to-end (od vás až k príjemcovi)."; -"room_participants_security_information_room_not_encrypted_for_dm" = "Správy tu nie sú šifrované end-to-end (od vás až k príjemcovi)."; +"room_participants_security_information_room_encrypted_for_dm" = "Správy sú tu end-to-end šifrované.\n\nVaše správy sú zabezpečené zámkami a jedinečné kľúče na ich odomknutie máte len vy a príjemca."; +"room_participants_security_information_room_encrypted" = "Správy v tejto miestnosti sú end-to-end šifrované.\n\nVaše správy sú zabezpečené zámkami a jedinečné kľúče na ich odomknutie máte len vy a príjemca."; +"room_participants_security_information_room_not_encrypted" = "Správy v tejto miestnosti nie sú end-to-end šifrované (od vás až k príjemcovi)."; +"room_participants_security_information_room_not_encrypted_for_dm" = "Správy tu nie sú end-to-end šifrované (od vás až k príjemcovi)."; "room_participants_security_loading" = "Načítavanie…"; "room_participants_action_security_status_loading" = "Načítavanie…"; "room_participants_action_security_status_warning" = "Upozornenie"; @@ -584,7 +584,7 @@ "directory_cell_title" = "Prezrieť priečinok"; "receipt_status_read" = "Prečítané: "; "room_details_files" = "Nahrané súbory"; -"settings_ui_theme_auto" = "Automaticky"; +"settings_ui_theme_auto" = "Automatický"; // Success from passphrase "key_backup_setup_success_from_passphrase_info" = "Vaše kľúče sa zálohujú.\n\nVáš bezpečnostný kľúč je bezpečnostná sieť - môžete ho použiť na obnovenie prístupu k zašifrovaným správam, ak zabudnete svoju prístupovú frázu.\n\nBezpečnostný kľúč uchovávajte na veľmi bezpečnom mieste, napríklad v správcovi hesiel (alebo v trezore)."; @@ -619,7 +619,7 @@ "event_formatter_call_has_ended" = "Hovor ukončený"; // Image picker -"image_picker_action_camera" = "Urobiť fotografiu"; +"image_picker_action_camera" = "Spraviť fotku"; "group_participants_invite_malformed_id_title" = "Chyba pozvania"; "group_participants_leave_prompt_title" = "Opustiť skupinu"; @@ -729,7 +729,7 @@ // Re-request confirmation dialog "rerequest_keys_alert_title" = "Žiadosť odoslaná"; -"deactivate_account_password_alert_message" = "Aby ste mohli pokračovať, prosím zadajte svoje heslo"; +"deactivate_account_password_alert_message" = "Aby ste mohli pokračovať, prosím zadajte svoje heslo k účtu Matrix"; "deactivate_account_password_alert_title" = "Deaktivovať účet"; "deactivate_account_validate_action" = "Deaktivovať účet"; "deactivate_account_informations_part2_emphasize" = "Táto akcia je nezvratná."; @@ -901,13 +901,13 @@ "room_creation_wait_for_creation" = "Miestnosť sa už vytvára. Počkajte, prosím."; "room_creation_make_public_prompt_msg" = "Ste si istí, že chcete túto konverzáciu zverejniť? Ktokoľvek si môže prečítať vaše správy a pripojiť sa ku konverzácii."; "social_login_button_title_sign_in" = "Prihlásiť sa s %@"; -"auth_reset_password_error_is_required" = "Nie je nastavený žiadny server identity: pridajte ho v možnostiach servera a obnovte heslo."; +"auth_reset_password_error_is_required" = "Nie je nastavený žiadny server identity: pridajte ho v možnostiach servera a obnovte heslo k účtu Matrix."; "security_settings_crosssigning_info_trusted" = "Krížové podpisovanie je povolené. Na základe krížového podpisovania môžete dôverovať iným používateľom a ostatným reláciám, ale nemôžete krížovo podpisovať z tejto relácie, pretože nemá súkromné kľúče na krížové podpisovanie. Dokončite zabezpečenie tejto relácie."; "manage_session_sign_out" = "Odhlásiť sa z tejto relácie"; "security_settings_crosssigning_info_ok" = "Krížové podpisovanie je pripravené na použitie."; "settings_discovery_error_message" = "Vyskytla sa chyba. Prosím, skúste to znova."; "settings_crypto_blacklist_unverified_devices" = "Šifrovať len overeným reláciám"; -"settings_password_updated" = "Vaše heslo bolo aktualizované"; +"settings_password_updated" = "Vaše heslo k účtu Matrix bolo aktualizované"; "settings_notifications_disabled_alert_message" = "Ak chcete povoliť upozornenia, prejdite do nastavení zariadenia."; "settings_enable_push_notif" = "Oznámenia na tomto zariadení"; "auth_autodiscover_invalid_response" = "Neplatná odpoveď pri zisťovaní domovského servera"; @@ -1156,7 +1156,7 @@ "identity_server_settings_alert_disconnect_still_sharing_3pid" = "Svoje osobné údaje stále zdieľate na serveri totožností %@.\n\nOdporúčame vám, aby ste pred odpojením odstránili svoje e-mailové adresy a telefónne čísla zo servera totožností."; "identity_server_settings_disconnect_info" = "Odpojenie od servera totožností bude znamenať, že vás nebudú môcť objaviť iní používatelia a že nebudete môcť pozývať ostatných prostredníctvom e-mailu alebo telefónu."; "identity_server_settings_description" = "V súčasnosti používate %@ na objavovanie existujúcich kontaktov, ktoré poznáte, a na to, aby vás mohli objaviť."; -"security_settings_crypto_sessions_description_2" = "Ak nespoznávate prihlásenie, zmeňte si heslo a vykonajte obnovenie funkcie Zabezpečené zálohovanie."; +"security_settings_crypto_sessions_description_2" = "Ak nespoznávate prihlásenie, zmeňte si heslo k účtu Matrix a obnovte Zabezpečenú zálohu."; "settings_identity_server_no_is_description" = "Momentálne nepoužívate žiadny server totožností. Ak chcete objaviť existujúce kontakty, ktoré poznáte, a byť nimi objaviteľní, pridajte jeden vyššie."; "settings_analytics_and_crash_data" = "Odoslať údaje o páde a analytické údaje"; "settings_labs_create_conference_with_jitsi" = "Vytvárajte konferenčné hovory pomocou aplikácie jitsi"; @@ -1251,7 +1251,7 @@ "settings_discovery_three_pids_management_information_part1" = "Spravujte, ktoré e-mailové adresy alebo telefónne čísla môžu ostatní používatelia použiť na vaše objavenie a pozvanie do miestností. E-mailové adresy alebo telefónne čísla môžete pridávať alebo odstraňovať z tohto zoznamu v "; "settings_discovery_terms_not_signed" = "Musíte súhlasiť s podmienkami služby servera totožnosti (%@), aby vás mohli nájsť podľa e-mailovej adresy alebo telefónneho čísla."; "settings_key_backup_button_connect" = "Pripojiť túto reláciu k zálohovaniu kľúčov"; -"settings_fail_to_update_password" = "Nepodarilo sa aktualizovať heslo"; +"settings_fail_to_update_password" = "Nepodarilo sa aktualizovať heslo k účtu Matrix"; "settings_labs_enable_ringing_for_group_calls" = "Zvonenie pre skupinové hovory"; "widget_integration_positive_power_level" = "Úroveň oprávnenia musí byť kladné celé číslo."; "settings_integrations_allow_description" = "Použite správcu integrácií (%@) na spravovanie botov, premostení, widgetov a balíčkov s nálepkami. \n\nSprávcovia integrácie dostávajú konfiguračné údaje a môžu vo vašom mene upravovať widgety, posielať pozvánky do miestnosti a nastavovať úrovne oprávnení."; @@ -1262,7 +1262,7 @@ "settings_room_invitations" = "Pozvánky do miestnosti"; "settings_global_settings_info" = "Všeobecné nastavenia oznámení sú k dispozícii vo webovom klientovi %@"; "settings_confirm_media_size_description" = "Keď je táto funkcia zapnutá, budete vyzvaní, aby ste potvrdili, v akej veľkosti sa budú obrázky a videá odosielať."; -"settings_three_pids_management_information_part1" = "Tu môžete spravovať e-mailové adresy alebo telefónne čísla, ktoré môžete použiť na prihlásenie alebo obnovenie účtu. Ovládajte, kto vás môže nájsť v "; +"settings_three_pids_management_information_part1" = "Tu môžete spravovať e-mailové adresy alebo telefónne čísla, ktoré môžete použiť na prihlásenie alebo obnovenie účtu. Ovládajte, kto vás môže nájsť v sekcii "; "settings_fail_to_update_profile" = "Nepodarilo sa aktualizovať profil"; "settings_email_address_placeholder" = "Zadajte svoju e-mailovú adresu"; "settings_remove_phone_prompt_msg" = "Určite chcete odstrániť telefónne číslo %@?"; @@ -1283,10 +1283,10 @@ "auth_softlogout_recover_encryption_keys" = "Prihláste sa, aby ste obnovili šifrovacie kľúče uložené výlučne v tomto zariadení. Potrebujete ich na čítanie všetkých svojich zabezpečených správ na akomkoľvek zariadení."; "auth_softlogout_reason" = "Váš správca domovského servera (%1$@) vás odhlásil z vášho účtu %2$@ (%3$@)."; "auth_add_email_and_phone_warning" = "Registrácia pomocou e-mailu a telefónneho čísla naraz zatiaľ nie je podporovaná, kým nebude existovať api. Do úvahy sa bude brať iba telefónne číslo. Svoj e-mail môžete pridať do svojho profilu v nastaveniach."; -"auth_reset_password_success_message" = "Vaše heslo bolo obnovené.\n\nBoli ste odhlásení zo všetkých relácií a už nebudete dostávať push oznámenia. Ak chcete opätovne povoliť oznámenia, znovu sa prihláste na každom zariadení."; +"auth_reset_password_success_message" = "Vaše heslo k účtu Matrix bolo obnovené.\n\nBoli ste odhlásení zo všetkých relácií a už nebudete dostávať push oznámenia. Ak chcete opätovne povoliť oznámenia, znovu sa prihláste na každom zariadení."; "auth_untrusted_id_server" = "Server totožností nie je dôveryhodný"; -"auth_phone_is_required" = "Nie je nastavený žiadny server totožností, takže nemôžete pridať e-telefónne číslo, aby ste mohli v budúcnosti obnoviť svoje heslo."; -"auth_email_is_required" = "Nie je nastavený žiadny server totožností, takže nemôžete pridať e-mailovú adresu, aby ste mohli v budúcnosti obnoviť svoje heslo."; +"auth_phone_is_required" = "Nie je nastavený žiadny server totožností, takže nemôžete pridať e-telefónne číslo, aby ste mohli v budúcnosti obnoviť svoje heslo k účtu Matrix."; +"auth_email_is_required" = "Nie je nastavený žiadny server totožností, takže nemôžete pridať e-mailovú adresu, aby ste mohli v budúcnosti obnoviť svoje heslo k účtu Matrix."; "auth_add_email_phone_message_2" = "Nastavte si e-mail na obnovenie účtu. Neskôr môžete voliteľne použiť e-mail alebo telefón, aby vás mohli nájsť ľudia, ktorí vás poznajú."; "cross_signing_setup_banner_subtitle" = "Jednoduchšie overenie ostatných zariadení"; "room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "Šifrovať len overeným reláciám"; @@ -1296,9 +1296,9 @@ "room_intro_cell_information_multiple_dm_sentence2" = "V tejto konverzácii ste len vy, pokiaľ niekto z vás niekoho nepozve, aby sa pripojil."; "room_intro_cell_information_dm_sentence2" = "V tejto konverzácii ste len vy dvaja, nikto iný sa nemôže pripojiť."; "room_intro_cell_information_room_without_topic_sentence2_part2" = " aby ľudia vedeli, o čom je táto miestnosť."; -"room_intro_cell_information_room_without_topic_sentence2_part1" = "Pridať tému"; +"room_intro_cell_information_room_without_topic_sentence2_part1" = "Pridajte tému"; "room_avatar_view_accessibility_hint" = "Zmeniť obrázok miestnosti"; -"invite_friends_share_text" = "Hej, ozvi sa mi na %@: %@"; +"invite_friends_share_text" = "Ahoj, ozvi sa mi na %@: %@"; // MARK: - Invite friends @@ -1338,11 +1338,11 @@ // Banner "cross_signing_setup_banner_title" = "Nastaviť šifrovanie"; -"secrets_reset_authentication_message" = "Zadajte heslo svojho účtu pre potvrdenie"; +"secrets_reset_authentication_message" = "Zadajte heslo svojho účtu Matrix pre potvrdenie"; "secrets_setup_recovery_passphrase_summary_information" = "Zapamätajte si bezpečnostnú frázu. Môžete ju použiť na odomknutie zašifrovaných správ a údajov."; "secrets_setup_recovery_passphrase_summary_title" = "Uložte si bezpečnostnú frázu"; "secrets_setup_recovery_passphrase_confirm_information" = "Opätovne zadajte bezpečnostnú frázu a potvrďte ju."; -"secrets_setup_recovery_passphrase_additional_information" = "Nepoužívajte heslo k svojmu účtu."; +"secrets_setup_recovery_passphrase_additional_information" = "Nepoužívajte heslo k svojmu účtu Matrix."; "secrets_setup_recovery_passphrase_information" = "Zadajte bezpečnostnú frázu, ktorú poznáte len vy a ktorá sa používa na zabezpečenie tajomstiev na vašom serveri."; "secrets_recovery_with_key_recovery_key_title" = "Zadajte"; "secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Ak chcete pokračovať, zadajte bezpečnostný kľúč."; @@ -1449,7 +1449,7 @@ // Success from recovery key "key_backup_setup_success_from_recovery_key_info" = "Vaše kľúče sa zálohujú.\n\nVytvorte si kópiu tohto bezpečnostného kľúča a uschovajte ho na bezpečnom mieste."; "key_backup_setup_passphrase_passphrase_invalid" = "Skúste pridať slovo"; -"key_backup_setup_passphrase_info" = "Šifrovanú kópiu vašich kľúčov uložíme na našom serveri. Chráňte svoju zálohu frázou, aby bola bezpečná.\n\nV záujme maximálneho zabezpečenia by sa mala líšiť od hesla k vášmu účtu."; +"key_backup_setup_passphrase_info" = "Šifrovanú kópiu vašich kľúčov uložíme na našom serveri. Chráňte svoju zálohu frázou, aby bola bezpečná.\n\nV záujme maximálneho zabezpečenia by sa mala líšiť od hesla k vášmu účtu Matrix."; "key_backup_setup_intro_info" = "Správy v šifrovaných miestnostiach sú zabezpečené end-to-end šifrovaním. Kľúče na čítanie týchto správ máte len vy a príjemca (príjemcovia).\n\nKľúče si bezpečne zálohujte, aby ste ich nestratili."; "secure_key_backup_setup_existing_backup_error_delete_it" = "Vymazať"; "secure_key_backup_setup_existing_backup_error_unlock_it" = "Odomknúť"; @@ -1572,7 +1572,7 @@ "room_details_access_section_anyone_apart_from_guest_for_dm" = "Ktokoľvek, kto pozná odkaz, okrem hostí"; "room_details_access_section_anyone" = "Ktokoľvek, kto pozná odkaz na miestnosť, vrátane hostí"; "room_details_access_section_invited_only" = "Iba ľudia, ktorí boli pozvaní"; -"room_details_access_section_for_dm" = "Kto má k tomuto prístup?"; +"room_details_access_section_for_dm" = "Kto má sem prístup?"; "room_details_access_section" = "Kto má prístup do tejto miestnosti?"; "identity_server_settings_alert_error_invalid_identity_server" = "%@ nie je platný server totožností."; "identity_server_settings_alert_disconnect" = "Odpojiť sa od servera totožností %@?"; @@ -1580,13 +1580,13 @@ // AuthenticatedSessionViewControllerFactory "authenticated_session_flow_not_supported" = "Táto aplikácia nepodporuje mechanizmus overovania na vašom domovskom serveri."; -"security_settings_user_password_description" = "Potvrďte svoju totožnosť zadaním hesla k účtu"; +"security_settings_user_password_description" = "Potvrďte svoju totožnosť zadaním hesla k účtu Matrix"; "security_settings_coming_soon" = "Prepáčte. Táto akcia zatiaľ nie je dostupná v systéme %@ iOS. Na jej nastavenie použite iného klienta Matrix. Systém %@ iOS to bude používať."; "security_settings_complete_security_alert_title" = "Dokončiť zabezpečenie"; "security_settings_complete_security_alert_message" = "Najprv by ste mali dokončiť zabezpečenie aktuálnej relácie."; "security_settings_blacklist_unverified_devices_description" = "Overte všetky relácie používateľov, aby ste ich mohli označiť za dôveryhodné a odosielať im správy."; "security_settings_secure_backup_info_valid" = "Táto relácia zálohuje vaše kľúče."; -"settings_show_NSFW_public_rooms" = "Zobraziť verejné miestnosti nevhodné do práce"; +"settings_show_NSFW_public_rooms" = "Zobraziť aj verejné miestnosti nevhodné do práce"; "settings_identity_server_no_is" = "Nebol nastavený server totožností"; "settings_discovery_three_pid_details_enter_sms_code_action" = "Zadajte aktivačný kód SMS"; "settings_discovery_three_pid_details_information_email" = "Spravujte predvoľby pre túto e-mailovú adresu, ktorú môžu ostatní používatelia použiť na vaše objavenie a pozvanie do miestností. Pridávajte alebo odstraňujte e-mailové adresy v časti Účty."; @@ -1610,7 +1610,7 @@ "settings_key_backup_info_signout_warning" = "Zálohujte si šifrovacie kľúče pred odhlásením, aby ste o ne neprišli."; "settings_key_backup_info_none" = "Vaše kľúče sa z tejto relácie nezálohujú."; "settings_key_backup_info" = "Šifrované správy sú zabezpečené end-to-end šifrovaním. Kľúče na čítanie týchto správ máte len vy a príjemca (príjemcovia)."; -"settings_add_3pid_password_message" = "Aby ste mohli pokračovať, prosím zadajte svoje heslo"; +"settings_add_3pid_password_message" = "Aby ste mohli pokračovať, prosím zadajte svoje heslo k účtu Matrix"; "settings_labs_message_reaction" = "Reagujte na správy pomocou emoji"; "settings_labs_e2e_encryption_prompt_message" = "Ak chcete dokončiť nastavenie šifrovania, musíte sa znova prihlásiť."; "settings_contacts_enable_sync_description" = "Na prepojenie s vašimi kontaktmi sa použije váš server totožností a pomôže im vás nájsť."; @@ -1627,7 +1627,7 @@ "rage_shake_prompt" = "Zdá sa, že rozčúlene trasiete telefónom. Chceli by ste odoslať hlásenie o chybe?"; "room_details_addresses_disable_main_address_prompt_msg" = "Nebudete mať zadanú žiadnu hlavnú adresu. Predvolená hlavná adresa pre túto miestnosť sa vyberie náhodne"; "settings_enable_rageshake" = "Zúrivo potraste pre nahlásenie chyby"; -"settings_ui_theme_picker_message_match_system_theme" = "\"Auto\" zodpovedá systémovej téme vášho zariadenia"; +"settings_ui_theme_picker_message_match_system_theme" = "\"Automatický\" zodpovedá systémovej téme vášho zariadenia"; "settings_pin_rooms_with_unread" = "Pripnúť miestnosti s neprečítanými správami"; "settings_pin_rooms_with_missed_notif" = "Pripnúť miestnosti so zmeškanými oznámeniami"; "settings_confirm_media_size" = "Potvrdiť veľkosť pri odosielaní"; @@ -1688,7 +1688,7 @@ "poll_timeline_total_one_vote" = "1 odovzdaný hlas"; "poll_edit_form_poll_type_closed" = "Uzavretá anketa"; "poll_edit_form_post_failure_title" = "Nepodarilo sa odoslať anketu"; -"call_transfer_error_message" = "Prenos hovoru zlyhal"; +"call_transfer_error_message" = "Presmerovanie hovoru zlyhalo"; "user_verification_session_details_information_untrusted_other_user" = " sa prihlásil pomocou novej relácie:"; "key_verification_bootstrap_not_setup_message" = "Najprv musíte zaviesť krížové podpisovanie."; "device_verification_emoji_pin" = "Špendlík"; @@ -1706,6 +1706,54 @@ "room_details_addresses_disable_main_address_prompt_title" = "Upozornenie na hlavnú adresu"; "manage_session_trusted" = "Dôveryhodný pre vás"; "settings_contacts_phonebook_country" = "Krajina telefónneho zoznamu"; -"settings_ui_theme_picker_message_invert_colours" = "\"Auto\" používa nastavenia \"Invertovať farby\" vášho zariadenia"; +"settings_ui_theme_picker_message_invert_colours" = "\"Automatický\" používa nastavenia \"Invertovať farby\" vášho zariadenia"; "external_link_confirmation_title" = "Prekontrolujte tento odkaz"; "callbar_only_single_active_group" = "Ťuknutím sa pripojíte k skupinovému hovoru (%@)"; + +// MARK: - Call Transfer +"call_transfer_title" = "Presmerovať"; + +// Events formatter +"event_formatter_member_updates" = "%tu zmeny členstva"; +"location_sharing_post_failure_subtitle" = "%@ sa nepodarilo odoslať vašu polohu. Skúste to prosím neskôr."; +"location_sharing_post_failure_title" = "Nepodarilo sa nám odoslať vašu polohu"; +"space_tag" = "priestor"; +"home_context_menu_leave" = "Opustiť"; +"home_context_menu_normal_priority" = "Normálna priorita"; +"home_context_menu_low_priority" = "Nízka priorita"; +"home_context_menu_unfavourite" = "Odstrániť z obľúbených"; +"home_context_menu_favourite" = "Obľúbené"; +"home_context_menu_unmute" = "Zrušiť stlmenie"; +"home_context_menu_mute" = "Stlmiť"; +"home_context_menu_notifications" = "Oznámenia"; +"home_context_menu_make_dm" = "Presunúť do Ľudí"; +"bug_report_progress_uploading" = "Nahrávanie záznamu"; +"event_formatter_message_deleted" = "Správa vymazaná"; +"room_event_copy_link_info" = "Odkaz skopírovaný do schránky."; +"room_event_action_view_in_room" = "Zobraziť v miestnosti"; +"home_context_menu_make_room" = "Presunúť do miestností"; +"settings_labs_enable_threads" = "Správy vo vláknach"; +"message_from_a_thread" = "Z vlákna"; +"threads_empty_show_all_threads" = "Zobraziť všetky vlákna"; +"threads_empty_tip" = "Tip: Ťuknite na správu a použite \"Vlákno\" pre začatie nového."; +"threads_empty_info_my" = "Odpovedzte na prebiehajúce vlákno alebo ťuknite na správu a použite \"Vlákno\" pre začatie nového."; +"threads_empty_info_all" = "Vlákna pomáhajú udržiavať konverzácie v téme a ľahké sledovanie."; +"threads_empty_title" = "Udržujte diskusie organizované pomocou vlákien"; +"threads_action_my_threads" = "Moje vlákna"; +"threads_action_all_threads" = "Všetky vlákna"; +"threads_title" = "Vlákna"; +"thread_copy_link_to_thread" = "Kopírovať odkaz na vlákno"; + +// MARK: Threads +"room_thread_title" = "Vlákno"; +"room_accessibility_thread_more" = "Viac"; +"room_accessibility_threads" = "Vlákna"; +"room_event_action_reply_in_thread" = "Vlákno"; +"settings_calls_stun_server_fallback_button" = "Povoliť náhradnú službu hovorov asistenčného servera"; +"settings_calls_stun_server_fallback_description" = "Povoľte náhradné službu hovorov asistenčného servera %@, keď ho váš domovský server neponúka (vaša IP adresa by bola počas volania zdieľaná)."; +"deactivate_account_informations_part4_emphasize" = "štandardne nespôsobí, že zabudneme na správy, ktoré ste odoslali. "; +"room_details_fail_to_update_room_join_rule" = "Nepodarí sa aktualizovať pravidlo pripojenia"; +"settings_enable_callkit" = "Integrované volanie"; +"room_resource_usage_limit_reached_message_contact_3" = " aby ste tento limit zvýšili."; +"room_resource_limit_exceeded_message_contact_3" = " pokračovať v používaní tejto služby."; +"find_your_contacts_title" = "Začnite so zoznamom vašich kontaktov"; diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 0b0f12415..848a9fedb 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -46,7 +46,7 @@ "auth_optional_phone_placeholder" = "Numër telefoni (opsionale)"; "auth_phone_placeholder" = "Numër telefoni"; "auth_repeat_password_placeholder" = "Rijepeni fjalëkalimin"; -"auth_repeat_new_password_placeholder" = "Ripohoni fjalëkalimin tuaj të ri"; +"auth_repeat_new_password_placeholder" = "Që ta ripohoni, jepni fjalëkalimin e llogarisë tuaj Matrix"; "auth_home_server_placeholder" = "URL (p.sh., https://matrix.org)"; "auth_identity_server_placeholder" = "URL (p.sh., https://vector.im)"; "auth_invalid_login_param" = "Emër përdoruesi dhe/ose fjalëkalim i pasaktë"; @@ -62,12 +62,12 @@ "auth_untrusted_id_server" = "Shërbyesi i identiteteve s’është i besuar"; "auth_password_dont_match" = "Fjalëkalimet s’përputhen"; "auth_username_in_use" = "Emër përdoruesi i përdorur"; -"auth_forgot_password" = "Harruat fjalëkalimin?"; +"auth_forgot_password" = "Harruat fjalëkalimin e llogarisë tuaj Matrix?"; "auth_email_not_found" = "S’u arrit të dërgohej email: Kjo adresë email s’u gjet"; "auth_email_validation_message" = "Ju lutemi, që të vazhdojë regjistrimi, kontrolloni email-in tuaj"; "auth_msisdn_validation_title" = "Verifikim Në Pritje të Miratimit"; "auth_msisdn_validation_error" = "S’arrihet të verifikohet numër telefoni."; -"auth_reset_password_message" = "Që të ricaktoni fjalëkalimin, jepni adresën email të lidhur me llogarinë tuaj:"; +"auth_reset_password_message" = "Që të ricaktoni fjalëkalimin e llogarisë tuaj Matrix, jepni adresën email të lidhur me llogarinë tuaj:"; "auth_reset_password_missing_email" = "Duhet dhënë adresa email e lidhur me llogarinë tuaj."; "auth_reset_password_missing_password" = "Duhet dhënë një fjalëkalim i ri."; // Chat creation @@ -180,7 +180,7 @@ "room_event_action_copy" = "Kopjoje"; "room_event_action_quote" = "Citim"; "room_event_action_more" = "Më tepër"; -"room_event_action_permalink" = "Permalidhje"; +"room_event_action_permalink" = "Kopjoje lidhjen te mesazh"; "room_event_action_view_source" = "Shihini Burimin"; "room_event_action_report" = "Raportoni lëndë"; "room_event_action_report_prompt_reason" = "Arsye për raportimin e kësaj lënde"; @@ -257,11 +257,11 @@ "settings_privacy_policy" = "Rregulla Privatësie"; "settings_third_party_notices" = "Njoftime Palësh të Treta"; "settings_clear_cache" = "Pastroje fshehtinën"; -"settings_change_password" = "Ndryshoni fjalëkalimin"; +"settings_change_password" = "Ndryshoni fjalëkalim llogarie Matrix"; "settings_old_password" = "fjalëkalim i vjetër"; "settings_new_password" = "fjalëkalim i ri"; "settings_confirm_password" = "ripohoni fjalëkalimin"; -"settings_password_updated" = "Fjalëkalimi juaj u përditësua"; +"settings_password_updated" = "Fjalëkalimi i llogarisë tuaj Matrix u përditësua"; "settings_crypto_device_name" = "Emër sesioni: "; "settings_crypto_device_id" = "\nID sesioni: "; "settings_crypto_device_key" = "\nKyç sesioni:\n"; @@ -426,7 +426,7 @@ "settings_pin_rooms_with_missed_notif" = "Fikso dhomat me njoftime të humbura"; "settings_pin_rooms_with_unread" = "Fikso dhomat me mesazhe të palexuar"; "settings_labs_create_conference_with_jitsi" = "Krijoni thirrje konferencë me Jitsi-n"; -"settings_fail_to_update_password" = "S’arrihet të përditësohet fjalëkalimi"; +"settings_fail_to_update_password" = "S’u arrit të përditësohet fjalëkalimi i llogarisë Matrix"; "settings_deactivate_my_account" = "Çaktivizoje llogarinë time"; "room_details_mute_notifs" = "Heshtoji njoftimet"; "room_details_access_section_no_address_warning" = "Që të lidhni një dhomë, ajo duhet të ketë një adresë"; @@ -464,10 +464,10 @@ "deactivate_account_forget_messages_information_part2_emphasize" = "Kujdes"; "deactivate_account_validate_action" = "Çaktivizoje llogarinë"; "deactivate_account_password_alert_title" = "Çaktivizoni Llogarinë"; -"deactivate_account_password_alert_message" = "Që të vazhdohet, ju lutemi, jepni fjalëkalimin tuaj"; +"deactivate_account_password_alert_message" = "Që të vazhdohet, ju lutemi, jepni fjalëkalimin e llogarisë tuaj Matrix."; // Re-request confirmation dialog "rerequest_keys_alert_title" = "Kërkesa u Dërgua"; -"auth_reset_password_success_message" = "Fjalëkalimi juaj u ri caktua.\n\nËshtë bërë dalja juaj nga llogaria në krejt sesionet dhe s’do të merrni më njoftime push. Për riaktivizim të njoftimeve, ribëni hyrjen në çdo pajisje."; +"auth_reset_password_success_message" = "Fjalëkalimi i llogarisë tuaj Matrix u ricaktua.\n\nËshtë bërë dalja juaj nga llogaria në krejt sesionet dhe s’do të merrni më njoftime push. Për riaktivizim të njoftimeve, ribëni hyrjen në çdo pajisje."; "room_creation_make_public_prompt_msg" = "Jeni i sigurt se doni ta bëni publike këtë fjalosje? Në një të tillë, mesazhet tuaj mund t’i lexojë cilido dhe mund të hyjë në bisedë."; "room_creation_wait_for_creation" = "Po krijohet tashmë një dhomë. Ju lutemi, prisni."; "contacts_address_book_permission_required" = "Lypset leje për hyrje në kontaktet vendore"; @@ -580,7 +580,7 @@ "key_backup_setup_intro_title" = "Mos humbni kurrë mesazhe të fshehtëzuar"; "key_backup_setup_intro_info" = "Mesazhet në dhoma të fshehtëzuara sigurohen me fshehtëzim skaj-më-skaj. Vetëm ju dhe marrësi(t) keni kyçet për leximin e këtyre mesazheve.\n\nBëni një kopjeruajtje të sigurt të kyçeve tuaj, për të shmangur humbjen e tyre."; "key_backup_setup_intro_setup_action" = "Rregulloje"; -"key_backup_setup_passphrase_info" = "Do të depozitojmë një kopje të fshehtëzuar të kyçeve tuaj në shërbyesin tonë. Mbrojeni kopjeruajtjen tuaj me një frazë, për ta mbajtur të parrezikuar.\n\nPër maksimumin e sigurisë, ky duhet të jetë i ndryshëm nga fjalëkalimi juaj për llogarinë."; +"key_backup_setup_passphrase_info" = "Do të depozitojmë një kopje të fshehtëzuar të kyçeve tuaj në shërbyesin tonë. Mbrojeni kopjeruajtjen tuaj me një frazë, për ta mbajtur të parrezikuar.\n\nPër maksimumin e sigurisë, ky duhet të jetë i ndryshëm nga fjalëkalimi juaj për llogarinë Matrix."; "key_backup_setup_passphrase_passphrase_title" = "Jepeni"; "key_backup_setup_passphrase_passphrase_placeholder" = "Jepni frazë"; "key_backup_setup_passphrase_passphrase_valid" = "Bukur!"; @@ -812,7 +812,7 @@ "emoji_picker_flags_category" = "Flamuj"; // MARK: Reaction history "reaction_history_title" = "Reagime"; -"auth_forgot_password_error_no_configured_identity_server" = "S’ka shërbyes identitetesh të formësuar: shtoni një të tillë që të ricaktoni fjalëkalimin tuaj."; +"auth_forgot_password_error_no_configured_identity_server" = "S’ka shërbyes identitetesh të formësuar: shtoni një të tillë që të ricaktoni fjalëkalimin e llogarisë tuaj Matrix."; "room_creation_error_invite_user_by_email_without_identity_server" = "S’ka shërbyes identitetesh të formësua, ndaj s’mund të shtoni një pjesëmarrës me një email."; "room_participants_start_new_chat_error_using_user_email_without_identity_server" = "S’ka shërbyes identitetesh të formësuar, ndaj s’mund të nisni një fjalosje me një kontakt duke përdorur një email."; // Service terms @@ -839,9 +839,9 @@ "auth_add_email_message_2" = "Caktoni një email për rimarrje llogarie, dhe që më vonë të jeni (opsionale) i zbulueshëm nga persona që ju njohin."; "auth_add_phone_message_2" = "Caktoni një telefon, dhe jini më vonë i zbulueshëm nga persona që ju njohin."; "auth_add_email_phone_message_2" = "Caktoni një email për rimarrje llogarie. Përdorni më vonë email ose telefon që të jeni (opsionale) i zbulueshëm nga persona që ju njohin."; -"auth_email_is_required" = "S’ka të formësuar shërbyes identitetesh, ndaj s’mund të shtoni adresë email që të mund të ricaktoni fjalëkalimin tuaj në të ardhmen."; -"auth_phone_is_required" = "S’ka të formësuar shërbyes identitetesh, ndaj s’mund të shtoni numër telefoni që të mund të ricaktoni fjalëkalimin tuaj në të ardhmen."; -"auth_reset_password_error_is_required" = "S’ka të formësuar shërbyes identitetesh: shtoni një të tillë që nga mundësitë rreth shërbyesit, që të ricaktoni fjalëkalimin tuaj."; +"auth_email_is_required" = "S’ka të formësuar shërbyes identitetesh, ndaj s’mund të shtoni adresë email që të mund të ricaktoni fjalëkalimin e llogarisë tuaj Matrix në të ardhmen."; +"auth_phone_is_required" = "S’ka të formësuar shërbyes identitetesh, ndaj s’mund të shtoni numër telefoni që të mund të ricaktoni fjalëkalimin e llogarisë tuaj Matrix në të ardhmen."; +"auth_reset_password_error_is_required" = "S’ka të formësuar shërbyes identitetesh: shtoni një të tillë që nga mundësitë rreth shërbyesit, që të ricaktoni fjalëkalimin e llogarisë tuaj Matrix."; "contacts_address_book_no_identity_server" = "S’ka të formësuar shërbyes identitetesh"; "settings_discovery_settings" = "ZBULIM"; "settings_identity_server_settings" = "SHËRBYES IDENTITETESH"; @@ -850,7 +850,7 @@ "settings_three_pids_management_information_part3" = "."; "settings_add_3pid_password_title_email" = "Shtoni adresë email"; "settings_add_3pid_password_title_msidsn" = "Shtoni numër telefoni"; -"settings_add_3pid_password_message" = "Që të vazhdohet, ju lutemi, jepni fjalëkalimin tuaj"; +"settings_add_3pid_password_message" = "Që të vazhdohet, ju lutemi, jepni fjalëkalimin e llogarisë tuaj Matrix."; "settings_add_3pid_invalid_password_message" = "Kredenciale të pavlefshme"; "settings_devices_description" = "Emri publik i një sesioni është i dukshëm për persona me të cilët komunikoni"; "settings_discovery_no_identity_server" = "S’po përdorni ndonjë shërbyes identitetesh. Që të jeni i zbulueshëm nga kontakte ekzistuese që njihni, shtoni një të tillë."; @@ -1122,7 +1122,7 @@ "secrets_setup_recovery_key_storage_alert_message" = "✓ Shtypeni dhe vendoseni diku të parrezik\n✓ Ruajeni në një diskth USB ose në një pajisje kopjeruajtjesh\n✓ Kopjojeni te depoja juaj personale në re"; "secrets_setup_recovery_passphrase_title" = "Caktoni një Frazë Sigurie"; "secrets_setup_recovery_passphrase_information" = "Jepni një frazë sigurie që e dini vetëm ju, për ta përdorur për sigurim të fshehtash në shërbyesin tuaj."; -"secrets_setup_recovery_passphrase_additional_information" = "Mos përdorni fjalëkalimin e llogarisë tuaj."; +"secrets_setup_recovery_passphrase_additional_information" = "Mos përdorni fjalëkalimin e llogarisë tuaj Matrix."; "secrets_setup_recovery_passphrase_validate_action" = "U bë"; "secrets_setup_recovery_passphrase_confirm_information" = "Që ta ripohoni, rijepeni Frazën e Sigurisë."; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Ripohojeni"; @@ -1132,13 +1132,13 @@ "security_settings_secure_backup_setup" = "Ujdiseni"; "security_settings_secure_backup_synchronise" = "Njëkohësoje"; "security_settings_secure_backup_delete" = "Fshije Kopjeruajtjen"; -"security_settings_user_password_description" = "Ripohoni identitetin tuaj duke dhënë fjalëkalimin e llogarisë tuaj"; +"security_settings_user_password_description" = "Ripohoni identitetin tuaj duke dhënë fjalëkalimin e llogarisë tuaj Matrix"; "secure_key_backup_setup_existing_backup_error_title" = "Një kopjeruajtje për mesazhe që ekzistojnë tashmë"; "secure_key_backup_setup_existing_backup_error_info" = "Shkyçeni, që ta ripërdorni te kopjeruajtja e sigurt ose për ta fshirë që të krijoni një kopjeruajtje të re mesazhesh te kopjeruajtja e sigurt."; "secure_key_backup_setup_existing_backup_error_unlock_it" = "Shkyçe"; "secure_key_backup_setup_existing_backup_error_delete_it" = "Fshije"; "sign_out_non_existing_key_backup_alert_setup_secure_backup_action" = "Fillo të përdorësh Kojperuajtje të Sigurt"; -"security_settings_crypto_sessions_description_2" = "Nëse nuk njihni një palë kredenciale, ndryshoni fjalëkalimin tuaj dhe riujdisni Kopjeruajtjen e Sigurt."; +"security_settings_crypto_sessions_description_2" = "Nëse nuk njihni një palë kredenciale, ndryshoni fjalëkalimin tuaj Matrix dhe riujdisni Kopjeruajtjen e Sigurt."; "cross_signing_setup_banner_title" = "Ujdisni fshehtëzim"; "cross_signing_setup_banner_subtitle" = "Verifikoni më me lehtësi pajisje të tjera"; // Events formatter with you @@ -1229,7 +1229,7 @@ "pin_protection_kick_user_alert_message" = "Shumë gabime, u bë nxjerrja juaj jashtë"; "switch" = "Këmbe"; "joined" = "Erdhi"; -"secrets_reset_authentication_message" = "Që ta ripohoni, jepni fjalëkalimin e llogarisë tuaj"; +"secrets_reset_authentication_message" = "Që ta ripohoni, jepni fjalëkalimin e llogarisë tuaj Matrix"; "secrets_reset_reset_action" = "Riktheji"; "secrets_reset_warning_message" = "Do të filloni pa historik, pa mesazhe, pajisje të besuara ose përdorues të besuar."; "secrets_reset_warning_title" = "Nëse ktheni gjithçka te parazgjedhjet"; @@ -1615,3 +1615,35 @@ "poll_edit_form_update_failure_subtitle" = "Ju lutemi, riprovoni"; "poll_edit_form_update_failure_title" = "S’u arrit të përditësohet anketimi"; "poll_edit_form_poll_type" = "Lloj anketimi"; +"location_sharing_post_failure_subtitle" = "%@ s’dërgoi dot vendndodhjen tuaj. Ju lutemi, riprovoni më vonë."; +"location_sharing_post_failure_title" = "S’dërguam dot vendndodhjen tuaj"; +"home_context_menu_leave" = "Ikni"; +"home_context_menu_normal_priority" = "Përparësi normale"; +"home_context_menu_low_priority" = "Me përparësi të ulët"; +"home_context_menu_unfavourite" = "Hiqe prej të Parapëlqyerish"; +"home_context_menu_favourite" = "Bëje të parapëlqyer"; +"home_context_menu_unmute" = "Ktheji zërin"; +"home_context_menu_mute" = "Heshtoje"; +"home_context_menu_notifications" = "Njoftime"; +"home_context_menu_make_room" = "Shpjere te Dhoma"; +"home_context_menu_make_dm" = "Shpjere te Persona"; +"event_formatter_message_deleted" = "Mesazhi u fshi"; +"settings_labs_enable_threads" = "Mesazhe me rrjedha"; +"message_from_a_thread" = "Nga një rrjedhë"; +"threads_empty_show_all_threads" = "Shfaqi krejt rrjedhat"; +"threads_empty_tip" = "Ndihmëz: Prekni një mesazh dhe përdorni “Rrjedhë”, që të nisni një të re."; +"threads_empty_info_my" = "Përgjigjuni te një rrjedhë në zhvillim, ose prekni një mesazh dhe përdorni “Rrjedhë”, që të nisni një të re."; +"threads_empty_info_all" = "Rrjedhat ndihmojnë që të mbahet biseda juaj brenda temës dhe të ndiqet kollaj."; +"threads_empty_title" = "Mbajini diskutimet të sistemuara në rrjedha"; +"threads_action_my_threads" = "Rrjedhat e mia"; +"threads_action_all_threads" = "Krejt rrjedhat"; +"threads_title" = "Rrjedha"; +"thread_copy_link_to_thread" = "Kopjoje lidhjen te rrjedha"; + +// MARK: Threads +"room_thread_title" = "Rrjedhë"; +"room_accessibility_thread_more" = "Më tepër"; +"room_accessibility_threads" = "Rrjedha"; +"room_event_copy_link_info" = "Lidhja u kopjua në të papastër."; +"room_event_action_reply_in_thread" = "Rrjedhë"; +"room_event_action_view_in_room" = "Shiheni në dhomë"; diff --git a/Riot/Assets/sv.lproj/Vector.strings b/Riot/Assets/sv.lproj/Vector.strings index 88d35e629..5b2a7675f 100644 --- a/Riot/Assets/sv.lproj/Vector.strings +++ b/Riot/Assets/sv.lproj/Vector.strings @@ -47,7 +47,7 @@ "auth_optional_phone_placeholder" = "Telefonnummer (valfritt)"; "auth_phone_placeholder" = "Telefonnummer"; "auth_repeat_password_placeholder" = "Upprepa lösenord"; -"auth_repeat_new_password_placeholder" = "Bekräfta ditt lösenord"; +"auth_repeat_new_password_placeholder" = "Bekräfta ditt nya Matrixkontolösenord"; "auth_identity_server_placeholder" = "URL (t.ex. https://vector.im)"; "auth_invalid_login_param" = "Felaktigt användarnamn och/eller lösenord"; "auth_invalid_password" = "Lösenordet är för kort (minst 6 tecken)"; @@ -59,17 +59,17 @@ "auth_missing_email_or_phone" = "E-postadress eller telefonnummer saknas"; "auth_email_in_use" = "Den här e-postadressen är upptagen"; "auth_phone_in_use" = "Det här telefonnumret är upptaget"; -"auth_email_is_required" = "Ingen identitetsserver är konfigurerad, så du kan inte lägga till en e-postadress för att återställa ditt lösenord i framtiden."; -"auth_phone_is_required" = "Ingen identitetsserver är konfigurerad, så du kan inte lägga till ett telefonnummer för att återställa ditt lösenord i framtiden."; +"auth_email_is_required" = "Ingen identitetsserver är konfigurerad, så du kan inte lägga till en e-postadress för att återställa ditt Matrixkontolösenord i framtiden."; +"auth_phone_is_required" = "Ingen identitetsserver är konfigurerad, så du kan inte lägga till ett telefonnummer för att återställa ditt Matrixkontolösenord i framtiden."; "auth_password_dont_match" = "Lösenorden matchar inte"; "auth_username_in_use" = "Användarnamn upptaget"; -"auth_forgot_password" = "Glömt lösenordet?"; +"auth_forgot_password" = "Glömt Matrixkontolösenordet?"; "auth_email_not_found" = "Misslyckades att skicka e-post: Den här e-postadressen hittades inte"; "auth_use_server_options" = "Använd anpassade serveralternativ (avancerat)"; "auth_email_validation_message" = "Kolla din e-post för att fortsätta registreringen"; "auth_msisdn_validation_message" = "Vi har skickat ett SMS med en aktiveringskod. Ange den koden nedan."; "auth_msisdn_validation_error" = "Kunde inte verifiera telefonnummer."; -"auth_reset_password_message" = "För att återställa ditt lösenord, ange e-postadressen som är länkad till ditt konto:"; +"auth_reset_password_message" = "För att återställa ditt Matrixkontolösenord, ange e-postadressen som är länkad till ditt konto:"; "auth_reset_password_missing_email" = "Du måste ange e-postadressen som är länkad till ditt konto."; "auth_reset_password_missing_password" = "Ett nytt lösenord måste anges."; "auth_reset_password_email_validation_message" = "Ett e-brev har skickats till %@. När du har följt länken i det, tryck nedan."; @@ -204,7 +204,7 @@ "room_event_action_redact" = "Ta bort"; "room_event_action_more" = "Mer"; "room_event_action_share" = "Dela"; -"room_event_action_permalink" = "Permalänk"; +"room_event_action_permalink" = "Kopiera länk till meddelande"; "room_event_action_view_source" = "Visa källa"; "room_event_action_view_decrypted_source" = "Visa avkrypterad källa"; "room_event_action_report" = "Rapportera innehåll"; @@ -297,7 +297,7 @@ "settings_add_email_address" = "Lägg till e-postadress"; "settings_phone_number" = "Telefon"; "settings_add_phone_number" = "Lägg till telefonnummer"; -"settings_change_password" = "Ändra lösenord"; +"settings_change_password" = "Byt Matrixkontolösenord"; "settings_night_mode" = "Nattläge"; "settings_fail_to_update_profile" = "Misslyckades att uppdatera profil"; "settings_three_pids_management_information_part1" = "Hantera vilka e-postadresser eller telefonnummer som du kan använda för att logga in eller återfå ditt konto här. Kontrollera vilka som kan hitta dig i "; @@ -331,11 +331,11 @@ "settings_old_password" = "gammalt lösenord"; "settings_new_password" = "nytt lösenord"; "settings_confirm_password" = "bekräfta lösenord"; -"settings_fail_to_update_password" = "Misslyckades att uppdatera lösenord"; -"settings_password_updated" = "Ditt lösenord har uppdaterats"; +"settings_fail_to_update_password" = "Misslyckades att uppdatera Matrixkontolösenord"; +"settings_password_updated" = "Ditt Matrixkontolösenord har uppdaterats"; "settings_add_3pid_password_title_email" = "Lägg till e-postadress"; "settings_add_3pid_password_title_msidsn" = "Lägg till telefonnummer"; -"settings_add_3pid_password_message" = "För att fortsätta, ange ditt lösenord"; +"settings_add_3pid_password_message" = "För att fortsätta, ange ditt Matrixkontolösenord"; "settings_add_3pid_invalid_password_message" = "Ogiltiga uppgifter"; "settings_crypto_export" = "Exportera nycklar"; "settings_crypto_blacklist_unverified_devices" = "Kryptera endast till verifierade sessioner"; @@ -379,7 +379,7 @@ "security_settings_advanced" = "AVANCERAT"; "security_settings_blacklist_unverified_devices" = "Skicka aldrig meddelanden till obetrodda sessioner"; "security_settings_blacklist_unverified_devices_description" = "Verifiera en användares alla sessioner för att markera den som betrodd och skicka meddelanden till den."; -"security_settings_user_password_description" = "Bekräfta din identitet genom att ange ditt kontolösenord"; +"security_settings_user_password_description" = "Bekräfta din identitet genom att ange ditt Matrixkontolösenord"; // Manage session "manage_session_title" = "Hantera session"; "manage_session_info" = "SESSIONSINFO"; @@ -406,11 +406,11 @@ "auth_home_server_placeholder" = "URL (t.ex. https://matrix.org)"; "auth_invalid_user_name" = "Användarnamn får endast innehålla bokstäver, siffror, punkter, bindestreck och understreck"; "auth_add_email_message_2" = "Ställ in en e-postadress för kontoåterställning, och senare för att valfritt bli upptäckbar av personer som känner dig."; -"auth_forgot_password_error_no_configured_identity_server" = "Ingen identitetsserver är konfigurerad: lägg till en för att återställa ditt lösenord."; +"auth_forgot_password_error_no_configured_identity_server" = "Ingen identitetsserver är konfigurerad: lägg till en för att återställa ditt Matrixkontolösenord."; "auth_msisdn_validation_title" = "Avvaktar verifiering"; "auth_recaptcha_message" = "Den här hemservern vill kontrollera att du inte är en robot"; -"auth_reset_password_error_is_required" = "Ingen identitetsserver är konfigurerad: lägg till en i serveralternativen för att återställa ditt lösenord."; -"auth_reset_password_success_message" = "Ditt lösenord har återställts.\n\nDu har loggats ut från alla sessioner och kommer inte längre att ta emot pushnotiser. För att återaktivera pushnotiser, logga in igen på varje enhet."; +"auth_reset_password_error_is_required" = "Ingen identitetsserver är konfigurerad: lägg till en i serveralternativen för att återställa ditt Matrixkontolösenord."; +"auth_reset_password_success_message" = "Ditt Matrixkontolösenord har återställts.\n\nDu har loggats ut från alla sessioner och kommer inte längre att ta emot pushnotiser. För att återaktivera pushnotiser, logga in igen på varje enhet."; "auth_accept_policies" = "Granska och acceptera villkoren för denna hemserver:"; "auth_softlogout_reason" = "Admin hos din hemserver (%1$@) har loggat ut dig från ditt konto %2$@ (%3$@)."; "auth_softlogout_clear_data" = "Rensa personlig data"; @@ -653,7 +653,7 @@ "settings_discovery_three_pids_management_information_part3" = "."; "settings_identity_server_description" = "Med hjälp av identitetsservern som anges ovan kan du upptäcka och upptäckas av befintliga kontakter du känner."; "settings_identity_server_no_is_description" = "Du använder för närvarande inte en identitetsserver. För att upptäcka och bli upptäckt av befintliga kontakter du känner, lägg till en ovan."; -"security_settings_crypto_sessions_description_2" = "Om du inte känner igen en inloggning, ändra ditt lösenord och återställ säker säkerhetskopiering."; +"security_settings_crypto_sessions_description_2" = "Om du inte känner igen en inloggning, byt ditt Matrixkontolösenord och återställ säker säkerhetskopiering."; "security_settings_secure_backup" = "SÄKER SÄKERHETSKOPIERING"; "security_settings_secure_backup_description" = "Säkerhetskopiera dina nycklar med din kontodata ifall du förlorar åtkomst till dina sessioner. Dina nycklar kommer att säkras med en unik säkerhetsnyckel."; "security_settings_backup" = "MEDDELANDESÄKERHETSKOPIERING"; @@ -751,7 +751,7 @@ "deactivate_account_forget_messages_information_part3" = ": detta leder till att framtida användare kommer att se en ofullständig bild av konversationer)"; "deactivate_account_validate_action" = "Inaktivera konto"; "deactivate_account_password_alert_title" = "Inaktivera konto"; -"deactivate_account_password_alert_message" = "För att fortsätta, ange ditt lösenord"; +"deactivate_account_password_alert_message" = "För att fortsätta, ange ditt Matrixkontolösenord"; "rerequest_keys_alert_message" = "Starta %@ på en annan enhet som dekryptera meddelandet så att den kan skicka nycklarna till den här sessionen."; "secure_key_backup_setup_intro_use_security_key_title" = "Använd en säkerhetsnyckel"; "secure_key_backup_setup_intro_use_security_key_info" = "Generera en säkerhetsnyckel att lagra på en säker plats, exempelvis i en lösenordshanterare eller ett kassaskåp."; @@ -860,7 +860,7 @@ "key_backup_setup_intro_title" = "Bli aldrig av med krypterade meddelanden"; "key_backup_setup_intro_setup_action_without_existing_backup" = "Börja använda nyckelsäkerhetskopiering"; "key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Anslut den här enheten till nyckelsäkerhetskopiering"; -"key_backup_setup_passphrase_info" = "Vi kommer lagra en krypterad kopia av dina nycklar på vår server. Skydda din säkerhetskopia med en fras för att hålla den säker.\n\nFör maximal säkerhet bör denna skilja sig från ditt kontolösenord."; +"key_backup_setup_passphrase_info" = "Vi kommer lagra en krypterad kopia av dina nycklar på vår server. Skydda din säkerhetskopia med en fras för att hålla den säker.\n\nFör maximal säkerhet bör denna skilja sig från ditt Matrixkontokontolösenord."; "key_backup_setup_passphrase_setup_recovery_key_info" = "Eller säkra din säkerhetskopia med en säkerhetsnyckel och spara den någonstans säkert."; "key_backup_recover_invalid_passphrase" = "Säkerhetskopian kunde inte avkrypteras med den här frasen: vänligen kontrollera att du angav rätt säkerhetsfras."; "key_backup_recover_invalid_recovery_key_title" = "Säkerhetsnyckeln matchade inte"; @@ -1093,7 +1093,7 @@ "secrets_setup_recovery_key_storage_alert_message" = "✓ Skriv ut den och förvara den säkert\n✓ Spara den på ett USB-minne eller säkerhetskopieringsenhet\n✓ Kopiera den till din personliga molnlagring"; "secrets_setup_recovery_passphrase_title" = "Sätt en säkerhetsfras"; "secrets_setup_recovery_passphrase_information" = "Ange en säkerhetsfras endast du känner till vilken används för att säkra hemligheter på din server."; -"secrets_setup_recovery_passphrase_additional_information" = "Använd inte ditt kontolösenord."; +"secrets_setup_recovery_passphrase_additional_information" = "Använd inte ditt Matrixkontolösenord."; "secrets_setup_recovery_passphrase_validate_action" = "Klart"; "secrets_setup_recovery_passphrase_confirm_information" = "Ange din säkerhetsfras igen för att bekräfta den."; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Bekräfta"; @@ -1180,7 +1180,7 @@ "room_info_list_one_member" = "1 medlem"; "room_info_list_several_members" = "%@ medlemmar"; "room_info_list_section_other" = "Andra"; -"secrets_reset_authentication_message" = "Ange ditt kontolösenord för att bekräfta"; +"secrets_reset_authentication_message" = "Ange ditt Matrixkontolösenord för att bekräfta"; "secrets_reset_reset_action" = "Återställ"; "secrets_reset_warning_message" = "så kommer du att börja om utan historik, meddelanden, betrodda enheter eller betrodda användare."; "secrets_reset_information" = "Gör bara detta om du inte har någon annan enhet att verifiera den här enheten med."; @@ -1217,7 +1217,7 @@ "invite_friends_action" = "Bjud in vänner till %@"; "pin_protection_settings_change_pin" = "Byt PIN-kod"; -"pin_protection_confirm_pin_to_change" = "Bekräfta PIN-kod för att fortsätta"; +"pin_protection_confirm_pin_to_change" = "Bekräfta PIN-kod för att byta PIN-kod"; "bug_report_background_mode" = "Fortsätt i bakgrunden"; "social_login_button_title_sign_up" = "Skapa konto med %@"; "social_login_button_title_sign_in" = "Logga in med %@"; @@ -1468,3 +1468,126 @@ "leave_space_only_action" = "Lämna inga rum"; "leave_space_message_admin_warning" = "Du är admin för det här utrymmet, försäkra att du har överfört adminrättigheter till en annan medlem innan du lämnar."; "leave_space_message" = "Är du säker på att du vill lämna %@? Vill du även lämna alla rum och utrymmen i det här utrymmet?"; +"location_sharing_settings_toggle_title" = "Aktivera platsdelning"; +"location_sharing_settings_header" = "Platsdelning"; +"location_sharing_open_google_maps" = "Öppna i Google Maps"; +"location_sharing_open_apple_maps" = "Öppna i Apple Maps"; +"location_sharing_invalid_authorization_settings" = "Inställningar"; +"location_sharing_invalid_authorization_not_now" = "Inte nu"; +"location_sharing_invalid_authorization_error_title" = "%@ är inte behörig att komma åt din plats. Du kan aktivera åtkomst i Inställningar > Plats"; +"location_sharing_locating_user_error_title" = "%@ kunde inte komma åt din plats. Vänligen pröva igen senare."; +"location_sharing_loading_map_error_title" = "%@ kunde inte ladda kartan. Vänligen pröva igen senare."; +"location_sharing_post_failure_subtitle" = "%@ kunde inte skicka din plats. Vänligen pröva igen senare."; +"location_sharing_post_failure_title" = "Vi kunde inte skicka din plats"; +"location_sharing_share_action" = "Dela"; +"location_sharing_close_action" = "Stäng"; + +// MARK: - Location sharing + +"location_sharing_title" = "Plats"; +"poll_timeline_not_closed_subtitle" = "Vänligen pröva igen"; +"poll_timeline_not_closed_title" = "Misslyckades att avsluta omröstning"; +"poll_timeline_vote_not_registered_subtitle" = "Tyvärr registrerades inte din röst, vänligen pröva igen"; +"poll_timeline_vote_not_registered_title" = "Röst registrerades inte"; +"poll_timeline_total_final_results" = "Slutgiltigt resultat baserat på %lu röst"; +"poll_timeline_total_final_results_one_vote" = "Slutgiltigt resultat baserat på 1 röst"; +"poll_timeline_total_votes_not_voted" = "%lu röster avgivna. Rösta för att se resultatet"; +"poll_timeline_total_one_vote_not_voted" = "1 röst avgiven. Rösta för att se resultatet"; +"poll_timeline_total_votes" = "%lu röster avgivna"; +"poll_timeline_total_one_vote" = "1 röst avgiven"; +"poll_timeline_total_no_votes" = "Inga röster avgivna"; +"poll_timeline_votes_count" = "%lu röster"; +"poll_timeline_one_vote" = "1 röst"; +"poll_edit_form_poll_type_closed_description" = "Resultat avslöjas när du avslutar omröstningen"; +"poll_edit_form_poll_type_closed" = "Sluten omröstning"; +"poll_edit_form_poll_type_open_description" = "Röstare ser resultat så fort de har röstat"; +"poll_edit_form_poll_type_open" = "Öppna omröstning"; +"poll_edit_form_update_failure_subtitle" = "Vänligen pröva igen"; +"poll_edit_form_update_failure_title" = "Misslyckades att uppdatera omröstning"; +"poll_edit_form_post_failure_subtitle" = "Vänligen pröva igen"; +"poll_edit_form_post_failure_title" = "Misslyckades att lägga upp omröstning"; +"poll_edit_form_add_option" = "Lägg till alternativ"; +"poll_edit_form_option_number" = "Alternativ %lu"; +"poll_edit_form_create_options" = "Skapa alternativ"; +"poll_edit_form_input_placeholder" = "Skriv något"; +"poll_edit_form_question_or_topic" = "Fråga eller ämne"; +"poll_edit_form_poll_question_or_topic" = "Omröstningens fråga eller ämne"; +"poll_edit_form_poll_type" = "Omröstningstyp"; + +// Mark: - Polls + +"poll_edit_form_create_poll" = "Skapa omröstning"; +"home_context_menu_leave" = "Lämna"; +"home_context_menu_normal_priority" = "Normal prioritet"; +"home_context_menu_low_priority" = "Låg prioritet"; +"home_context_menu_unfavourite" = "Ta bort från favoriter"; +"home_context_menu_favourite" = "Favoritmarkera"; +"home_context_menu_unmute" = "Avtysta"; +"home_context_menu_mute" = "Tysta"; +"home_context_menu_notifications" = "Aviseringar"; +"home_context_menu_make_room" = "Flytta till rum"; +"home_context_menu_make_dm" = "Flytta till personer"; +"share_extension_send_now" = "Skicka nu"; +"share_extension_low_quality_video_message" = "Skicka i %@ för bättre kvalité, eller skicka i låg kvalité nedan."; +"share_extension_low_quality_video_title" = "Video kommer skickas i låg kvalité"; +"analytics_prompt_stop" = "Sluta dela"; +"analytics_prompt_yes" = "Ja, det är okej"; +"analytics_prompt_not_now" = "Inte ny"; +"analytics_prompt_point_3" = "Du kan stänga av det här när som helst i inställningarna"; +/* Note: The word "don't" is formatted in bold */ +"analytics_prompt_point_2" = "Vi delar inte någon information med tredje parter"; +/* Note: The word "don't" is formatted in bold */ +"analytics_prompt_point_1" = "Vi spelar inte in eller profilerar någon kontodata"; +"analytics_prompt_terms_link_upgrade" = "här"; +/* Note: The placeholder is for the contents of analytics_prompt_terms_link_upgrade */ +"analytics_prompt_terms_upgrade" = "Läs alla våra villkor %@. Är det OK?"; +"analytics_prompt_terms_link_new_user" = "här"; +/* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ +"analytics_prompt_terms_new_user" = "Du kan läsa alla våra villkor %@."; +"analytics_prompt_message_upgrade" = "Du har tidigare gått med på att dela anonym användningsdata med oss. Nu, för att hjälpa oss att förstå hur folk använder multipla enheter, så kommer vi att generera en slumpmässig identifierare som delas mellan dina enheter."; +"analytics_prompt_message_new_user" = "Hjälp oss att identifiera problem och förbättra %@ genom att dela anonym användningsdata. För att förstå hur personer använder multipla enheter så kommer vi att generera en slumpmässig identifierare som delas mellan dina enheter."; + +// Analytics +"analytics_prompt_title" = "Hjälp till att förbättra %@"; +"event_formatter_message_deleted" = "Meddelande raderat"; +"settings_enable_room_message_bubbles" = "Meddelandebubblor"; +"settings_discovery_accept_terms" = "Acceptera identitetsserverns villkor"; +"settings_analytics_and_crash_data" = "Skicka krasch- och analysdata"; +"settings_labs_enable_threads" = "Trådade meddelanden"; +"settings_labs_enabled_polls" = "Omröstningar"; +"settings_about" = "OM"; +"message_from_a_thread" = "Från en tråd"; +"threads_empty_show_all_threads" = "Visa alla trådar"; +"threads_empty_tip" = "Tips: Tryck på ett meddelande och använd “Tråd” för att starta en."; +"threads_empty_info_my" = "Svara på en pågående tråd eller tryck på ett meddelande och använd “Tråd” för att starta en ny."; +"threads_empty_info_all" = "Trådar hjälper till att hålla dina konversationer till ämnet och lätta att följa."; +"threads_empty_title" = "Håll diskussioner organiserade med trådar"; +"threads_action_my_threads" = "Mina trådar"; +"threads_action_all_threads" = "Alla trådar"; +"threads_title" = "Trådar"; +"thread_copy_link_to_thread" = "Kopiera länk till tråd"; + +// MARK: Threads +"room_thread_title" = "Tråd"; +"room_accessibility_thread_more" = "Mer"; +"room_accessibility_threads" = "Trådar"; +"room_event_copy_link_info" = "Länk kopierad till klippbordet."; +"room_event_action_reply_in_thread" = "Tråd"; +"room_event_action_view_in_room" = "Visa i rum"; +"room_event_action_end_poll" = "Avsluta omröstning"; +"room_event_action_remove_poll" = "Ta bort omröstning"; +"onboarding_splash_page_4_message" = "Element är också bra för arbetsplatser. Den är betrodd av världens säkraste organisationer."; +"onboarding_splash_page_4_title_no_pun" = "Meddelanden för ditt team."; +"onboarding_splash_page_3_message" = "Totalsträckskrypterad och inget telefonnummer krävs. Ingen reklam eller datainsamling."; +"onboarding_splash_page_3_title" = "Säkra meddelanden."; +"onboarding_splash_page_2_message" = "Välj vart dina konversationer lagras, vilket ger dig kontroll och självständighet. Ansluter via Matrix."; +"onboarding_splash_page_2_title" = "Du har kontrollen."; +"onboarding_splash_page_1_message" = "Säker och oberoende kommunikation som ger dig samma sekretessnivå som en personlig konversation i dit eget hem."; +"onboarding_splash_page_1_title" = "Äg dina konversationer."; +"onboarding_splash_login_button_title" = "Jag har redan ett konto"; + +// Onboarding +"onboarding_splash_register_button_title" = "Skapa konto"; +"accessibility_button_label" = "knapp"; +"ok" = "OK"; +"enable" = "Aktivera"; diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index dd0e93cac..d3fa52880 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -50,7 +50,7 @@ "auth_optional_phone_placeholder" = "Номер телефону (необов'язково)"; "auth_phone_placeholder" = "Номер телефону"; "auth_repeat_password_placeholder" = "Повторіть пароль"; -"auth_repeat_new_password_placeholder" = "Підтвердьте новий пароль"; +"auth_repeat_new_password_placeholder" = "Підтвердьте новий пароль облікового запису Matrix"; "auth_home_server_placeholder" = "URL (наприклад, https://matrix.org)"; "auth_identity_server_placeholder" = "URL (наприклад, https://vector.im)"; "auth_invalid_login_param" = "Неправильне ім'я користувача або пароль"; @@ -71,7 +71,7 @@ "auth_untrusted_id_server" = "Сервер облікових даних не довірений"; "auth_password_dont_match" = "Паролі не збігаються"; "auth_username_in_use" = "Ім'я користувача зайняте"; -"auth_forgot_password" = "Забули пароль?"; +"auth_forgot_password" = "Забули пароль облікового запису Matrix?"; "auth_email_not_found" = "Не вдалося надіслати лист: Таку адресу е-пошти не знайдено"; "auth_use_server_options" = "Власні налаштування сервера"; "auth_email_validation_message" = "Перевірте свою пошту для продовження реєстрації"; @@ -79,7 +79,7 @@ "auth_msisdn_validation_message" = "Ми надіслали СМС із кодом активації. Введіть цей код унизу."; "auth_msisdn_validation_error" = "Не вдалося перевірити номер телефону."; "auth_recaptcha_message" = "Цей домашній сервер бажає переконатися, що ви не робот"; -"auth_reset_password_message" = "Щоб відновити пароль, введіть адресу е-пошти, пов'язану з вашим обліковим записом:"; +"auth_reset_password_message" = "Щоб відновити пароль облікового запису Matrix, введіть адресу е-пошти, пов'язану з вашим обліковим записом:"; "auth_reset_password_missing_email" = "Необхідно ввести адресу е-пошти, пов'язану з вашим обліковим записом."; // String for App Store "store_short_description" = "Захищений, децентралізований чат/VoIP"; @@ -89,16 +89,16 @@ "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_forgot_password_error_no_configured_identity_server" = "Ідентифікаційний сервер не налаштовано: додайте його, щоб мати можливість відновити пароль в майбутньому."; +"auth_email_is_required" = "Сервер ідентифікації не налаштовано, тому ви не можете додати адресу електронної пошти, щоб мати можливість відновити пароль облікового запису Matrix у майбутньому."; +"auth_phone_is_required" = "Сервер ідентифікації не налаштовано, тому ви не можете додати номер телефону, щоб мати можливість відновити пароль облікового запису Matrix у майбутньому."; +"auth_forgot_password_error_no_configured_identity_server" = "Сервер ідентифікації не налаштовано: додайте його, щоб мати можливість відновити пароль облікового запису Matrix у майбутньому."; "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_error_is_required" = "Ідентифікаційний сервер не налаштовано: додайте його в параметрах сервера, щоб мати можливість відновити пароль в майбутньому."; -"auth_reset_password_success_message" = "Ваш пароль було скинуто.\n\nСеанс входу завершено на всіх сесіях і припинено отримання push-сповіщень. Щоб активувати сповіщення, виконайте вхід з новим паролем на кожному пристрої."; +"auth_reset_password_error_is_required" = "Сервер ідентифікації не налаштовано: додайте його у параметрах сервера, щоб мати можливість відновити пароль облікового запису Matrix у майбутньому."; +"auth_reset_password_success_message" = "Ваш пароль облікового запису Matrix було скинуто.\n\nСеанс входу завершено у всіх сеансах і припинено отримання push-сповіщень. Щоб активувати сповіщення, увійдіть з новим паролем на кожному пристрої."; "auth_add_email_and_phone_warning" = "Реєстрація за допомогою електронної пошти і номера телефону водночас не підтримується, доки немає API. Лише номер телефону буде додано до облікового запису. Ви можете додати електронну пошту до свого профілю в налаштуваннях."; "auth_accept_policies" = "Ознайомтесь та прийміть правила цього домашнього сервера:"; "auth_softlogout_signed_out" = "Ви вийшли"; @@ -231,7 +231,7 @@ "settings_enable_push_notif" = "Сповіщення на цьому пристрої"; "settings_fail_to_update_profile" = "Не вдалося оновити профіль"; "settings_night_mode" = "Нічний режим"; -"settings_change_password" = "Змінити пароль"; +"settings_change_password" = "Змінити пароль облікового запису Matrix"; "settings_add_phone_number" = "Додати номер телефону"; "settings_phone_number" = "Телефон"; "settings_add_email_address" = "Додати адресу е-пошти"; @@ -832,8 +832,8 @@ "settings_add_3pid_password_title_msidsn" = "Додати номер телефону"; "settings_device_notifications" = "Сповіщення пристрою"; "settings_add_3pid_password_title_email" = "Додати адресу е-пошти"; -"settings_password_updated" = "Пароль оновлено"; -"settings_fail_to_update_password" = "Не вдалося оновити пароль"; +"settings_password_updated" = "Пароль облікового запису Matrix оновлено"; +"settings_fail_to_update_password" = "Не вдалося оновити пароль облікового запису Matrix"; "settings_confirm_password" = "підтвердити пароль"; "settings_new_password" = "новий пароль"; "settings_old_password" = "старий пароль"; @@ -862,8 +862,8 @@ "settings_crypto_device_id" = "\nID сеансу: "; "settings_crypto_device_name" = "Назва сеансу: "; "settings_add_3pid_invalid_password_message" = "Хибні облікові дані"; -"settings_add_3pid_password_message" = "Щоб продовжити, введіть пароль"; -"deactivate_account_password_alert_message" = "Щоб продовжити, введіть пароль"; +"settings_add_3pid_password_message" = "Щоб продовжити, введіть пароль облікового запису Matrix"; +"deactivate_account_password_alert_message" = "Щоб продовжити, введіть пароль облікового запису Matrix"; // Deactivate account @@ -970,7 +970,7 @@ "room_event_action_report" = "Поскаржитися на вміст"; "room_event_action_view_decrypted_source" = "Переглянути розшифроване джерело"; "room_event_action_view_source" = "Переглянути джерело"; -"room_event_action_permalink" = "Постійне посилання"; +"room_event_action_permalink" = "Скопіювати посилання на повідомлення"; "room_event_action_forward" = "Переслати"; "room_resend_unsent_messages" = "Повторно надіслати не надіслані повідомлення"; "room_prompt_cancel" = "скасувати всі"; @@ -1034,7 +1034,7 @@ // AuthenticatedSessionViewControllerFactory "authenticated_session_flow_not_supported" = "Цей застосунок не підтримує способу входу, доступного на вашому домашньому сервері."; -"security_settings_user_password_description" = "Підтвердьте свою особу, ввівши пароль свого облікового запису"; +"security_settings_user_password_description" = "Підтвердьте свою особу, ввівши пароль свого облікового запису Matrix"; "security_settings_coming_soon" = "Вибачте, ця дія ще не доступна в %@ iOS. Якщо налаштуєте це іншим клієнтом Matrix, %@ iOS зможе також це використати."; "security_settings_complete_security_alert_message" = "Спершу слід доповнити захист вашого поточного сеансу."; "security_settings_blacklist_unverified_devices_description" = "Звірте всі сеанси користувача, щоб позначити його довіреним і надіслати йому повідомлення."; @@ -1043,7 +1043,7 @@ "security_settings_crosssigning_info_trusted" = "Перехресне підписування увімкнено. Ви можете робити інших користувачів і свої інші сеанси довіреними на підставі перехресного підпису, але ви не можете перехресно підписувати цим сеансом, бо в нього ще нема закритих ключів перехресного підписування. Доповніть захист цього сеансу."; "security_settings_crosssigning_info_exists" = "Ваш обліковий запис має ідентичність перехресного підписування, але вона ще не довірена цим сеансом. Доповніть захист цього сеансу."; "security_settings_secure_backup_description" = "Зробіть резервну копію своїх ключів шифрування й даних облікового запису на випадок втрати доступу до своїх сеансів. Ваші ключі будуть захищені унікальним ключем безпеки."; -"security_settings_crypto_sessions_description_2" = "Якщо не впізнаєте вхід, скиньте пароль і налаштування безпечного резервного копіювання."; +"security_settings_crypto_sessions_description_2" = "Якщо не впізнаєте сеанс, скиньте пароль облікового запису Matrix і налаштування безпечного резервного копіювання."; "settings_identity_server_no_is_description" = "Зараз ви не використовуєте сервер ідентифікації. Щоб знаходити наявні контакти й вони знаходили вас, додайте сервер угорі."; "settings_identity_server_description" = "Налаштований угорі сервер ідентифікації дає вам змогу знаходити людей за наявними контактами, а їм знаходити вас."; "settings_discovery_three_pid_details_information_phone_number" = "Керуйте параметрами для цього номера телефону, за яким інші користувачі можуть вас знаходити й запрошувати до кімнат. Додавайте й видаляйте номери в Облікових записах."; @@ -1109,7 +1109,7 @@ // Banner "cross_signing_setup_banner_title" = "Налаштування шифрування"; -"secrets_reset_authentication_message" = "Введіть пароль свого облікового запису, щоб продовжити"; +"secrets_reset_authentication_message" = "Введіть пароль свого облікового запису Matrix, щоб продовжити"; "security_settings_blacklist_unverified_devices" = "Ніколи не надсилати повідомлення не довіреним сеансам"; "security_settings_advanced" = "ДОДАТКОВО"; "security_settings_export_keys_manually" = "Експорт ключів вручну"; @@ -1357,7 +1357,7 @@ "secrets_setup_recovery_passphrase_title" = "Встановіть фразу безпеки"; "secrets_setup_recovery_passphrase_information" = "Введіть відому лише вам фразу безпеки для захисту таємниць на вашому сервері."; -"secrets_setup_recovery_passphrase_additional_information" = "Не застосовуйте пароль облікового запису повторно."; +"secrets_setup_recovery_passphrase_additional_information" = "Не застосовуйте пароль облікового запису Matrix повторно."; "secrets_setup_recovery_passphrase_confirm_information" = "Для підтвердження введіть таємну фразу ще раз."; "secure_key_backup_setup_existing_backup_error_title" = "Резервна копія повідомлень уже існує"; "secure_key_backup_setup_existing_backup_error_info" = "Розблокуйте її, щоб продовжити попереднє захищене резервне копіювання, або видаліть її, щоб почати захищене резервне копіювання повідомлень заново."; @@ -1590,7 +1590,7 @@ "key_backup_setup_intro_title" = "Ніколи не втрачайте зашифровані повідомлення"; "key_backup_setup_intro_info" = "Повідомлення в зашифрованих кімнатах захищені наскрізним шифруванням. Тільки ви та одержувачі маєте ключі для читання цих повідомлень.\n\nСтворіть захищену резервну копію ключів, щоб їх не втратити."; -"key_backup_setup_passphrase_info" = "Ми збережемо зашифровану копію ваших ключів на нашому сервері. Захистіть свою резервну копію парольною фразою.\n\nДля максимальної безпеки фраза повинна відрізнятися від пароля вашого облікового запису."; +"key_backup_setup_passphrase_info" = "Ми збережемо зашифровану копію ваших ключів на нашому сервері. Захистіть свою резервну копію парольною фразою.\n\nДля максимальної безпеки фраза повинна відрізнятися від пароля вашого облікового запису Matrix."; "key_backup_setup_passphrase_set_passphrase_action" = "Встановити фразу"; // MARK: Key backup recover @@ -1738,3 +1738,35 @@ "poll_edit_form_update_failure_subtitle" = "Повторіть спробу"; "poll_edit_form_update_failure_title" = "Не вдалося оновити опитування"; "poll_edit_form_poll_type" = "Тип опитування"; +"location_sharing_post_failure_subtitle" = "%@ не може отримати доступ до вашого місцеперебування. Повторіть спробу пізніше."; +"location_sharing_post_failure_title" = "Ми не можемо надіслати дані про ваше місцеперебування"; +"home_context_menu_leave" = "Вийти"; +"home_context_menu_normal_priority" = "Звичайна пріоритетність"; +"home_context_menu_low_priority" = "Неважливі"; +"home_context_menu_unfavourite" = "Вилучити з улюблених"; +"home_context_menu_favourite" = "Улюблені"; +"home_context_menu_unmute" = "Увімкнути звук"; +"home_context_menu_mute" = "Без звуку"; +"home_context_menu_notifications" = "Сповіщення"; +"home_context_menu_make_room" = "Перемістити на вкладку «Кімнати»"; +"home_context_menu_make_dm" = "Перемістити на вкладку «Люди»"; +"event_formatter_message_deleted" = "Повідомлення видалено"; +"settings_labs_enable_threads" = "Треди спілкування"; +"message_from_a_thread" = "З треду"; +"threads_empty_show_all_threads" = "Показати всі треди"; +"threads_empty_tip" = "Порада: Торкніться повідомлення та натисніть «Тред», щоб розпочати його."; +"threads_empty_info_my" = "Відповідайте у поточний тред або торкніться повідомлення та натисніть «Тред», щоб почати новий."; +"threads_empty_info_all" = "Треди допомагають підтримувати розмови за темою та за ними легко стежити."; +"threads_empty_title" = "Спілкуйтеся за темою в тредах"; +"threads_action_my_threads" = "Мої треди"; +"threads_action_all_threads" = "Усі треди"; +"threads_title" = "Треди"; +"thread_copy_link_to_thread" = "Копіювати посилання на тред"; + +// MARK: Threads +"room_thread_title" = "Тред"; +"room_accessibility_thread_more" = "Більше"; +"room_accessibility_threads" = "Треди"; +"room_event_copy_link_info" = "Посилання скопійовано до буфера обміну."; +"room_event_action_reply_in_thread" = "Тред"; +"room_event_action_view_in_room" = "Переглянути у кімнаті"; diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index 12863f8fa..181d299c6 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -251,7 +251,7 @@ "settings_labs" = "实验室"; "settings_devices" = "会话"; "settings_cryptography" = "加密"; -"settings_sign_out" = "离开"; +"settings_sign_out" = "退出登陆"; "settings_sign_out_confirmation" = "你确定?"; "settings_sign_out_e2e_warn" = "您将丢失所有端对端加密密钥。这意味着在此设备上您将再也无法阅读已加密聊天室里的旧消息。"; "settings_profile_picture" = "档案图片"; @@ -1605,3 +1605,8 @@ "settings_analytics_and_crash_data" = "发送崩溃和分析数据"; "accessibility_button_label" = "按钮"; "enable" = "启用"; +"onboarding_splash_page_1_message" = "独立安全的通信,为您提供与家中面谈相同的私密性。"; +"onboarding_splash_page_1_title" = "掌控你的对话。"; + +// Onboarding +"onboarding_splash_register_button_title" = "注册"; diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 98fde6a11..6924cf8c6 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -106,7 +106,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = CGFloat timeLabelPosX; CGFloat timeLabelPosY; - CGFloat timeLabelHeight = RoomBubbleCellLayout.timestampLabelHeight; + CGFloat timeLabelHeight = PlainRoomCellLayoutConstants.timestampLabelHeight; CGFloat timeLabelWidth; NSTextAlignment timeLabelTextAlignment; @@ -133,7 +133,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = } else { - timeLabelPosX = self.bubbleInfoContainer.frame.size.width - RoomBubbleCellLayout.timestampLabelWidth; + timeLabelPosX = self.bubbleInfoContainer.frame.size.width - PlainRoomCellLayoutConstants.timestampLabelWidth; if (isFirstDisplayedComponent) { @@ -148,7 +148,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = timeLabelPosY = component.position.y + self.msgTextViewTopConstraint.constant - timeLabelHeight - self.bubbleInfoContainerTopConstraint.constant; } - timeLabelWidth = RoomBubbleCellLayout.timestampLabelWidth; + timeLabelWidth = PlainRoomCellLayoutConstants.timestampLabelWidth; timeLabelTextAlignment = NSTextAlignmentRight; } @@ -340,7 +340,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = NSDate *date = bubbleData.date; if (date) { - UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.bubbleInfoContainer.frame.size.width, RoomBubbleCellLayout.timestampLabelHeight)]; + UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.bubbleInfoContainer.frame.size.width, PlainRoomCellLayoutConstants.timestampLabelHeight)]; timeLabel.text = [bubbleData.eventFormatter dateStringFromDate:date withTime:NO]; timeLabel.textAlignment = NSTextAlignmentRight; @@ -387,7 +387,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 - constant:RoomBubbleCellLayout.timestampLabelHeight]; + constant:PlainRoomCellLayoutConstants.timestampLabelHeight]; // Available on iOS 8 and later [NSLayoutConstraint activateConstraints:@[rightConstraint, topConstraint, widthConstraint, heightConstraint]]; @@ -539,7 +539,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = CGFloat componentBottomY = componentFrameInContentView.origin.y + componentFrameInContentView.size.height; CGFloat x = 0; - CGFloat y = componentFrameInContentView.origin.y - RoomBubbleCellLayout.timestampLabelHeight; + CGFloat y = componentFrameInContentView.origin.y - PlainRoomCellLayoutConstants.timestampLabelHeight; CGFloat width = roomBubbleTableViewCell.contentView.frame.size.width; CGFloat height = componentBottomY - y; @@ -697,7 +697,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = if ([bubbleData isKindOfClass:RoomBubbleCellData.class] && ((RoomBubbleCellData*)bubbleData).componentIndexOfSentMessageTick >= 0) { - UIImage *image = [UIImage imageNamed:@"sent_message_tick"]; + UIImage *image = AssetImages.sentMessageTick.image; image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; tickView = [[UIImageView alloc] initWithImage:image]; tickView.tintColor = ThemeService.shared.theme.textTertiaryColor; @@ -733,7 +733,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = } else { - UIImage *image = [UIImage imageNamed:@"sending_message_tick"]; + UIImage *image = AssetImages.sendingMessageTick.image; image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; tickView = [[UIImageView alloc] initWithImage:image]; tickView.tintColor = ThemeService.shared.theme.textTertiaryColor; @@ -746,7 +746,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = if (component.event.sentState == MXEventSentStateFailed) { - tickView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"error_message_tick"]]; + tickView = [[UIImageView alloc] initWithImage:AssetImages.errorMessageTick.image]; [statusViews addObject:tickView]; [self addTickView:tickView atIndex:index]; } @@ -798,7 +798,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = { CGRect componentFrame = [self componentFrameInContentViewForIndex: index]; - tickView.frame = CGRectMake(self.contentView.bounds.size.width - tickView.frame.size.width - 2 * RoomBubbleCellLayout.readReceiptsViewRightMargin, CGRectGetMaxY(componentFrame) - tickView.frame.size.height, tickView.frame.size.width, tickView.frame.size.height); + tickView.frame = CGRectMake(self.contentView.bounds.size.width - tickView.frame.size.width - 2 * PlainRoomCellLayoutConstants.readReceiptsViewRightMargin, CGRectGetMaxY(componentFrame) - tickView.frame.size.height, tickView.frame.size.width, tickView.frame.size.height); [self.contentView addSubview:tickView]; } @@ -815,8 +815,8 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = } // Define 'Edit' button frame - UIImage *editIcon = [UIImage imageNamed:@"edit_icon"]; - CGFloat editBtnPosX = self.bubbleInfoContainer.frame.size.width - RoomBubbleCellLayout.timestampLabelWidth - 22 - editIcon.size.width / 2; + UIImage *editIcon = AssetImages.editIcon.image; + CGFloat editBtnPosX = self.bubbleInfoContainer.frame.size.width - PlainRoomCellLayoutConstants.timestampLabelWidth - 22 - editIcon.size.width / 2; CGFloat editBtnPosY = isFirstDisplayedComponent ? -13 : component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant - 13; UIButton *editButton = [[UIButton alloc] initWithFrame:CGRectMake(editBtnPosX, editBtnPosY, 44, 44)]; diff --git a/Riot/Categories/UIView+Toast.swift b/Riot/Categories/UIView+Toast.swift index e395578e3..b89d63ac1 100644 --- a/Riot/Categories/UIView+Toast.swift +++ b/Riot/Categories/UIView+Toast.swift @@ -60,7 +60,7 @@ extension UIView { duration: TimeInterval = Constants.defaultDuration, position: ToastPosition = Constants.defaultPosition, additionalMargin: CGFloat = 0.0) { - let view = ToastView(withMessage: message, image: image) + let view = BasicToastView(withMessage: message, image: image) vc_toast(view: view, duration: duration, position: position, additionalMargin: additionalMargin) } @@ -91,6 +91,9 @@ extension UIView { // MARK: - ToastOperation /// Async toast UI operation. Will run on the main thread. +/// +/// Note: a more recent `Activity` and `ActivityCenter` aim to achieve the same goal of abstracting away the scheduling and display +/// of visual notifications, without using `OperationQueue`. private class ToastOperation: AsyncOperation { private enum Constants { @@ -212,100 +215,3 @@ private class ToastOperation: AsyncOperation { } } - -// MARK: - ToastView - -/// Default view for a basic toast. -private class ToastView: UIView, Themable { - - private enum Constants { - static let padding: UIEdgeInsets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) - static let cornerRadius: CGFloat = 8.0 - } - - private lazy var imageView: UIImageView = { - let view = UIImageView() - view.translatesAutoresizingMaskIntoConstraints = false - view.backgroundColor = .clear - return view - }() - - private lazy var messageLabel: UILabel = { - let label = UILabel() - label.font = ThemeService.shared().theme.fonts.body - label.backgroundColor = .clear - label.numberOfLines = 0 - label.textAlignment = .left - label.translatesAutoresizingMaskIntoConstraints = false - return label - }() - - private lazy var stackView: UIStackView = { - let result = UIStackView() - result.axis = .horizontal - result.distribution = .fill - result.alignment = .center - result.spacing = 8.0 - result.backgroundColor = .clear - - addSubview(result) - result.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - result.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.padding.left), - result.topAnchor.constraint(equalTo: topAnchor, constant: Constants.padding.top), - result.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.padding.right), - result.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Constants.padding.bottom) - ]) - - return result - }() - - init(withMessage message: String?, - image: UIImage? = nil) { - super.init(frame: .zero) - - if let image = image { - imageView.image = image - NSLayoutConstraint.activate([ - imageView.widthAnchor.constraint(equalToConstant: image.size.width), - imageView.heightAnchor.constraint(equalToConstant: image.size.height) - ]) - stackView.addArrangedSubview(imageView) - } - - messageLabel.text = message - stackView.addArrangedSubview(messageLabel) - - stackView.layoutIfNeeded() - layer.cornerRadius = Constants.cornerRadius - layer.masksToBounds = true - registerThemeServiceDidChangeThemeNotification() - themeDidChange() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func registerThemeServiceDidChangeThemeNotification() { - NotificationCenter.default.addObserver(self, - selector: #selector(themeDidChange), - name: .themeServiceDidChangeTheme, - object: nil) - } - - @objc - private func themeDidChange() { - self.update(theme: ThemeService.shared().theme) - } - - // MARK: Themable - - func update(theme: Theme) { - backgroundColor = theme.colors.quinaryContent - imageView.tintColor = theme.colors.tertiaryContent - messageLabel.textColor = theme.colors.primaryContent - messageLabel.font = theme.fonts.body - } - -} diff --git a/Riot/Categories/UIViewController+RiotSearch.h b/Riot/Categories/UIViewController+RiotSearch.h index db29e0183..ac95a4139 100644 --- a/Riot/Categories/UIViewController+RiotSearch.h +++ b/Riot/Categories/UIViewController+RiotSearch.h @@ -33,15 +33,6 @@ */ @property (nonatomic, readonly) BOOL searchBarHidden; -/** - The Riot empty search background image (champagne bubbles). - The image adapts its width to its parent view width. - Its bottom is aligned to the top of the keyboard. - */ -@property (nonatomic, readonly) UIImageView *backgroundImageView; - -@property (nonatomic, readonly) NSLayoutConstraint *backgroundImageViewBottomConstraint; - /** Show/Hide the search bar. @@ -50,18 +41,4 @@ - (void)showSearch:(BOOL)animated; - (void)hideSearch:(BOOL)animated; -/** - Initialise `backgroundImageView` and add it to the passed parent view. - - @param view the view to add `backgroundImageView` to. - */ -- (void)addBackgroundImageViewToView:(UIView*)view; - -/** - Provide the new height of the keyboard to align `backgroundImageView` - - @param keyboardHeight the keyboard height. - */ -- (void)setKeyboardHeightForBackgroundImage:(CGFloat)keyboardHeight; - @end diff --git a/Riot/Categories/UIViewController+RiotSearch.m b/Riot/Categories/UIViewController+RiotSearch.m index 09b7896b3..76d3af972 100644 --- a/Riot/Categories/UIViewController+RiotSearch.m +++ b/Riot/Categories/UIViewController+RiotSearch.m @@ -38,10 +38,6 @@ @property (nonatomic) UIBarButtonItem *backupLeftBarButtonItem; @property (nonatomic) UIBarButtonItem *backupRightBarButtonItem; -// The Riot empty search background image (champagne bubbles) -@property (nonatomic) UIImageView *backgroundImageView; -@property (nonatomic) NSLayoutConstraint *backgroundImageViewBottomConstraint; - @end @implementation UIViewControllerRiotSearchInternals @@ -70,16 +66,6 @@ return self.searchInternals.searchBarHidden; } -- (UIImageView*)backgroundImageView -{ - return self.searchInternals.backgroundImageView; -} - -- (NSLayoutConstraint *)backgroundImageViewBottomConstraint -{ - return self.searchInternals.backgroundImageViewBottomConstraint; -} - - (void)showSearch:(BOOL)animated { if (self.searchInternals.searchBarHidden) @@ -134,80 +120,6 @@ } } -- (void)addBackgroundImageViewToView:(UIView*)view -{ - UIImage *searchBgImage = [MXKTools paintImage:[UIImage imageNamed:@"search_bg"] withColor:ThemeService.shared.theme.matrixSearchBackgroundImageTintColor]; - UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:searchBgImage]; - backgroundImageView.translatesAutoresizingMaskIntoConstraints = NO; - - [view addSubview:backgroundImageView]; - - // Keep it at left - NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:view - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:backgroundImageView - attribute:NSLayoutAttributeLeading - multiplier:1.0 - constant:0]; - // Same width as parent - NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:view - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:backgroundImageView - attribute:NSLayoutAttributeWidth - multiplier:1.0f - constant:0.0f]; - - // Keep the image aspect ratio - NSLayoutConstraint *aspectRatioConstraint = [NSLayoutConstraint - constraintWithItem:backgroundImageView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:backgroundImageView - attribute:NSLayoutAttributeWidth - multiplier:(backgroundImageView.frame.size.height / backgroundImageView.frame.size.width) - constant:0]; - - // Set its position according to its bottom - NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:view - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:backgroundImageView - attribute:NSLayoutAttributeBottom - multiplier:1.0f - constant:216]; - - [NSLayoutConstraint activateConstraints:@[leftConstraint, - widthConstraint, - aspectRatioConstraint, - bottomConstraint - ]]; - - self.searchInternals.backgroundImageView = backgroundImageView; - self.searchInternals.backgroundImageViewBottomConstraint = bottomConstraint; - - // It will be showed once the keyboard appears - backgroundImageView.hidden = YES; -} - -- (void)setKeyboardHeightForBackgroundImage:(CGFloat)keyboardHeight -{ - // keyboardHeight = 0 means no keyboard - if (keyboardHeight > 0) - { - self.searchInternals.backgroundImageView.hidden = NO; - - // 60 = 18 + 42 from the Riot design - self.searchInternals.backgroundImageViewBottomConstraint.constant = keyboardHeight - 60; - } - else - { - // Hide the search - self.searchInternals.backgroundImageView.hidden = YES; - } -} - #pragma mark - UISearchBarDelegate - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar2 diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index eca6c861e..ee18b509e 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -18,8 +18,10 @@ internal typealias AssetImageTypeAlias = ImageAsset.Image // MARK: - Asset Catalogs // swiftlint:disable identifier_name line_length nesting type_body_length type_name -internal enum Asset { - internal enum Images { +@objcMembers +internal class Asset: NSObject { + @objcMembers + @objc(AssetImages) internal class Images: NSObject { internal static let analyticsCheckmark = ImageAsset(name: "AnalyticsCheckmark") internal static let analyticsLogo = ImageAsset(name: "AnalyticsLogo") internal static let socialLoginButtonApple = ImageAsset(name: "social_login_button_apple") @@ -61,6 +63,8 @@ internal enum Asset { internal static let disclosureIcon = ImageAsset(name: "disclosure_icon") internal static let errorIcon = ImageAsset(name: "error_icon") internal static let faceidIcon = ImageAsset(name: "faceid_icon") + internal static let filterOff = ImageAsset(name: "filter_off") + internal static let filterOn = ImageAsset(name: "filter_on") internal static let group = ImageAsset(name: "group") internal static let informationButton = ImageAsset(name: "information_button") internal static let monitor = ImageAsset(name: "monitor") @@ -79,6 +83,7 @@ internal enum Asset { internal static let removeIconBlue = ImageAsset(name: "remove_icon_blue") internal static let findYourContactsFacepile = ImageAsset(name: "find_your_contacts_facepile") internal static let captureAvatar = ImageAsset(name: "capture_avatar") + internal static let deleteAvatar = ImageAsset(name: "delete_avatar") internal static let e2eBlocked = ImageAsset(name: "e2e_blocked") internal static let e2eUnencrypted = ImageAsset(name: "e2e_unencrypted") internal static let e2eWarning = ImageAsset(name: "e2e_warning") @@ -118,6 +123,13 @@ internal enum Asset { internal static let onboardingSplashScreenPage3Dark = ImageAsset(name: "OnboardingSplashScreenPage3Dark") internal static let onboardingSplashScreenPage4 = ImageAsset(name: "OnboardingSplashScreenPage4") internal static let onboardingSplashScreenPage4Dark = ImageAsset(name: "OnboardingSplashScreenPage4Dark") + internal static let onboardingUseCaseCommunity = ImageAsset(name: "onboarding_use_case_community") + internal static let onboardingUseCaseCommunityDark = ImageAsset(name: "onboarding_use_case_community_dark") + internal static let onboardingUseCaseIcon = ImageAsset(name: "onboarding_use_case_icon") + internal static let onboardingUseCasePersonal = ImageAsset(name: "onboarding_use_case_personal") + internal static let onboardingUseCasePersonalDark = ImageAsset(name: "onboarding_use_case_personal_dark") + internal static let onboardingUseCaseWork = ImageAsset(name: "onboarding_use_case_work") + internal static let onboardingUseCaseWorkDark = ImageAsset(name: "onboarding_use_case_work_dark") internal static let peopleEmptyScreenArtwork = ImageAsset(name: "people_empty_screen_artwork") internal static let peopleEmptyScreenArtworkDark = ImageAsset(name: "people_empty_screen_artwork_dark") internal static let peopleFloatingAction = ImageAsset(name: "people_floating_action") @@ -203,7 +215,6 @@ internal enum Asset { internal static let fileMusicIcon = ImageAsset(name: "file_music_icon") internal static let filePhotoIcon = ImageAsset(name: "file_photo_icon") internal static let fileVideoIcon = ImageAsset(name: "file_video_icon") - internal static let searchBg = ImageAsset(name: "search_bg") internal static let searchIcon = ImageAsset(name: "search_icon") internal static let secretsRecoveryKey = ImageAsset(name: "secrets_recovery_key") internal static let secretsRecoveryPassphrase = ImageAsset(name: "secrets_recovery_passphrase") @@ -237,7 +248,8 @@ internal enum Asset { internal static let tabRooms = ImageAsset(name: "tab_rooms") internal static let launchScreenLogo = ImageAsset(name: "launch_screen_logo") } - internal enum SharedImages { + @objcMembers + @objc(AssetSharedImages) internal class SharedImages: NSObject { internal static let cancel = ImageAsset(name: "cancel") internal static let e2eVerified = ImageAsset(name: "e2e_verified") internal static let horizontalLogo = ImageAsset(name: "horizontal_logo") @@ -249,7 +261,8 @@ internal enum Asset { // MARK: - Implementation Details -internal struct ImageAsset { +@objcMembers +internal class ImageAsset: NSObject { internal fileprivate(set) var name: String #if os(macOS) @@ -275,6 +288,10 @@ internal struct ImageAsset { return result } + internal init(name: String) { + self.name = name + } + #if os(iOS) || os(tvOS) @available(iOS 8.0, tvOS 9.0, *) internal func image(compatibleWith traitCollection: UITraitCollection) -> Image { @@ -314,3 +331,4 @@ private final class BundleToken { }() } // swiftlint:enable convenience_type + diff --git a/Riot/Generated/MatrixKitStrings.swift b/Riot/Generated/MatrixKitStrings.swift index b178f6d8d..f59c7d116 100644 --- a/Riot/Generated/MatrixKitStrings.swift +++ b/Riot/Generated/MatrixKitStrings.swift @@ -467,7 +467,7 @@ public class MatrixKitL10n: NSObject { public static var inviteUser: String { return MatrixKitL10n.tr("invite_user") } - /// Kick + /// Remove from chat public static var kick: String { return MatrixKitL10n.tr("kick") } @@ -1059,11 +1059,11 @@ public class MatrixKitL10n: NSObject { public static func noticeRoomJoinRulePublicForDm(_ p1: String) -> String { return MatrixKitL10n.tr("notice_room_join_rule_public_for_dm", p1) } - /// %@ kicked %@ + /// %@ removed %@ public static func noticeRoomKick(_ p1: String, _ p2: String) -> String { return MatrixKitL10n.tr("notice_room_kick", p1, p2) } - /// You kicked %@ + /// You removed %@ public static func noticeRoomKickByYou(_ p1: String) -> String { return MatrixKitL10n.tr("notice_room_kick_by_you", p1) } diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 168c6e544..84b6970a6 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2267,6 +2267,10 @@ public class VectorL10n: NSObject { public static var locationSharingOpenGoogleMaps: String { return VectorL10n.tr("Vector", "location_sharing_open_google_maps") } + /// Open in OpenStreetMap + public static var locationSharingOpenOpenStreetMaps: String { + return VectorL10n.tr("Vector", "location_sharing_open_open_street_maps") + } /// %@ could not send your location. Please try again later. public static func locationSharingPostFailureSubtitle(_ p1: String) -> String { return VectorL10n.tr("Vector", "location_sharing_post_failure_subtitle", p1) @@ -2443,6 +2447,42 @@ public class VectorL10n: NSObject { public static var onboardingSplashRegisterButtonTitle: String { return VectorL10n.tr("Vector", "onboarding_splash_register_button_title") } + /// Communities + public static var onboardingUseCaseCommunityMessaging: String { + return VectorL10n.tr("Vector", "onboarding_use_case_community_messaging") + } + /// Connect to server + public static var onboardingUseCaseExistingServerButton: String { + return VectorL10n.tr("Vector", "onboarding_use_case_existing_server_button") + } + /// Looking to join an existing server? + public static var onboardingUseCaseExistingServerMessage: String { + return VectorL10n.tr("Vector", "onboarding_use_case_existing_server_message") + } + /// We’ll help you get connected. + public static var onboardingUseCaseMessage: String { + return VectorL10n.tr("Vector", "onboarding_use_case_message") + } + /// Not sure yet? You can %@ + public static func onboardingUseCaseNotSureYet(_ p1: String) -> String { + return VectorL10n.tr("Vector", "onboarding_use_case_not_sure_yet", p1) + } + /// Friends and family + public static var onboardingUseCasePersonalMessaging: String { + return VectorL10n.tr("Vector", "onboarding_use_case_personal_messaging") + } + /// skip this question + public static var onboardingUseCaseSkipButton: String { + return VectorL10n.tr("Vector", "onboarding_use_case_skip_button") + } + /// Who will you chat to the most? + public static var onboardingUseCaseTitle: String { + return VectorL10n.tr("Vector", "onboarding_use_case_title") + } + /// Teams + public static var onboardingUseCaseWorkMessaging: String { + return VectorL10n.tr("Vector", "onboarding_use_case_work_messaging") + } /// Open public static var `open`: String { return VectorL10n.tr("Vector", "open") @@ -3223,7 +3263,7 @@ public class VectorL10n: NSObject { public static var roomEventActionForward: String { return VectorL10n.tr("Vector", "room_event_action_forward") } - /// Reason for kicking this user + /// Reason for removing this user public static var roomEventActionKickPromptReason: String { return VectorL10n.tr("Vector", "room_event_action_kick_prompt_reason") } @@ -3987,6 +4027,10 @@ public class VectorL10n: NSObject { public static var searchFiles: String { return VectorL10n.tr("Vector", "search_files") } + /// Filter + public static var searchFilterPlaceholder: String { + return VectorL10n.tr("Vector", "search_filter_placeholder") + } /// Searching… public static var searchInProgress: String { return VectorL10n.tr("Vector", "search_in_progress") diff --git a/Riot/Managers/BugReport/RageShakeManager.m b/Riot/Managers/BugReport/RageShakeManager.m index 4ce848240..5f04baa9e 100644 --- a/Riot/Managers/BugReport/RageShakeManager.m +++ b/Riot/Managers/BugReport/RageShakeManager.m @@ -200,7 +200,7 @@ static RageShakeManager* sharedInstance = nil; UIImage *image; - AppDelegate* theDelegate = [AppDelegate theDelegate]; + LegacyAppDelegate* theDelegate = [AppDelegate theDelegate]; UIGraphicsBeginImageContextWithOptions(theDelegate.window.bounds.size, NO, [UIScreen mainScreen].scale); // Iterate over every window from back to front diff --git a/Riot/Managers/PushNotification/PushNotificationService.h b/Riot/Managers/PushNotification/PushNotificationService.h index aaec583d7..c16b65792 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.h +++ b/Riot/Managers/PushNotification/PushNotificationService.h @@ -108,7 +108,6 @@ NS_ASSUME_NONNULL_BEGIN @param pushNotificationService PushNotificationService object. @param roomId Room identifier to be navigated. - @param eventId Event identifier to be navigated around. */ - (void)pushNotificationService:(PushNotificationService *)pushNotificationService shouldNavigateToRoomWithId:(NSString *)roomId; diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 1b1af2591..fdff093ce 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -99,7 +99,7 @@ final class RiotSettings: NSObject { @UserDefault(key: "userInterfaceTheme", defaultValue: nil, storage: defaults) var userInterfaceTheme - // MARK: Other + // MARK: Analytics & Rageshakes /// Whether the user was previously shown the Matomo analytics prompt. var hasSeenAnalyticsPrompt: Bool { @@ -130,6 +130,12 @@ final class RiotSettings: NSObject { @UserDefault(key: "enableRageShake", defaultValue: false, storage: defaults) var enableRageShake + // MARK: User + + /// A dictionary of dictionaries keyed by user ID for storage of the `UserSessionProperties` from any active `UserSession`s. + @UserDefault(key: "userSessionProperties", defaultValue: [:], storage: defaults) + var userSessionProperties: [String: [String: Any]] + // MARK: Labs /// Indicates if CallKit ringing is enabled for group calls. This setting does not disable the CallKit integration for group calls, only relates to ringing. diff --git a/Riot/Managers/Theme/Theme.swift b/Riot/Managers/Theme/Theme.swift index 2418cc967..f8e0a382d 100644 --- a/Riot/Managers/Theme/Theme.swift +++ b/Riot/Managers/Theme/Theme.swift @@ -112,11 +112,18 @@ import DesignKit /// - Parameter tabBar: The tab bar to customise. func applyStyle(onTabBar tabBar: UITabBar) - /// Apply the theme on a navigation bar. + /// Apply the theme on a navigation bar, without enabling the iOS 15's scroll edges appearance. /// /// - Parameter navigationBar: the navigation bar to customise. func applyStyle(onNavigationBar navigationBar: UINavigationBar) + /// Apply the theme on a navigation bar. + /// + /// - Parameter navigationBar: the navigation bar to customise. + /// - Parameter modernScrollEdgesAppearance: whether or not to use the iOS 15 style scroll edges appearance + func applyStyle(onNavigationBar navigationBar: UINavigationBar, + withModernScrollEdgesAppearance modernScrollEdgesAppearance: Bool) + /// Apply the theme on a search bar. /// /// - Parameter searchBar: the search bar to customise. diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift index ebac661c8..8ec84240d 100644 --- a/Riot/Managers/Theme/Themes/DarkTheme.swift +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -45,7 +45,7 @@ class DarkTheme: NSObject, Theme { var textTertiaryColor: UIColor = UIColor(rgb: 0x8E99A4) var textQuinaryColor: UIColor = UIColor(rgb: 0x394049) - var tintColor: UIColor = UIColor(displayP3Red: 0.05098039216, green: 0.7450980392, blue: 0.5450980392, alpha: 1.0) + var tintColor: UIColor = UIColor(rgb: 0x0DBD8B) var tintBackgroundColor: UIColor = UIColor(rgb: 0x1F6954) var tabBarUnselectedItemTintColor: UIColor = UIColor(rgb: 0x8E99A4) var unreadRoomIndentColor: UIColor = UIColor(rgb: 0x2E3648) @@ -112,28 +112,36 @@ class DarkTheme: NSObject, Theme { } } - // Note: We are not using UINavigationBarAppearance on iOS 13/14 because of UINavigationBar directly including UISearchBar on their titleView that cause crop issues with UINavigationController pop. + // Protocols don't support default parameter values and a protocol extension won't work for @objc func applyStyle(onNavigationBar navigationBar: UINavigationBar) { - navigationBar.tintColor = self.tintColor + applyStyle(onNavigationBar: navigationBar, withModernScrollEdgesAppearance: false) + } + + // Note: We are not using UINavigationBarAppearance on iOS 13/14 because of UINavigationBar directly including UISearchBar on their titleView that cause crop issues with UINavigationController pop. + func applyStyle(onNavigationBar navigationBar: UINavigationBar, + withModernScrollEdgesAppearance modernScrollEdgesAppearance: Bool) { + navigationBar.tintColor = tintColor // On iOS 15 use UINavigationBarAppearance to fix visual issues with the scrollEdgeAppearance style. if #available(iOS 15.0, *) { let appearance = UINavigationBarAppearance() appearance.configureWithOpaqueBackground() - appearance.backgroundColor = self.baseColor - appearance.shadowColor = nil + appearance.backgroundColor = baseColor + if !modernScrollEdgesAppearance { + appearance.shadowColor = nil + } appearance.titleTextAttributes = [ - NSAttributedString.Key.foregroundColor: self.textPrimaryColor + NSAttributedString.Key.foregroundColor: textPrimaryColor ] navigationBar.standardAppearance = appearance - navigationBar.scrollEdgeAppearance = appearance + navigationBar.scrollEdgeAppearance = modernScrollEdgesAppearance ? nil : appearance } else { navigationBar.titleTextAttributes = [ - NSAttributedString.Key.foregroundColor: self.textPrimaryColor + NSAttributedString.Key.foregroundColor: textPrimaryColor ] - navigationBar.barTintColor = self.baseColor + navigationBar.barTintColor = baseColor navigationBar.shadowImage = UIImage() // Remove bottom shadow // The navigation bar needs to be opaque so that its background color is the expected one diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift index 2be798d23..f8f69f7a6 100644 --- a/Riot/Managers/Theme/Themes/DefaultTheme.swift +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -45,7 +45,7 @@ class DefaultTheme: NSObject, Theme { var textTertiaryColor: UIColor = UIColor(rgb: 0x8D99A5) var textQuinaryColor: UIColor = UIColor(rgb: 0xE3E8F0) - var tintColor: UIColor = UIColor(displayP3Red: 0.05098039216, green: 0.7450980392, blue: 0.5450980392, alpha: 1.0) + var tintColor: UIColor = UIColor(rgb: 0x0DBD8B) var tintBackgroundColor: UIColor = UIColor(rgb: 0xe9fff9) var tabBarUnselectedItemTintColor: UIColor = UIColor(rgb: 0xC1C6CD) var unreadRoomIndentColor: UIColor = UIColor(rgb: 0x2E3648) @@ -118,9 +118,15 @@ class DefaultTheme: NSObject, Theme { } } - // Note: We are not using UINavigationBarAppearance on iOS 13/14 because of UINavigationBar directly including UISearchBar on their titleView that cause crop issues with UINavigationController pop. + // Protocols don't support default parameter values and a protocol extension doesn't work for @objc func applyStyle(onNavigationBar navigationBar: UINavigationBar) { - navigationBar.tintColor = self.tintColor + applyStyle(onNavigationBar: navigationBar, withModernScrollEdgesAppearance: false) + } + + // Note: We are not using UINavigationBarAppearance on iOS 13/14 because of UINavigationBar directly including UISearchBar on their titleView that cause crop issues with UINavigationController pop. + func applyStyle(onNavigationBar navigationBar: UINavigationBar, + withModernScrollEdgesAppearance modernScrollEdgesAppearance: Bool) { + navigationBar.tintColor = tintColor // On iOS 15 use UINavigationBarAppearance to fix visual issues with the scrollEdgeAppearance style. if #available(iOS 15.0, *) { @@ -128,13 +134,15 @@ class DefaultTheme: NSObject, Theme { appearance.configureWithOpaqueBackground() appearance.backgroundColor = baseColor - appearance.shadowColor = nil + if !modernScrollEdgesAppearance { + appearance.shadowColor = nil + } appearance.titleTextAttributes = [ NSAttributedString.Key.foregroundColor: textPrimaryColor ] navigationBar.standardAppearance = appearance - navigationBar.scrollEdgeAppearance = appearance + navigationBar.scrollEdgeAppearance = modernScrollEdgesAppearance ? nil : appearance } else { navigationBar.titleTextAttributes = [ NSAttributedString.Key.foregroundColor: textPrimaryColor diff --git a/Riot/Managers/URLPreviews/Core Data/URLPreviewStore.swift b/Riot/Managers/URLPreviews/Core Data/URLPreviewStore.swift index da955da40..ecd152fa2 100644 --- a/Riot/Managers/URLPreviews/Core Data/URLPreviewStore.swift +++ b/Riot/Managers/URLPreviews/Core Data/URLPreviewStore.swift @@ -61,6 +61,14 @@ class URLPreviewStore { if let error = error { MXLog.error("[URLPreviewStore] Core Data container error: \(error.localizedDescription)") } + + if let url = storeDescription.url { + do { + try FileManager.default.excludeItemFromBackup(at: url) + } catch { + MXLog.error("[URLPreviewStore] Cannot exclude Core Data from backup: \(error.localizedDescription)") + } + } } } diff --git a/Riot/Managers/UserSessions/UserSession.swift b/Riot/Managers/UserSessions/UserSession.swift index 786fed88d..8795812e6 100644 --- a/Riot/Managers/UserSessions/UserSession.swift +++ b/Riot/Managers/UserSessions/UserSession.swift @@ -33,19 +33,21 @@ class UserSession: NSObject, UserSessionProtocol { let account: MXKAccount // Keep strong reference to the MXSession because account.mxSession can become nil on logout or failure let matrixSession: MXSession - - var userId: String { - guard let userId = self.account.mxCredentials.userId else { - fatalError("[UserSession] identifier: account.mxCredentials.userId should be defined") - } - return userId - } + let userId: String + /// An object that contains user specific properties. + let userProperties: UserSessionProperties // MARK: - Setup init(account: MXKAccount, matrixSession: MXSession) { + guard let userId = account.mxCredentials.userId else { + fatalError("[UserSession] identifier: account.mxCredentials.userId should be defined") + } + self.account = account self.matrixSession = matrixSession + self.userId = userId + self.userProperties = UserSessionProperties(userId: userId) super.init() } } diff --git a/Riot/Managers/UserSessions/UserSessionProperties.swift b/Riot/Managers/UserSessions/UserSessionProperties.swift new file mode 100644 index 000000000..4bac87579 --- /dev/null +++ b/Riot/Managers/UserSessions/UserSessionProperties.swift @@ -0,0 +1,86 @@ +// +// Copyright 2021 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 + +/// User properties that are tied to a particular user ID. +class UserSessionProperties: NSObject { + + // MARK: - Constants + private enum Constants { + static let useCaseKey = "useCase" + } + + // MARK: - Properties + + // MARK: Private + + /// The user ID for these properties + private let userId: String + + /// The underlying dictionary for this userId from user defaults. + private var dictionary: [String: Any] { + get { + RiotSettings.shared.userSessionProperties[userId] ?? [:] + } + set { + var sharedProperties = RiotSettings.shared.userSessionProperties + sharedProperties[userId] = newValue + RiotSettings.shared.userSessionProperties = sharedProperties + } + } + + // MARK: Public + + /// The user's use case selection if this session was the one used to register the account. + var useCase: UseCase? { + get { + guard let useCaseRawValue = dictionary[Constants.useCaseKey] as? String else { return nil } + return UseCase(rawValue: useCaseRawValue) + } set { + dictionary[Constants.useCaseKey] = newValue?.rawValue + } + } + + /// Represents a selected use case for the app. + /// Note: The raw string value is used for storage. + enum UseCase: String { + case personalMessaging + case workMessaging + case communityMessaging + case skipped + } + + // MARK: - Setup + + /// Create new properties for the specified user ID. + /// - Parameter userId: The user ID to load properties for. + init(userId: String) { + self.userId = userId + super.init() + } + + // MARK: - Public + + /// Clear all of the stored properties. + func delete() { + dictionary = [:] + + var sharedProperties = RiotSettings.shared.userSessionProperties + sharedProperties[userId] = nil + RiotSettings.shared.userSessionProperties = sharedProperties + } +} diff --git a/Riot/Managers/UserSessions/UserSessionsService.swift b/Riot/Managers/UserSessions/UserSessionsService.swift index 7a68d9752..c94497161 100644 --- a/Riot/Managers/UserSessions/UserSessionsService.swift +++ b/Riot/Managers/UserSessions/UserSessionsService.swift @@ -131,6 +131,9 @@ class UserSessionsService: NSObject { NotificationCenter.default.post(name: UserSessionsService.willRemoveUserSession, object: self, userInfo: [NotificationUserInfoKey.userSession: userSession]) } + // Clear any stored user properties from this session. + userSession.userProperties.delete() + self.userSessions.removeAll { (userSession) -> Bool in return userId == userSession.userId } diff --git a/Riot/Managers/Widgets/WidgetManager.m b/Riot/Managers/Widgets/WidgetManager.m index 2b25d2e13..240d573c1 100644 --- a/Riot/Managers/Widgets/WidgetManager.m +++ b/Riot/Managers/Widgets/WidgetManager.m @@ -338,7 +338,6 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; Check user's power for widgets management in a room. @param room the room to check. - @return an NSError if the user cannot act on widgets in this room. Else, nil. */ - (void)checkWidgetPermissionInRoom:(MXRoom *)room success:(dispatch_block_t)success failure:(void (^)(NSError *))failure { @@ -742,6 +741,8 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; return configs[mxSession.myUser.userId].scalarToken; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)loadConfigs { NSUserDefaults *userDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults; @@ -787,6 +788,7 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; [userDefaults setObject:[NSKeyedArchiver archivedDataWithRootObject:configs] forKey:@"integrationManagerConfigs"]; } +#pragma clang diagnostic pop #pragma mark - Errors diff --git a/Riot/Modules/Analytics/Analytics.swift b/Riot/Modules/Analytics/Analytics.swift index d3b9c3ac1..48716f78e 100644 --- a/Riot/Modules/Analytics/Analytics.swift +++ b/Riot/Modules/Analytics/Analytics.swift @@ -171,6 +171,14 @@ import AnalyticsEvents // The following methods are exposed for compatibility with Objective-C as // the `capture` method and the generated events cannot be bridged from Swift. extension Analytics { + /// Updates any user properties to help with creating cohorts. + /// + /// Only non-nil properties will be updated when calling this method. + func updateUserProperties(ftueUseCase: UserSessionProperties.UseCase? = nil) { + let userProperties = AnalyticsEvent.UserProperties(ftueUseCaseSelection: ftueUseCase?.analyticsName, numSpaces: nil) + client.updateUserProperties(userProperties) + } + /// Track the presentation of a screen /// - Parameters: /// - screen: The screen that was shown. @@ -186,20 +194,21 @@ extension Analytics { trackScreen(screen, duration: nil) } - /// Track an element that has been tapped + /// Track an element that has been interacted with /// - Parameters: - /// - tap: The element that was tapped + /// - uiElement: The element that was interacted with + /// - interactionType: The way in with the element was interacted with /// - index: The index of the element, if it's in a list of elements - func trackTap(_ tap: AnalyticsUIElement, index: Int?) { - let event = AnalyticsEvent.Click(index: index, name: tap.elementName) + func trackInteraction(_ uiElement: AnalyticsUIElement, interactionType: AnalyticsEvent.Interaction.InteractionType, index: Int?) { + let event = AnalyticsEvent.Interaction(index: index, interactionType: interactionType, name: uiElement.name) client.capture(event) } /// Track an element that has been tapped without including an index /// - Parameters: - /// - tap: The element that was tapped - func trackTap(_ tap: AnalyticsUIElement) { - trackTap(tap, index: nil) + /// - uiElement: The element that was tapped + func trackInteraction(_ uiElement: AnalyticsUIElement) { + trackInteraction(uiElement, interactionType: .Touch, index: nil) } /// Track an E2EE error that occurred diff --git a/Riot/Modules/Analytics/AnalyticsClientProtocol.swift b/Riot/Modules/Analytics/AnalyticsClientProtocol.swift index af07a58fb..e16b962e5 100644 --- a/Riot/Modules/Analytics/AnalyticsClientProtocol.swift +++ b/Riot/Modules/Analytics/AnalyticsClientProtocol.swift @@ -45,4 +45,12 @@ protocol AnalyticsClientProtocol { /// Capture the supplied analytics screen event. /// - Parameter event: The screen event to capture. func screen(_ event: AnalyticsScreenProtocol) + + /// Updates any user properties to help with creating cohorts. + /// - Parameter userProperties: The user properties to be updated. + /// + /// Only non-nil properties will be updated when calling this method. There might + /// be a delay when updating user properties as these are cached to be included + /// as part of the next event that gets captured. + func updateUserProperties(_ userProperties: AnalyticsEvent.UserProperties) } diff --git a/Riot/Modules/Analytics/AnalyticsScreen.swift b/Riot/Modules/Analytics/AnalyticsScreen.swift index 86f567acd..048e484e8 100644 --- a/Riot/Modules/Analytics/AnalyticsScreen.swift +++ b/Riot/Modules/Analytics/AnalyticsScreen.swift @@ -18,6 +18,10 @@ import Foundation import AnalyticsEvents @objc enum AnalyticsScreen: Int { + case welcome + case login + case register + case forgotPassword case sidebar case home case favourites @@ -51,6 +55,14 @@ import AnalyticsEvents /// The screen name reported to the AnalyticsEvent. var screenName: AnalyticsEvent.Screen.ScreenName { switch self { + case .welcome: + return .Welcome + case .login: + return .Login + case .register: + return .Register + case .forgotPassword: + return .ForgotPassword case .sidebar: return .MobileSidebar case .home: diff --git a/Riot/Modules/Analytics/AnalyticsUIElement.swift b/Riot/Modules/Analytics/AnalyticsUIElement.swift index 93a08e7e2..5a25d453e 100644 --- a/Riot/Modules/Analytics/AnalyticsUIElement.swift +++ b/Riot/Modules/Analytics/AnalyticsUIElement.swift @@ -16,17 +16,17 @@ import AnalyticsEvents -/// A tappable UI element that can be track in Analytics. +/// A tappable UI element that can be tracked in Analytics. @objc enum AnalyticsUIElement: Int { - case sendMessageButton + case removeMe /// The element name reported to the AnalyticsEvent. - var elementName: AnalyticsEvent.Click.Name { + var name: AnalyticsEvent.Interaction.Name { switch self { // Note: This is a test element that doesn't need to be captured. - // It will likely be removed when the AnalyticsEvent.Click is updated. - case .sendMessageButton: - return .SendMessageButton + // It can be removed when some elements are added that relate to mobile. + case .removeMe: + return .WebRoomSettingsLeaveButton } } } diff --git a/Riot/Modules/Analytics/Helpers/UserSessionPropertiesUseCase+Analytics.swift b/Riot/Modules/Analytics/Helpers/UserSessionPropertiesUseCase+Analytics.swift new file mode 100644 index 000000000..c5e601b37 --- /dev/null +++ b/Riot/Modules/Analytics/Helpers/UserSessionPropertiesUseCase+Analytics.swift @@ -0,0 +1,33 @@ +// +// Copyright 2021 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 AnalyticsEvents + +extension UserSessionProperties.UseCase { + var analyticsName: AnalyticsEvent.UserProperties.FtueUseCaseSelection { + switch self { + case .personalMessaging: + return .PersonalMessaging + case .workMessaging: + return .WorkMessaging + case .communityMessaging: + return .CommunityMessaging + case .skipped: + return .Skip + } + } +} diff --git a/Riot/Modules/Analytics/PostHogAnalyticsClient.swift b/Riot/Modules/Analytics/PostHogAnalyticsClient.swift index 1c7172112..434ce978e 100644 --- a/Riot/Modules/Analytics/PostHogAnalyticsClient.swift +++ b/Riot/Modules/Analytics/PostHogAnalyticsClient.swift @@ -22,6 +22,9 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol { /// The PHGPostHog object used to report events. private var postHog: PHGPostHog? + /// Any user properties to be included with the next captured event. + private(set) var pendingUserProperties: AnalyticsEvent.UserProperties? + var isRunning: Bool { postHog?.enabled ?? false } func start() { @@ -36,11 +39,18 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol { } func identify(id: String) { - postHog?.identify(id) + if let userProperties = pendingUserProperties { + // As user properties overwrite old ones, compactMap the dictionary to avoid resetting any missing properties + postHog?.identify(id, properties: userProperties.properties.compactMapValues { $0 }) + pendingUserProperties = nil + } else { + postHog?.identify(id) + } } func reset() { postHog?.reset() + pendingUserProperties = nil } func stop() { @@ -55,11 +65,38 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol { } func capture(_ event: AnalyticsEventProtocol) { - postHog?.capture(event.eventName, properties: event.properties) + postHog?.capture(event.eventName, properties: attachUserProperties(to: event.properties)) } func screen(_ event: AnalyticsScreenProtocol) { - postHog?.screen(event.screenName.rawValue, properties: event.properties) + postHog?.screen(event.screenName.rawValue, properties: attachUserProperties(to: event.properties)) } + func updateUserProperties(_ userProperties: AnalyticsEvent.UserProperties) { + guard let pendingUserProperties = pendingUserProperties else { + pendingUserProperties = userProperties + return + } + + // Merge the updated user properties with the existing ones + self.pendingUserProperties = AnalyticsEvent.UserProperties(ftueUseCaseSelection: userProperties.ftueUseCaseSelection ?? pendingUserProperties.ftueUseCaseSelection, + numSpaces: userProperties.numSpaces ?? pendingUserProperties.numSpaces) + } + + // MARK: - Private + + /// Given a dictionary containing properties from an event, this method will return those properties + /// with any pending user properties included under the `$set` key. + /// - Parameter properties: A dictionary of properties from an event. + /// - Returns: The `properties` dictionary with any user properties included. + private func attachUserProperties(to properties: [String: Any]) -> [String: Any] { + guard isRunning, let userProperties = pendingUserProperties else { return properties } + + var properties = properties + + // As user properties overwrite old ones via $set, compactMap the dictionary to avoid resetting any missing properties + properties["$set"] = userProperties.properties.compactMapValues { $0 } + pendingUserProperties = nil + return properties + } } diff --git a/Riot/Modules/Application/AppAlertPresenter.swift b/Riot/Modules/Application/AppAlertPresenter.swift deleted file mode 100644 index f60a5ef34..000000000 --- a/Riot/Modules/Application/AppAlertPresenter.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright 2021 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 - -class AppAlertPresenter: AlertPresentable { - - // MARK: - Properties - - // swiftlint:disable weak_delegate - private let legacyAppDelegate: LegacyAppDelegate - // swiftlint:enable weak_delegate - - // MARK: - Setup - - init(legacyAppDelegate: LegacyAppDelegate) { - self.legacyAppDelegate = legacyAppDelegate - } - - // MARK: - Public - - func showError(_ error: Error, animated: Bool, completion: (() -> Void)?) { - // FIXME: Present an error on coordinator.toPresentable() - self.legacyAppDelegate.showError(asAlert: error) - } - - func show(title: String?, message: String?, animated: Bool, completion: (() -> Void)?) { - // FIXME: Present an error on coordinator.toPresentable() - self.legacyAppDelegate.showAlert(withTitle: title, message: message) - } -} diff --git a/Riot/Modules/Application/AppCoordinator.swift b/Riot/Modules/Application/AppCoordinator.swift index d60faf924..8f1b61d58 100755 --- a/Riot/Modules/Application/AppCoordinator.swift +++ b/Riot/Modules/Application/AppCoordinator.swift @@ -16,6 +16,9 @@ import Foundation import Intents +import MatrixSDK +import CommonKit +import UIKit #if DEBUG import FLEX @@ -46,7 +49,7 @@ final class AppCoordinator: NSObject, AppCoordinatorType { return AppNavigator(appCoordinator: self) }() - private weak var splitViewCoordinator: SplitViewCoordinatorType? + fileprivate weak var splitViewCoordinator: SplitViewCoordinatorType? fileprivate weak var sideMenuCoordinator: SideMenuCoordinatorType? private let userSessionsService: UserSessionsService @@ -79,6 +82,7 @@ final class AppCoordinator: NSObject, AppCoordinatorType { func start() { self.setupLogger() self.setupTheme() + self.excludeAllItemsFromBackup() // Setup navigation router store _ = NavigationRouterStore.shared @@ -129,6 +133,17 @@ final class AppCoordinator: NSObject, AppCoordinatorType { } } + private func excludeAllItemsFromBackup() { + let manager = FileManager.default + + // Individual files and directories created by the application or SDK are excluded case-by-case, + // but sometimes the lifecycle of a file is not directly controlled by the app (e.g. plists for + // UserDefaults). For that reason the app will always exclude all top-level directories as well + // as individual files. + manager.excludeAllUserDirectoriesFromBackup() + manager.excludeAllAppGroupDirectoriesFromBackup() + } + private func showAuthentication() { // TODO: Implement } @@ -275,8 +290,6 @@ fileprivate class AppNavigator: AppNavigatorProtocol { private unowned let appCoordinator: AppCoordinator - let alert: AlertPresentable - lazy var sideMenu: SideMenuPresentable = { guard let sideMenuCoordinator = appCoordinator.sideMenuCoordinator else { fatalError("sideMenuCoordinator is not initialized") @@ -285,11 +298,22 @@ fileprivate class AppNavigator: AppNavigatorProtocol { return SideMenuPresenter(sideMenuCoordinator: sideMenuCoordinator) }() + private var appNavigationVC: UINavigationController { + guard + let splitVC = appCoordinator.splitViewCoordinator?.toPresentable() as? UISplitViewController, + // Picking out the first view controller currently works only on iPhones, not iPads + let navigationVC = splitVC.viewControllers.first as? UINavigationController + else { + MXLog.error("[AppNavigator] Missing root split view controller") + return UINavigationController() + } + return navigationVC + } + // MARK: - Setup init(appCoordinator: AppCoordinator) { self.appCoordinator = appCoordinator - self.alert = AppAlertPresenter(legacyAppDelegate: appCoordinator.legacyAppDelegate) } // MARK: - Public @@ -297,4 +321,16 @@ fileprivate class AppNavigator: AppNavigatorProtocol { func navigate(to destination: AppNavigatorDestination) { self.appCoordinator.navigate(to: destination) } + + func addLoadingActivity() -> Activity { + let presenter = ActivityIndicatorToastPresenter( + text: VectorL10n.roomParticipantsSecurityLoading, + navigationController: appNavigationVC + ) + let request = ActivityRequest( + presenter: presenter, + dismissal: .manual + ) + return ActivityCenter.shared.add(request) + } } diff --git a/Riot/Modules/Application/AppNavigator.swift b/Riot/Modules/Application/AppNavigator.swift index d3b7eddf4..c33b572f4 100644 --- a/Riot/Modules/Application/AppNavigator.swift +++ b/Riot/Modules/Application/AppNavigator.swift @@ -15,6 +15,7 @@ // import Foundation +import CommonKit /// AppNavigatorProtocol abstract a navigator at app level. /// It enables to perform the navigation within the global app scope (open the side menu, open a room and so on) @@ -22,9 +23,16 @@ import Foundation protocol AppNavigatorProtocol { var sideMenu: SideMenuPresentable { get } - var alert: AlertPresentable { get } /// Navigate to a destination screen or a state /// Do not use protocol with associatedtype for the moment like presented here https://www.swiftbysundell.com/articles/navigation-in-swift/#where-to-navigator use a separate enum func navigate(to destination: AppNavigatorDestination) + + /// Add loading activity to an app-wide queue of other activitie + /// + /// If the queue is empty, the activity will be displayed immediately, otherwise it will be pending + /// until the previously added activities have completed / been cancelled. + /// + /// To remove an activity indicator, cancel or deallocate the returned `Activity` + func addLoadingActivity() -> Activity } diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index a462a4bc0..1c74e5ede 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -232,6 +232,17 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni */ @property (nonatomic, assign, getter=isRoomListDataReady) BOOL roomListDataReady; +/** + An observer token for `RecentsViewControllerDataReadyNotification`s notifications. + */ +@property (nonatomic, nullable) id roomListDataReadyObserver; + +/** + An optional completion block that will be called when a `RecentsViewControllerDataReadyNotification` + is observed during app launch. + */ +@property (nonatomic, copy, nullable) void (^roomListDataReadyCompletion)(void); + /** Flag to indicate whether a cache clear is being performed. */ @@ -838,28 +849,28 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni } // Enable error notifications - isErrorNotificationSuspended = NO; + self->isErrorNotificationSuspended = NO; - if (noCallSupportAlert) + if (self->noCallSupportAlert) { MXLogDebug(@"[AppDelegate] restoreInitialDisplay: keep visible noCall support alert"); - [self showNotificationAlert:noCallSupportAlert]; + [self showNotificationAlert:self->noCallSupportAlert]; } - else if (cryptoDataCorruptedAlert) + else if (self->cryptoDataCorruptedAlert) { MXLogDebug(@"[AppDelegate] restoreInitialDisplay: keep visible log in again"); - [self showNotificationAlert:cryptoDataCorruptedAlert]; + [self showNotificationAlert:self->cryptoDataCorruptedAlert]; } - else if (wrongBackupVersionAlert) + else if (self->wrongBackupVersionAlert) { MXLogDebug(@"[AppDelegate] restoreInitialDisplay: keep visible wrongBackupVersionAlert"); - [self showNotificationAlert:wrongBackupVersionAlert]; + [self showNotificationAlert:self->wrongBackupVersionAlert]; } // Check whether an error notification is pending - else if (_errorNotification) + else if (self->_errorNotification) { - [self showNotificationAlert:_errorNotification]; + [self showNotificationAlert:self->_errorNotification]; } }]; @@ -876,10 +887,10 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni } // Enable error notification (Check whether a notification is pending) - isErrorNotificationSuspended = NO; - if (_errorNotification) + self->isErrorNotificationSuspended = NO; + if (self->_errorNotification) { - [self showNotificationAlert:_errorNotification]; + [self showNotificationAlert:self->_errorNotification]; } }]; } @@ -1429,9 +1440,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni else { void(^findRoom)(void) = ^{ - if ([_masterTabBarController.selectedViewController conformsToProtocol:@protocol(MXKViewControllerActivityHandling)]) + if ([self->_masterTabBarController.selectedViewController conformsToProtocol:@protocol(MXKViewControllerActivityHandling)]) { - UIViewController *homeViewController = (UIViewController*)_masterTabBarController.selectedViewController; + UIViewController *homeViewController = (UIViewController*)self->_masterTabBarController.selectedViewController; [homeViewController startActivityIndicator]; @@ -1439,7 +1450,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni { // The alias may be not part of user's rooms states // Ask the HS to resolve the room alias into a room id and then retry - universalLinkFragmentPending = fragment; + self->universalLinkFragmentPending = fragment; MXKAccount* account = accountManager.activeAccounts.firstObject; [account.mxSession.matrixRestClient roomIDForRoomAlias:roomIdOrAlias success:^(NSString *roomId) { @@ -1447,7 +1458,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni [homeViewController stopActivityIndicator]; // Check that 'fragment' has not been cancelled - if ([universalLinkFragmentPending isEqualToString:fragment]) + if ([self->universalLinkFragmentPending isEqualToString:fragment]) { // Retry opening the link but with the returned room id NSString *newUniversalLinkFragment = @@ -1467,7 +1478,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni if (![newUniversalLinkFragment isEqualToString:fragment]) { - universalLinkFragmentPendingRoomAlias = @{roomId: roomIdOrAlias}; + self->universalLinkFragmentPendingRoomAlias = @{roomId: roomIdOrAlias}; UniversalLinkParameters *newParameters = [[UniversalLinkParameters alloc] initWithFragment:newUniversalLinkFragment universalLinkURL:universalLinkURL presentationParameters:presentationParameters]; @@ -1498,12 +1509,12 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni MXKAccount* account = accountManager.activeAccounts.firstObject; MXLogDebug(@"[AppDelegate] Universal link: Need to wait for the session to be sync'ed and running"); - universalLinkFragmentPending = fragment; + self->universalLinkFragmentPending = fragment; - universalLinkWaitingObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionStateDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull notif) { + self->universalLinkWaitingObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionStateDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull notif) { // Check that 'fragment' has not been cancelled - if ([universalLinkFragmentPending isEqualToString:fragment]) + if ([self->universalLinkFragmentPending isEqualToString:fragment]) { // Check whether the concerned session is the associated one if (notif.object == account.mxSession && account.mxSession.state == MXSessionStateRunning) @@ -1582,7 +1593,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni universalLinkWaitingObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidAddAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { // Check that 'fragment' has not been cancelled - if ([universalLinkFragmentPending isEqualToString:fragment]) + if ([self->universalLinkFragmentPending isEqualToString:fragment]) { MXLogDebug(@"[AppDelegate] Universal link: The user is now logged in. Retry the link"); [self handleUniversalLinkWithParameters:universalLinkParameters]; @@ -1647,7 +1658,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni universalLinkWaitingObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidAddAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { // Check that 'fragment' has not been cancelled - if ([universalLinkFragmentPending isEqualToString:fragment]) + if ([self->universalLinkFragmentPending isEqualToString:fragment]) { MXLogDebug(@"[AppDelegate] Universal link: The user is now logged in. Retry the link"); [self handleUniversalLinkWithParameters:universalLinkParameters]; @@ -2319,7 +2330,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni } // Return to authentication screen - [_masterTabBarController showOnboardingFlow]; + [self->_masterTabBarController showOnboardingFlow]; // Note: Keep App settings // But enforce usage of member lazy loading @@ -2374,6 +2385,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni case MXSessionStateInitialised: case MXSessionStateBackgroundSyncInProgress: self.roomListDataReady = NO; + [self listenForRoomListDataReady]; isLaunching = YES; break; case MXSessionStateStoreDataReady: @@ -2398,6 +2410,12 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni MXLogDebug(@"[AppDelegate] handleAppState: isLaunching: %@", isLaunching ? @"YES" : @"NO"); + if (self.masterTabBarController.isOnboardingInProgress) + { + MXLogDebug(@"[AppDelegate] handleAppState: Skipping LaunchLoadingView due to Onboarding."); + return; + } + if (isLaunching) { MXLogDebug(@"[AppDelegate] handleAppState: LaunchLoadingView"); @@ -2411,7 +2429,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni return; } - [self ensureRoomListDataReadyWithCompletion:^{ + void (^finishAppLaunch)(void) = ^{ [self hideLaunchAnimation]; if (self.setPinCoordinatorBridgePresenter) @@ -2443,7 +2461,17 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Enable listening of incoming key verification requests [self enableIncomingKeyVerificationObserver:mainSession]; } - }]; + }; + + if (self.isRoomListDataReady) + { + finishAppLaunch(); + } + else + { + // An observer has been set in didFinishLaunching that will call the stored block when ready + self.roomListDataReadyCompletion = finishAppLaunch; + } } } @@ -2599,29 +2627,31 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni [self handleAppState]; } -/** - Ensures room list data is ready. - - @param completion Completion block to be called when it's ready. Not dispatched in case the data is already ready. - */ -- (void)ensureRoomListDataReadyWithCompletion:(void(^)(void))completion +- (void)listenForRoomListDataReady { - if (self.isRoomListDataReady) + if (self.roomListDataReadyObserver) { - completion(); - } - else - { - NSNotificationCenter * __weak notificationCenter = [NSNotificationCenter defaultCenter]; - __block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:RecentsViewControllerDataReadyNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification * _Nonnull notification) { - [notificationCenter removeObserver:observer]; - self.roomListDataReady = YES; - completion(); - }]; + return; } + + MXWeakify(self); + NSNotificationCenter * __weak notificationCenter = [NSNotificationCenter defaultCenter]; + self.roomListDataReadyObserver = [[NSNotificationCenter defaultCenter] addObserverForName:RecentsViewControllerDataReadyNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification * _Nonnull notification) { + MXStrongifyAndReturnIfNil(self); + + [notificationCenter removeObserver:self.roomListDataReadyObserver]; + self.roomListDataReady = YES; + self.roomListDataReadyObserver = nil; + + if (self.roomListDataReadyCompletion) + { + self.roomListDataReadyCompletion(); + self.roomListDataReadyCompletion = nil; + } + }]; } #pragma mark - @@ -2730,7 +2760,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni if ([[ruleAction.parameters valueForKey:@"set_tweak"] isEqualToString:@"sound"]) { // Play message sound - AudioServicesPlaySystemSound(_messageSound); + AudioServicesPlaySystemSound(self->_messageSound); } } } @@ -3425,9 +3455,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni { case MXEventTypeCallInvite: { - if (noCallSupportAlert) + if (self->noCallSupportAlert) { - [noCallSupportAlert dismissViewControllerAnimated:NO completion:nil]; + [self->noCallSupportAlert dismissViewControllerAnimated:NO completion:nil]; } MXCallInviteEventContent *callInviteEventContent = [MXCallInviteEventContent modelFromJSON:event.content]; @@ -3449,15 +3479,15 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni NSString *message = [VectorL10n noVoip:callerDisplayname :appDisplayName]; - noCallSupportAlert = [UIAlertController alertControllerWithTitle:[VectorL10n noVoipTitle] - message:message - preferredStyle:UIAlertControllerStyleAlert]; + self->noCallSupportAlert = [UIAlertController alertControllerWithTitle:[VectorL10n noVoipTitle] + message:message + preferredStyle:UIAlertControllerStyleAlert]; __weak typeof(self) weakSelf = self; - [noCallSupportAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ignore] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self->noCallSupportAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ignore] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { if (weakSelf) { @@ -3467,9 +3497,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni }]]; - [noCallSupportAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n rejectCall] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self->noCallSupportAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n rejectCall] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { // Reject the call by sending the hangup event NSDictionary *content = @{ @@ -3490,7 +3520,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni }]]; - [self showNotificationAlert:noCallSupportAlert]; + [self showNotificationAlert:self->noCallSupportAlert]; break; } @@ -3498,10 +3528,10 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni case MXEventTypeCallHangup: case MXEventTypeCallReject: // The call has ended. The alert is no more needed. - if (noCallSupportAlert) + if (self->noCallSupportAlert) { - [noCallSupportAlert dismissViewControllerAnimated:YES completion:nil]; - noCallSupportAlert = nil; + [self->noCallSupportAlert dismissViewControllerAnimated:YES completion:nil]; + self->noCallSupportAlert = nil; } break; diff --git a/Riot/Modules/Authentication/AuthenticationCoordinator.swift b/Riot/Modules/Authentication/AuthenticationCoordinator.swift index a310cdb1a..865161941 100644 --- a/Riot/Modules/Authentication/AuthenticationCoordinator.swift +++ b/Riot/Modules/Authentication/AuthenticationCoordinator.swift @@ -16,7 +16,6 @@ limitations under the License. */ -import Foundation import UIKit /// A coordinator that handles authentication, verification and setting a PIN. @@ -26,17 +25,27 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc // MARK: Private + private let navigationRouter: NavigationRouterType + private let authenticationViewController: AuthenticationViewController + private let crossSigningService = CrossSigningService() + + /// The password entered, for use when setting up cross-signing. + private var password: String? + /// The session created when successfully authenticated. + private var session: MXSession? // MARK: Public // Must be used only internally var childCoordinators: [Coordinator] = [] - var completion: (() -> Void)? + var completion: ((AuthenticationCoordinatorResult) -> Void)? // MARK: - Setup - override init() { + init(parameters: AuthenticationCoordinatorParameters) { + self.navigationRouter = parameters.navigationRouter + let authenticationViewController = AuthenticationViewController() self.authenticationViewController = authenticationViewController @@ -62,6 +71,10 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc authenticationViewController.authType = authenticationType } + func showCustomServer() { + authenticationViewController.setCustomServerFieldsVisible(true) + } + func update(externalRegistrationParameters: [AnyHashable: Any]) { authenticationViewController.externalRegistrationParameters = externalRegistrationParameters } @@ -77,11 +90,157 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc func continueSSOLogin(withToken loginToken: String, transactionID: String) -> Bool { authenticationViewController.continueSSOLogin(withToken: loginToken, txnId: transactionID) } + + // MARK: - Private + + private func showLoadingAnimation() { + let loadingViewController = LaunchLoadingViewController() + loadingViewController.modalPresentationStyle = .fullScreen + + // Replace the navigation stack with the loading animation + // as there is nothing to navigate back to. + navigationRouter.setRootModule(loadingViewController) + } + + private func presentCompleteSecurity(with session: MXSession) { + let isNewSignIn = true + let keyVerificationCoordinator = KeyVerificationCoordinator(session: session, flow: .completeSecurity(isNewSignIn)) + + keyVerificationCoordinator.delegate = self + let presentable = keyVerificationCoordinator.toPresentable() + presentable.presentationController?.delegate = self + navigationRouter.present(presentable, animated: true) + keyVerificationCoordinator.start() + add(childCoordinator: keyVerificationCoordinator) + } + + private func authenticationDidComplete() { + completion?(.didComplete(authenticationViewController.authType)) + } + + private func registerSessionStateChangeNotification(for session: MXSession) { + NotificationCenter.default.addObserver(self, selector: #selector(sessionStateDidChange), name: .mxSessionStateDidChange, object: session) + } + + private func unregisterSessionStateChangeNotification() { + NotificationCenter.default.removeObserver(self, name: .mxSessionStateDidChange, object: nil) + } + + @objc private func sessionStateDidChange(_ notification: Notification) { + guard let session = notification.object as? MXSession else { + MXLog.error("[AuthenticationCoordinator] sessionStateDidChange: Missing session in the notification") + return + } + + if session.state == .storeDataReady { + if let crypto = session.crypto, crypto.crossSigning != nil { + // Do not make key share requests while the "Complete security" is not complete. + // If the device is self-verified, the SDK will restore the existing key backup. + // Then, it will re-enable outgoing key share requests + crypto.setOutgoingKeyRequestsEnabled(false, onComplete: nil) + } + } else if session.state == .running { + unregisterSessionStateChangeNotification() + + if let crypto = session.crypto, let crossSigning = crypto.crossSigning { + crossSigning.refreshState { [weak self] stateUpdated in + guard let self = self else { return } + + MXLog.debug("[AuthenticationCoordinator] sessionStateDidChange: crossSigning.state: \(crossSigning.state)") + + switch crossSigning.state { + case .notBootstrapped: + // TODO: This is still not sure we want to disable the automatic cross-signing bootstrap + // if the admin disabled e2e by default. + // Do like riot-web for the moment + if session.vc_homeserverConfiguration().isE2EEByDefaultEnabled { + // Bootstrap cross-signing on user's account + // We do it for both registration and new login as long as cross-signing does not exist yet + if let password = self.password, !password.isEmpty { + MXLog.debug("[AuthenticationCoordinator] sessionStateDidChange: Bootstrap with password") + + crossSigning.setup(withPassword: password) { + MXLog.debug("[AuthenticationCoordinator] sessionStateDidChange: Bootstrap succeeded") + self.authenticationDidComplete() + } failure: { error in + MXLog.error("[AuthenticationCoordinator] sessionStateDidChange: Bootstrap failed. Error: \(error)") + crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil) + self.authenticationDidComplete() + } + } else { + // Try to setup cross-signing without authentication parameters in case if a grace period is enabled + self.crossSigningService.setupCrossSigningWithoutAuthentication(for: session) { + MXLog.debug("[AuthenticationCoordinator] sessionStateDidChange: Bootstrap succeeded without credentials") + self.authenticationDidComplete() + } failure: { error in + MXLog.error("[AuthenticationCoordinator] sessionStateDidChange: Do not know how to bootstrap cross-signing. Skip it.") + crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil) + self.authenticationDidComplete() + } + } + } else { + crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil) + self.authenticationDidComplete() + } + case .crossSigningExists: + MXLog.debug("[AuthenticationCoordinator] sessionStateDidChange: Complete security") + self.presentCompleteSecurity(with: session) + default: + MXLog.debug("[AuthenticationCoordinator] sessionStateDidChange: Nothing to do") + + crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil) + self.authenticationDidComplete() + } + } failure: { [weak self] error in + MXLog.error("[AuthenticationCoordinator] sessionStateDidChange: Fail to refresh crypto state with error: \(error)") + crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil) + self?.authenticationDidComplete() + } + } else { + authenticationDidComplete() + } + } + } } // MARK: - AuthenticationViewControllerDelegate extension AuthenticationCoordinator: AuthenticationViewControllerDelegate { - func authenticationViewControllerDidDismiss(_ authenticationViewController: AuthenticationViewController!) { - completion?() + func authenticationViewController(_ authenticationViewController: AuthenticationViewController!, didLoginWith session: MXSession!, andPassword password: String!) { + registerSessionStateChangeNotification(for: session) + + self.session = session + self.password = password + + self.showLoadingAnimation() + completion?(.didLogin(session)) + } +} + +// MARK: - KeyVerificationCoordinatorDelegate +extension AuthenticationCoordinator: KeyVerificationCoordinatorDelegate { + func keyVerificationCoordinatorDidComplete(_ coordinator: KeyVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) { + if let crypto = session?.crypto, + !crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled { + MXLog.debug("[AuthenticationCoordinator][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys") + crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil) + } + + navigationRouter.dismissModule(animated: true) { [weak self] in + self?.authenticationDidComplete() + } + } + + func keyVerificationCoordinatorDidCancel(_ coordinator: KeyVerificationCoordinatorType) { + navigationRouter.dismissModule(animated: true) { [weak self] in + self?.authenticationDidComplete() + } + } +} + +// MARK: - UIAdaptivePresentationControllerDelegate +extension AuthenticationCoordinator: UIAdaptivePresentationControllerDelegate { + func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool { + // Prevent Key Verification from using swipe to dismiss + return false } } diff --git a/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift b/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift index 7037f9d1f..c024cddbe 100644 --- a/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift +++ b/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift @@ -18,13 +18,28 @@ import Foundation +struct AuthenticationCoordinatorParameters { + let navigationRouter: NavigationRouterType +} + +enum AuthenticationCoordinatorResult { + /// The user has authenticated but key verification is yet to happen. The session value is + /// for a fresh session that still needs to load, sync etc before being ready. + case didLogin(MXSession) + /// All of the required authentication steps including key verification is complete. + case didComplete(MXKAuthenticationType) +} + /// `AuthenticationCoordinatorProtocol` is a protocol describing a Coordinator that handle's the authentication navigation flow. protocol AuthenticationCoordinatorProtocol: Coordinator, Presentable { - var completion: (() -> Void)? { get set } + var completion: ((AuthenticationCoordinatorResult) -> Void)? { get set } /// Update the screen to display registration or login. func update(authenticationType: MXKAuthenticationType) + /// Enable the custom server checkbox to allow the user to enter a homeserver URL. + func showCustomServer() + /// Force a registration process based on a predefined set of parameters from a server provisioning link. /// For more information see `AuthenticationViewController.externalRegistrationParameters`. func update(externalRegistrationParameters: [AnyHashable: Any]) diff --git a/Riot/Modules/Authentication/AuthenticationViewController.h b/Riot/Modules/Authentication/AuthenticationViewController.h index 894ea8b45..dd76303a4 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.h +++ b/Riot/Modules/Authentication/AuthenticationViewController.h @@ -37,7 +37,6 @@ @property (weak, nonatomic) IBOutlet UIButton *customServersTickButton; @property (weak, nonatomic) IBOutlet UIView *customServersContainer; @property (weak, nonatomic) IBOutlet UIView *homeServerContainer; -@property (weak, nonatomic) IBOutlet UIView *identityServerContainer; @property (weak, nonatomic) IBOutlet UIView *homeServerSeparator; @property (weak, nonatomic) IBOutlet UIView *identityServerSeparator; @@ -54,11 +53,17 @@ /// returns YES if the SSO login can be continued. - (BOOL)continueSSOLoginWithToken:(NSString*)loginToken txnId:(NSString*)txnId; +/// Show or hide the custom server textfields. +/// @param isVisible YES to show, NO to hide. +- (void)setCustomServerFieldsVisible:(BOOL)isVisible; + @end @protocol AuthenticationViewControllerDelegate -- (void)authenticationViewControllerDidDismiss:(AuthenticationViewController *)authenticationViewController; +- (void)authenticationViewController:(AuthenticationViewController *)authenticationViewController + didLoginWithSession:(MXSession *)session + andPassword:(NSString *)password; @end; diff --git a/Riot/Modules/Authentication/AuthenticationViewController.m b/Riot/Modules/Authentication/AuthenticationViewController.m index 4f76a029b..425fb4da6 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.m +++ b/Riot/Modules/Authentication/AuthenticationViewController.m @@ -28,7 +28,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; -@interface AuthenticationViewController () @@ -63,7 +63,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; } @property (nonatomic, readonly) BOOL isIdentityServerConfigured; -@property (nonatomic, strong) KeyVerificationCoordinatorBridgePresenter *keyVerificationCoordinatorBridgePresenter; @property (nonatomic, strong) SetPinCoordinatorBridgePresenter *setPinCoordinatorBridgePresenter; @property (nonatomic, strong) KeyboardAvoider *keyboardAvoider; @@ -78,8 +77,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; // Current SSO transaction id used to identify and validate the SSO authentication callback @property (nonatomic, strong) NSString *ssoCallbackTxnId; -@property (nonatomic, strong) CrossSigningService *crossSigningService; - @property (nonatomic, getter = isFirstViewAppearing) BOOL firstViewAppearing; @property (nonatomic, strong) MXKErrorAlertPresentation *errorPresenter; @@ -118,7 +115,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; _firstViewAppearing = YES; - self.crossSigningService = [CrossSigningService new]; self.errorPresenter = [MXKErrorAlertPresentation new]; } @@ -136,7 +132,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; self.defaultIdentityServerUrl = RiotSettings.shared.identityServerUrlString; - self.welcomeImageView.image = [UIImage imageNamed:@"horizontal_logo"]; + self.welcomeImageView.image = AssetSharedImages.horizontalLogo.image; [self.submitButton.layer setCornerRadius:5]; self.submitButton.clipsToBounds = YES; @@ -150,8 +146,8 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; [self.skipButton setTitle:[VectorL10n authSkip] forState:UIControlStateHighlighted]; self.skipButton.enabled = YES; - [self.customServersTickButton setImage:[UIImage imageNamed:@"selection_untick"] forState:UIControlStateNormal]; - [self.customServersTickButton setImage:[UIImage imageNamed:@"selection_untick"] forState:UIControlStateHighlighted]; + [self.customServersTickButton setImage:AssetImages.selectionUntick.image forState:UIControlStateNormal]; + [self.customServersTickButton setImage:AssetImages.selectionUntick.image forState:UIControlStateHighlighted]; if (!BuildSettings.authScreenShowRegister) { @@ -160,7 +156,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; } self.serverOptionsContainer.hidden = !BuildSettings.authScreenShowCustomServerOptions; - [self hideCustomServers:YES]; + [self setCustomServerFieldsVisible:NO]; // Soft logout section self.softLogoutClearDataButton.layer.cornerRadius = 5; @@ -214,7 +210,8 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; - (void)userInterfaceThemeDidChange { - [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar + withModernScrollEdgesAppearance:YES]; self.view.backgroundColor = ThemeService.shared.theme.backgroundColor; @@ -317,11 +314,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; { self.firstViewAppearing = NO; } - - if (self.keyVerificationCoordinatorBridgePresenter) - { - return; - } // Verify that the app does not show the authentication screen whereas // the user has already logged in. @@ -378,7 +370,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; [self.authenticationActivityIndicator removeObserver:self forKeyPath:@"hidden"]; autoDiscovery = nil; - _keyVerificationCoordinatorBridgePresenter = nil; _keyboardAvoider = nil; } @@ -556,30 +547,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; } } -- (void)presentCompleteSecurityWithSession:(MXSession*)session -{ - KeyVerificationCoordinatorBridgePresenter *keyVerificationCoordinatorBridgePresenter = [[KeyVerificationCoordinatorBridgePresenter alloc] initWithSession:session]; - keyVerificationCoordinatorBridgePresenter.delegate = self; - - [keyVerificationCoordinatorBridgePresenter presentCompleteSecurityFrom:self isNewSignIn:YES animated:YES]; - - self.keyVerificationCoordinatorBridgePresenter = keyVerificationCoordinatorBridgePresenter; -} - -- (void)dismiss -{ - self.userInteractionEnabled = YES; - [self.authenticationActivityIndicator stopAnimating]; - - // Dismiss (key verification) on successful login - [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; - - if (self.authVCDelegate) - { - [self.authVCDelegate authenticationViewControllerDidDismiss:self]; - } -} - - (BOOL)continueSSOLoginWithToken:(NSString*)loginToken txnId:(NSString*)txnId { // Check if transaction id is the same as expected @@ -887,7 +854,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; { if (sender == self.customServersTickButton) { - [self hideCustomServers:!self.customServersContainer.hidden]; + [self setCustomServerFieldsVisible:self.customServersContainer.hidden]; } else if (sender == self.forgotPasswordButton) { @@ -1235,14 +1202,14 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; [self.view layoutIfNeeded]; } -- (void)hideCustomServers:(BOOL)hidden +- (void)setCustomServerFieldsVisible:(BOOL)isVisible { - if (self.customServersContainer.isHidden == hidden) + if (self.customServersContainer.isHidden != isVisible) { return; } - if (hidden) + if (!isVisible) { [self.homeServerTextField resignFirstResponder]; [self.identityServerTextField resignFirstResponder]; @@ -1272,7 +1239,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; [self setHomeServerTextFieldText:self.defaultHomeServerUrl]; [self setIdentityServerTextFieldText:self.defaultIdentityServerUrl]; - [self.customServersTickButton setImage:[UIImage imageNamed:@"selection_untick"] forState:UIControlStateNormal]; + [self.customServersTickButton setImage:AssetImages.selectionUntick.image forState:UIControlStateNormal]; self.customServersContainer.hidden = YES; // Refresh content view height @@ -1296,7 +1263,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; [self setIdentityServerTextFieldText:customIdentityServerURL]; } - [self.customServersTickButton setImage:[UIImage imageNamed:@"selection_tick"] forState:UIControlStateNormal]; + [self.customServersTickButton setImage:AssetImages.selectionTick.image forState:UIControlStateNormal]; self.customServersContainer.hidden = NO; // Refresh content view height @@ -1360,7 +1327,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; [self.authenticationActivityIndicator startAnimating]; // Hide the custom server details in order to save customized inputs - [self hideCustomServers:YES]; + [self setCustomServerFieldsVisible:NO]; MXKAccount *account = [[MXKAccountManager sharedManager] accountForUserId:userId]; MXSession *session = account.mxSession; @@ -1376,119 +1343,8 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; }]; } - // Wait for session change to present complete security screen if needed - [self registerSessionStateChangeNotificationForSession:session]; -} - -- (void)registerSessionStateChangeNotificationForSession:(MXSession*)session -{ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sessionStateDidChangeNotification:) name:kMXSessionStateDidChangeNotification object:session]; -} - -- (void)unregisterSessionStateChangeNotification -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionStateDidChangeNotification object:nil]; -} - -- (void)sessionStateDidChangeNotification:(NSNotification*)notification -{ - MXSession *session = (MXSession*)notification.object; - - if (session.state == MXSessionStateStoreDataReady) - { - if (session.crypto.crossSigning) - { - // Do not make key share requests while the "Complete security" is not complete. - // If the device is self-verified, the SDK will restore the existing key backup. - // Then, it will re-enable outgoing key share requests - [session.crypto setOutgoingKeyRequestsEnabled:NO onComplete:nil]; - } - } - else if (session.state == MXSessionStateRunning) - { - [self unregisterSessionStateChangeNotification]; - - if (session.crypto.crossSigning) - { - [session.crypto.crossSigning refreshStateWithSuccess:^(BOOL stateUpdated) { - - MXLogDebug(@"[AuthenticationVC] sessionStateDidChange: crossSigning.state: %@", @(session.crypto.crossSigning.state)); - - switch (session.crypto.crossSigning.state) - { - case MXCrossSigningStateNotBootstrapped: - { - // TODO: This is still not sure we want to disable the automatic cross-signing bootstrap - // if the admin disabled e2e by default. - // Do like riot-web for the moment - if ([session vc_homeserverConfiguration].isE2EEByDefaultEnabled) - { - // Bootstrap cross-signing on user's account - // We do it for both registration and new login as long as cross-signing does not exist yet - if (self.authInputsView.password.length) - { - MXLogDebug(@"[AuthenticationVC] sessionStateDidChange: Bootstrap with password"); - - [session.crypto.crossSigning setupWithPassword:self.authInputsView.password success:^{ - MXLogDebug(@"[AuthenticationVC] sessionStateDidChange: Bootstrap succeeded"); - [self dismiss]; - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[AuthenticationVC] sessionStateDidChange: Bootstrap failed. Error: %@", error); - [session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; - [self dismiss]; - }]; - } - else - { - // Try to setup cross-signing without authentication parameters in case if a grace period is enabled - [self.crossSigningService setupCrossSigningWithoutAuthenticationFor:session success:^{ - MXLogDebug(@"[AuthenticationVC] sessionStateDidChange: Bootstrap succeeded without credentials"); - [self dismiss]; - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[AuthenticationVC] sessionStateDidChange: Do not know how to bootstrap cross-signing. Skip it."); - [session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; - [self dismiss]; - }]; - } - } - else - { - [session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; - [self dismiss]; - } - break; - } - case MXCrossSigningStateCrossSigningExists: - { - MXLogDebug(@"[AuthenticationVC] sessionStateDidChange: Complete security"); - - // Ask the user to verify this session - self.userInteractionEnabled = YES; - [self.authenticationActivityIndicator stopAnimating]; - - [self presentCompleteSecurityWithSession:session]; - break; - } - - default: - MXLogDebug(@"[AuthenticationVC] sessionStateDidChange: Nothing to do"); - - [session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; - [self dismiss]; - break; - } - - } failure:^(NSError * _Nonnull error) { - MXLogDebug(@"[AuthenticationVC] sessionStateDidChange: Fail to refresh crypto state with error: %@", error); - [session.crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; - [self dismiss]; - }]; - } - else - { - [self dismiss]; - } - } + // Ask the coordinator to show the loading spinner whilst waiting. + [self.authVCDelegate authenticationViewController:self didLoginWithSession:session andPassword:self.authInputsView.password]; } #pragma mark - MXKAuthInputsViewDelegate @@ -1585,7 +1441,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; { // wellKnown matches with application default servers // Hide custom servers - [self hideCustomServers:YES]; + [self setCustomServerFieldsVisible:NO]; } else { @@ -1617,25 +1473,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; } // And show custom servers - [self hideCustomServers:NO]; -} - -#pragma mark - KeyVerificationCoordinatorBridgePresenterDelegate - -- (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId -{ - MXCrypto *crypto = coordinatorBridgePresenter.session.crypto; - if (!crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled) - { - MXLogDebug(@"[AuthenticationVC][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys"); - [crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil]; - } - [self dismiss]; -} - -- (void)keyVerificationCoordinatorBridgePresenterDelegateDidCancel:(KeyVerificationCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter -{ - [self dismiss]; + [self setCustomServerFieldsVisible:YES]; } #pragma mark - SetPinCoordinatorBridgePresenterDelegate diff --git a/Riot/Modules/Authentication/Views/AuthInputsView.m b/Riot/Modules/Authentication/Views/AuthInputsView.m index 43efbce8b..ea0bc4741 100644 --- a/Riot/Modules/Authentication/Views/AuthInputsView.m +++ b/Riot/Modules/Authentication/Views/AuthInputsView.m @@ -431,7 +431,7 @@ style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - inputsAlert = nil; + self->inputsAlert = nil; }]]; @@ -551,10 +551,10 @@ } // Check whether a second 3pid is available - _isThirdPartyIdentifierPending = (!self.emailContainer.isHidden && self.emailTextField.text.length && ![self isFlowCompleted:kMXLoginFlowTypeEmailIdentity]); + self->_isThirdPartyIdentifierPending = (!self.emailContainer.isHidden && self.emailTextField.text.length && ![self isFlowCompleted:kMXLoginFlowTypeEmailIdentity]); // Launch msisdn validation - NSString *e164 = [[NBPhoneNumberUtil sharedInstance] format:nbPhoneNumber numberFormat:NBEPhoneNumberFormatE164 error:nil]; + NSString *e164 = [[NBPhoneNumberUtil sharedInstance] format:self->nbPhoneNumber numberFormat:NBEPhoneNumberFormatE164 error:nil]; NSString *msisdn; if ([e164 hasPrefix:@"+"]) { @@ -564,12 +564,12 @@ { msisdn = [e164 substringFromIndex:2]; } - submittedMSISDN = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumMSISDN andAddress:msisdn]; - - [submittedMSISDN requestValidationTokenWithMatrixRestClient:restClient - isDuringRegistration:YES - nextLink:nil - success:^ + self->submittedMSISDN = [[MXK3PID alloc] initWithMedium:kMX3PIDMediumMSISDN andAddress:msisdn]; + + [self->submittedMSISDN requestValidationTokenWithMatrixRestClient:restClient + isDuringRegistration:YES + nextLink:nil + success:^ { [self showValidationMSISDNDialogToPrepareParameters:callback]; @@ -763,7 +763,7 @@ { NSDictionary *parameters = @{ @"auth": @{ - @"session":currentSession.session, + @"session": self->currentSession.session, @"response": response, @"type": kMXLoginFlowTypeRecaptcha }, @@ -870,7 +870,7 @@ { // We finalize here a registration triggered from external inputs. All the required data are handled by the session id NSDictionary *parameters = @{ - @"auth": @{@"session": currentSession.session, @"response": response, @"type": kMXLoginFlowTypeRecaptcha}, + @"auth": @{@"session": self->currentSession.session, @"response": response, @"type": kMXLoginFlowTypeRecaptcha}, }; callback (parameters, nil); } @@ -1322,7 +1322,7 @@ [phoneNumberPickerNavigationController pushViewController:phoneNumberCountryPicker animated:NO]; - UIBarButtonItem *leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"back_icon"] style:UIBarButtonItemStylePlain target:self action:@selector(dismissCountryPicker)]; + UIBarButtonItem *leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:AssetImages.backIcon.image style:UIBarButtonItemStylePlain target:self action:@selector(dismissCountryPicker)]; phoneNumberCountryPicker.navigationItem.leftBarButtonItem = leftBarButtonItem; [self.delegate authInputsView:self presentViewController:phoneNumberPickerNavigationController animated:YES]; @@ -1705,7 +1705,7 @@ if (weakSelf) { typeof(self) self = weakSelf; - UITextField *textField = [inputsAlert textFields].firstObject; + UITextField *textField = [self->inputsAlert textFields].firstObject; NSString *smsCode = textField.text; self->inputsAlert = nil; @@ -1776,9 +1776,9 @@ self->inputsAlert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert]; - [inputsAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [self->inputsAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok] + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { if (weakSelf) { diff --git a/Riot/Modules/Authentication/Views/ForgotPasswordInputsView.m b/Riot/Modules/Authentication/Views/ForgotPasswordInputsView.m index 9173043f7..386df8331 100644 --- a/Riot/Modules/Authentication/Views/ForgotPasswordInputsView.m +++ b/Riot/Modules/Authentication/Views/ForgotPasswordInputsView.m @@ -219,7 +219,7 @@ style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - inputsAlert = nil; + self->inputsAlert = nil; }]]; diff --git a/Riot/Modules/BugReport/BugReportViewController.m b/Riot/Modules/BugReport/BugReportViewController.m index e43ec06d7..3dd714654 100644 --- a/Riot/Modules/BugReport/BugReportViewController.m +++ b/Riot/Modules/BugReport/BugReportViewController.m @@ -222,11 +222,11 @@ _sendLogs = sendLogs; if (_sendLogs) { - _sendLogsButtonImage.image = [UIImage imageNamed:@"selection_tick"]; + _sendLogsButtonImage.image = AssetImages.selectionTick.image; } else { - _sendLogsButtonImage.image = [UIImage imageNamed:@"selection_untick"]; + _sendLogsButtonImage.image = AssetImages.selectionUntick.image; } } @@ -235,11 +235,11 @@ _sendScreenshot = sendScreenshot; if (_sendScreenshot) { - _sendScreenshotButtonImage.image = [UIImage imageNamed:@"selection_tick"]; + _sendScreenshotButtonImage.image = AssetImages.selectionTick.image; } else { - _sendScreenshotButtonImage.image = [UIImage imageNamed:@"selection_untick"]; + _sendScreenshotButtonImage.image = AssetImages.selectionUntick.image; } } diff --git a/Riot/Modules/Call/CallViewController.m b/Riot/Modules/Call/CallViewController.m index 8ec636401..d4ff8d4d5 100644 --- a/Riot/Modules/Call/CallViewController.m +++ b/Riot/Modules/Call/CallViewController.m @@ -73,20 +73,20 @@ CallAudioRouteMenuViewDelegate> // Back button - UIImage *backButtonImage = [UIImage imageNamed:@"back_icon"]; + UIImage *backButtonImage = AssetImages.backIcon.image; [self.backToAppButton setImage:backButtonImage forState:UIControlStateNormal]; [self.backToAppButton setImage:backButtonImage forState:UIControlStateHighlighted]; // Camera switch - UIImage *cameraSwitchButtonImage = [UIImage imageNamed:@"camera_switch"]; + UIImage *cameraSwitchButtonImage = AssetImages.cameraSwitch.image; [self.cameraSwitchButton setImage:cameraSwitchButtonImage forState:UIControlStateNormal]; [self.cameraSwitchButton setImage:cameraSwitchButtonImage forState:UIControlStateHighlighted]; // Audio mute - UIImage *audioMuteOffButtonImage = [UIImage imageNamed:@"call_audio_mute_off_icon"]; - UIImage *audioMuteOnButtonImage = [UIImage imageNamed:@"call_audio_mute_on_icon"]; + UIImage *audioMuteOffButtonImage = AssetImages.callAudioMuteOffIcon.image; + UIImage *audioMuteOnButtonImage = AssetImages.callAudioMuteOnIcon.image; [self.audioMuteButton setImage:audioMuteOffButtonImage forState:UIControlStateNormal]; [self.audioMuteButton setImage:audioMuteOffButtonImage forState:UIControlStateHighlighted]; @@ -94,8 +94,8 @@ CallAudioRouteMenuViewDelegate> // Video mute - UIImage *videoOffButtonImage = [UIImage imageNamed:@"call_video_mute_off_icon"]; - UIImage *videoOnButtonImage = [UIImage imageNamed:@"call_video_mute_on_icon"]; + UIImage *videoOffButtonImage = AssetImages.callVideoMuteOffIcon.image; + UIImage *videoOnButtonImage = AssetImages.callVideoMuteOnIcon.image; [self.videoMuteButton setImage:videoOffButtonImage forState:UIControlStateNormal]; [self.videoMuteButton setImage:videoOffButtonImage forState:UIControlStateHighlighted]; @@ -103,14 +103,14 @@ CallAudioRouteMenuViewDelegate> // More - UIImage *moreButtonImage = [UIImage imageNamed:@"call_more_icon"]; + UIImage *moreButtonImage = AssetImages.callMoreIcon.image; [self.moreButtonForVoice setImage:moreButtonImage forState:UIControlStateNormal]; [self.moreButtonForVideo setImage:moreButtonImage forState:UIControlStateNormal]; // Hang up - UIImage *hangUpButtonImage = [UIImage imageNamed:@"call_hangup_large"]; + UIImage *hangUpButtonImage = AssetImages.callHangupLarge.image; [self.endCallButton setTitle:nil forState:UIControlStateNormal]; [self.endCallButton setTitle:nil forState:UIControlStateHighlighted]; @@ -269,17 +269,17 @@ CallAudioRouteMenuViewDelegate> switch (self.mxCall.audioOutputRouter.currentRoute.routeType) { case MXiOSAudioOutputRouteTypeBuiltIn: - [self.speakerButton setImage:[UIImage imageNamed:@"call_speaker_off_icon"] + [self.speakerButton setImage:AssetImages.callSpeakerOffIcon.image forState:UIControlStateNormal]; break; case MXiOSAudioOutputRouteTypeLoudSpeakers: - [self.speakerButton setImage:[UIImage imageNamed:@"call_speaker_on_icon"] + [self.speakerButton setImage:AssetImages.callSpeakerOnIcon.image forState:UIControlStateNormal]; break; case MXiOSAudioOutputRouteTypeExternalWired: case MXiOSAudioOutputRouteTypeExternalBluetooth: case MXiOSAudioOutputRouteTypeExternalCar: - [self.speakerButton setImage:[UIImage imageNamed:@"call_speaker_external_icon"] + [self.speakerButton setImage:AssetImages.callSpeakerExternalIcon.image forState:UIControlStateNormal]; break; } @@ -484,7 +484,7 @@ CallAudioRouteMenuViewDelegate> andFontSize:fontSize]; } - return [MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + return [MXKTools paintImage:AssetImages.placeholder.image withColor:self.overriddenTheme.tintColor]; } @@ -721,7 +721,9 @@ CallAudioRouteMenuViewDelegate> { MXWeakify(self); [self.mainSession.callManager getThirdPartyUserFrom:phoneNumber success:^(MXThirdPartyUserInstance * _Nonnull user) { - MXStrongifyAndReturnIfNil(self); + if (weakself == nil) { + return; + } continueBlock(user.userId); } failure:^(NSError * _Nullable error) { diff --git a/Riot/Modules/Call/Views/IncomingCallView.h b/Riot/Modules/Call/Views/IncomingCallView.h index 3035aebb2..45f897115 100644 --- a/Riot/Modules/Call/Views/IncomingCallView.h +++ b/Riot/Modules/Call/Views/IncomingCallView.h @@ -43,9 +43,6 @@ typedef void (^IncomingCallViewAction)(void); @param mxcAvatarURI the Matrix Content URI of the caller avatar. @param mediaManager the media manager used to download this avatar if it is not cached yet. - @param placeholderImage - @param callerName - @param callInfo */ - (instancetype)initWithCallerAvatar:(NSString *)mxcAvatarURI mediaManager:(MXMediaManager *)mediaManager diff --git a/Riot/Modules/Call/Views/IncomingCallView.m b/Riot/Modules/Call/Views/IncomingCallView.m index 35500cf4e..b58145546 100644 --- a/Riot/Modules/Call/Views/IncomingCallView.m +++ b/Riot/Modules/Call/Views/IncomingCallView.m @@ -89,7 +89,7 @@ static const CGFloat kButtonSize = 80.0; UIColor *answerButtonBorderColor = ThemeService.shared.theme.tintColor; - self.answerButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"voice_call_hangon_icon"] + self.answerButton = [[CircleButton alloc] initWithImage:AssetImages.voiceCallHangonIcon.image borderColor:answerButtonBorderColor]; self.answerButton.defaultBackgroundColor = ThemeService.shared.theme.backgroundColor; self.answerButton.tintColor = answerButtonBorderColor; @@ -103,7 +103,7 @@ static const CGFloat kButtonSize = 80.0; UIColor *rejectButtonBorderColor = ThemeService.shared.theme.warningColor; - self.rejectButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"voice_call_hangup_icon"] + self.rejectButton = [[CircleButton alloc] initWithImage:AssetImages.voiceCallHangupIcon.image borderColor:rejectButtonBorderColor]; self.rejectButton.defaultBackgroundColor = ThemeService.shared.theme.backgroundColor; self.rejectButton.tintColor = rejectButtonBorderColor; diff --git a/Riot/Modules/Common/ActivityIndicator/AppActivityIndicatorPresenter.swift b/Riot/Modules/Common/ActivityIndicator/AppActivityIndicatorPresenter.swift new file mode 100644 index 000000000..53a5d2453 --- /dev/null +++ b/Riot/Modules/Common/ActivityIndicator/AppActivityIndicatorPresenter.swift @@ -0,0 +1,47 @@ +// +// Copyright 2021 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 UIKit +import MatrixSDK +import CommonKit + +/// Presenter which displays activity / loading indicators using app-wide `AppNavigator`, thus displaying them in a unified way, +/// and `ActivityCenter`/`Activity`, which ensures that only one activity is shown at a given time. +/// +/// Note: clients can skip using `AppActivityIndicatorPresenter` and instead coordiinate with `AppNavigatorProtocol` directly. +/// The presenter exists mostly as a transition for view controllers already using `ActivityIndicatorPresenterType` and / or view controllers +/// written in objective-c. +@objc final class AppActivityIndicatorPresenter: NSObject, ActivityIndicatorPresenterType { + private let appNavigator: AppNavigatorProtocol + private var activity: Activity? + + init(appNavigator: AppNavigatorProtocol) { + self.appNavigator = appNavigator + } + + @objc func presentActivityIndicator() { + activity = appNavigator.addLoadingActivity() + } + + @objc func removeCurrentActivityIndicator(animated: Bool, completion: (() -> Void)?) { + activity = nil + } + + func presentActivityIndicator(on view: UIView, animated: Bool, completion: (() -> Void)?) { + MXLog.error("[AppActivityIndicatorPresenter] Shared activity indicator does not support presenting from custom views") + } +} diff --git a/Riot/Modules/Common/Presentation/CustomSizedPresentationController.swift b/Riot/Modules/Common/Presentation/CustomSizedPresentationController.swift index 554705d73..7c7dfb47d 100644 --- a/Riot/Modules/Common/Presentation/CustomSizedPresentationController.swift +++ b/Riot/Modules/Common/Presentation/CustomSizedPresentationController.swift @@ -86,7 +86,7 @@ class CustomSizedPresentationController: UIPresentationController { // effect calls for only the top two corners to be rounded we size // the view such that the bottom CORNER_RADIUS points lie below // the bottom edge of the screen. - let cornerViewRect = presentationWrapperView.bounds//.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: -cornerRadius, right: 0)) + let cornerViewRect = presentationWrapperView.bounds// .inset(by: UIEdgeInsets(top: 0, left: 0, bottom: -cornerRadius, right: 0)) let presentationRoundedCornerView = UIView(frame: cornerViewRect) presentationRoundedCornerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h index 745ab180c..8cc599eba 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.h @@ -142,9 +142,10 @@ extern NSString *const kRecentsDataSourceTapOnDirectoryServerChange; @param section the section index @param frame the drawing area for the header of the specified section. + @param tableView the table view @return the sticky header view. */ -- (UIView *)viewForStickyHeaderInSection:(NSInteger)section withFrame:(CGRect)frame; +- (UIView *)viewForStickyHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView; /** Get the height of the section header view. diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m index 0a7328bef..faaa6554b 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m @@ -198,7 +198,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou [self.recentsListService updateSpace:currentSpace]; } -- (UIView *)viewForStickyHeaderInSection:(NSInteger)section withFrame:(CGRect)frame +- (UIView *)viewForStickyHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView { UIView *stickyHeader; @@ -209,7 +209,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou shrinkedSectionsBitMask = RECENTSDATASOURCE_SECTION_DIRECTORY; } - stickyHeader = [self viewForHeaderInSection:section withFrame:frame]; + stickyHeader = [self viewForHeaderInSection:section withFrame:frame inTableView:tableView]; shrinkedSectionsBitMask = savedShrinkedSectionsBitMask; @@ -752,7 +752,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou return missedNotifAndUnreadBadgeBgView; } -- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame +- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView { // No header view in key backup banner section if (section == self.secureBackupBannerSection || section == self.crossSigningBannerSection) @@ -760,8 +760,14 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou return nil; } - SectionHeaderView *sectionHeader = [[SectionHeaderView alloc] initWithFrame:frame]; - sectionHeader.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + SectionHeaderView *sectionHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:SectionHeaderView.defaultReuseIdentifier]; + if (sectionHeader == nil) + { + sectionHeader = [[SectionHeaderView alloc] initWithReuseIdentifier:SectionHeaderView.defaultReuseIdentifier]; + } + sectionHeader.backgroundView = [UIView new]; + sectionHeader.frame = frame; + sectionHeader.backgroundView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; sectionHeader.topViewHeight = RECENTSDATASOURCE_DEFAULT_SECTION_HEADER_HEIGHT; NSInteger sectionBitwise = 0; @@ -808,7 +814,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou shrinkButton.backgroundColor = [UIColor clearColor]; [shrinkButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; shrinkButton.tag = sectionBitwise; - [sectionHeader addSubview:shrinkButton]; sectionHeader.topSpanningView = shrinkButton; sectionHeader.userInteractionEnabled = YES; @@ -816,16 +821,15 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou UIImage *chevron; if (shrinkedSectionsBitMask & sectionBitwise) { - chevron = [UIImage imageNamed:@"disclosure_icon"]; + chevron = AssetImages.disclosureIcon.image; } else { - chevron = [UIImage imageNamed:@"shrink_icon"]; + chevron = AssetImages.shrinkIcon.image; } UIImageView *chevronView = [[UIImageView alloc] initWithImage:chevron]; chevronView.tintColor = ThemeService.shared.theme.textSecondaryColor; chevronView.contentMode = UIViewContentModeCenter; - [sectionHeader addSubview:chevronView]; sectionHeader.accessoryView = chevronView; } else if (_recentsDataSourceMode == RecentsDataSourceModeHome) @@ -835,7 +839,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou if (badgeView) { - [sectionHeader addSubview:badgeView]; sectionHeader.accessoryView = badgeView; } } @@ -845,7 +848,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou UILabel *headerLabel = [[UILabel alloc] initWithFrame:frame]; headerLabel.backgroundColor = [UIColor clearColor]; headerLabel.attributedText = [self attributedStringForHeaderTitleInSection:section]; - [sectionHeader addSubview:headerLabel]; sectionHeader.headerLabel = headerLabel; return sectionHeader; @@ -1469,7 +1471,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou - (void)recentsListServiceDidChangeData:(id)service totalCountsChanged:(BOOL)totalCountsChanged { - // no-op + [[AppDelegate theDelegate].masterTabBarController refreshTabBarBadges]; } - (void)recentsListServiceDidChangeData:(id)service diff --git a/Riot/Modules/Common/Recents/RecentsViewController.h b/Riot/Modules/Common/Recents/RecentsViewController.h index 9e9e357a7..00f728d24 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.h +++ b/Riot/Modules/Common/Recents/RecentsViewController.h @@ -19,6 +19,7 @@ @class RootTabEmptyView; @class AnalyticsScreenTimer; +@class AppActivityIndicatorPresenter; /** Notification to be posted when recents data is ready. Notification object will be the RecentsViewController instance. @@ -96,6 +97,11 @@ FOUNDATION_EXPORT NSString *const RecentsViewControllerDataReadyNotification; */ @property (nonatomic) AnalyticsScreenTimer *screenTimer; +/** + Presenter for displaying app-wide activity / loading indicators. If not set, the view controller will use legacy activity indicators + */ +@property (nonatomic, strong) AppActivityIndicatorPresenter *activityPresenter; + /** Return the sticky header for the specified section of the table view diff --git a/Riot/Modules/Common/Recents/RecentsViewController.m b/Riot/Modules/Common/Recents/RecentsViewController.m index 0aa137a5a..dbd280337 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.m +++ b/Riot/Modules/Common/Recents/RecentsViewController.m @@ -30,12 +30,13 @@ #import "DirectoryRecentTableViewCell.h" #import "RoomIdOrAliasTableViewCell.h" #import "TableViewCellWithCollectionView.h" +#import "SectionHeaderView.h" #import "GeneratedInterface-Swift.h" NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewControllerDataReadyNotification"; -@interface RecentsViewController () +@interface RecentsViewController () { // Tell whether a recents refresh is pending (suspended during editing mode). BOOL isRefreshPending; @@ -120,7 +121,11 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro tableSearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 600, 44)]; tableSearchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; tableSearchBar.showsCancelButton = NO; - tableSearchBar.placeholder = [VectorL10n searchDefaultPlaceholder]; + tableSearchBar.placeholder = [VectorL10n searchFilterPlaceholder]; + [tableSearchBar setImage:AssetImages.filterOff.image + forSearchBarIcon:UISearchBarIconSearch + state:UIControlStateNormal]; + tableSearchBar.delegate = self; displayedSectionHeaders = [NSMutableArray array]; @@ -145,6 +150,9 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro // Register key verification banner cells [self.recentsTableView registerNib:CrossSigningSetupBannerCell.nib forCellReuseIdentifier:CrossSigningSetupBannerCell.defaultReuseIdentifier]; + + [self.recentsTableView registerClass:SectionHeaderView.class + forHeaderFooterViewReuseIdentifier:SectionHeaderView.defaultReuseIdentifier]; // Hide line separators of empty cells self.recentsTableView.tableFooterView = [[UIView alloc] init]; @@ -165,8 +173,11 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro }]; self.recentsSearchBar.autocapitalizationType = UITextAutocapitalizationTypeNone; - self.recentsSearchBar.placeholder = [VectorL10n searchDefaultPlaceholder]; - + self.recentsSearchBar.placeholder = [VectorL10n searchFilterPlaceholder]; + [self.recentsSearchBar setImage:AssetImages.filterOff.image + forSearchBarIcon:UISearchBarIconSearch + state:UIControlStateNormal]; + // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { @@ -1018,6 +1029,18 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro TableViewCellWithCollectionView *collectionViewCell = (TableViewCellWithCollectionView *)cell; [collectionViewCell.collectionView reloadData]; cellReloaded = YES; + + CGRect headerFrame = [self.recentsTableView rectForHeaderInSection:update.sectionIndex]; + UIView *headerView = [self.recentsTableView headerViewForSection:update.sectionIndex]; + UIView *updatedHeaderView = [self.dataSource viewForHeaderInSection:update.sectionIndex withFrame:headerFrame inTableView:self.recentsTableView]; + if ([headerView isKindOfClass:SectionHeaderView.class] + && [updatedHeaderView isKindOfClass:SectionHeaderView.class]) + { + SectionHeaderView *sectionHeaderView = (SectionHeaderView *)headerView; + SectionHeaderView *updatedSectionHeaderView = (SectionHeaderView *)updatedHeaderView; + sectionHeaderView.headerLabel = updatedSectionHeaderView.headerLabel; + sectionHeaderView.accessoryView = updatedSectionHeaderView.accessoryView; + } } } } @@ -1093,7 +1116,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro }]; directChatAction.backgroundColor = actionBackgroundColor; - UIImage *directChatImage = [UIImage imageNamed:@"room_action_direct_chat"]; + UIImage *directChatImage = AssetImages.roomActionDirectChat.image; directChatImage = [directChatImage vc_tintedImageUsingColor:isDirect ? selectedColor : unselectedColor]; directChatAction.image = [directChatImage vc_notRenderedImage]; @@ -1120,13 +1143,13 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro muteAction.backgroundColor = actionBackgroundColor; UIImage *notificationImage; - if([BuildSettings showNotificationsV2]) + if([BuildSettings showNotificationsV2] && isMuted) { - notificationImage = isMuted ? [UIImage imageNamed:@"room_action_notification_muted"] : [UIImage imageNamed:@"room_action_notification"]; + notificationImage = AssetImages.roomActionNotificationMuted.image; } else { - notificationImage = [UIImage imageNamed:@"room_action_notification"]; + notificationImage = AssetImages.roomActionNotification.image; } notificationImage = [notificationImage vc_tintedImageUsingColor:isMuted ? unselectedColor : selectedColor]; @@ -1157,7 +1180,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro }]; favouriteAction.backgroundColor = actionBackgroundColor; - UIImage *favouriteImage = [UIImage imageNamed:@"room_action_favourite"]; + UIImage *favouriteImage = AssetImages.roomActionFavourite.image; favouriteImage = [favouriteImage vc_tintedImageUsingColor:isFavourite ? selectedColor : unselectedColor]; favouriteAction.image = [favouriteImage vc_notRenderedImage]; @@ -1174,7 +1197,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro }]; priorityAction.backgroundColor = actionBackgroundColor; - UIImage *priorityImage = isInLowPriority ? [UIImage imageNamed:@"room_action_priority_high"] : [UIImage imageNamed:@"room_action_priority_low"]; + UIImage *priorityImage = isInLowPriority ? AssetImages.roomActionPriorityHigh.image : AssetImages.roomActionPriorityLow.image; priorityImage = [priorityImage vc_tintedImageUsingColor:unselectedColor]; priorityAction.image = [priorityImage vc_notRenderedImage]; @@ -1188,7 +1211,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro }]; leaveAction.backgroundColor = actionBackgroundColor; - UIImage *leaveImage = [UIImage imageNamed:@"room_action_leave"]; + UIImage *leaveImage = AssetImages.roomActionLeave.image; leaveImage = [leaveImage vc_tintedImageUsingColor:unselectedColor]; leaveAction.image = [leaveImage vc_notRenderedImage]; @@ -1292,7 +1315,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro [self stopActivityIndicator]; // Leave editing mode - [self cancelEditionMode:isRefreshPending]; + [self cancelEditionMode:self->isRefreshPending]; } }]; @@ -1300,7 +1323,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro else { // Leave editing mode - [self cancelEditionMode:isRefreshPending]; + [self cancelEditionMode:self->isRefreshPending]; } } @@ -1358,7 +1381,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro [self stopActivityIndicator]; // Leave editing mode - [self cancelEditionMode:isRefreshPending]; + [self cancelEditionMode:self->isRefreshPending]; } failure:^(NSError *error) { @@ -1366,7 +1389,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro [self stopActivityIndicator]; - MXLogDebug(@"[RecentsViewController] Failed to update direct tag of the room (%@)", editedRoomId); + MXLogDebug(@"[RecentsViewController] Failed to update direct tag of the room (%@)", self->editedRoomId); // Notify the end user NSString *userId = self.mainSession.myUser.userId; // TODO: handle multi-account @@ -1375,7 +1398,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil]; // Leave editing mode - [self cancelEditionMode:isRefreshPending]; + [self cancelEditionMode:self->isRefreshPending]; }]; } @@ -2177,6 +2200,16 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro [self.recentsSearchBar setShowsCancelButton:NO animated:NO]; } +- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText +{ + [super searchBar:searchBar textDidChange:searchText]; + + UIImage *filterIcon = searchText.length > 0 ? AssetImages.filterOn.image : AssetImages.filterOff.image; + [self.recentsSearchBar setImage:filterIcon + forSearchBarIcon:UISearchBarIconSearch + state:UIControlStateNormal]; +} + #pragma mark - CreateRoomCoordinatorBridgePresenterDelegate - (void)createRoomCoordinatorBridgePresenterDelegate:(CreateRoomCoordinatorBridgePresenter *)coordinatorBridgePresenter didCreateNewRoom:(MXRoom *)room @@ -2374,4 +2407,26 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro self.roomNotificationSettingsCoordinatorBridgePresenter = nil; } +#pragma mark - Activity Indicator + +- (BOOL)providesCustomActivityIndicator { + return self.activityPresenter != nil; +} + +- (void)startActivityIndicator { + if (self.activityPresenter) { + [self.activityPresenter presentActivityIndicator]; + } else { + [super startActivityIndicator]; + } +} + +- (void)stopActivityIndicator { + if (self.activityPresenter) { + [self.activityPresenter removeCurrentActivityIndicatorWithAnimated:YES completion:nil]; + } else { + [super stopActivityIndicator]; + } +} + @end diff --git a/Riot/Modules/Common/SectionHeaders/SectionHeaderView.h b/Riot/Modules/Common/SectionHeaders/SectionHeaderView.h index e4e37c25c..515ee86a4 100644 --- a/Riot/Modules/Common/SectionHeaders/SectionHeaderView.h +++ b/Riot/Modules/Common/SectionHeaders/SectionHeaderView.h @@ -20,7 +20,7 @@ /** Section header view class. Respects left and right safe area insets and layouts its subviews. */ -@interface SectionHeaderView : UIView +@interface SectionHeaderView : UITableViewHeaderFooterView /** Default value: 20.0 @@ -57,4 +57,6 @@ */ @property (nonatomic, strong) UIView *bottomView; ++ (NSString*)defaultReuseIdentifier; + @end diff --git a/Riot/Modules/Common/SectionHeaders/SectionHeaderView.m b/Riot/Modules/Common/SectionHeaders/SectionHeaderView.m index 855c4e2c0..54150ac8e 100644 --- a/Riot/Modules/Common/SectionHeaders/SectionHeaderView.m +++ b/Riot/Modules/Common/SectionHeaders/SectionHeaderView.m @@ -17,10 +17,17 @@ #import "SectionHeaderView.h" +#import "GeneratedInterface-Swift.h" + static const CGFloat kInterItemsSpaceHorizontal = 8.0; @implementation SectionHeaderView ++ (NSString*)defaultReuseIdentifier +{ + return NSStringFromClass([self class]); +} + - (void)setMinimumLeftInset:(CGFloat)minimumLeftInset { _minimumLeftInset = minimumLeftInset; @@ -41,25 +48,53 @@ static const CGFloat kInterItemsSpaceHorizontal = 8.0; - (void)setTopSpanningView:(UIView *)topSpanningView { + // remove old one + [_topSpanningView removeFromSuperview]; _topSpanningView = topSpanningView; + if (_topSpanningView) + { + // add new one + [self.contentView addSubview:_topSpanningView]; + } [self setNeedsLayout]; } - (void)setHeaderLabel:(UILabel *)headerLabel { + // remove old one + [_headerLabel removeFromSuperview]; _headerLabel = headerLabel; + if (_headerLabel) + { + // add new one + [self.contentView addSubview:_headerLabel]; + } [self setNeedsLayout]; } - (void)setAccessoryView:(UIView *)accessoryView { + // remove old one + [_accessoryView removeFromSuperview]; _accessoryView = accessoryView; + if (_accessoryView) + { + // add new one + [self.contentView addSubview:_accessoryView]; + } [self setNeedsLayout]; } - (void)setBottomView:(UIView *)bottomView { + // remove old one + [_bottomView removeFromSuperview]; _bottomView = bottomView; + if (_bottomView) + { + // add new one + [self.contentView addSubview:_bottomView]; + } [self setNeedsLayout]; } @@ -88,6 +123,15 @@ static const CGFloat kInterItemsSpaceHorizontal = 8.0; return self; } +- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier +{ + if (self = [super initWithReuseIdentifier:reuseIdentifier]) + { + [self setup]; + } + return self; +} + - (void)setup { _minimumLeftInset = 20; @@ -95,21 +139,24 @@ static const CGFloat kInterItemsSpaceHorizontal = 8.0; _topViewHeight = 30; } +- (void)prepareForReuse +{ + [self.contentView vc_removeAllSubviews]; + [super prepareForReuse]; +} + - (void)layoutSubviews { [super layoutSubviews]; CGFloat _leftInset = 0.0, _rightInset = 0.0; - - _leftInset += self.safeAreaInsets.left; - _rightInset += self.safeAreaInsets.right; - CGFloat leftMargin = MAX(_leftInset, _minimumLeftInset); - CGFloat rightMargin = MAX(_rightInset, _minimumRightInset); + CGFloat leftMargin = _minimumLeftInset; + CGFloat rightMargin = _minimumRightInset; if (_topSpanningView) { - CGRect frame = self.bounds; + CGRect frame = self.contentView.bounds; frame.size.height = _topViewHeight; _topSpanningView.frame = frame; } @@ -130,9 +177,9 @@ static const CGFloat kInterItemsSpaceHorizontal = 8.0; else { // center header label vertically - frame.origin.y = MAX(0, (self.bounds.size.height - frame.size.height)/2); + frame.origin.y = MAX(0, (self.contentView.bounds.size.height - frame.size.height)/2); } - frame.size.width = self.bounds.size.width - leftMargin - rightMargin; + frame.size.width = self.contentView.bounds.size.width - leftMargin - rightMargin; _headerLabel.frame = frame; } @@ -143,7 +190,7 @@ static const CGFloat kInterItemsSpaceHorizontal = 8.0; rightMargin = MAX(_rightInset, 20); CGRect frame = _accessoryView.frame; - frame.origin.x = self.bounds.size.width - frame.size.width - rightMargin; + frame.origin.x = self.contentView.bounds.size.width - frame.size.width - rightMargin; frame.origin.y = MAX(0, (_topViewHeight - frame.size.height)/2); _accessoryView.frame = frame; } @@ -157,8 +204,8 @@ static const CGFloat kInterItemsSpaceHorizontal = 8.0; CGRect frame = _bottomView.frame; frame.origin.x = leftMargin; frame.origin.y = CGRectGetMaxY(_headerLabel.frame); - frame.size.width = self.bounds.size.width - leftMargin - rightMargin; - frame.size.height = self.bounds.size.height - frame.origin.y; + frame.size.width = self.contentView.bounds.size.width - leftMargin - rightMargin; + frame.size.height = self.contentView.bounds.size.height - frame.origin.y; _bottomView.frame = frame; } } diff --git a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m index 60547f8c5..883a9c416 100644 --- a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m +++ b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m @@ -165,6 +165,8 @@ // Adjust Top [NSLayoutConstraint deactivateConstraints:@[self.selectionContainerTopConstraint]]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" // it is not possible to define a constraint to the topLayoutGuide in the xib editor // so do it in the code .. self.selectionContainerTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide @@ -174,6 +176,7 @@ attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f]; + #pragma clang diagnostic pop [NSLayoutConstraint activateConstraints:@[self.selectionContainerTopConstraint]]; diff --git a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift index 677780e4f..8afaaa060 100644 --- a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift +++ b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift @@ -28,6 +28,10 @@ class VectorHostingController: UIHostingController { private var theme: Theme + // MARK: Public + + var enableNavigationBarScrollEdgesAppearance = false + init(rootView: Content) where Content: View { self.theme = ThemeService.shared().theme super.init(rootView: AnyView(rootView.vectorContent())) @@ -67,7 +71,7 @@ class VectorHostingController: UIHostingController { private func update(theme: Theme) { if let navigationBar = self.navigationController?.navigationBar { - theme.applyStyle(onNavigationBar: navigationBar) + theme.applyStyle(onNavigationBar: navigationBar, withModernScrollEdgesAppearance: enableNavigationBarScrollEdgesAppearance) } } } diff --git a/Riot/Modules/Common/Toasts/ActivityIndicatorToastPresenter.swift b/Riot/Modules/Common/Toasts/ActivityIndicatorToastPresenter.swift new file mode 100644 index 000000000..1c592a2a2 --- /dev/null +++ b/Riot/Modules/Common/Toasts/ActivityIndicatorToastPresenter.swift @@ -0,0 +1,76 @@ +// +// Copyright 2021 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 UIKit +import CommonKit + +/// An `ActivityPresenter` responsible for showing / hiding a toast view for activity indicators, and managed by an `Activity`, +/// meaning the `present` and `dismiss` methods will be called when the parent `Activity` starts or completes. +class ActivityIndicatorToastPresenter: ActivityPresentable { + private let text: String + private weak var navigationController: UINavigationController? + private weak var view: UIView? + + init(text: String, navigationController: UINavigationController) { + self.text = text + self.navigationController = navigationController + } + + func present() { + guard let navigationController = navigationController else { + return + } + + let view = ActivityIndicatorToastView(text: text) + view.update(theme: ThemeService.shared().theme) + self.view = view + + view.translatesAutoresizingMaskIntoConstraints = false + navigationController.view.addSubview(view) + NSLayoutConstraint.activate([ + view.centerXAnchor.constraint(equalTo: navigationController.navigationBar.centerXAnchor), + view.topAnchor.constraint(equalTo: navigationController.navigationBar.bottomAnchor) + ]) + + view.alpha = 0 + view.transform = .init(translationX: 0, y: 5) + UIView.animate(withDuration: 0.2) { + view.alpha = 1 + view.transform = .identity + } + } + + func dismiss() { + guard let view = view, view.superview != nil else { + return + } + + // If `present` and `dismiss` are called right after each other without delay, + // the view does not correctly pick up `currentState` of alpha. Dispatching onto + // the main queue skips a few run loops, giving the system time to render + // current state. + DispatchQueue.main.async { + UIView.animate(withDuration: 0.2, delay: 0, options: .beginFromCurrentState) { + view.alpha = 0 + view.transform = .init(translationX: 0, y: -5) + } completion: { _ in + view.removeFromSuperview() + self.view = nil + } + } + } +} diff --git a/Riot/Modules/Common/Toasts/ActivityIndicatorToastView.swift b/Riot/Modules/Common/Toasts/ActivityIndicatorToastView.swift new file mode 100644 index 000000000..6964ec445 --- /dev/null +++ b/Riot/Modules/Common/Toasts/ActivityIndicatorToastView.swift @@ -0,0 +1,91 @@ +// +// Copyright 2021 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 UIKit +import DesignKit + +class ActivityIndicatorToastView: UIView, Themable { + private struct Constants { + static let padding = UIEdgeInsets(top: 10, left: 12, bottom: 10, right: 12) + static let shadowOffset = CGSize(width: 0, height: 4) + static let shadowRadius = CGFloat(12) + static let shadowOpacity = Float(0.1) + } + + private let stackView: UIStackView = { + let stack = UIStackView() + stack.axis = .horizontal + stack.spacing = 5 + return stack + }() + + private let activityIndicator: UIActivityIndicatorView = { + let view = UIActivityIndicatorView() + view.transform = .init(scaleX: 0.75, y: 0.75) + view.startAnimating() + return view + }() + + private let label: UILabel = { + return UILabel() + }() + + init(text: String) { + super.init(frame: .zero) + setup(text: text) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setup(text: String) { + setupLayer() + setupStackView() + stackView.addArrangedSubview(activityIndicator) + stackView.addArrangedSubview(label) + label.text = text + } + + private func setupStackView() { + addSubview(stackView) + stackView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + stackView.topAnchor.constraint(equalTo: topAnchor, constant: Constants.padding.top), + stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Constants.padding.bottom), + stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.padding.left), + stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.padding.right) + ]) + } + + private func setupLayer() { + layer.shadowColor = UIColor.black.cgColor + layer.shadowOffset = Constants.shadowOffset + layer.shadowRadius = Constants.shadowRadius + layer.shadowOpacity = Constants.shadowOpacity + } + + override func layoutSubviews() { + super.layoutSubviews() + layer.cornerRadius = layer.frame.height / 2 + } + + func update(theme: Theme) { + backgroundColor = UIColor.white + label.font = theme.fonts.subheadline + } +} diff --git a/Riot/Modules/Common/Toasts/BasicToastView.swift b/Riot/Modules/Common/Toasts/BasicToastView.swift new file mode 100644 index 000000000..a4b9a39b6 --- /dev/null +++ b/Riot/Modules/Common/Toasts/BasicToastView.swift @@ -0,0 +1,112 @@ +// +// Copyright 2021 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 UIKit + +class BasicToastView: UIView, Themable { + + private enum Constants { + static let padding: UIEdgeInsets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) + static let cornerRadius: CGFloat = 8.0 + } + + private lazy var imageView: UIImageView = { + let view = UIImageView() + view.translatesAutoresizingMaskIntoConstraints = false + view.backgroundColor = .clear + return view + }() + + private lazy var messageLabel: UILabel = { + let label = UILabel() + label.font = ThemeService.shared().theme.fonts.body + label.backgroundColor = .clear + label.numberOfLines = 0 + label.textAlignment = .left + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var stackView: UIStackView = { + let result = UIStackView() + result.axis = .horizontal + result.distribution = .fill + result.alignment = .center + result.spacing = 8.0 + result.backgroundColor = .clear + + addSubview(result) + result.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + result.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.padding.left), + result.topAnchor.constraint(equalTo: topAnchor, constant: Constants.padding.top), + result.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.padding.right), + result.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Constants.padding.bottom) + ]) + + return result + }() + + init(withMessage message: String?, + image: UIImage? = nil) { + super.init(frame: .zero) + + if let image = image { + imageView.image = image + NSLayoutConstraint.activate([ + imageView.widthAnchor.constraint(equalToConstant: image.size.width), + imageView.heightAnchor.constraint(equalToConstant: image.size.height) + ]) + stackView.addArrangedSubview(imageView) + } + + messageLabel.text = message + stackView.addArrangedSubview(messageLabel) + + stackView.layoutIfNeeded() + layer.cornerRadius = Constants.cornerRadius + layer.masksToBounds = true + registerThemeServiceDidChangeThemeNotification() + themeDidChange() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, + selector: #selector(themeDidChange), + name: .themeServiceDidChangeTheme, + object: nil) + } + + @objc + private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + // MARK: Themable + + func update(theme: Theme) { + backgroundColor = theme.colors.quinaryContent + imageView.tintColor = theme.colors.tertiaryContent + messageLabel.textColor = theme.colors.primaryContent + messageLabel.font = theme.fonts.body + } + +} diff --git a/Riot/Modules/Communities/Home/GroupHomeViewController.h b/Riot/Modules/Communities/Home/GroupHomeViewController.h index 69b2300da..510e1dfcc 100644 --- a/Riot/Modules/Communities/Home/GroupHomeViewController.h +++ b/Riot/Modules/Communities/Home/GroupHomeViewController.h @@ -63,9 +63,6 @@ /** Set the group for which the details are displayed. Provide the related matrix session. - - @param group - @param mxSession */ - (void)setGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession; diff --git a/Riot/Modules/Communities/Home/GroupHomeViewController.m b/Riot/Modules/Communities/Home/GroupHomeViewController.m index 6e5d5d3b5..a5a48ebba 100644 --- a/Riot/Modules/Communities/Home/GroupHomeViewController.m +++ b/Riot/Modules/Communities/Home/GroupHomeViewController.m @@ -40,7 +40,7 @@ // The options used to load long description html content. NSDictionary *options; - NSString *groupLongDescription; + NSString *groupLongDescriptionString; // The current pushed view controller UIViewController *pushedViewController; @@ -229,25 +229,25 @@ // Indeed the group update notifications are triggered by the matrix session only for the user's groups. void (^success)(void) = ^void(void) { - [self refreshDisplayWithGroup:_group]; + [self refreshDisplayWithGroup:self->_group]; }; // Trigger a refresh on the group summary. - [self.mxSession updateGroupSummary:_group success:(isPreview ? success : nil) failure:^(NSError *error) { + [self.mxSession updateGroupSummary:self->_group success:(isPreview ? success : nil) failure:^(NSError *error) { - MXLogDebug(@"[GroupHomeViewController] viewWillAppear: group summary update failed %@", _group.groupId); + MXLogDebug(@"[GroupHomeViewController] viewWillAppear: group summary update failed %@", self->_group.groupId); }]; // Trigger a refresh on the group members (ignore here the invited users). - [self.mxSession updateGroupUsers:_group success:(isPreview ? success : nil) failure:^(NSError *error) { + [self.mxSession updateGroupUsers:self->_group success:(isPreview ? success : nil) failure:^(NSError *error) { - MXLogDebug(@"[GroupHomeViewController] viewWillAppear: group members update failed %@", _group.groupId); + MXLogDebug(@"[GroupHomeViewController] viewWillAppear: group members update failed %@", self->_group.groupId); }]; // Trigger a refresh on the group rooms. - [self.mxSession updateGroupRooms:_group success:(isPreview ? success : nil) failure:^(NSError *error) { + [self.mxSession updateGroupRooms:self->_group success:(isPreview ? success : nil) failure:^(NSError *error) { - MXLogDebug(@"[GroupHomeViewController] viewWillAppear: group rooms update failed %@", _group.groupId); + MXLogDebug(@"[GroupHomeViewController] viewWillAppear: group rooms update failed %@", self->_group.groupId); }]; } @@ -523,11 +523,11 @@ { if (_group.summary.profile.longDescription.length) { - groupLongDescription = _group.summary.profile.longDescription; + groupLongDescriptionString = _group.summary.profile.longDescription; } else { - groupLongDescription = nil; + groupLongDescriptionString = nil; } [self renderGroupLongDescription]; @@ -535,13 +535,13 @@ - (void)renderGroupLongDescription { - if (groupLongDescription) + if (groupLongDescriptionString) { // Using DTCoreText, which renders static string, helps to avoid code injection attacks // that could happen with the default HTML renderer of NSAttributedString which is a // webview. // The supplied options include a callback to sanitize html tags and load image data. - NSAttributedString *attributedString = [[NSAttributedString alloc] initWithHTMLData:[groupLongDescription dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:NULL]; + NSAttributedString *attributedString = [[NSAttributedString alloc] initWithHTMLData:[groupLongDescriptionString dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:NULL]; // Apply additional treatments NSInteger mxIdsBitMask = (MXKTOOLS_USER_IDENTIFIER_BITWISE | MXKTOOLS_ROOM_IDENTIFIER_BITWISE | MXKTOOLS_ROOM_ALIAS_BITWISE | MXKTOOLS_EVENT_IDENTIFIER_BITWISE | MXKTOOLS_GROUP_IDENTIFIER_BITWISE); @@ -694,12 +694,12 @@ self->currentRequest = nil; [self stopActivityIndicator]; - [self refreshDisplayWithGroup:[_mxSession groupWithGroupId:_group.groupId]]; + [self refreshDisplayWithGroup:[self->_mxSession groupWithGroupId:self->_group.groupId]]; } } failure:^(NSError *error) { - MXLogDebug(@"[GroupDetailsViewController] join group (%@) failed", _group.groupId); + MXLogDebug(@"[GroupDetailsViewController] join group (%@) failed", self->_group.groupId); if (weakSelf) { @@ -732,7 +732,7 @@ } failure:^(NSError *error) { - MXLogDebug(@"[GroupDetailsViewController] leave group (%@) failed", _group.groupId); + MXLogDebug(@"[GroupDetailsViewController] leave group (%@) failed", self->_group.groupId); if (weakSelf) { @@ -802,6 +802,8 @@ #pragma mark - UITextView delegate +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange { BOOL shouldInteractWithURL = YES; @@ -903,5 +905,6 @@ return shouldInteractWithURL; } +#pragma clang diagnostic pop @end diff --git a/Riot/Modules/Communities/Members/GroupParticipantsViewController.h b/Riot/Modules/Communities/Members/GroupParticipantsViewController.h index 83b862664..e5b4a9ba8 100644 --- a/Riot/Modules/Communities/Members/GroupParticipantsViewController.h +++ b/Riot/Modules/Communities/Members/GroupParticipantsViewController.h @@ -76,9 +76,6 @@ /** Set the group for which the details are displayed. Provide the related matrix session. - - @param group - @param mxSession */ - (void)setGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession; diff --git a/Riot/Modules/Communities/Members/GroupParticipantsViewController.m b/Riot/Modules/Communities/Members/GroupParticipantsViewController.m index 9a87f6562..26cf2b02d 100644 --- a/Riot/Modules/Communities/Members/GroupParticipantsViewController.m +++ b/Riot/Modules/Communities/Members/GroupParticipantsViewController.m @@ -93,6 +93,8 @@ // Adjust Top and Bottom constraints to take into account potential navBar and tabBar. [NSLayoutConstraint deactivateConstraints:@[_searchBarTopConstraint, _tableViewBottomConstraint]]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" _searchBarTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual @@ -108,6 +110,7 @@ attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]; + #pragma clang diagnostic pop [NSLayoutConstraint activateConstraints:@[_searchBarTopConstraint, _tableViewBottomConstraint]]; @@ -239,18 +242,18 @@ // Indeed the group update notifications are triggered by the matrix session only for the user's groups. void (^success)(void) = ^void(void) { - [self refreshDisplayWithGroup:_group]; + [self refreshDisplayWithGroup:self->_group]; }; // Trigger a refresh on the group members and the invited users. [self.mxSession updateGroupUsers:_group success:(isPreview ? success : nil) failure:^(NSError *error) { - MXLogDebug(@"[GroupParticipantsViewController] viewWillAppear: group members update failed %@", _group.groupId); + MXLogDebug(@"[GroupParticipantsViewController] viewWillAppear: group members update failed %@", self->_group.groupId); }]; [self.mxSession updateGroupInvitedUsers:_group success:(isPreview ? success : nil) failure:^(NSError *error) { - MXLogDebug(@"[GroupParticipantsViewController] viewWillAppear: invited users update failed %@", _group.groupId); + MXLogDebug(@"[GroupParticipantsViewController] viewWillAppear: invited users update failed %@", self->_group.groupId); }]; } @@ -305,7 +308,7 @@ [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ - tableViewMaskLayer.bounds = newBounds; + self->tableViewMaskLayer.bounds = newBounds; } completion:^(BOOL finished){ @@ -421,7 +424,7 @@ [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ - addParticipantButtonImageViewBottomConstraint.constant = keyboardHeight + 9; + self->addParticipantButtonImageViewBottomConstraint.constant = keyboardHeight + 9; // Force to render the view [self.view layoutIfNeeded]; @@ -472,7 +475,7 @@ addParticipantButtonImageView.backgroundColor = [UIColor clearColor]; addParticipantButtonImageView.contentMode = UIViewContentModeCenter; - addParticipantButtonImageView.image = [UIImage imageNamed:@"add_group_participant"]; + addParticipantButtonImageView.image = AssetImages.addGroupParticipant.image; CGFloat side = 78.0f; NSLayoutConstraint* widthConstraint = [NSLayoutConstraint constraintWithItem:addParticipantButtonImageView @@ -534,7 +537,7 @@ contactsDataSource.displaySearchInputInContactsList = YES; contactsDataSource.forceMatrixIdInDisplayName = YES; // Add a plus icon to the contact cell in the contacts picker, in order to make it more understandable for the end user. - contactsDataSource.contactCellAccessoryImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]; + contactsDataSource.contactCellAccessoryImage = [AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]; // List all the participants matrix user id to ignore them during the contacts search. for (Contact *contact in actualParticipants) @@ -653,7 +656,7 @@ pendingMaskSpinnerView.alpha = 0; [UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ - pendingMaskSpinnerView.alpha = 1; + self->pendingMaskSpinnerView.alpha = 1; } completion:^(BOOL finished) { }]; @@ -1075,14 +1078,14 @@ self->currentAlert = nil; [self addPendingActionMask]; - [self.mxSession leaveGroup:_group.groupId success:^{ + [self.mxSession leaveGroup:self->_group.groupId success:^{ [self withdrawViewControllerAnimated:YES completion:nil]; } failure:^(NSError *error) { [self removePendingActionMask]; - MXLogDebug(@"[GroupParticipantsVC] Leave group %@ failed", _group.groupId); + MXLogDebug(@"[GroupParticipantsVC] Leave group %@ failed", self->_group.groupId); // Alert user [[AppDelegate theDelegate] showErrorAsAlert:error]; diff --git a/Riot/Modules/Communities/Rooms/GroupRoomsViewController.h b/Riot/Modules/Communities/Rooms/GroupRoomsViewController.h index 792865b96..fe0a504a7 100644 --- a/Riot/Modules/Communities/Rooms/GroupRoomsViewController.h +++ b/Riot/Modules/Communities/Rooms/GroupRoomsViewController.h @@ -63,9 +63,6 @@ /** Set the group for which the rooms are listed. Provide the related matrix session. - - @param group - @param mxSession */ - (void)setGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession; diff --git a/Riot/Modules/Communities/Rooms/GroupRoomsViewController.m b/Riot/Modules/Communities/Rooms/GroupRoomsViewController.m index 855c46580..4f60c4970 100644 --- a/Riot/Modules/Communities/Rooms/GroupRoomsViewController.m +++ b/Riot/Modules/Communities/Rooms/GroupRoomsViewController.m @@ -79,6 +79,8 @@ // Adjust Top and Bottom constraints to take into account potential navBar and tabBar. [NSLayoutConstraint deactivateConstraints:@[_searchBarTopConstraint, _tableViewBottomConstraint]]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" _searchBarTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual @@ -94,6 +96,7 @@ attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]; + #pragma clang diagnostic pop [NSLayoutConstraint activateConstraints:@[_searchBarTopConstraint, _tableViewBottomConstraint]]; @@ -203,13 +206,13 @@ // Indeed the group update notifications are triggered by the matrix session only for the user's groups. void (^success)(void) = ^void(void) { - [self refreshDisplayWithGroup:_group]; + [self refreshDisplayWithGroup:self->_group]; }; // Trigger a refresh on the group rooms. [self.mxSession updateGroupRooms:_group success:(isPreview ? success : nil) failure:^(NSError *error) { - MXLogDebug(@"[GroupRoomsViewController] viewWillAppear: group rooms update failed %@", _group.groupId); + MXLogDebug(@"[GroupRoomsViewController] viewWillAppear: group rooms update failed %@", self->_group.groupId); }]; } diff --git a/Riot/Modules/Communities/TabDetail/GroupDetailsViewController.h b/Riot/Modules/Communities/TabDetail/GroupDetailsViewController.h index e228101d4..faaab6b94 100644 --- a/Riot/Modules/Communities/TabDetail/GroupDetailsViewController.h +++ b/Riot/Modules/Communities/TabDetail/GroupDetailsViewController.h @@ -40,9 +40,6 @@ /** Set the group for which the details are displayed. Provide the related matrix session. - - @param group - @param mxSession */ - (void)setGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession; diff --git a/Riot/Modules/Contacts/ContactsTableViewController.m b/Riot/Modules/Contacts/ContactsTableViewController.m index bf83b6372..badf8a085 100644 --- a/Riot/Modules/Contacts/ContactsTableViewController.m +++ b/Riot/Modules/Contacts/ContactsTableViewController.m @@ -17,6 +17,7 @@ #import #import "ContactsTableViewController.h" +#import "SectionHeaderView.h" #import "UIViewController+RiotSearch.h" @@ -95,6 +96,9 @@ self.contactsTableView.dataSource = contactsDataSource; // Note: dataSource may be nil here [self.contactsTableView registerClass:ContactTableViewCell.class forCellReuseIdentifier:ContactTableViewCell.defaultReuseIdentifier]; + + [self.contactsTableView registerClass:SectionHeaderView.class + forHeaderFooterViewReuseIdentifier:SectionHeaderView.defaultReuseIdentifier]; // Hide line separators of empty cells self.contactsTableView.tableFooterView = [[UIView alloc] init]; @@ -455,7 +459,9 @@ - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { - return [contactsDataSource viewForHeaderInSection:section withFrame:[tableView rectForHeaderInSection:section]]; + return [contactsDataSource viewForHeaderInSection:section + withFrame:[tableView rectForHeaderInSection:section] + inTableView:tableView]; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath diff --git a/Riot/Modules/Contacts/DataSources/ContactsDataSource.h b/Riot/Modules/Contacts/DataSources/ContactsDataSource.h index 5b665377e..6d6205e9b 100644 --- a/Riot/Modules/Contacts/DataSources/ContactsDataSource.h +++ b/Riot/Modules/Contacts/DataSources/ContactsDataSource.h @@ -95,18 +95,20 @@ typedef enum : NSUInteger @param section the section index @param frame the drawing area for the header of the specified section. + @param tableView the table view @return the section header. */ -- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame; +- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView *)tableView; /** Get the sticky header view for the specified section. @param section the section index @param frame the drawing area for the header of the specified section. + @param tableView the table view @return the sticky header view. */ -- (UIView *)viewForStickyHeaderInSection:(NSInteger)section withFrame:(CGRect)frame; +- (UIView *)viewForStickyHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView *)tableView; /** Refresh the contacts data source and notify its delegate. diff --git a/Riot/Modules/Contacts/DataSources/ContactsDataSource.m b/Riot/Modules/Contacts/DataSources/ContactsDataSource.m index c7d1692bd..9a2935903 100644 --- a/Riot/Modules/Contacts/DataSources/ContactsDataSource.m +++ b/Riot/Modules/Contacts/DataSources/ContactsDataSource.m @@ -791,19 +791,24 @@ return sectionTitle; } -- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame +- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView *)tableView { NSInteger sectionBitwise = 0; - SectionHeaderView *sectionHeader = [[SectionHeaderView alloc] initWithFrame:frame]; - sectionHeader.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + SectionHeaderView *sectionHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:SectionHeaderView.defaultReuseIdentifier]; + if (sectionHeader == nil) + { + sectionHeader = [[SectionHeaderView alloc] initWithReuseIdentifier:SectionHeaderView.defaultReuseIdentifier]; + } + sectionHeader.frame = frame; + sectionHeader.backgroundView = [UIView new]; + sectionHeader.backgroundView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; sectionHeader.topViewHeight = CONTACTSDATASOURCE_DEFAULT_SECTION_HEADER_HEIGHT; frame.size.height = CONTACTSDATASOURCE_DEFAULT_SECTION_HEADER_HEIGHT - 10; UILabel *headerLabel = [[UILabel alloc] initWithFrame:frame]; headerLabel.attributedText = [self attributedStringForHeaderTitleInSection:section]; headerLabel.backgroundColor = [UIColor clearColor]; - [sectionHeader addSubview:headerLabel]; sectionHeader.headerLabel = headerLabel; if (_areSectionsShrinkable) @@ -832,7 +837,6 @@ shrinkButton.backgroundColor = [UIColor clearColor]; [shrinkButton addTarget:self action:@selector(onButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; shrinkButton.tag = sectionBitwise; - [sectionHeader addSubview:shrinkButton]; sectionHeader.topSpanningView = shrinkButton; sectionHeader.userInteractionEnabled = YES; @@ -840,16 +844,15 @@ UIImage *chevron; if (shrinkedSectionsBitMask & sectionBitwise) { - chevron = [UIImage imageNamed:@"disclosure_icon"]; + chevron = AssetImages.disclosureIcon.image; } else { - chevron = [UIImage imageNamed:@"shrink_icon"]; + chevron = AssetImages.shrinkIcon.image; } UIImageView *chevronView = [[UIImageView alloc] initWithImage:chevron]; chevronView.tintColor = ThemeService.shared.theme.textSecondaryColor; chevronView.contentMode = UIViewContentModeCenter; - [sectionHeader addSubview:chevronView]; sectionHeader.accessoryView = chevronView; } @@ -889,24 +892,23 @@ checkboxLabel.textColor = ThemeService.shared.theme.textPrimaryColor; // Set the right value of the tick box - localContactsCheckbox.image = hideNonMatrixEnabledContacts ? [UIImage imageNamed:@"selection_tick"] : [UIImage imageNamed:@"selection_untick"]; + localContactsCheckbox.image = hideNonMatrixEnabledContacts ? AssetImages.selectionTick.image : AssetImages.selectionUntick.image; localContactsCheckbox.tintColor = ThemeService.shared.theme.tintColor; // Add the check box container - [sectionHeader addSubview:localContactsCheckboxContainer]; sectionHeader.bottomView = localContactsCheckboxContainer; } return sectionHeader; } -- (UIView *)viewForStickyHeaderInSection:(NSInteger)section withFrame:(CGRect)frame +- (UIView *)viewForStickyHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView *)tableView { // Return the section header used when the section is shrinked NSInteger savedShrinkedSectionsBitMask = shrinkedSectionsBitMask; shrinkedSectionsBitMask = CONTACTSDATASOURCE_LOCALCONTACTS_BITWISE | CONTACTSDATASOURCE_USERDIRECTORY_BITWISE; - UIView *stickyHeader = [self viewForHeaderInSection:section withFrame:frame]; + UIView *stickyHeader = [self viewForHeaderInSection:section withFrame:frame inTableView:tableView]; shrinkedSectionsBitMask = savedShrinkedSectionsBitMask; diff --git a/Riot/Modules/Contacts/Details/ContactDetailsViewController.m b/Riot/Modules/Contacts/Details/ContactDetailsViewController.m index be2fe5596..fe2b2d6e1 100644 --- a/Riot/Modules/Contacts/Details/ContactDetailsViewController.m +++ b/Riot/Modules/Contacts/Details/ContactDetailsViewController.m @@ -733,7 +733,7 @@ } else { - roomCell.avatarImageView.image = [UIImage imageNamed:@"start_chat"]; + roomCell.avatarImageView.image = AssetImages.startChat.image; roomCell.avatarImageView.defaultBackgroundColor = [UIColor clearColor]; roomCell.titleLabel.text = [VectorL10n roomParticipantsActionStartNewChat]; } @@ -1084,7 +1084,7 @@ MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters parametersForDirectRoomWithUser:matrixId]; roomCreationRequest = [self.mainSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) { - roomCreationRequest = nil; + self->roomCreationRequest = nil; // Delay the call in order to be sure that the room is ready dispatch_async(dispatch_get_main_queue(), ^{ @@ -1096,7 +1096,7 @@ MXLogDebug(@"[ContactDetailsViewController] Create room failed"); - roomCreationRequest = nil; + self->roomCreationRequest = nil; [self removePendingActionMask]; diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.swift index bcca816cf..336e4b0fc 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.swift @@ -19,6 +19,7 @@ import Reusable protocol ChooseAvatarTableViewCellDelegate: AnyObject { func chooseAvatarTableViewCellDidTapChooseAvatar(_ cell: ChooseAvatarTableViewCell, sourceView: UIView) + func chooseAvatarTableViewCellDidTapRemoveAvatar(_ cell: ChooseAvatarTableViewCell) } class ChooseAvatarTableViewCell: UITableViewCell { @@ -29,15 +30,30 @@ class ChooseAvatarTableViewCell: UITableViewCell { } } @IBOutlet private weak var chooseAvatarButton: UIButton! + @IBOutlet private weak var removeAvatarButton: UIButton! { + didSet { + removeAvatarButton.imageView?.contentMode = .scaleAspectFit + } + } weak var delegate: ChooseAvatarTableViewCellDelegate? @IBAction private func chooseAvatarButtonTapped(_ sender: UIButton) { delegate?.chooseAvatarTableViewCellDidTapChooseAvatar(self, sourceView: sender) } + + @IBAction private func removeAvatarButtonTapped(_ sender: UIButton) { + delegate?.chooseAvatarTableViewCellDidTapRemoveAvatar(self) + } func configure(withViewModel viewModel: ChooseAvatarTableViewCellVM) { - avatarImageView.image = viewModel.avatarImage + if let image = viewModel.avatarImage { + avatarImageView.image = image + removeAvatarButton.isHidden = false + } else { + avatarImageView.image = Asset.Images.captureAvatar.image + removeAvatarButton.isHidden = true + } } } @@ -49,7 +65,6 @@ extension ChooseAvatarTableViewCell: Themable { func update(theme: Theme) { backgroundView = UIView() backgroundView?.backgroundColor = theme.backgroundColor - avatarImageView.backgroundColor = theme.tintColor } } diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.xib b/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.xib index 72ebdf925..42afad97a 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.xib +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -18,39 +18,45 @@ - + - - + + - - - - - - - - + + - - + - + + - @@ -64,11 +70,13 @@ + - + + diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewAction.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewAction.swift index 93dc56de1..8833b80b9 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewAction.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewAction.swift @@ -16,12 +16,13 @@ limitations under the License. */ -import Foundation +import UIKit /// EnterNewRoomDetailsViewController view actions exposed to view model enum EnterNewRoomDetailsViewAction { case loadData case chooseAvatar(sourceView: UIView) + case removeAvatar case cancel case create } diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift index 63fe65197..914f3ce67 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewController.swift @@ -31,6 +31,8 @@ final class EnterNewRoomDetailsViewController: UIViewController { static let roomNameMaximumNumberOfChars = 50 static let roomAddressMaximumNumberOfChars = 50 static let roomTopicMaximumNumberOfChars = 250 + static let chooseAvatarTableViewCellHeight: CGFloat = 140 + static let textViewTableViewCellHeight: CGFloat = 150 } // MARK: - Properties @@ -481,9 +483,9 @@ extension EnterNewRoomDetailsViewController: UITableViewDelegate { switch row.type { case .avatar: - return 100 + return Constants.chooseAvatarTableViewCellHeight case .textView: - return 150 + return Constants.textViewTableViewCellHeight default: return UITableView.automaticDimension } @@ -512,7 +514,11 @@ extension EnterNewRoomDetailsViewController: ChooseAvatarTableViewCellDelegate { func chooseAvatarTableViewCellDidTapChooseAvatar(_ cell: ChooseAvatarTableViewCell, sourceView: UIView) { viewModel.process(viewAction: .chooseAvatar(sourceView: sourceView)) } - + + func chooseAvatarTableViewCellDidTapRemoveAvatar(_ cell: ChooseAvatarTableViewCell) { + viewModel.process(viewAction: .removeAvatar) + } + } // MARK: - EnterNewRoomDetailsViewModelViewDelegate @@ -532,14 +538,6 @@ extension EnterNewRoomDetailsViewController: UITextFieldDelegate { switch textField.tag { case Constants.roomNameTextFieldTag: viewModel.roomCreationParameters.name = textField.text - if viewModel.roomCreationParameters.userSelectedAvatar == nil { - // if no image selected by the user, set initials as image - let avatar = AvatarGenerator.generateAvatar(forMatrixItem: nil, - withDisplayName: textField.text, - size: 60, - andFontSize: 30) - viewModel.roomCreationParameters.initialsAvatar = avatar - } case Constants.roomAddressTextFieldTag: viewModel.roomCreationParameters.address = textField.text default: diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift index 0e4721b6e..407e0366b 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModel.swift @@ -62,6 +62,8 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType { self.loadData() case .chooseAvatar(let sourceView): self.chooseAvatar(sourceView: sourceView) + case .removeAvatar: + self.removeAvatar() case .cancel: self.cancelOperations() self.coordinatorDelegate?.enterNewRoomDetailsViewModelDidCancel(self) @@ -79,6 +81,11 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType { private func chooseAvatar(sourceView: UIView) { self.coordinatorDelegate?.enterNewRoomDetailsViewModel(self, didTapChooseAvatar: sourceView) } + + private func removeAvatar() { + self.roomCreationParameters.userSelectedAvatar = nil + self.process(viewAction: .loadData) + } private func fixRoomAlias(alias: String?) -> String? { guard var alias = alias else { return nil } diff --git a/Riot/Modules/CreateRoom/Models/RoomCreationParameters.swift b/Riot/Modules/CreateRoom/Models/RoomCreationParameters.swift index f7149fc3a..c84f8eb27 100644 --- a/Riot/Modules/CreateRoom/Models/RoomCreationParameters.swift +++ b/Riot/Modules/CreateRoom/Models/RoomCreationParameters.swift @@ -21,7 +21,7 @@ struct RoomCreationParameters { var topic: String? var address: String? var avatarImage: UIImage? { - return userSelectedAvatar ?? initialsAvatar + return userSelectedAvatar } var isEncrypted: Bool = false var isPublic: Bool = false { @@ -36,5 +36,4 @@ struct RoomCreationParameters { var showInDirectory: Bool = false var userSelectedAvatar: UIImage? - var initialsAvatar: UIImage? } diff --git a/Riot/Modules/Favorites/FavouritesViewController.m b/Riot/Modules/Favorites/FavouritesViewController.m index 23b43b856..62ef8cd8f 100644 --- a/Riot/Modules/Favorites/FavouritesViewController.m +++ b/Riot/Modules/Favorites/FavouritesViewController.m @@ -155,11 +155,11 @@ { if (ThemeService.shared.isCurrentThemeDark) { - return [UIImage imageNamed:@"favourites_empty_screen_artwork_dark"]; + return AssetImages.favouritesEmptyScreenArtworkDark.image; } else { - return [UIImage imageNamed:@"favourites_empty_screen_artwork"]; + return AssetImages.favouritesEmptyScreenArtwork.image; } } diff --git a/Riot/Modules/GlobalSearch/DataSources/UnifiedSearchRecentsDataSource.m b/Riot/Modules/GlobalSearch/DataSources/UnifiedSearchRecentsDataSource.m index 59d169d7f..94daf7c44 100644 --- a/Riot/Modules/GlobalSearch/DataSources/UnifiedSearchRecentsDataSource.m +++ b/Riot/Modules/GlobalSearch/DataSources/UnifiedSearchRecentsDataSource.m @@ -35,18 +35,6 @@ @implementation UnifiedSearchRecentsDataSource -- (instancetype)init -{ - self = [super init]; - if (self) - { - searchedRoomIdOrAliasSection = -1; - - _hideRecents = NO; - } - return self; -} - #pragma mark - - (void)setPublicRoomsDirectoryDataSource:(PublicRoomsDirectoryDataSource *)publicRoomsDirectoryDataSource @@ -148,13 +136,13 @@ return count; } -- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame +- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView { UIView *sectionHeader = nil; if (section != searchedRoomIdOrAliasSection) { - sectionHeader = [super viewForHeaderInSection:section withFrame:frame]; + sectionHeader = [super viewForHeaderInSection:section withFrame:frame inTableView:tableView]; } return sectionHeader; diff --git a/Riot/Modules/GlobalSearch/Files/CellData/FilesSearchCellData.m b/Riot/Modules/GlobalSearch/Files/CellData/FilesSearchCellData.m index 6e509e089..5a183820f 100644 --- a/Riot/Modules/GlobalSearch/Files/CellData/FilesSearchCellData.m +++ b/Riot/Modules/GlobalSearch/Files/CellData/FilesSearchCellData.m @@ -132,19 +132,19 @@ if ([msgtype isEqualToString:kMXMessageTypeImage]) { - return [UIImage imageNamed:@"file_photo_icon"]; + return AssetImages.filePhotoIcon.image; } else if ([msgtype isEqualToString:kMXMessageTypeAudio]) { - return [UIImage imageNamed:@"file_music_icon"]; + return AssetImages.fileMusicIcon.image; } else if ([msgtype isEqualToString:kMXMessageTypeVideo]) { - return [UIImage imageNamed:@"file_video_icon"]; + return AssetImages.fileVideoIcon.image; } else if ([msgtype isEqualToString:kMXMessageTypeFile]) { - return [UIImage imageNamed:@"file_doc_icon"]; + return AssetImages.fileDocIcon.image; } return nil; diff --git a/Riot/Modules/GlobalSearch/Files/HomeFilesSearchViewController.m b/Riot/Modules/GlobalSearch/Files/HomeFilesSearchViewController.m index 5ec57f136..d51cfaa64 100644 --- a/Riot/Modules/GlobalSearch/Files/HomeFilesSearchViewController.m +++ b/Riot/Modules/GlobalSearch/Files/HomeFilesSearchViewController.m @@ -160,7 +160,7 @@ threadParameters = [[ThreadParameters alloc] initWithThreadId:event.threadId stackRoomScreen:NO]; } - else if ([self.mainSession.threadingService isEventThreadRoot:event]) + else if (event.unsignedData.relations.thread || [self.mainSession.threadingService isEventThreadRoot:event]) { threadParameters = [[ThreadParameters alloc] initWithThreadId:event.eventId stackRoomScreen:NO]; diff --git a/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m b/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m index e2633d41c..287a71a6f 100644 --- a/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m +++ b/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m @@ -107,19 +107,19 @@ switch (type) { case MXKAttachmentTypeImage: - image = [UIImage imageNamed:@"file_photo_icon"]; + image = AssetImages.filePhotoIcon.image; break; case MXKAttachmentTypeAudio: - image = [UIImage imageNamed:@"file_music_icon"]; + image = AssetImages.fileMusicIcon.image; break; case MXKAttachmentTypeVoiceMessage: - image = [UIImage imageNamed:@"file_music_icon"]; + image = AssetImages.fileMusicIcon.image; break; case MXKAttachmentTypeVideo: - image = [UIImage imageNamed:@"file_video_icon"]; + image = AssetImages.fileVideoIcon.image; break; case MXKAttachmentTypeFile: - image = [UIImage imageNamed:@"file_doc_icon"]; + image = AssetImages.fileDocIcon.image; break; default: break; diff --git a/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m b/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m index 14a736b36..f2f2cd406 100644 --- a/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m +++ b/Riot/Modules/GlobalSearch/Messages/DataSources/HomeMessagesSearchDataSource.m @@ -89,6 +89,10 @@ { continueBlock(); } + else if (result.result.unsignedData.relations.thread) + { + continueBlock(); + } else if (room) { [room liveTimeline:^(id liveTimeline) { @@ -164,14 +168,15 @@ { if (cellData.hasThreadRoot) { - MXThread *thread = cellData.bubbleComponents.firstObject.thread; - ThreadSummaryView *threadSummaryView = [[ThreadSummaryView alloc] initWithThread:thread]; + id thread = cellData.bubbleComponents.firstObject.thread; + ThreadSummaryView *threadSummaryView = [[ThreadSummaryView alloc] initWithThread:thread + session:self.mxSession]; [bubbleCell.tmpSubviews addObject:threadSummaryView]; threadSummaryView.translatesAutoresizingMaskIntoConstraints = NO; [bubbleCell.contentView addSubview:threadSummaryView]; - CGFloat leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin; + CGFloat leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin; CGFloat height = [ThreadSummaryView contentViewHeightForThread:thread fitting:cellData.maxTextViewWidth]; CGRect bubbleComponentFrame = [bubbleCell componentFrameInContentViewForIndex:0]; @@ -182,9 +187,9 @@ [threadSummaryView.leadingAnchor constraintEqualToAnchor:threadSummaryView.superview.leadingAnchor constant:leftMargin], [threadSummaryView.topAnchor constraintEqualToAnchor:threadSummaryView.superview.topAnchor - constant:bottomPositionY + RoomBubbleCellLayout.threadSummaryViewTopMargin], + constant:bottomPositionY + PlainRoomCellLayoutConstants.threadSummaryViewTopMargin], [threadSummaryView.heightAnchor constraintEqualToConstant:height], - [threadSummaryView.trailingAnchor constraintLessThanOrEqualToAnchor:threadSummaryView.superview.trailingAnchor constant:-RoomBubbleCellLayout.reactionsViewRightMargin] + [threadSummaryView.trailingAnchor constraintLessThanOrEqualToAnchor:threadSummaryView.superview.trailingAnchor constant:-PlainRoomCellLayoutConstants.reactionsViewRightMargin] ]]; } else if (event.isInThread) @@ -195,7 +200,7 @@ fromAThreadView.translatesAutoresizingMaskIntoConstraints = NO; [bubbleCell.contentView addSubview:fromAThreadView]; - CGFloat leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin; + CGFloat leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin; CGFloat height = [FromAThreadView contentViewHeightForEvent:event fitting:cellData.maxTextViewWidth]; CGRect bubbleComponentFrame = [bubbleCell componentFrameInContentViewForIndex:0]; @@ -206,9 +211,9 @@ [fromAThreadView.leadingAnchor constraintEqualToAnchor:fromAThreadView.superview.leadingAnchor constant:leftMargin], [fromAThreadView.topAnchor constraintEqualToAnchor:fromAThreadView.superview.topAnchor - constant:bottomPositionY + RoomBubbleCellLayout.fromAThreadViewTopMargin], + constant:bottomPositionY + PlainRoomCellLayoutConstants.fromAThreadViewTopMargin], [fromAThreadView.heightAnchor constraintEqualToConstant:height], - [fromAThreadView.trailingAnchor constraintLessThanOrEqualToAnchor:fromAThreadView.superview.trailingAnchor constant:-RoomBubbleCellLayout.reactionsViewRightMargin] + [fromAThreadView.trailingAnchor constraintLessThanOrEqualToAnchor:fromAThreadView.superview.trailingAnchor constant:-PlainRoomCellLayoutConstants.reactionsViewRightMargin] ]]; } } diff --git a/Riot/Modules/GlobalSearch/Messages/HomeMessagesSearchViewController.m b/Riot/Modules/GlobalSearch/Messages/HomeMessagesSearchViewController.m index b8ba693be..8ba3d8241 100644 --- a/Riot/Modules/GlobalSearch/Messages/HomeMessagesSearchViewController.m +++ b/Riot/Modules/GlobalSearch/Messages/HomeMessagesSearchViewController.m @@ -167,7 +167,7 @@ threadParameters = [[ThreadParameters alloc] initWithThreadId:event.threadId stackRoomScreen:NO]; } - else if ([self.mainSession.threadingService isEventThreadRoot:event]) + else if (event.unsignedData.relations.thread || [self.mainSession.threadingService isEventThreadRoot:event]) { threadParameters = [[ThreadParameters alloc] initWithThreadId:event.eventId stackRoomScreen:NO]; diff --git a/Riot/Modules/GlobalSearch/UnifiedSearchViewController.m b/Riot/Modules/GlobalSearch/UnifiedSearchViewController.m index 8c1956330..f701d8b08 100644 --- a/Riot/Modules/GlobalSearch/UnifiedSearchViewController.m +++ b/Riot/Modules/GlobalSearch/UnifiedSearchViewController.m @@ -105,9 +105,6 @@ [self initWithTitles:titles viewControllers:viewControllers defaultSelected:0]; [super viewDidLoad]; - - // Add the Riot background image when search bar is empty - [self addBackgroundImageViewToView:self.view]; // Initialize here the data sources if a matrix session has been already set. [self initializeDataSources]; @@ -118,18 +115,6 @@ [super showSearch:NO]; } -- (void)userInterfaceThemeDidChange -{ - [super userInterfaceThemeDidChange]; - - UIImageView *backgroundImageView = self.backgroundImageView; - if (backgroundImageView) - { - UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:ThemeService.shared.theme.matrixSearchBackgroundImageTintColor]; - backgroundImageView.image = image; - } -} - - (void)destroy { [super destroy]; @@ -178,9 +163,6 @@ { [super viewDidAppear:animated]; - // Here the actual view size is available, check the background image display if any - [self checkAndShowBackgroundImage]; - if (self.splitViewController && !self.splitViewController.isCollapsed) { // In case of split view controller where the primary and secondary view controllers are displayed side-by-side onscreen, @@ -189,13 +171,6 @@ } } -- (void)viewDidLayoutSubviews -{ - [super viewDidLayoutSubviews]; - - [self checkAndShowBackgroundImage]; -} - - (UIStatusBarStyle)preferredStatusBarStyle { return ThemeService.shared.theme.statusBarStyle; @@ -251,7 +226,7 @@ peopleSearchDataSource.showLocalContacts = NO; peopleSearchDataSource.areSectionsShrinkable = YES; peopleSearchDataSource.displaySearchInputInContactsList = YES; - peopleSearchDataSource.contactCellAccessoryImage = [[UIImage imageNamed: @"disclosure_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textSecondaryColor];; + peopleSearchDataSource.contactCellAccessoryImage = [AssetImages.disclosureIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textSecondaryColor];; [peopleSearchViewController displayList:peopleSearchDataSource]; // Check whether there are others sessions @@ -326,15 +301,6 @@ #pragma mark - Override MXKViewController -- (void)setKeyboardHeight:(CGFloat)keyboardHeight -{ - [self setKeyboardHeightForBackgroundImage:keyboardHeight]; - - [super setKeyboardHeight:keyboardHeight]; - - [self checkAndShowBackgroundImage]; -} - - (void)startActivityIndicator { // Redirect the operation to the currently displayed VC @@ -352,58 +318,6 @@ } } -#pragma mark - Override UIViewController+VectorSearch - -- (void)setKeyboardHeightForBackgroundImage:(CGFloat)keyboardHeight -{ - [super setKeyboardHeightForBackgroundImage:keyboardHeight]; - - if (keyboardHeight > 0) - { - [self checkAndShowBackgroundImage]; - } -} - -// Check conditions before displaying the background -- (void)checkAndShowBackgroundImage -{ - // Note: This background is hidden when keyboard is dismissed. - // The other conditions depend on the current selected view controller. - if (self.selectedViewController == recentsViewController) - { - self.backgroundImageView.hidden = YES; - } - else if (self.selectedViewController == messagesSearchViewController) - { - self.backgroundImageView.hidden = ((messagesSearchDataSource.serverCount != 0) || !messagesSearchViewController.noResultsLabel.isHidden || (self.keyboardHeight == 0)); - } - else if (self.selectedViewController == peopleSearchViewController) - { - self.backgroundImageView.hidden = (([peopleSearchViewController.contactsTableView numberOfSections] != 0) || (self.keyboardHeight == 0)); - } - else if (self.selectedViewController == filesSearchViewController) - { - self.backgroundImageView.hidden = ((filesSearchDataSource.serverCount != 0) || !filesSearchViewController.noResultsLabel.isHidden || (self.keyboardHeight == 0)); - } - else - { - self.backgroundImageView.hidden = (self.keyboardHeight == 0); - } - - if (!self.backgroundImageView.hidden) - { - [self.backgroundImageView layoutIfNeeded]; - [self.selectedViewController.view layoutIfNeeded]; - - // Check whether there is enough space to display this background - // For example, in landscape with the iPhone 5 & 6 screen size, the backgroundImageView must be hidden. - if (self.backgroundImageView.frame.origin.y < 0 || (self.selectedViewController.view.frame.size.height - self.backgroundImageViewBottomConstraint.constant) < self.backgroundImageView.frame.size.height) - { - self.backgroundImageView.hidden = YES; - } - } -} - #pragma mark - Override SegmentedViewController - (void)setSelectedIndex:(NSUInteger)selectedIndex @@ -463,7 +377,6 @@ if (self.searchBar.text.length) { recentsDataSource.hideRecents = NO; - self.backgroundImageView.hidden = YES; // Forward the search request to the data source if (self.selectedViewController == recentsViewController) @@ -482,8 +395,8 @@ // Do it asynchronously to give time to messagesSearchViewController to be set up // so that it can display its loading wheel dispatch_async(dispatch_get_main_queue(), ^{ - [messagesSearchDataSource searchMessages:self.searchBar.text force:NO]; - messagesSearchViewController.shouldScrollToBottomOnRefresh = YES; + [self->messagesSearchDataSource searchMessages:self.searchBar.text force:NO]; + self->messagesSearchViewController.shouldScrollToBottomOnRefresh = YES; }); } } @@ -499,8 +412,8 @@ // Do it asynchronously to give time to filesSearchViewController to be set up // so that it can display its loading wheel dispatch_async(dispatch_get_main_queue(), ^{ - [filesSearchDataSource searchMessages:self.searchBar.text force:NO]; - filesSearchViewController.shouldScrollToBottomOnRefresh = YES; + [self->filesSearchDataSource searchMessages:self.searchBar.text force:NO]; + self->filesSearchViewController.shouldScrollToBottomOnRefresh = YES; }); } } @@ -524,8 +437,6 @@ [filesSearchDataSource searchMessages:nil force:NO]; } } - - [self checkAndShowBackgroundImage]; } #pragma mark - UISearchBarDelegate diff --git a/Riot/Modules/GlobalSearch/Views/RoomIdOrAliasTableViewCell.m b/Riot/Modules/GlobalSearch/Views/RoomIdOrAliasTableViewCell.m index 0a798aefc..820f2ac03 100644 --- a/Riot/Modules/GlobalSearch/Views/RoomIdOrAliasTableViewCell.m +++ b/Riot/Modules/GlobalSearch/Views/RoomIdOrAliasTableViewCell.m @@ -49,7 +49,7 @@ } else { - self.avatarImageView.image = [MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + self.avatarImageView.image = [MXKTools paintImage:AssetImages.placeholder.image withColor:ThemeService.shared.theme.tintColor]; } diff --git a/Riot/Modules/Home/Fallback/AuthFallBackViewController.m b/Riot/Modules/Home/Fallback/AuthFallBackViewController.m index 93a770610..50f7b5f5c 100644 --- a/Riot/Modules/Home/Fallback/AuthFallBackViewController.m +++ b/Riot/Modules/Home/Fallback/AuthFallBackViewController.m @@ -157,7 +157,7 @@ NSString *FallBackViewControllerJavascriptOnLogin = @"window.matrixLogin.onLogin if ([urlString hasPrefix:@"js:"]) { // Listen only to scheme of the JS-WKWebView bridge - NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding]; + NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; NSError *error; diff --git a/Riot/Modules/Home/HomeViewController.m b/Riot/Modules/Home/HomeViewController.m index 3a02c310e..e94b4c472 100644 --- a/Riot/Modules/Home/HomeViewController.m +++ b/Riot/Modules/Home/HomeViewController.m @@ -89,7 +89,7 @@ self.recentsTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; // Add the (+) button programmatically - plusButtonImageView = [self vc_addFABWithImage:[UIImage imageNamed:@"plus_floating_action"] + plusButtonImageView = [self vc_addFABWithImage:AssetImages.plusFloatingAction.image target:self action:@selector(onPlusButtonPressed)]; @@ -416,19 +416,19 @@ // Update the edition menu content (Use the button tag to store the current value). tableViewCell.directChatButton.tag = room.isDirect; [tableViewCell.directChatButton addTarget:self action:@selector(onDirectChatButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - tableViewCell.directChatImageView.image = [UIImage imageNamed:@"room_action_direct_chat"]; + tableViewCell.directChatImageView.image = AssetImages.roomActionDirectChat.image; tableViewCell.directChatImageView.tintColor = room.isDirect ? selectedColor : unselectedColor; tableViewCell.notificationsButton.tag = room.isMute || room.isMentionsOnly; [tableViewCell.notificationsButton addTarget:self action:@selector(onNotificationsButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - if ([BuildSettings showNotificationsV2]) + if ([BuildSettings showNotificationsV2] && tableViewCell.notificationsButton.tag) { - tableViewCell.notificationsImageView.image = tableViewCell.notificationsButton.tag ? [UIImage imageNamed:@"room_action_notification_muted"] : [UIImage imageNamed:@"room_action_notification"]; + tableViewCell.notificationsImageView.image = AssetImages.roomActionNotificationMuted.image; } else { - tableViewCell.notificationsImageView.image = [UIImage imageNamed:@"room_action_notification"]; + tableViewCell.notificationsImageView.image = AssetImages.roomActionNotification.image; } tableViewCell.notificationsImageView.tintColor = tableViewCell.notificationsButton.tag ? unselectedColor : selectedColor; @@ -446,16 +446,16 @@ tableViewCell.favouriteButton.tag = (currentTag && [kMXRoomTagFavourite isEqualToString:currentTag.name]); [tableViewCell.favouriteButton addTarget:self action:@selector(onFavouriteButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - tableViewCell.favouriteImageView.image = [UIImage imageNamed:@"room_action_favourite"]; + tableViewCell.favouriteImageView.image = AssetImages.roomActionFavourite.image; tableViewCell.favouriteImageView.tintColor = tableViewCell.favouriteButton.tag ? selectedColor : unselectedColor; tableViewCell.priorityButton.tag = (currentTag && [kMXRoomTagLowPriority isEqualToString:currentTag.name]); [tableViewCell.priorityButton addTarget:self action:@selector(onPriorityButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - tableViewCell.priorityImageView.image = tableViewCell.priorityButton.tag ? [UIImage imageNamed:@"room_action_priority_high"] : [UIImage imageNamed:@"room_action_priority_low"]; + tableViewCell.priorityImageView.image = tableViewCell.priorityButton.tag ? AssetImages.roomActionPriorityHigh.image : AssetImages.roomActionPriorityLow.image; tableViewCell.priorityImageView.tintColor = unselectedColor; [tableViewCell.leaveButton addTarget:self action:@selector(onLeaveButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - tableViewCell.leaveImageView.image = [UIImage imageNamed:@"room_action_leave"]; + tableViewCell.leaveImageView.image = AssetImages.roomActionLeave.image; tableViewCell.leaveImageView.tintColor = unselectedColor; } } @@ -1048,11 +1048,11 @@ { if (ThemeService.shared.isCurrentThemeDark) { - return [UIImage imageNamed:@"home_empty_screen_artwork_dark"]; + return AssetImages.homeEmptyScreenArtworkDark.image; } else { - return [UIImage imageNamed:@"home_empty_screen_artwork"]; + return AssetImages.homeEmptyScreenArtwork.image; } } diff --git a/Riot/Modules/Home/VersionCheck/HomeViewControllerWithBannerWrapperViewController.swift b/Riot/Modules/Home/VersionCheck/HomeViewControllerWithBannerWrapperViewController.swift index 764dc2d59..1c6c24173 100644 --- a/Riot/Modules/Home/VersionCheck/HomeViewControllerWithBannerWrapperViewController.swift +++ b/Riot/Modules/Home/VersionCheck/HomeViewControllerWithBannerWrapperViewController.swift @@ -95,6 +95,10 @@ class HomeViewControllerWithBannerWrapperViewController: UIViewController, MXKVi homeViewController.activityIndicator = newValue } } + + var providesCustomActivityIndicator: Bool { + return homeViewController.providesCustomActivityIndicator + } func startActivityIndicator() { homeViewController.startActivityIndicator() diff --git a/Riot/Modules/Home/Views/AccessibleCollectionView.swift b/Riot/Modules/Home/Views/AccessibleCollectionView.swift new file mode 100644 index 000000000..9ac0d1f16 --- /dev/null +++ b/Riot/Modules/Home/Views/AccessibleCollectionView.swift @@ -0,0 +1,37 @@ +// +// Copyright 2021 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 UIKit + +/// A collection view that returns an accessibility element count equal to the number of +/// items in its first section. This allows voiceover user to swipe through the entire collection. +class AccessibleCollectionView: UICollectionView { + private var numberOfItemsInFirstSection = 0 + + override func accessibilityElementCount() -> Int { + return numberOfItemsInFirstSection + } + + override func numberOfItems(inSection section: Int) -> Int { + let numberOfItems = super.numberOfItems(inSection: section) + + if section == 0 { + numberOfItemsInFirstSection = numberOfItems + } + + return numberOfItems + } +} diff --git a/Riot/Modules/Home/Views/RoomCollectionViewCell.m b/Riot/Modules/Home/Views/RoomCollectionViewCell.m index f13d7bc3b..a6e115192 100644 --- a/Riot/Modules/Home/Views/RoomCollectionViewCell.m +++ b/Riot/Modules/Home/Views/RoomCollectionViewCell.m @@ -52,6 +52,8 @@ [path closePath]; // arrow top side arrowMaskLayer.path = path.CGPath; self.editionArrowView.layer.mask = arrowMaskLayer; + + self.isAccessibilityElement = YES; } - (void)customizeCollectionViewCellRendering @@ -86,6 +88,8 @@ self.roomTitle1.hidden = YES; self.roomTitle2.hidden = YES; + NSMutableString *accessibilityLabel = [self.roomTitle.text mutableCopy]; + // Check whether the room display name is an alias to keep visible the HS. if ([MXTools isMatrixRoomAlias:roomCellData.roomDisplayname]) { @@ -97,6 +101,7 @@ self.roomTitle1.text = [roomCellData.roomDisplayname substringToIndex:range.location + 1]; self.roomTitle2.hidden = NO; self.roomTitle2.text = [roomCellData.roomDisplayname substringFromIndex:range.location + 1]; + accessibilityLabel = [[NSString stringWithFormat:@"%@, %@", self.roomTitle1.text, self.roomTitle2.text] mutableCopy]; } } @@ -118,6 +123,10 @@ self.badgeLabel.hidden = NO; self.badgeLabel.badgeColor = roomCellData.highlightCount ? ThemeService.shared.theme.noticeColor : ThemeService.shared.theme.noticeSecondaryColor; self.badgeLabel.text = roomCellData.notificationCountStringValue; + + NSUInteger count = roomCellData.notificationCount; + NSString *newMessagesLabel = count == 1 ? [VectorL10n roomNewMessageNotification:count] : [VectorL10n roomNewMessagesNotification:count]; + [accessibilityLabel appendFormat:@", %@", newMessagesLabel]; } // Use bold font for the room title @@ -130,6 +139,8 @@ } + self.accessibilityLabel = accessibilityLabel; + [self.roomAvatar vc_setRoomAvatarImageWith:roomCellData.avatarUrl roomId:roomCellData.roomIdentifier displayName:roomCellData.roomDisplayname diff --git a/Riot/Modules/Home/Views/TableViewCellWithCollectionView.m b/Riot/Modules/Home/Views/TableViewCellWithCollectionView.m index 4da67b857..694a55089 100644 --- a/Riot/Modules/Home/Views/TableViewCellWithCollectionView.m +++ b/Riot/Modules/Home/Views/TableViewCellWithCollectionView.m @@ -30,6 +30,11 @@ static CGFloat const kEditionViewCornerRadius = 10.0; self.editionViewBottomConstraint.constant = 0; self.editionView.layer.masksToBounds = YES; + + // Hide both the cell and its collection view from voiceover. + // Instead we expose the individual cells as accessibility elements. + self.isAccessibilityElement = NO; + self.collectionView.isAccessibilityElement = NO; } - (void)customizeTableViewCellRendering diff --git a/Riot/Modules/Home/Views/TableViewCellWithCollectionView.xib b/Riot/Modules/Home/Views/TableViewCellWithCollectionView.xib index 2031df16a..518de25d1 100644 --- a/Riot/Modules/Home/Views/TableViewCellWithCollectionView.xib +++ b/Riot/Modules/Home/Views/TableViewCellWithCollectionView.xib @@ -1,9 +1,9 @@ - + - + @@ -16,7 +16,7 @@ - + @@ -83,7 +83,7 @@ - + @@ -155,5 +155,8 @@ + + + diff --git a/Riot/Modules/Integrations/WidgetPermission/WidgetPermissionViewController.swift b/Riot/Modules/Integrations/WidgetPermission/WidgetPermissionViewController.swift index 2bb3a48bd..fbee5bb4b 100644 --- a/Riot/Modules/Integrations/WidgetPermission/WidgetPermissionViewController.swift +++ b/Riot/Modules/Integrations/WidgetPermission/WidgetPermissionViewController.swift @@ -218,8 +218,6 @@ extension WidgetPermissionViewController: SlidingModalPresentable { let widthConstraint = sizingViewContollerView.widthAnchor.constraint(equalToConstant: width) widthConstraint.isActive = true WidgetPermissionViewController.Sizing.widthConstraint = widthConstraint - - sizingViewContollerView.heightAnchor.constraint(equalToConstant: 0) } sizingViewContollerView.layoutIfNeeded() diff --git a/Riot/Modules/Integrations/Widgets/WidgetViewController.m b/Riot/Modules/Integrations/Widgets/WidgetViewController.m index a5e93b152..77056bcb0 100644 --- a/Riot/Modules/Integrations/Widgets/WidgetViewController.m +++ b/Riot/Modules/Integrations/Widgets/WidgetViewController.m @@ -60,7 +60,7 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse { self.navigationItem.title = widget.name ? widget.name : widget.type; - UIBarButtonItem *menuButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"room_context_menu_more"] style:UIBarButtonItemStylePlain target:self action:@selector(onMenuButtonPressed:)]; + UIBarButtonItem *menuButton = [[UIBarButtonItem alloc] initWithImage:AssetImages.roomContextMenuMore.image style:UIBarButtonItemStylePlain target:self action:@selector(onMenuButtonPressed:)]; self.navigationItem.rightBarButtonItem = menuButton; } diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift index 0c33c5915..fd2ab986a 100644 --- a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift @@ -143,7 +143,7 @@ final class KeyBackupSetupSuccessFromRecoveryKeyViewController: UIViewController private func shareRecoveryKey() { // Set up activity view controller - let activityItems: [Any] = [ self.recoveryKey ] + let activityItems: [Any] = [ self.recoveryKey as Any ] let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil) activityViewController.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in diff --git a/Riot/Modules/LaunchLoading/LaunchLoadingViewController.swift b/Riot/Modules/LaunchLoading/LaunchLoadingViewController.swift new file mode 100644 index 000000000..bd7dba409 --- /dev/null +++ b/Riot/Modules/LaunchLoading/LaunchLoadingViewController.swift @@ -0,0 +1,38 @@ +// +// Copyright 2021 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 UIKit +import Reusable + +class LaunchLoadingViewController: UIViewController, Reusable { + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + init() { + super.init(nibName: "LaunchLoadingViewController", bundle: nil) + + let launchLoadingView = LaunchLoadingView.instantiate() + launchLoadingView.update(theme: ThemeService.shared().theme) + view.vc_addSubViewMatchingParent(launchLoadingView) + + // The launch time isn't profiled for analytics as it's presentation length + // will be artificially changed based on other views in the flow. + } + + override func viewWillAppear(_ animated: Bool) { + navigationController?.setNavigationBarHidden(true, animated: false) + } +} diff --git a/Riot/Modules/LaunchLoading/LaunchLoadingViewController.xib b/Riot/Modules/LaunchLoading/LaunchLoadingViewController.xib new file mode 100644 index 000000000..19628372f --- /dev/null +++ b/Riot/Modules/LaunchLoading/LaunchLoadingViewController.xib @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/LaunchLoading/LoadingAnimation/ElementView.swift b/Riot/Modules/LaunchLoading/LoadingAnimation/ElementView.swift index f65e8a124..2e80a9c6f 100644 --- a/Riot/Modules/LaunchLoading/LoadingAnimation/ElementView.swift +++ b/Riot/Modules/LaunchLoading/LoadingAnimation/ElementView.swift @@ -102,7 +102,7 @@ public class ElementView: UIView { path.layer.bounds = CGRect(x: 0, y: 0, width: 55.2, height: 55.2) path.layer.masksToBounds = false path.shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd - path.shapeLayer.fillColor = UIColor(displayP3Red: 0.052, green: 0.743, blue: 0.543, alpha: 1).cgColor + path.shapeLayer.fillColor = UIColor(rgb: 0x0DBD8B).cgColor path.shapeLayer.lineDashPattern = [] path.shapeLayer.lineDashPhase = 0 path.shapeLayer.lineWidth = 0 @@ -120,7 +120,7 @@ public class ElementView: UIView { path_1.layer.bounds = CGRect(x: 0, y: 0, width: 55.2, height: 55.2) path_1.layer.masksToBounds = false path_1.shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd - path_1.shapeLayer.fillColor = UIColor(displayP3Red: 0.052, green: 0.743, blue: 0.543, alpha: 1).cgColor + path_1.shapeLayer.fillColor = UIColor(rgb: 0x0DBD8B).cgColor path_1.shapeLayer.lineDashPattern = [] path_1.shapeLayer.lineDashPhase = 0 path_1.shapeLayer.lineWidth = 0 @@ -138,7 +138,7 @@ public class ElementView: UIView { path_2.layer.bounds = CGRect(x: 0, y: 0, width: 55.2, height: 55.2) path_2.layer.masksToBounds = false path_2.shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd - path_2.shapeLayer.fillColor = UIColor(displayP3Red: 0.052, green: 0.743, blue: 0.543, alpha: 1).cgColor + path_2.shapeLayer.fillColor = UIColor(rgb: 0x0DBD8B).cgColor path_2.shapeLayer.lineDashPattern = [] path_2.shapeLayer.lineDashPhase = 0 path_2.shapeLayer.lineWidth = 0 @@ -156,7 +156,7 @@ public class ElementView: UIView { path_3.layer.bounds = CGRect(x: 0, y: 0, width: 55.2, height: 55.2) path_3.layer.masksToBounds = false path_3.shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd - path_3.shapeLayer.fillColor = UIColor(displayP3Red: 0.052, green: 0.743, blue: 0.543, alpha: 1).cgColor + path_3.shapeLayer.fillColor = UIColor(rgb: 0x0DBD8B).cgColor path_3.shapeLayer.lineDashPattern = [] path_3.shapeLayer.lineDashPhase = 0 path_3.shapeLayer.lineWidth = 0 diff --git a/Riot/Modules/MajorUpdate/MajorUpdateViewController.swift b/Riot/Modules/MajorUpdate/MajorUpdateViewController.swift index 0b57526ef..e10c7e715 100644 --- a/Riot/Modules/MajorUpdate/MajorUpdateViewController.swift +++ b/Riot/Modules/MajorUpdate/MajorUpdateViewController.swift @@ -165,8 +165,6 @@ extension MajorUpdateViewController: SlidingModalPresentable { let widthConstraint = sizingViewContollerView.widthAnchor.constraint(equalToConstant: width) widthConstraint.isActive = true MajorUpdateViewController.Sizing.widthConstraint = widthConstraint - - sizingViewContollerView.heightAnchor.constraint(equalToConstant: 0) } sizingViewContollerView.layoutIfNeeded() diff --git a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/en.lproj/MatrixKit.strings b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/en.lproj/MatrixKit.strings index b7937f194..1b6c229ac 100644 --- a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/en.lproj/MatrixKit.strings +++ b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/en.lproj/MatrixKit.strings @@ -367,7 +367,7 @@ "notice_room_join" = "%@ joined"; "notice_room_leave" = "%@ left"; "notice_room_reject" = "%@ rejected the invitation"; -"notice_room_kick" = "%@ kicked %@"; +"notice_room_kick" = "%@ removed %@"; "notice_room_unban" = "%@ unbanned %@"; "notice_room_ban" = "%@ banned %@"; "notice_room_withdraw" = "%@ withdrew %@'s invitation"; @@ -399,7 +399,7 @@ "notice_room_join_by_you" = "You joined"; "notice_room_leave_by_you" = "You left"; "notice_room_reject_by_you" = "You rejected the invitation"; -"notice_room_kick_by_you" = "You kicked %@"; +"notice_room_kick_by_you" = "You removed %@"; "notice_room_unban_by_you" = "You unbanned %@"; "notice_room_ban_by_you" = "You banned %@"; "notice_room_withdraw_by_you" = "You withdrew %@'s invitation"; @@ -479,7 +479,7 @@ "num_members_one" = "%@ user"; "num_members_other" = "%@ users"; "invite" = "Invite"; -"kick" = "Kick"; +"kick" = "Remove from chat"; "ban" = "Ban"; "unban" = "Un-ban"; "message_unsaved_changes" = "There are unsaved changes. Leaving will discard them."; diff --git a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/fr.lproj/MatrixKit.strings b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/fr.lproj/MatrixKit.strings index 03688dc76..380b96677 100644 --- a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/fr.lproj/MatrixKit.strings +++ b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/fr.lproj/MatrixKit.strings @@ -478,3 +478,4 @@ "attachment_unsupported_preview_message" = "Ce type de fichier n’est pas pris en charge."; "attachment_unsupported_preview_title" = "Prévisualisation impossible"; "room_displayname_all_other_members_left" = "%@ (Quitté)"; +"message_reply_to_sender_sent_their_location" = "a partagé sa localisation."; diff --git a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/ja.lproj/MatrixKit.strings b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/ja.lproj/MatrixKit.strings index c4918a35e..b6611ded7 100644 --- a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/ja.lproj/MatrixKit.strings +++ b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/ja.lproj/MatrixKit.strings @@ -2,9 +2,9 @@ // Login Screen "login_create_account" = "アカウント作成:"; "login_server_url_placeholder" = "URL (例 https://matrix.org)"; -"login_home_server_title" = "接続先サーバURL:"; -"login_home_server_info" = "あなたの接続先サーバは、あなたの全ての会話とアカウント情報を保存します"; -"login_identity_server_title" = "認証サーバURL:"; +"login_home_server_title" = "接続先サーバーURL:"; +"login_home_server_info" = "あなたの接続先サーバーは、あなたの全ての会話とアカウント情報を保存します"; +"login_identity_server_title" = "認証サーバーURL:"; "login_password_placeholder" = "パスワード"; "login_email_placeholder" = "メールアドレス"; // Action @@ -12,11 +12,11 @@ "yes" = "はい"; "back" = "戻る"; "close" = "閉じる"; -"continue" = "続く"; +"continue" = "続ける"; "sign_up" = "登録"; -"resend_message" = "メッセージを再送信する"; +"resend_message" = "メッセージを再送信"; "select_all" = "全て選択"; -"show_details" = "詳細を表示する"; +"show_details" = "詳細を表示"; "login_identity_server_info" = "Matrixは、どの電子メールなどがどのMatrix IDに属しているかを追跡するアイデンティティサーバーを提供します。 現在 https://matrix.org のみが存在します。"; "login_user_id_placeholder" = "Matrix ID(例 @bob:matrix.org または bob)"; "login_optional_field" = "オプション"; @@ -25,16 +25,16 @@ "login_prompt_email_token" = "メールの検証トークンを入力してください:"; "login_error_title" = "ログインに失敗しました"; "login_error_no_login_flow" = "このホームサーバーから認証情報を取得できませんでした"; -"login_error_do_not_support_login_flows" = "現在、このホームサーバーによって定義されたログインフローの一部またはすべてをサポートしていません"; +"login_error_do_not_support_login_flows" = "現在、このホームサーバーによって定義されたログインフローの一部または全てをサポートしていません"; "login_error_registration_is_not_supported" = "登録は現在サポートされていません"; "login_error_forbidden" = "無効なユーザー名/パスワード"; "login_error_unknown_token" = "指定されたアクセストークンが認識されませんでした"; "login_error_bad_json" = "不正な形式のJSON"; "login_error_not_json" = "有効なJSONを含んでいませんでした"; "login_error_limit_exceeded" = "あまりにも多くのリクエストが送られました"; -"login_error_user_in_use" = "このユーザー名はすでに使用されています"; +"login_error_user_in_use" = "このユーザー名は既に使用されています"; "login_error_login_email_not_yet" = "まだクリックされていないメールリンク"; -"login_use_fallback" = "フォールバックページを使用する"; +"login_use_fallback" = "フォールバックページを使用"; "login_leave_fallback" = "キャンセル"; "login_invalid_param" = "無効なパラメーター"; "register_error_title" = "登録に失敗しました"; @@ -46,46 +46,46 @@ "login_error_resource_limit_exceeded_message_default" = "このホームサーバーは、リソース制限の1つを超えています。"; "login_error_resource_limit_exceeded_message_monthly_active_user" = "このホームサーバーは、月間アクティブユーザー制限を超えています。"; "login_error_resource_limit_exceeded_message_contact" = "\n\nこのサービスを続行するには、サービス管理者に連絡してください。"; -"login_error_resource_limit_exceeded_contact_button" = "管理者に連絡する"; -"abort" = "中断する"; +"login_error_resource_limit_exceeded_contact_button" = "管理者に連絡"; +"abort" = "中断"; "discard" = "破棄"; -"dismiss" = "却下する"; +"dismiss" = "却下"; "retry" = "再試行"; "submit" = "提出"; "submit_code" = "コードを送信"; "set_default_power_level" = "権限レベルをリセット"; "set_moderator" = "モデレーターを設定"; "set_admin" = "管理者を設定"; -"start_chat" = "チャットを始める"; +"start_chat" = "チャットを開始"; "start_voice_call" = "音声通話を開始"; "start_video_call" = "ビデオ通話を開始"; -"mention" = "記載"; +"mention" = "メンション"; "select_account" = "アカウントを選択"; "attach_media" = "ライブラリからメディアを添付"; "capture_media" = "写真/ビデオを撮る"; -"invite_user" = "matrixユーザーを招待"; +"invite_user" = "Matrixユーザーを招待"; "reset_to_default" = "デフォルトにリセット"; "cancel_upload" = "アップロードをキャンセル"; "cancel_download" = "ダウンロードをキャンセル"; -"answer_call" = "通話に応答する"; -"reject_call" = "通話を拒否する"; +"answer_call" = "通話に応答"; +"reject_call" = "通話を拒否"; "end_call" = "通話終了"; "ignore" = "無視"; // Events formatter -"notice_avatar_changed_too" = "(アバターも変わった)"; -"notice_room_name_removed" = "%@は部屋名を削除しました"; -"notice_room_topic_removed" = "%@はトピックを削除しました"; +"notice_avatar_changed_too" = "(アバターも変更されました)"; +"notice_room_name_removed" = "%@がルーム名を削除しました"; +"notice_room_topic_removed" = "%@がトピックを削除しました"; "notice_event_redacted" = "<編集された%@>"; "notice_event_redacted_by" = " %@により"; "notice_event_redacted_reason" = " [理由: %@]"; -"notice_profile_change_redacted" = "%@は彼らのプロフィール %@を更新しました"; -"notice_room_created" = "%@は部屋を作成しました"; +"notice_profile_change_redacted" = "%@が彼らのプロフィール %@を更新しました"; +"notice_room_created" = "%@がルームを作成しました"; "notice_room_join_rule" = "結合ルールは次のとおり: %@"; "notice_room_power_level_intro" = "ルームメンバーの権限レベル:"; "notice_room_power_level_acting_requirement" = "アクション前にユーザーの必要な最小権限レベル:"; "notice_room_power_level_event_requirement" = "イベントに関連する最小権限レベル:"; "notice_room_aliases" = "ルームエイリアス: %@"; -"notice_room_related_groups" = "この部屋に関連付けられたグループ: %@"; +"notice_room_related_groups" = "このルームに関連付けられたグループ: %@"; "notice_encrypted_message" = "暗号化されたメッセージ"; "notice_encryption_enabled" = "%@はエンドツーエンド暗号化を有効にする (アルゴリズム %@)"; "notice_image_attachment" = "画像添付"; @@ -100,16 +100,16 @@ "notice_error_unsupported_event" = "サポートされていないイベント"; "notice_error_unexpected_event" = "予期しないイベント"; "notice_error_unknown_event_type" = "不明なイベントタイプ"; -"notice_room_history_visible_to_anyone" = "%@ 誰でも将来の部屋履歴を表示されます。"; -"notice_room_history_visible_to_members" = "%@ ルームメンバー全員に将来の部屋履歴を表示されます。"; -"notice_room_history_visible_to_members_from_invited_point" = "%@ ルームメンバー全員に招待した時点からの部屋履歴を表示されます。"; -"notice_room_history_visible_to_members_from_joined_point" = "%@ ルームメンバー全員に参加した時点からの部屋履歴を表示されます。"; +"notice_room_history_visible_to_anyone" = "%@が今後のルーム履歴を「誰でも」閲覧可能に設定しました。"; +"notice_room_history_visible_to_members" = "%@が今後のルーム履歴を「メンバーのみ」閲覧可能に設定しました。"; +"notice_room_history_visible_to_members_from_invited_point" = "%@が今後のルーム履歴を「メンバーのみ (招待された時点以降)」閲覧可能に設定しました。"; +"notice_room_history_visible_to_members_from_joined_point" = "%@が今後のルーム履歴を「メンバーのみ (参加した時点以降)」閲覧可能に設定しました。"; "notice_crypto_unable_to_decrypt" = "** 復号化できません: %@ **"; -"notice_crypto_error_unknown_inbound_session_id" = "送信者のセッションからこのメッセージのキーが送信されていません。"; +"notice_crypto_error_unknown_inbound_session_id" = "送信者のセッションからこのメッセージ用の鍵が送信されていません。"; "notice_sticker" = "ステッカー"; -"notice_in_reply_to" = "に返信"; +"notice_in_reply_to" = "返信先"; // room display name -"room_displayname_empty_room" = "空の部屋"; +"room_displayname_empty_room" = "空のルーム"; "room_displayname_two_members" = "%@ と %@"; "room_displayname_more_than_two_members" = "%@ と %@ 他"; // Settings @@ -117,26 +117,26 @@ "settings_enable_inapp_notifications" = "アプリ内通知を有効にする"; "settings_enable_push_notifications" = "プッシュ通知を有効にする"; "settings_enter_validation_token_for" = "%@の検証トークンを入力:"; -"notification_settings_room_rule_title" = "部屋: '%@'"; +"notification_settings_room_rule_title" = "ルーム: '%@'"; // Devices "device_details_title" = "セッション情報\n"; "device_details_name" = "名前\n"; "device_details_identifier" = "ID\n"; -"device_details_last_seen" = "ラストシーン\n"; +"device_details_last_seen" = "最終接続日\n"; "device_details_last_seen_format" = "%@ @ %@\n"; -"device_details_rename_prompt_message" = "端末名:"; +"device_details_rename_prompt_message" = "セッションの公開名は、あなたとやり取りする人々に対して表示されます"; "device_details_delete_prompt_title" = "認証"; "device_details_delete_prompt_message" = "この操作には、追加の認証が必要です。\n続行するには、パスワードを入力してください。"; // Encryption information "room_event_encryption_info_title" = "エンドツーエンド暗号化情報\n\n"; "room_event_encryption_info_event" = "イベント情報\n"; "room_event_encryption_info_event_user_id" = "ユーザーID\n"; -"room_event_encryption_info_event_identity_key" = "Curve25519 identity key\n"; -"room_event_encryption_info_event_fingerprint_key" = "クレームされたEd25519指紋キー\n"; +"room_event_encryption_info_event_identity_key" = "Curve25519 固有鍵\n"; +"room_event_encryption_info_event_fingerprint_key" = "要求されたフィンガープリントキー Ed25519\n"; "room_event_encryption_info_event_algorithm" = "アルゴリズム\n"; "room_event_encryption_info_event_session_id" = "セッションID\n"; "room_event_encryption_info_event_decryption_error" = "復号化エラー\n"; -"room_event_encryption_info_event_unencrypted" = "暗号化されていない"; +"room_event_encryption_info_event_unencrypted" = "暗号化されていません"; "room_event_encryption_info_event_none" = "なし"; "room_event_encryption_info_device" = "\n送信者セッション情報\n"; "room_event_encryption_info_device_unknown" = "未知のセッション\n"; @@ -144,52 +144,52 @@ "room_event_encryption_info_device_id" = "ID\n"; "room_event_encryption_info_device_verification" = "検証\n"; "room_event_encryption_info_device_fingerprint" = "Ed25519 fingerprint\n"; -"room_event_encryption_info_device_verified" = "検証済み"; +"room_event_encryption_info_device_verified" = "検証済"; "room_event_encryption_info_device_not_verified" = "検証されていない"; "room_event_encryption_info_device_blocked" = "ブラックリストに載せた"; -"room_event_encryption_info_verify" = "検証中..."; +"room_event_encryption_info_verify" = "検証しています…"; "room_event_encryption_info_unverify" = "未検証"; "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セッションID: %@\nセッションkey: %@\n\n一致する場合は、下の確認ボタンを押します。 それ以外の人がこのセッションを傍受している場合は、代わりにブラックリストボタンを押してください。\n\n将来この検証プロセスはより洗練されたものになるでしょう。"; +"room_event_encryption_verify_message" = "このセッションが信頼できることを確認するには、他の方法(対面や電話など)で所有者に連絡し、セッションのユーザー設定で表示される鍵が以下の鍵と一致するかどうかを訪ねてください。\n\nセッション名: %@\nセッションID: %@\nセッションキー: %@\n\n一致する場合は、下の確認ボタンを押します。 それ以外の人がこのセッションを傍受している場合は、代わりにブラックリストボタンを押してください。\n\n将来この検証プロセスはより洗練されたものになります。"; "room_event_encryption_verify_ok" = "検証"; // Account "account_save_changes" = "変更を保存"; "account_link_email" = "リンクメール"; "account_linked_emails" = "リンクされたメール"; -"account_email_validation_title" = "検証保留中"; -"account_email_validation_message" = "メールをチェックし、それに含まれているリンクをクリックしてください。 これが完了したら、[続行]をクリックします。"; -"account_email_validation_error" = "メールアドレスを確認できません。 あなたのメールをチェックし、それに含まれているリンクをクリックしてください。 これが完了したら、[続行]をクリックします"; -"account_msisdn_validation_title" = "検証保留中"; -"account_msisdn_validation_message" = "アクティベーションコード付きのSMSを送信しました。 以下にこのコードを入力してください。"; +"account_email_validation_title" = "認証の保留中"; +"account_email_validation_message" = "電子メールを確認して、本文中のURLをクリックしてください。完了したら「続行する」をクリックしてください。"; +"account_email_validation_error" = "メールアドレスを認証できません。メールを確認して、記載されているリンクをクリックしてください。その後、「続行する」をクリックしてください"; +"account_msisdn_validation_title" = "認証の保留中"; +"account_msisdn_validation_message" = "SMSで認証番号を送りました。以下にその番号を入力してください。"; "account_msisdn_validation_error" = "電話番号を確認できません。"; "account_error_display_name_change_failed" = "表示名の変更に失敗しました"; "account_error_picture_change_failed" = "画像の変更に失敗しました"; "account_error_matrix_session_is_not_opened" = "Matrixセッションが開かれていません"; "account_error_email_wrong_title" = "無効な電子メールアドレス"; -"account_error_email_wrong_description" = "これは有効なメールアドレスではないようです"; +"account_error_email_wrong_description" = "メールアドレスの形式が正しくありません"; "account_error_msisdn_wrong_title" = "無効な電話番号"; -"account_error_msisdn_wrong_description" = "これは有効な電話番号ではないようです"; +"account_error_msisdn_wrong_description" = "電話番号の形式が正しくありません"; // Room creation -"room_creation_name_title" = "部屋名:"; +"room_creation_name_title" = "ルーム名:"; "room_creation_name_placeholder" = "(例 ランチグループ)"; -"room_creation_alias_title" = "部屋の別名:"; +"room_creation_alias_title" = "ルームの別名:"; "room_creation_alias_placeholder" = "(例 #foo:example.org)"; "room_creation_alias_placeholder_with_homeserver" = "(例 #foo%@)"; "room_creation_participants_title" = "参加者:"; -"room_creation_participants_placeholder" = "(例 @bob:homeserver1; @john:homeserver2...)"; +"room_creation_participants_placeholder" = "(例 @bob:homeserver1; @john:homeserver2…)"; // Room -"room_please_select" = "部屋を選択してください"; -"room_error_join_failed_title" = "部屋に参加できませんでした"; -"room_error_join_failed_empty_room" = "現在空の部屋に再参加することはできません。"; -"room_error_name_edition_not_authorized" = "この部屋の名前を編集する権限がありません"; -"room_error_topic_edition_not_authorized" = "この部屋のトピックを編集する権限がありません"; +"room_please_select" = "ルームを選択してください"; +"room_error_join_failed_title" = "ルームに参加できませんでした"; +"room_error_join_failed_empty_room" = "現在空のルームに再参加することはできません。"; +"room_error_name_edition_not_authorized" = "このルームの名前を編集する権限がありません"; +"room_error_topic_edition_not_authorized" = "このルームのトピックを編集する権限がありません"; "room_error_cannot_load_timeline" = "タイムラインの読み込みに失敗しました"; "room_error_timeline_event_not_found_title" = "タイムラインの位置を読み込めませんでした"; "room_error_timeline_event_not_found" = "アプリケーションがこのルームのタイムラインに特定のポイントをロードしようとしましたが、それを見つけることができませんでした"; -"room_left" = "あなたは部屋を出ました"; -"room_no_power_to_create_conference_call" = "この部屋で会議を開始するために招待する権限が必要です"; +"room_left" = "あなたはルームを出ました"; +"room_no_power_to_create_conference_call" = "このルームで会議を開始するために招待する権限が必要です"; "room_no_conference_call_in_encrypted_rooms" = "暗号化された会議室では会議通話はサポートされません"; // Reply to message "message_reply_to_sender_sent_an_image" = "画像を送信しました。"; @@ -198,7 +198,7 @@ "message_reply_to_sender_sent_a_file" = "ファイルを送信しました。"; "message_reply_to_message_to_reply_to_prefix" = "に返信"; // Room members -"room_member_ignore_prompt" = "このユーザーからのすべてのメッセージを非表示にしますか?"; +"room_member_ignore_prompt" = "このユーザーからの全てのメッセージを非表示にしますか?"; "room_member_power_level_prompt" = "この変更を元に戻すことはできません。ユーザーが自分と同じレベルの権限を持つように促しますが、よろしいですか?"; // Attachment "attachment_size_prompt" = "次のように送信しますか:"; @@ -210,8 +210,8 @@ "attachment_cancel_upload" = "アップロードをキャンセルしますか?"; "attachment_multiselection_size_prompt" = "画像を次のように送信しますか:"; "attachment_multiselection_original" = "実際のサイズ"; -"attachment_e2e_keys_file_prompt" = "このファイルには、Matrixクライアントからエクスポートされた暗号化キーが含まれています。\nファイルの内容を表示するか、ファイルの内容をインポートしますか?"; -"attachment_e2e_keys_import" = "インポート..."; +"attachment_e2e_keys_file_prompt" = "このファイルには、Matrixクライアントからエクスポートされた暗号鍵が含まれています。\nファイルの内容を表示するか、ファイル内の鍵をインポートしますか?"; +"attachment_e2e_keys_import" = "インポート…"; // Contacts "contact_mx_users" = "Matrixユーザー"; "contact_local_contacts" = "ローカルの連絡先"; @@ -220,20 +220,20 @@ "group_section" = "グループ"; // Search "search_no_results" = "結果がありません"; -"search_searching" = "検索中..."; +"search_searching" = "検索しています…"; // Time "format_time_s" = "秒"; "format_time_m" = "分"; "format_time_h" = "時"; "format_time_d" = "日"; // E2E import -"e2e_import_room_keys" = "ルームキーをインポート"; -"e2e_import_prompt" = "このプロセスでは、以前に別のMatrixクライアントからエクスポートした暗号化キーをインポートできます。 これにより、他のクライアントが解読できるすべてのメッセージを解読することができます。\nエクスポートファイルはパスフレーズで保護されています。 ファイルを解読するには、パスフレーズをここに入力する必要があります。"; +"e2e_import_room_keys" = "ルームの暗号鍵をインポート"; +"e2e_import_prompt" = "このプロセスでは、以前に別のMatrixクライアントからエクスポートした暗号鍵をインポートできます。 これにより、他のクライアントが解読できる全てのメッセージを解読することができます。\nエクスポートした暗号鍵のファイルは、パスフレーズで保護されています。 ファイルを復号化するには、パスフレーズをここに入力する必要があります。"; "e2e_import" = "インポート"; "e2e_passphrase_enter" = "パスフレーズを入力"; // E2E export -"e2e_export_room_keys" = "ルームキーのエクスポート"; -"e2e_export_prompt" = "このプロセスでは、暗号化されたルームで受信したメッセージのキーをローカルファイルにエクスポートできます。 その後、クライアントがこれらのメッセージを復号化できるように、ファイルを別のMatrixクライアントにインポートすることができます。\nエクスポートされたファイルは、誰でも閲覧できる暗号化されたメッセージを復号化することができるので、安全に保つように注意する必要があります。"; +"e2e_export_room_keys" = "ルームの暗号鍵をエクスポート"; +"e2e_export_prompt" = "このプロセスでは、暗号化されたルームで受信したメッセージの鍵をローカルファイルにエクスポートできます。 そのファイルを別のMatrixクライアントにインポートすると、クライアントはこれらのメッセージを復号化することができます。\nエクスポートしたファイルを使えば、誰でも暗号化されたメッセージを復号化できるので、ファイルを安全に保つように注意する必要があります。"; "e2e_export" = "エクスポート"; "e2e_passphrase_confirm" = "パスフレーズを確認"; "e2e_passphrase_empty" = "パスフレーズは空であってはいけません"; @@ -247,8 +247,8 @@ "error_common_message" = "エラーが発生しました。 後でもう一度お試しください。"; "not_supported_yet" = "まだサポートされていません"; "default" = "既定"; -"private" = "Private"; -"public" = "Public"; +"private" = "非公開"; +"public" = "公開"; "power_level" = "権限レベル"; "network_error_not_reachable" = "ネットワーク接続を確認してください"; "user_id_placeholder" = "例: @bob:homeserver"; @@ -260,39 +260,39 @@ "local_contacts_access_discovery_warning_title" = "ユーザーの探索"; "local_contacts_access_discovery_warning" = "%@は、ユーザーを検索するためにあなたの連絡先から電子メールと電話番号をアップロードしたい"; // Country picker -"country_picker_title" = "国を選択する"; +"country_picker_title" = "国を選択"; // Language picker -"language_picker_title" = "言語を選択する"; +"language_picker_title" = "言語を選択"; "language_picker_default_language" = "既定値 (%@)"; -"notice_room_invite" = "%@は%@を招待しました"; -"notice_room_third_party_invite" = "%@は、部屋に参加するよう%@へ招待状を送った"; -"notice_room_third_party_registered_invite" = "%@は%@の招待を受け入れました"; -"notice_room_join" = "%@ は参加しました"; -"notice_room_leave" = "%@ は退出しました"; -"notice_room_reject" = "%@は招待を拒否しました"; -"notice_room_kick" = "%@は%@を追い出しました"; -"notice_room_unban" = "%@は%@を追放解除した"; -"notice_room_ban" = "%@は%@を追放した"; -"notice_room_withdraw" = "%@は%@の招待を辞退しました"; +"notice_room_invite" = "%@が%@を招待しました"; +"notice_room_third_party_invite" = "%@が%@にルームへの招待状を送りました"; +"notice_room_third_party_registered_invite" = "%@が%@の招待を受け入れました"; +"notice_room_join" = "%@が参加しました"; +"notice_room_leave" = "%@が退出しました"; +"notice_room_reject" = "%@が招待を拒否しました"; +"notice_room_kick" = "%@が%@を追い出しました"; +"notice_room_unban" = "%@が%@を追放解除しました"; +"notice_room_ban" = "%@が%@を追放しました"; +"notice_room_withdraw" = "%@が%@の招待を辞退しました"; "notice_room_reason" = ". 理由: %@"; -"notice_avatar_url_changed" = "%@はアバターを変更しました"; -"notice_display_name_set" = "%@は表示名を%@に設定しました"; -"notice_display_name_changed_from" = "%@は表示名を%@から%@に変更しました"; -"notice_display_name_removed" = "%@は表示名を削除しました"; -"notice_topic_changed" = "%@はトピックを次のように変更しました: %@"; -"notice_room_name_changed" = "%@は部屋名を次のように変更しました: %@"; -"notice_placed_voice_call" = "%@は電話をかけました"; -"notice_placed_video_call" = "%@はビデオ電話をかけました"; -"notice_answered_video_call" = "%@は通話に応答しました"; -"notice_ended_video_call" = "%@は通話を終了しました"; -"notice_conference_call_request" = "%@はVoIP会議をリクエストしました"; -"notice_conference_call_started" = "VoIP会議が開始されました"; +"notice_avatar_url_changed" = "%@がアバターを変更しました"; +"notice_display_name_set" = "%@が表示名を%@に設定しました"; +"notice_display_name_changed_from" = "%@が表示名を%@から%@に変更しました"; +"notice_display_name_removed" = "%@が表示名を削除しました"; +"notice_topic_changed" = "%@がトピックを次のように変更しました:%@"; +"notice_room_name_changed" = "%@がルーム名を次のように変更しました:%@"; +"notice_placed_voice_call" = "%@が電話をかけました"; +"notice_placed_video_call" = "%@がビデオ電話をかけました"; +"notice_answered_video_call" = "%@が電話に出ました"; +"notice_ended_video_call" = "%@が通話を終了しました"; +"notice_conference_call_request" = "%@がVoIP会議をリクエストしました"; +"notice_conference_call_started" = "VoIP会議が開始しました"; "notice_conference_call_finished" = "VoIP会議が終了しました"; // button names "ok" = "OK"; "cancel" = "キャンセル"; "save" = "保存"; -"leave" = "保存しない"; +"leave" = "退出"; "send" = "送信"; "copy_button_name" = "コピー"; "resend" = "再送信"; @@ -303,34 +303,34 @@ "view" = "表示"; // actions "action_logout" = "ログアウト"; -"create_room" = "部屋を作る"; +"create_room" = "ルームを作る"; "login" = "ログイン"; -"create_account" = "アカウントを作成する"; -"membership_invite" = "招待した"; -"membership_leave" = "退出"; -"membership_ban" = "追放"; +"create_account" = "アカウントを作成"; +"membership_invite" = "招待しました"; +"membership_leave" = "退出しました"; +"membership_ban" = "ブロックしました"; "num_members_one" = "%@ ユーザー"; "num_members_other" = "%@ ユーザー"; "invite" = "招待"; -"kick" = "追い出す"; -"ban" = "追放"; -"unban" = "追放解除"; -"message_unsaved_changes" = "保存されていない変更があります。 放置すると捨てられます。"; +"kick" = "キック"; +"ban" = "ブロック"; +"unban" = "ブロック解除"; +"message_unsaved_changes" = "保存されていない変更があります。 退出すると変更は取り消されます。"; // Login Screen -"login_error_already_logged_in" = "ログイン済み"; +"login_error_already_logged_in" = "ログイン済"; "login_error_must_start_http" = "URLは http[s]:// で始まる必要があります"; // room details dialog screen -"room_details_title" = "部屋の詳細"; +"room_details_title" = "ルームの詳細"; // contacts list screen "invitation_message" = "私はmatrixであなたとチャットしたい。 詳細はウェブサイトhttp://matrix.orgをお尋ねください。"; // Settings screen "settings_title_config" = "構成"; "settings_title_notifications" = "通知"; // Notification settings screen -"notification_settings_disable_all" = "すべての通知を無効にする"; +"notification_settings_disable_all" = "全ての通知を無効にする"; "notification_settings_enable_notifications" = "通知を有効にする"; -"notification_settings_enable_notifications_warning" = "現在、すべての端末ですべての通知が無効になっています。"; -"notification_settings_global_info" = "通知設定はユーザーアカウントに保存され、デスクトップ通知を含むすべてのクライアント間で共有されます。\n\nルールは順番に適用されます。 一致する最初のルールは、メッセージの結果を定義します。\nだから:単語ごとの通知は、送信者ごとの通知よりも重要な部屋ごとの通知よりも重要です。\n同じ種類の複数のルールの場合、一致するリストの最初のルールが優先されます。"; +"notification_settings_enable_notifications_warning" = "現在、全ての端末で全ての通知が無効になっています。"; +"notification_settings_global_info" = "通知設定はユーザーアカウントに保存され、デスクトップ通知を含む全てのクライアント間で共有されます。\n\nルールは順番に適用されます。 一致する最初のルールは、メッセージの結果を定義します。\nだから:単語ごとの通知は、送信者ごとの通知よりも重要なルームごとの通知よりも重要です。\n同じ種類の複数のルールの場合、一致するリストの最初のルールが優先されます。"; "notification_settings_per_word_notifications" = "単語単位の通知"; "notification_settings_per_word_info" = "単語は大文字と小文字を区別せずに一致させ、*ワイルドカードを含めることができます。 従って:\nfooは、区切り文字で囲まれた文字列foo(例 句読点や空白、行の開始/終了)と一致します。\nfoo*は、fooで始まる単語に一致します。\n*foo*は、3文字のfooを含む単語に一致します。"; "notification_settings_always_notify" = "常に通知"; @@ -338,27 +338,27 @@ "notification_settings_word_to_match" = "一致する単語"; "notification_settings_highlight" = "Highlight"; "notification_settings_custom_sound" = "カスタムサウンド"; -"notification_settings_per_room_notifications" = "1部屋あたりの通知"; +"notification_settings_per_room_notifications" = "1ルームあたりの通知"; "notification_settings_per_sender_notifications" = "送信者ごとの通知"; "notification_settings_sender_hint" = "@user:domain.com"; -"notification_settings_select_room" = "部屋を選択"; +"notification_settings_select_room" = "ルームを選択"; "notification_settings_other_alerts" = "その他のアラート"; "notification_settings_contain_my_user_name" = "私のユーザー名を含むメッセージについて音で私に通知してください"; -"notification_settings_contain_my_display_name" = "私の表示名が含まれているメッセージについて音で私に通知する"; +"notification_settings_contain_my_display_name" = "私の表示名が含まれているメッセージが届いた際に音で通知"; "notification_settings_just_sent_to_me" = "私に送られたメッセージについての音で私に知らせる"; -"notification_settings_invite_to_a_new_room" = "私が新しい部屋に招待されたときに知らせる"; -"notification_settings_people_join_leave_rooms" = "人が部屋に入退室したときに私に通知する"; -"notification_settings_receive_a_call" = "通話を受信したときに通知する"; -"notification_settings_suppress_from_bots" = "ボットからの通知を抑制する"; -"notification_settings_by_default" = "既定値では..."; -"notification_settings_notify_all_other" = "他のすべてのメッセージ/部屋について通知する"; +"notification_settings_invite_to_a_new_room" = "私が新しいルームに招待されたときに知らせる"; +"notification_settings_people_join_leave_rooms" = "誰かがルームに参加もしくは退出したときに通知"; +"notification_settings_receive_a_call" = "通話を受信したときに通知"; +"notification_settings_suppress_from_bots" = "ボットからの通知を抑制"; +"notification_settings_by_default" = "既定値では…"; +"notification_settings_notify_all_other" = "他の全てのメッセージ/ルームについて通知"; // gcm section "settings_config_home_server" = "ホームサーバー: %@"; "settings_config_identity_server" = "IDサーバー: %@"; "settings_config_user_id" = "ユーザーID: %@"; // call string "call_waiting" = "待機中..."; -"call_connecting" = "通話接続中..."; +"call_connecting" = "通話接続中…"; "call_ended" = "通話終了"; "call_ring" = "呼び出し中..."; "incoming_video_call" = "着信ビデオ通話"; @@ -374,36 +374,41 @@ "ssl_cert_new_account_expl" = "サーバー管理者がこれが予期されると述べた場合は、以下の指紋が提供された指紋と一致することを確認してください。"; "ssl_unexpected_existing_expl" = "証明書は、お使いの携帯電話にて信頼されたものから変更されました。 これは非常に珍しいことです。 この新しい証明書に同意しないことをお勧めします。"; "ssl_expected_existing_expl" = "証明書が以前に信頼されたものから信頼されていないものに変更されました。 サーバーが証明書を更新した可能性があります。 予想される指紋については、サーバー管理者にお問い合わせください。"; -"ssl_only_accept" = "サーバー管理者が上記のものと一致する指紋を発行した場合にのみ、証明書を受け入れます。"; +"ssl_only_accept" = "サーバー管理者が上記のものと一致する指紋を発行した場合にのみ、証明書を受け入れてください。"; "unignore" = "無視しない"; -"notice_encryption_enabled_ok" = "%@ がエンドツーエンド暗号化をオンにしました。"; -"notice_encryption_enabled_unknown_algorithm" = "%1$@ がエンドツーエンド暗号化をオンにしました (不明なアルゴリズム %2$@)。"; +"notice_encryption_enabled_ok" = "%@がエンドツーエンド暗号化をオンにしました。"; +"notice_encryption_enabled_unknown_algorithm" = "%1$@がエンドツーエンド暗号化をオンにしました(不明なアルゴリズム %2$@)。"; "device_details_rename_prompt_title" = "セッション名"; "account_error_push_not_allowed" = "通知は許可されていません"; -"notice_room_third_party_revoked_invite" = "%@ は %@ の部屋への招待を取り消しました"; +"notice_room_third_party_revoked_invite" = "%@が%@のルームへの招待を取り消しました"; // Notice Events with "You" "notice_room_invite_by_you" = "%@を招待しました"; "notice_room_invite_you" = "%@があなたを招待しました"; "notice_room_join_by_you" = "参加しました"; -"notice_room_leave_by_you" = "退室しました"; +"notice_room_leave_by_you" = "退出しました"; "notice_room_kick_by_you" = "%@をキックしました"; -"notice_room_unban_by_you" = "%@をBANしました"; -"notice_room_ban_by_you" = "%@をBANしました"; +"notice_room_unban_by_you" = "%@のブロックを解除しました"; +"notice_room_ban_by_you" = "%@をブロックしました"; "notice_avatar_url_changed_by_you" = "アバターを変更しました"; "notice_display_name_set_by_you" = "表示名を%@に変更しました"; "notice_display_name_changed_from_by_you" = "表示名を%@から%@に変更しました"; "notice_display_name_removed_by_you" = "表示名を削除しました"; "notice_topic_changed_by_you" = "トピックを変更しました: %@"; -"notice_room_name_changed_by_you" = "部屋の名前を変更しました: %@"; +"notice_room_name_changed_by_you" = "ルームの名前を変更しました: %@"; "notice_placed_voice_call_by_you" = "音声通話を開始しました"; "notice_placed_video_call_by_you" = "ビデオ通話を開始しました"; "notice_answered_video_call_by_you" = "電話に出ました"; "notice_ended_video_call_by_you" = "通話を終了しました"; "notice_conference_call_request_by_you" = "VoIP会議をリクエストしました"; -"notice_room_name_removed_by_you" = "部屋名を削除しました"; +"notice_room_name_removed_by_you" = "ルーム名を削除しました"; "notice_room_topic_removed_by_you" = "トピックを削除しました"; "notice_profile_change_redacted_by_you" = "プロフィール %@を更新しました"; -"notice_room_created_by_you" = "部屋を作成しました"; +"notice_room_created_by_you" = "ルームを作成しました"; "notice_encryption_enabled_ok_by_you" = "あなたはエンドツーエンド暗号化をオンにしました。"; "notice_encryption_enabled_unknown_algorithm_by_you" = "あなたはエンドツーエンド暗号化をオンにしました (不明なアルゴリズム %2$@)。"; "notice_redaction_by_you" = "イベントを編集しました (id: %@)"; +"auth_username_in_use" = "ユーザー名は既に使用されています"; +"resume_call" = "再開"; +"notice_room_history_visible_to_members_from_joined_point_for_dm" = "%@が今後のメッセージを「全員 (参加した時点以降)」閲覧可能に設定しました。"; +"notice_room_history_visible_to_members_from_invited_point_for_dm" = "%@が今後のメッセージを「メンバーのみ (招待された時点以降)」閲覧可能に設定しました。"; +"notice_room_history_visible_to_members_for_dm" = "%@が今後のメッセージを「メンバーのみ」閲覧可能に設定しました。"; diff --git a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/sk.lproj/MatrixKit.strings b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/sk.lproj/MatrixKit.strings index 7bd5de0d0..0d7cc424a 100644 --- a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/sk.lproj/MatrixKit.strings +++ b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/sk.lproj/MatrixKit.strings @@ -14,7 +14,7 @@ "account_save_changes" = "Uložiť zmeny"; "room_event_encryption_verify_title" = "Overiť reláciu\n\n"; "room_event_encryption_info_device_not_verified" = "Neoverené"; -"room_event_encryption_info_device_fingerprint" = "Ed25519 odtlačok prsta\n"; +"room_event_encryption_info_device_fingerprint" = "Odtlačok Ed25519\n"; "room_event_encryption_info_device_name" = "Verejný názov\n"; "room_event_encryption_info_device_unknown" = "neznáma relácia\n"; "room_event_encryption_info_event_decryption_error" = "Chyba dešifrovania\n"; @@ -439,16 +439,16 @@ "notice_room_power_level_event_requirement" = "Minimálne úrovne oprávnenia súvisiace s udalosťami sú:"; "notice_room_join_rule_public_for_dm" = "%@ ju zverejnil/a."; "notice_room_join_rule_public" = "%@ zverejnil/a túto miestnosť."; -"notice_room_join_rule_invite_by_you_for_dm" = "Ste ju vytvorili len na pozvanie."; -"notice_room_join_rule_invite_for_dm" = "%@ ju vytvoril len na pozvanie."; +"notice_room_join_rule_invite_by_you_for_dm" = "Ste ju nastavili len na pozvanie."; +"notice_room_join_rule_invite_for_dm" = "%@ ju nastavil/a len na pozvanie."; "notice_event_redacted" = ""; "capture_media" = "Spraviť fotografiu/video"; "auth_reset_password_error_unauthorized" = "Neoprávnené"; "auth_invalid_user_name" = "Neplatné používateľské meno"; -"ssl_only_accept" = "Certifikát akceptujte IBA vtedy, ak správca servera zverejnil odtlačok prsta, ktorý sa zhoduje s vyššie uvedeným."; +"ssl_only_accept" = "Certifikát akceptujte IBA vtedy, ak správca servera zverejnil odtlačok, ktorý sa zhoduje s vyššie uvedeným."; "ssl_expected_existing_expl" = "Certifikát sa zmenil z predtým dôveryhodného na nedôveryhodný. Server mohol obnoviť svoj certifikát. Obráťte sa na správcu servera, aby vám poskytol očakávaný odtlačok."; "ssl_unexpected_existing_expl" = "Certifikát sa zmenil na iný, ktorému dôveroval váš telefón. To je VEĽMI NEOBVYKLÉ. Odporúča sa, aby ste tento nový certifikát NEPRIJALI."; -"ssl_cert_new_account_expl" = "Ak správca servera uviedol, že sa to očakáva, skontrolujte, či sa odtlačok prsta uvedený nižšie zhoduje s odtlačkom prsta, ktorý poskytol."; +"ssl_cert_new_account_expl" = "Ak správca servera uviedol, že sa to očakáva, skontrolujte, či sa odtlačok uvedený nižšie zhoduje s odtlačkom, ktorý poskytol."; "ssl_cert_not_trust" = "Môže to znamenať, že niekto úmyselne zachytáva vašu komunikáciu alebo že váš telefón nedôveruje certifikátu, ktorý poskytol vzdialený server."; "ssl_could_not_verify" = "Nepodarilo sa overiť identitu vzdialeného servera."; @@ -488,9 +488,9 @@ "settings_enter_validation_token_for" = "Zadajte overovací token pre %@:"; "notice_error_unexpected_event" = "Neočakávaná udalosť"; "notice_error_unsupported_event" = "Nepodporovaná udalosť"; -"notice_room_join_rule_invite_by_you" = "Urobili ste miestnosť len pre pozvaných."; +"notice_room_join_rule_invite_by_you" = "Nastavili ste miestnosť len pre pozvaných."; // New -"notice_room_join_rule_invite" = "%@ vytvoril miestnosť len na pozvanie."; +"notice_room_join_rule_invite" = "%@ nastavil/a miestnosť len na pozvanie."; // Old "notice_room_join_rule" = "Pravidlo pripojenia je: %@"; "notice_room_created" = "%@ vytvoril a nastavil miestnosť."; @@ -546,5 +546,12 @@ "message_reply_to_sender_sent_their_location" = "zdieľal/a svoju polohu."; "account_link_email" = "Prepojený email"; "account_linked_emails" = "Prepojené e-maily"; -"room_event_encryption_info_event_fingerprint_key" = "Deklarovaný kľúč odtlačkov prstov Ed25519\n"; +"room_event_encryption_info_event_fingerprint_key" = "Deklarovaný kľúč odtlačku Ed25519\n"; "notification_settings_notify_all_other" = "Oznámiť pre všetky ostatné správy/miestnosti"; +"call_transfer_to_user" = "Presmerovať na používateľa %@"; +"call_more_actions_transfer" = "Presmerovať"; +"login_use_fallback" = "Použiť náhradnú stránku"; +"call_consulting_with_user" = "Konzultácia s %@"; +"notification_settings_per_sender_notifications" = "Oznámenia na odosielateľa"; +"notification_settings_per_room_notifications" = "Oznámenia na miestnosti"; +"notification_settings_per_word_notifications" = "Oznámenia na slovo"; diff --git a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/sv.lproj/MatrixKit.strings b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/sv.lproj/MatrixKit.strings index ecadb738e..79192a6e2 100644 --- a/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/sv.lproj/MatrixKit.strings +++ b/Riot/Modules/MatrixKit/Assets/MatrixKitAssets.bundle/sv.lproj/MatrixKit.strings @@ -74,7 +74,7 @@ "ignore" = "Ignorera"; "unignore" = "Avignorera"; // Events formatter -"notice_avatar_changed_too" = "(avataren ändrades också)"; +"notice_avatar_changed_too" = "(avataren byttes också)"; "notice_room_name_removed" = "%@ tog bort rumsnamnet"; "notice_room_topic_removed" = "%@ tog bort ämnet"; "notice_event_redacted" = ""; @@ -307,7 +307,7 @@ "notice_room_unban_by_you" = "Du avbannade %@"; "notice_room_ban_by_you" = "Du bannade %@"; "notice_room_withdraw_by_you" = "Du drog tillbaka inbjudan för %@"; -"notice_avatar_url_changed_by_you" = "Du ändrade din avatar"; +"notice_avatar_url_changed_by_you" = "Du bytte din avatar"; "notice_display_name_set_by_you" = "Du bytte ditt visningsnamn till %@"; "notice_display_name_changed_from_by_you" = "Du bytte ditt visningsnamn från %@ till %@"; "notice_display_name_removed_by_you" = "Du tog bort ditt visningsnamn"; diff --git a/Riot/Modules/MatrixKit/Controllers/MXKActivityHandlingViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKActivityHandlingViewController.m index 5628c7396..97cce741e 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKActivityHandlingViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKActivityHandlingViewController.m @@ -29,6 +29,11 @@ { [super viewDidLoad]; + if ([self providesCustomActivityIndicator]) { + // If a subclass provides custom activity indicator, the default one will not even be initialized. + return; + } + // Add default activity indicator activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; activityIndicator.backgroundColor = [UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0]; @@ -56,9 +61,13 @@ #pragma mark - Activity indicator +- (BOOL)providesCustomActivityIndicator { + return NO; +} + - (void)startActivityIndicator { - if (activityIndicator) + if (activityIndicator && ![self providesCustomActivityIndicator]) { [self.view bringSubviewToFront:activityIndicator]; [activityIndicator startAnimating]; @@ -79,5 +88,4 @@ [activityIndicator stopAnimating]; } - @end diff --git a/Riot/Modules/MatrixKit/Controllers/MXKAttachmentsViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKAttachmentsViewController.m index a21fbc396..06df81822 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKAttachmentsViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKAttachmentsViewController.m @@ -175,7 +175,11 @@ _attachmentsCollection.hidden = YES; // Display collection cell in full screen + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" self.automaticallyAdjustsScrollViewInsets = NO; + #pragma clang diagnostic pop } - (BOOL)prefersStatusBarHidden diff --git a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m index 3465c136b..f51004ede 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m @@ -215,6 +215,8 @@ // and report the inputAccessoryView.superview of the firstResponder in self.keyboardView. } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)setKeyboardHeight:(CGFloat)keyboardHeight { // Deduce the bottom inset for the scroll view (Don't forget the potential tabBar) @@ -229,6 +231,7 @@ insets.bottom = scrollViewInsetBottom; self.authenticationScrollView.contentInset = insets; } +#pragma clang diagnostic pop - (void)destroy { @@ -981,7 +984,10 @@ { // Trigger here a register request in order to associate the filled userId and password to the current session id // This will check the availability of the userId at the same time - NSDictionary *parameters = @{@"auth": @{}, + NSDictionary *parameters = @{@"auth": @{ + @"session": self.authInputsView.authSession.session, + @"type": kMXLoginFlowTypeDummy + }, @"username": self.authInputsView.userId, @"password": self.authInputsView.password, @"bind_email": @(NO), diff --git a/Riot/Modules/MatrixKit/Controllers/MXKCallViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKCallViewController.m index 5744e75ed..6c138160d 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKCallViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKCallViewController.m @@ -1422,29 +1422,31 @@ static const CGFloat kLocalPreviewMargin = 20; } } -- (void)updateProximityAndSleep -{ - BOOL inCall = (mxCall.state == MXCallStateConnected || mxCall.state == MXCallStateRinging || mxCall.state == MXCallStateInviteSent || mxCall.state == MXCallStateConnecting || mxCall.state == MXCallStateCreateOffer || mxCall.state == MXCallStateCreateAnswer); - - if (inCall) - { - BOOL isBuiltInReceiverUsed = self.isBuiltInReceiverAudioOuput; - - // Enable the proximity monitoring when the built in receiver is used as the audio output. - BOOL enableProxMonitoring = isBuiltInReceiverUsed; - [[UIDevice currentDevice] setProximityMonitoringEnabled:enableProxMonitoring]; - - // Disable the idle timer during a video call, or during a voice call which is performed with the built-in receiver. - // Note: if the device is locked, VoIP calling get dropped if an incoming GSM call is received. - BOOL disableIdleTimer = mxCall.isVideoCall || isBuiltInReceiverUsed; - - UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)]; - if (sharedApplication) - { - sharedApplication.idleTimerDisabled = disableIdleTimer; - } - } -} + - (void)updateProximityAndSleep + { + BOOL inCall = (mxCall.state == MXCallStateConnected || mxCall.state == MXCallStateRinging || mxCall.state == MXCallStateInviteSent || mxCall.state == MXCallStateConnecting || mxCall.state == MXCallStateCreateOffer || mxCall.state == MXCallStateCreateAnswer); + + BOOL isBuiltInReceiverUsed = self.isBuiltInReceiverAudioOuput; + + // Enable the proximity monitoring when the built in receiver is used as the audio output. + BOOL enableProxMonitoring = inCall && isBuiltInReceiverUsed; + + UIDevice *device = [UIDevice currentDevice]; + if (device && device.isProximityMonitoringEnabled != enableProxMonitoring) + { + [device setProximityMonitoringEnabled:enableProxMonitoring]; + } + + // Disable the idle timer during a video call, or during a voice call which is performed with the built-in receiver. + // Note: if the device is locked, VoIP calling get dropped if an incoming GSM call is received. + BOOL disableIdleTimer = inCall && (mxCall.isVideoCall || isBuiltInReceiverUsed); + + UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)]; + if (sharedApplication && sharedApplication.isIdleTimerDisabled != disableIdleTimer) + { + sharedApplication.idleTimerDisabled = disableIdleTimer; + } + } - (UIView *)createIncomingCallView { diff --git a/Riot/Modules/MatrixKit/Controllers/MXKCountryPickerViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKCountryPickerViewController.m index 273046244..80ea288f7 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKCountryPickerViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKCountryPickerViewController.m @@ -152,7 +152,7 @@ NSString* const kMXKCountryPickerViewControllerCountryCellId = @"kMXKCountryPick { UISearchController *searchController = [[UISearchController alloc] initWithSearchResultsController:nil]; - searchController.dimsBackgroundDuringPresentation = NO; + searchController.obscuresBackgroundDuringPresentation = NO; searchController.hidesNavigationBarDuringPresentation = NO; searchController.searchResultsUpdater = self; diff --git a/Riot/Modules/MatrixKit/Controllers/MXKGroupListViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKGroupListViewController.m index e9950fa3f..b23a55720 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKGroupListViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKGroupListViewController.m @@ -92,6 +92,8 @@ [[[self class] nib] instantiateWithOwner:self options:nil]; } + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" // Adjust search bar Top constraint to take into account potential navBar. if (_groupsSearchBarTopConstraint) { @@ -123,6 +125,7 @@ [NSLayoutConstraint activateConstraints:@[_groupsTableViewBottomConstraint]]; } + #pragma clang diagnostic pop // Hide search bar by default [self hideSearchBar:YES]; @@ -209,6 +212,8 @@ self.keyboardView = _groupsSearchBar.inputAccessoryView.superview; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)setKeyboardHeight:(CGFloat)keyboardHeight { // Deduce the bottom constraint for the table view (Don't forget the potential tabBar) @@ -225,6 +230,7 @@ // Force layout immediately to take into account new constraint [self.view layoutIfNeeded]; } +#pragma clang diagnostic pop - (void)destroy { diff --git a/Riot/Modules/MatrixKit/Controllers/MXKLanguagePickerViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKLanguagePickerViewController.m index 92d1a065c..b1c796356 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKLanguagePickerViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKLanguagePickerViewController.m @@ -160,7 +160,7 @@ NSString* const kMXKLanguagePickerCellDataKeyLanguage = @"language"; { UISearchController *searchController = [[UISearchController alloc] initWithSearchResultsController:nil]; - searchController.dimsBackgroundDuringPresentation = NO; + searchController.obscuresBackgroundDuringPresentation = NO; searchController.hidesNavigationBarDuringPresentation = NO; searchController.searchResultsUpdater = self; diff --git a/Riot/Modules/MatrixKit/Controllers/MXKRecentListViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKRecentListViewController.m index 828f84825..25b50ee61 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKRecentListViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKRecentListViewController.m @@ -97,6 +97,8 @@ [[[self class] nib] instantiateWithOwner:self options:nil]; } + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" // Adjust search bar Top constraint to take into account potential navBar. if (_recentsSearchBarTopConstraint) { @@ -126,6 +128,7 @@ _recentsTableViewBottomConstraint.active = YES; } + #pragma clang diagnostic pop // Hide search bar by default [self hideSearchBar:YES]; @@ -217,7 +220,7 @@ if ([MXKRoomDataSourceManager sharedManagerForMatrixSession:mxSession].isServerSyncInProgress) { // sync is in progress for at least one data source, keep running the loading wheel - [self.activityIndicator startAnimating]; + [self startActivityIndicator]; break; } } @@ -229,6 +232,8 @@ self.keyboardView = _recentsSearchBar.inputAccessoryView.superview; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)setKeyboardHeight:(CGFloat)keyboardHeight { // Deduce the bottom constraint for the table view (Don't forget the potential tabBar) @@ -245,6 +250,7 @@ // Force layout immediately to take into account new constraint [self.view layoutIfNeeded]; } +#pragma clang diagnostic pop - (void)destroy { @@ -421,7 +427,9 @@ - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { // Let dataSource provide the section header. - return [dataSource viewForHeaderInSection:section withFrame:[tableView rectForHeaderInSection:section]]; + return [dataSource viewForHeaderInSection:section + withFrame:[tableView rectForHeaderInSection:section] + inTableView:tableView]; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath diff --git a/Riot/Modules/MatrixKit/Controllers/MXKRoomMemberListViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKRoomMemberListViewController.m index 217fb4758..f53ccc57a 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKRoomMemberListViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKRoomMemberListViewController.m @@ -105,6 +105,8 @@ // Adjust Top and Bottom constraints to take into account potential navBar and tabBar. [NSLayoutConstraint deactivateConstraints:@[_membersSearchBarTopConstraint, _membersTableViewBottomConstraint]]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" _membersSearchBarTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual @@ -120,6 +122,7 @@ attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]; + #pragma clang diagnostic pop [NSLayoutConstraint activateConstraints:@[_membersSearchBarTopConstraint, _membersTableViewBottomConstraint]]; @@ -225,6 +228,8 @@ self.keyboardView = _membersSearchBar.inputAccessoryView.superview; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)setKeyboardHeight:(CGFloat)keyboardHeight { // Deduce the bottom constraint for the table view (Don't forget the potential tabBar) @@ -241,6 +246,7 @@ // Force layout immediately to take into account new constraint [self.view layoutIfNeeded]; } +#pragma clang diagnostic pop - (void)destroy { diff --git a/Riot/Modules/MatrixKit/Controllers/MXKSearchViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKSearchViewController.m index 7ac637b33..8d6a03b18 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKSearchViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKSearchViewController.m @@ -76,6 +76,8 @@ // Adjust Top and Bottom constraints to take into account potential navBar and tabBar. [NSLayoutConstraint deactivateConstraints:@[_searchSearchBarTopConstraint, _searchTableViewBottomConstraint]]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" _searchSearchBarTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual @@ -91,6 +93,7 @@ attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]; + #pragma clang diagnostic pop [NSLayoutConstraint activateConstraints:@[_searchSearchBarTopConstraint, _searchTableViewBottomConstraint]]; @@ -140,6 +143,8 @@ self.keyboardView = _searchSearchBar.inputAccessoryView.superview; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)setKeyboardHeight:(CGFloat)keyboardHeight { // Deduce the bottom constraint for the table view (Don't forget the potential tabBar) @@ -156,6 +161,7 @@ // Force layout immediately to take into account new constraint [self.view layoutIfNeeded]; } +#pragma clang diagnostic pop - (void)destroy { diff --git a/Riot/Modules/MatrixKit/Controllers/MXKViewControllerActivityHandling.h b/Riot/Modules/MatrixKit/Controllers/MXKViewControllerActivityHandling.h index 299071230..f80d2990d 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKViewControllerActivityHandling.h +++ b/Riot/Modules/MatrixKit/Controllers/MXKViewControllerActivityHandling.h @@ -35,6 +35,14 @@ */ @property (nonatomic) UIActivityIndicatorView *activityIndicator; +/** + A view controller may choose to implement a completely custom activity indicator (e.g. shared toast notification), + + In this case the default `activityIndicator` will be hidden, and the view controller is responsible for overriding + `startActivityIndicator` and `stopActivityIndicator` methods to show / hide the custom activity indicator. + */ +@property (nonatomic, readonly) BOOL providesCustomActivityIndicator; + /** Bring the activity indicator to the front and start it. */ diff --git a/Riot/Modules/MatrixKit/Controllers/MXKWebViewViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKWebViewViewController.m index 07c23b452..b96fe3bd5 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKWebViewViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKWebViewViewController.m @@ -133,6 +133,9 @@ NSString *const kMXKWebViewViewControllerJavaScriptEnableLog = multiplier:1.0 constant:0]; // Force webview in full height + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:webView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual @@ -147,6 +150,7 @@ NSString *const kMXKWebViewViewControllerJavaScriptEnableLog = attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; + #pragma clang diagnostic pop [NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint, topConstraint, bottomConstraint]]; diff --git a/Riot/Modules/MatrixKit/Libs/SwiftUTI/UTI.swift b/Riot/Modules/MatrixKit/Libs/SwiftUTI/UTI.swift index c3d66c481..4ea8ea88d 100644 --- a/Riot/Modules/MatrixKit/Libs/SwiftUTI/UTI.swift +++ b/Riot/Modules/MatrixKit/Libs/SwiftUTI/UTI.swift @@ -104,7 +104,9 @@ public class UTI: RawRepresentable, Equatable { // UTTypeCreatePreferredIdentifierForTag only returns nil if the tag class is unknwown, which can't happen to us since we use an // enum of known values. Hence we can force-cast the result. + // swiftlint:disable force_unwrapping let identifier = (unmanagedIdentifier?.takeRetainedValue() as String?)! + // swiftlint:enable force_unwrapping self.init(rawValue: identifier) } @@ -122,7 +124,7 @@ public class UTI: RawRepresentable, Equatable { public convenience init(withExtension fileExtension: String, conformingTo conforming: UTI? = nil) { - self.init(withTagClass:.fileExtension, value: fileExtension, conformingTo: conforming) + self.init(withTagClass: .fileExtension, value: fileExtension, conformingTo: conforming) } /** @@ -138,7 +140,7 @@ public class UTI: RawRepresentable, Equatable { public convenience init(withMimeType mimeType: String, conformingTo conforming: UTI? = nil) { - self.init(withTagClass:.mimeType, value: mimeType, conformingTo: conforming) + self.init(withTagClass: .mimeType, value: mimeType, conformingTo: conforming) } #if os(macOS) @@ -146,7 +148,9 @@ public class UTI: RawRepresentable, Equatable { /** Initialize an UTI with a pasteboard type. - - Important: **This function is de-facto deprecated!** The old cocoa pasteboard types ( `NSStringPboardType`, `NSPDFPboardType`, etc) have been deprecated in favour of actual UTIs, and the constants are not available anymore in Swift. This function only works correctly with the values of these old constants, but _not_ with the replacement values (like `NSPasteboardTypeString` etc), since these already are UTIs. + - Important: **This function is de-facto deprecated!** The old cocoa pasteboard types ( `NSStringPboardType`, `NSPDFPboardType`, etc) have been deprecated in favour of actual UTIs, and the + constants are not available anymore in Swift. This function only works correctly with the values of these old constants, but _not_ with the replacement values (like `NSPasteboardTypeString` etc), since these + already are UTIs. - Parameters: - pbType: The pasteboard type (e.g. NSPDFPboardType). - conformingTo: If specified, the returned UTI must conform to this UTI. If nil is specified, this parameter is ignored. The default is nil. @@ -155,7 +159,7 @@ public class UTI: RawRepresentable, Equatable { */ public convenience init(withPBType pbType: String, conformingTo conforming: UTI? = nil) { - self.init(withTagClass:.pbType, value: pbType, conformingTo: conforming) + self.init(withTagClass: .pbType, value: pbType, conformingTo: conforming) } /** @@ -172,7 +176,7 @@ public class UTI: RawRepresentable, Equatable { public convenience init(withOSType osType: String, conformingTo conforming: UTI? = nil) { - self.init(withTagClass:.osType, value: osType, conformingTo: conforming) + self.init(withTagClass: .osType, value: osType, conformingTo: conforming) } #endif @@ -297,7 +301,7 @@ public class UTI: RawRepresentable, Equatable { return UTTypeConformsTo(self.rawCFValue, otherUTI.rawCFValue) as Bool } - public static func ==(lhs: UTI, rhs: UTI) -> Bool { + public static func == (lhs: UTI, rhs: UTI) -> Bool { return UTTypeEqual(lhs.rawCFValue, rhs.rawCFValue) as Bool } @@ -319,11 +323,11 @@ public class UTI: RawRepresentable, Equatable { /// Returns a uniform type’s declaration as a Dictionary, or nil if if no declaration for that type can be found. - public var declaration: [AnyHashable:Any]? { + public var declaration: [AnyHashable: Any]? { let unmanagedDeclaration = UTTypeCopyDeclaration(self.rawCFValue) - guard let declaration = unmanagedDeclaration?.takeRetainedValue() as? [AnyHashable:Any] else { + guard let declaration = unmanagedDeclaration?.takeRetainedValue() as? [AnyHashable: Any] else { return nil } @@ -356,137 +360,137 @@ public class UTI: RawRepresentable, Equatable { public extension UTI { - static let item = UTI(rawValue: kUTTypeItem as String) - static let content = UTI(rawValue: kUTTypeContent as String) - static let compositeContent = UTI(rawValue: kUTTypeCompositeContent as String) - static let message = UTI(rawValue: kUTTypeMessage as String) - static let contact = UTI(rawValue: kUTTypeContact as String) - static let archive = UTI(rawValue: kUTTypeArchive as String) - static let diskImage = UTI(rawValue: kUTTypeDiskImage as String) - static let data = UTI(rawValue: kUTTypeData as String) - static let directory = UTI(rawValue: kUTTypeDirectory as String) - static let resolvable = UTI(rawValue: kUTTypeResolvable as String) - static let symLink = UTI(rawValue: kUTTypeSymLink as String) - static let executable = UTI(rawValue: kUTTypeExecutable as String) - static let mountPoint = UTI(rawValue: kUTTypeMountPoint as String) - static let aliasFile = UTI(rawValue: kUTTypeAliasFile as String) - static let aliasRecord = UTI(rawValue: kUTTypeAliasRecord as String) - static let urlBookmarkData = UTI(rawValue: kUTTypeURLBookmarkData as String) - static let url = UTI(rawValue: kUTTypeURL as String) - static let fileURL = UTI(rawValue: kUTTypeFileURL as String) - static let text = UTI(rawValue: kUTTypeText as String) - static let plainText = UTI(rawValue: kUTTypePlainText as String) - static let utf8PlainText = UTI(rawValue: kUTTypeUTF8PlainText as String) - static let utf16ExternalPlainText = UTI(rawValue: kUTTypeUTF16ExternalPlainText as String) - static let utf16PlainText = UTI(rawValue: kUTTypeUTF16PlainText as String) - static let delimitedText = UTI(rawValue: kUTTypeDelimitedText as String) - static let commaSeparatedText = UTI(rawValue: kUTTypeCommaSeparatedText as String) - static let tabSeparatedText = UTI(rawValue: kUTTypeTabSeparatedText as String) - static let utf8TabSeparatedText = UTI(rawValue: kUTTypeUTF8TabSeparatedText as String) - static let rtf = UTI(rawValue: kUTTypeRTF as String) - static let html = UTI(rawValue: kUTTypeHTML as String) - static let xml = UTI(rawValue: kUTTypeXML as String) - static let sourceCode = UTI(rawValue: kUTTypeSourceCode as String) - static let assemblyLanguageSource = UTI(rawValue: kUTTypeAssemblyLanguageSource as String) - static let cSource = UTI(rawValue: kUTTypeCSource as String) - static let objectiveCSource = UTI(rawValue: kUTTypeObjectiveCSource as String) + static let item = UTI(rawValue: kUTTypeItem as String) + static let content = UTI(rawValue: kUTTypeContent as String) + static let compositeContent = UTI(rawValue: kUTTypeCompositeContent as String) + static let message = UTI(rawValue: kUTTypeMessage as String) + static let contact = UTI(rawValue: kUTTypeContact as String) + static let archive = UTI(rawValue: kUTTypeArchive as String) + static let diskImage = UTI(rawValue: kUTTypeDiskImage as String) + static let data = UTI(rawValue: kUTTypeData as String) + static let directory = UTI(rawValue: kUTTypeDirectory as String) + static let resolvable = UTI(rawValue: kUTTypeResolvable as String) + static let symLink = UTI(rawValue: kUTTypeSymLink as String) + static let executable = UTI(rawValue: kUTTypeExecutable as String) + static let mountPoint = UTI(rawValue: kUTTypeMountPoint as String) + static let aliasFile = UTI(rawValue: kUTTypeAliasFile as String) + static let aliasRecord = UTI(rawValue: kUTTypeAliasRecord as String) + static let urlBookmarkData = UTI(rawValue: kUTTypeURLBookmarkData as String) + static let url = UTI(rawValue: kUTTypeURL as String) + static let fileURL = UTI(rawValue: kUTTypeFileURL as String) + static let text = UTI(rawValue: kUTTypeText as String) + static let plainText = UTI(rawValue: kUTTypePlainText as String) + static let utf8PlainText = UTI(rawValue: kUTTypeUTF8PlainText as String) + static let utf16ExternalPlainText = UTI(rawValue: kUTTypeUTF16ExternalPlainText as String) + static let utf16PlainText = UTI(rawValue: kUTTypeUTF16PlainText as String) + static let delimitedText = UTI(rawValue: kUTTypeDelimitedText as String) + static let commaSeparatedText = UTI(rawValue: kUTTypeCommaSeparatedText as String) + static let tabSeparatedText = UTI(rawValue: kUTTypeTabSeparatedText as String) + static let utf8TabSeparatedText = UTI(rawValue: kUTTypeUTF8TabSeparatedText as String) + static let rtf = UTI(rawValue: kUTTypeRTF as String) + static let html = UTI(rawValue: kUTTypeHTML as String) + static let xml = UTI(rawValue: kUTTypeXML as String) + static let sourceCode = UTI(rawValue: kUTTypeSourceCode as String) + static let assemblyLanguageSource = UTI(rawValue: kUTTypeAssemblyLanguageSource as String) + static let cSource = UTI(rawValue: kUTTypeCSource as String) + static let objectiveCSource = UTI(rawValue: kUTTypeObjectiveCSource as String) @available( OSX 10.11, iOS 9.0, * ) - static let swiftSource = UTI(rawValue: kUTTypeSwiftSource as String) - static let cPlusPlusSource = UTI(rawValue: kUTTypeCPlusPlusSource as String) - static let objectiveCPlusPlusSource = UTI(rawValue: kUTTypeObjectiveCPlusPlusSource as String) - static let cHeader = UTI(rawValue: kUTTypeCHeader as String) - static let cPlusPlusHeader = UTI(rawValue: kUTTypeCPlusPlusHeader as String) - static let javaSource = UTI(rawValue: kUTTypeJavaSource as String) - static let script = UTI(rawValue: kUTTypeScript as String) - static let appleScript = UTI(rawValue: kUTTypeAppleScript as String) - static let osaScript = UTI(rawValue: kUTTypeOSAScript as String) - static let osaScriptBundle = UTI(rawValue: kUTTypeOSAScriptBundle as String) - static let javaScript = UTI(rawValue: kUTTypeJavaScript as String) - static let shellScript = UTI(rawValue: kUTTypeShellScript as String) - static let perlScript = UTI(rawValue: kUTTypePerlScript as String) - static let pythonScript = UTI(rawValue: kUTTypePythonScript as String) - static let rubyScript = UTI(rawValue: kUTTypeRubyScript as String) - static let phpScript = UTI(rawValue: kUTTypePHPScript as String) - static let json = UTI(rawValue: kUTTypeJSON as String) - static let propertyList = UTI(rawValue: kUTTypePropertyList as String) - static let xmlPropertyList = UTI(rawValue: kUTTypeXMLPropertyList as String) - static let binaryPropertyList = UTI(rawValue: kUTTypeBinaryPropertyList as String) - static let pdf = UTI(rawValue: kUTTypePDF as String) - static let rtfd = UTI(rawValue: kUTTypeRTFD as String) - static let flatRTFD = UTI(rawValue: kUTTypeFlatRTFD as String) - static let txnTextAndMultimediaData = UTI(rawValue: kUTTypeTXNTextAndMultimediaData as String) - static let webArchive = UTI(rawValue: kUTTypeWebArchive as String) - static let image = UTI(rawValue: kUTTypeImage as String) - static let jpeg = UTI(rawValue: kUTTypeJPEG as String) - static let jpeg2000 = UTI(rawValue: kUTTypeJPEG2000 as String) - static let tiff = UTI(rawValue: kUTTypeTIFF as String) - static let pict = UTI(rawValue: kUTTypePICT as String) - static let gif = UTI(rawValue: kUTTypeGIF as String) - static let png = UTI(rawValue: kUTTypePNG as String) - static let quickTimeImage = UTI(rawValue: kUTTypeQuickTimeImage as String) - static let appleICNS = UTI(rawValue: kUTTypeAppleICNS as String) - static let bmp = UTI(rawValue: kUTTypeBMP as String) - static let ico = UTI(rawValue: kUTTypeICO as String) - static let rawImage = UTI(rawValue: kUTTypeRawImage as String) - static let scalableVectorGraphics = UTI(rawValue: kUTTypeScalableVectorGraphics as String) + static let swiftSource = UTI(rawValue: kUTTypeSwiftSource as String) + static let cPlusPlusSource = UTI(rawValue: kUTTypeCPlusPlusSource as String) + static let objectiveCPlusPlusSource = UTI(rawValue: kUTTypeObjectiveCPlusPlusSource as String) + static let cHeader = UTI(rawValue: kUTTypeCHeader as String) + static let cPlusPlusHeader = UTI(rawValue: kUTTypeCPlusPlusHeader as String) + static let javaSource = UTI(rawValue: kUTTypeJavaSource as String) + static let script = UTI(rawValue: kUTTypeScript as String) + static let appleScript = UTI(rawValue: kUTTypeAppleScript as String) + static let osaScript = UTI(rawValue: kUTTypeOSAScript as String) + static let osaScriptBundle = UTI(rawValue: kUTTypeOSAScriptBundle as String) + static let javaScript = UTI(rawValue: kUTTypeJavaScript as String) + static let shellScript = UTI(rawValue: kUTTypeShellScript as String) + static let perlScript = UTI(rawValue: kUTTypePerlScript as String) + static let pythonScript = UTI(rawValue: kUTTypePythonScript as String) + static let rubyScript = UTI(rawValue: kUTTypeRubyScript as String) + static let phpScript = UTI(rawValue: kUTTypePHPScript as String) + static let json = UTI(rawValue: kUTTypeJSON as String) + static let propertyList = UTI(rawValue: kUTTypePropertyList as String) + static let xmlPropertyList = UTI(rawValue: kUTTypeXMLPropertyList as String) + static let binaryPropertyList = UTI(rawValue: kUTTypeBinaryPropertyList as String) + static let pdf = UTI(rawValue: kUTTypePDF as String) + static let rtfd = UTI(rawValue: kUTTypeRTFD as String) + static let flatRTFD = UTI(rawValue: kUTTypeFlatRTFD as String) + static let txnTextAndMultimediaData = UTI(rawValue: kUTTypeTXNTextAndMultimediaData as String) + static let webArchive = UTI(rawValue: kUTTypeWebArchive as String) + static let image = UTI(rawValue: kUTTypeImage as String) + static let jpeg = UTI(rawValue: kUTTypeJPEG as String) + static let jpeg2000 = UTI(rawValue: kUTTypeJPEG2000 as String) + static let tiff = UTI(rawValue: kUTTypeTIFF as String) + static let pict = UTI(rawValue: kUTTypePICT as String) + static let gif = UTI(rawValue: kUTTypeGIF as String) + static let png = UTI(rawValue: kUTTypePNG as String) + static let quickTimeImage = UTI(rawValue: kUTTypeQuickTimeImage as String) + static let appleICNS = UTI(rawValue: kUTTypeAppleICNS as String) + static let bmp = UTI(rawValue: kUTTypeBMP as String) + static let ico = UTI(rawValue: kUTTypeICO as String) + static let rawImage = UTI(rawValue: kUTTypeRawImage as String) + static let scalableVectorGraphics = UTI(rawValue: kUTTypeScalableVectorGraphics as String) @available(OSX 10.12, iOS 9.1, watchOS 2.1, *) - static let livePhoto = UTI(rawValue: kUTTypeLivePhoto as String) + static let livePhoto = UTI(rawValue: kUTTypeLivePhoto as String) @available(OSX 10.12, iOS 9.1, *) - static let audiovisualContent = UTI(rawValue: kUTTypeAudiovisualContent as String) - static let movie = UTI(rawValue: kUTTypeMovie as String) - static let video = UTI(rawValue: kUTTypeVideo as String) - static let audio = UTI(rawValue: kUTTypeAudio as String) - static let quickTimeMovie = UTI(rawValue: kUTTypeQuickTimeMovie as String) - static let mpeg = UTI(rawValue: kUTTypeMPEG as String) - static let mpeg2Video = UTI(rawValue: kUTTypeMPEG2Video as String) - static let mpeg2TransportStream = UTI(rawValue: kUTTypeMPEG2TransportStream as String) - static let mp3 = UTI(rawValue: kUTTypeMP3 as String) - static let mpeg4 = UTI(rawValue: kUTTypeMPEG4 as String) - static let mpeg4Audio = UTI(rawValue: kUTTypeMPEG4Audio as String) - static let appleProtectedMPEG4Audio = UTI(rawValue: kUTTypeAppleProtectedMPEG4Audio as String) - static let appleProtectedMPEG4Video = UTI(rawValue: kUTTypeAppleProtectedMPEG4Video as String) - static let aviMovie = UTI(rawValue: kUTTypeAVIMovie as String) - static let audioInterchangeFileFormat = UTI(rawValue: kUTTypeAudioInterchangeFileFormat as String) - static let waveformAudio = UTI(rawValue: kUTTypeWaveformAudio as String) - static let midiAudio = UTI(rawValue: kUTTypeMIDIAudio as String) - static let playlist = UTI(rawValue: kUTTypePlaylist as String) - static let m3UPlaylist = UTI(rawValue: kUTTypeM3UPlaylist as String) - static let folder = UTI(rawValue: kUTTypeFolder as String) - static let volume = UTI(rawValue: kUTTypeVolume as String) - static let package = UTI(rawValue: kUTTypePackage as String) - static let bundle = UTI(rawValue: kUTTypeBundle as String) - static let pluginBundle = UTI(rawValue: kUTTypePluginBundle as String) - static let spotlightImporter = UTI(rawValue: kUTTypeSpotlightImporter as String) - static let quickLookGenerator = UTI(rawValue: kUTTypeQuickLookGenerator as String) - static let xpcService = UTI(rawValue: kUTTypeXPCService as String) - static let framework = UTI(rawValue: kUTTypeFramework as String) - static let application = UTI(rawValue: kUTTypeApplication as String) - static let applicationBundle = UTI(rawValue: kUTTypeApplicationBundle as String) - static let applicationFile = UTI(rawValue: kUTTypeApplicationFile as String) - static let unixExecutable = UTI(rawValue: kUTTypeUnixExecutable as String) - static let windowsExecutable = UTI(rawValue: kUTTypeWindowsExecutable as String) - static let javaClass = UTI(rawValue: kUTTypeJavaClass as String) - static let javaArchive = UTI(rawValue: kUTTypeJavaArchive as String) - static let systemPreferencesPane = UTI(rawValue: kUTTypeSystemPreferencesPane as String) - static let gnuZipArchive = UTI(rawValue: kUTTypeGNUZipArchive as String) - static let bzip2Archive = UTI(rawValue: kUTTypeBzip2Archive as String) - static let zipArchive = UTI(rawValue: kUTTypeZipArchive as String) - static let spreadsheet = UTI(rawValue: kUTTypeSpreadsheet as String) - static let presentation = UTI(rawValue: kUTTypePresentation as String) - static let database = UTI(rawValue: kUTTypeDatabase as String) - static let vCard = UTI(rawValue: kUTTypeVCard as String) - static let toDoItem = UTI(rawValue: kUTTypeToDoItem as String) - static let calendarEvent = UTI(rawValue: kUTTypeCalendarEvent as String) - static let emailMessage = UTI(rawValue: kUTTypeEmailMessage as String) - static let internetLocation = UTI(rawValue: kUTTypeInternetLocation as String) - static let inkText = UTI(rawValue: kUTTypeInkText as String) - static let font = UTI(rawValue: kUTTypeFont as String) - static let bookmark = UTI(rawValue: kUTTypeBookmark as String) - static let _3DContent = UTI(rawValue: kUTType3DContent as String) - static let pkcs12 = UTI(rawValue: kUTTypePKCS12 as String) - static let x509Certificate = UTI(rawValue: kUTTypeX509Certificate as String) - static let electronicPublication = UTI(rawValue: kUTTypeElectronicPublication as String) - static let log = UTI(rawValue: kUTTypeLog as String) + static let audiovisualContent = UTI(rawValue: kUTTypeAudiovisualContent as String) + static let movie = UTI(rawValue: kUTTypeMovie as String) + static let video = UTI(rawValue: kUTTypeVideo as String) + static let audio = UTI(rawValue: kUTTypeAudio as String) + static let quickTimeMovie = UTI(rawValue: kUTTypeQuickTimeMovie as String) + static let mpeg = UTI(rawValue: kUTTypeMPEG as String) + static let mpeg2Video = UTI(rawValue: kUTTypeMPEG2Video as String) + static let mpeg2TransportStream = UTI(rawValue: kUTTypeMPEG2TransportStream as String) + static let mp3 = UTI(rawValue: kUTTypeMP3 as String) + static let mpeg4 = UTI(rawValue: kUTTypeMPEG4 as String) + static let mpeg4Audio = UTI(rawValue: kUTTypeMPEG4Audio as String) + static let appleProtectedMPEG4Audio = UTI(rawValue: kUTTypeAppleProtectedMPEG4Audio as String) + static let appleProtectedMPEG4Video = UTI(rawValue: kUTTypeAppleProtectedMPEG4Video as String) + static let aviMovie = UTI(rawValue: kUTTypeAVIMovie as String) + static let audioInterchangeFileFormat = UTI(rawValue: kUTTypeAudioInterchangeFileFormat as String) + static let waveformAudio = UTI(rawValue: kUTTypeWaveformAudio as String) + static let midiAudio = UTI(rawValue: kUTTypeMIDIAudio as String) + static let playlist = UTI(rawValue: kUTTypePlaylist as String) + static let m3UPlaylist = UTI(rawValue: kUTTypeM3UPlaylist as String) + static let folder = UTI(rawValue: kUTTypeFolder as String) + static let volume = UTI(rawValue: kUTTypeVolume as String) + static let package = UTI(rawValue: kUTTypePackage as String) + static let bundle = UTI(rawValue: kUTTypeBundle as String) + static let pluginBundle = UTI(rawValue: kUTTypePluginBundle as String) + static let spotlightImporter = UTI(rawValue: kUTTypeSpotlightImporter as String) + static let quickLookGenerator = UTI(rawValue: kUTTypeQuickLookGenerator as String) + static let xpcService = UTI(rawValue: kUTTypeXPCService as String) + static let framework = UTI(rawValue: kUTTypeFramework as String) + static let application = UTI(rawValue: kUTTypeApplication as String) + static let applicationBundle = UTI(rawValue: kUTTypeApplicationBundle as String) + static let applicationFile = UTI(rawValue: kUTTypeApplicationFile as String) + static let unixExecutable = UTI(rawValue: kUTTypeUnixExecutable as String) + static let windowsExecutable = UTI(rawValue: kUTTypeWindowsExecutable as String) + static let javaClass = UTI(rawValue: kUTTypeJavaClass as String) + static let javaArchive = UTI(rawValue: kUTTypeJavaArchive as String) + static let systemPreferencesPane = UTI(rawValue: kUTTypeSystemPreferencesPane as String) + static let gnuZipArchive = UTI(rawValue: kUTTypeGNUZipArchive as String) + static let bzip2Archive = UTI(rawValue: kUTTypeBzip2Archive as String) + static let zipArchive = UTI(rawValue: kUTTypeZipArchive as String) + static let spreadsheet = UTI(rawValue: kUTTypeSpreadsheet as String) + static let presentation = UTI(rawValue: kUTTypePresentation as String) + static let database = UTI(rawValue: kUTTypeDatabase as String) + static let vCard = UTI(rawValue: kUTTypeVCard as String) + static let toDoItem = UTI(rawValue: kUTTypeToDoItem as String) + static let calendarEvent = UTI(rawValue: kUTTypeCalendarEvent as String) + static let emailMessage = UTI(rawValue: kUTTypeEmailMessage as String) + static let internetLocation = UTI(rawValue: kUTTypeInternetLocation as String) + static let inkText = UTI(rawValue: kUTTypeInkText as String) + static let font = UTI(rawValue: kUTTypeFont as String) + static let bookmark = UTI(rawValue: kUTTypeBookmark as String) + static let _3DContent = UTI(rawValue: kUTType3DContent as String) + static let pkcs12 = UTI(rawValue: kUTTypePKCS12 as String) + static let x509Certificate = UTI(rawValue: kUTTypeX509Certificate as String) + static let electronicPublication = UTI(rawValue: kUTTypeElectronicPublication as String) + static let log = UTI(rawValue: kUTTypeLog as String) } #if os(OSX) diff --git a/Riot/Modules/MatrixKit/MatrixKit.h b/Riot/Modules/MatrixKit/MatrixKit.h index b6e5806f4..d9cf57cf0 100644 --- a/Riot/Modules/MatrixKit/MatrixKit.h +++ b/Riot/Modules/MatrixKit/MatrixKit.h @@ -69,7 +69,6 @@ #import "MXKRoomCreationView.h" #import "MXKRoomInputToolbarView.h" -#import "MXKRoomInputToolbarViewWithHPGrowingText.h" #import "MXKRoomDataSourceManager.h" diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m index 9c37c1696..352e0ad6e 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m @@ -104,6 +104,8 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa } } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)saveAccounts { NSDate *startDate = [NSDate date]; @@ -123,6 +125,7 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa MXLogDebug(@"[MXKAccountManager] saveAccounts. Done (result: %@) in %.0fms", @(result), [[NSDate date] timeIntervalSinceDate:startDate] * 1000); } +#pragma clang diagnostic pop - (void)addAccount:(MXKAccount *)account andOpenSession:(BOOL)openSession { @@ -608,6 +611,8 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa return [matrixKitCacheFolder stringByAppendingPathComponent:kMXKAccountsKey]; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)loadAccounts { MXLogDebug(@"[MXKAccountManager] loadAccounts"); @@ -675,6 +680,7 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa mxAccounts = [NSMutableArray array]; } } +#pragma clang diagnostic pop - (NSData*)encryptData:(NSData*)data { @@ -708,6 +714,8 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa return data; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)migrateAccounts { NSString *pathOld = [[MXKAppSettings cacheFolder] stringByAppendingPathComponent:kMXKAccountsKeyOld]; @@ -727,6 +735,7 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa [fileManager removeItemAtPath:pathOld error:nil]; } } +#pragma clang diagnostic pop - (void)readAndWriteCredentials:(void (^)(NSArray * _Nullable readData, void (^completion)(BOOL didUpdateCredentials)))readAnWriteHandler { diff --git a/Riot/Modules/MatrixKit/Models/Contact/MXKContact.h b/Riot/Modules/MatrixKit/Models/Contact/MXKContact.h index 3d8e8af24..a927577e9 100644 --- a/Riot/Modules/MatrixKit/Models/Contact/MXKContact.h +++ b/Riot/Modules/MatrixKit/Models/Contact/MXKContact.h @@ -93,6 +93,8 @@ extern NSString *const kMXKContactDefaultContactPrefixId; */ - (void)resetMatrixThumbnail; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" /** The contact ID from native phonebook record */ @@ -105,6 +107,7 @@ extern NSString *const kMXKContactDefaultContactPrefixId; @return MXKContact instance */ - (id)initLocalContactWithABRecord:(ABRecordRef)record; +#pragma clang diagnostic pop /** Create a matrix contact with the dedicated info diff --git a/Riot/Modules/MatrixKit/Models/Contact/MXKContact.m b/Riot/Modules/MatrixKit/Models/Contact/MXKContact.m index 2b1e57727..f949b3a9b 100644 --- a/Riot/Modules/MatrixKit/Models/Contact/MXKContact.m +++ b/Riot/Modules/MatrixKit/Models/Contact/MXKContact.m @@ -40,6 +40,8 @@ NSString *const kMXKContactDefaultContactPrefixId = @"Default_"; @implementation MXKContact @synthesize isMatrixContact, isThirdPartyInvite; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + (NSString*)contactID:(ABRecordRef)record { return [NSString stringWithFormat:@"%@%d", kMXKContactLocalContactPrefixId, ABRecordGetRecordID(record)]; @@ -217,6 +219,7 @@ NSString *const kMXKContactDefaultContactPrefixId = @"Default_"; } return self; } +#pragma clang diagnostic pop - (id)initMatrixContactWithDisplayName:(NSString*)displayName andMatrixID:(NSString*)matrixID { diff --git a/Riot/Modules/MatrixKit/Models/Contact/MXKContactManager.m b/Riot/Modules/MatrixKit/Models/Contact/MXKContactManager.m index 52ac60a68..3226deef8 100644 --- a/Riot/Modules/MatrixKit/Models/Contact/MXKContactManager.m +++ b/Riot/Modules/MatrixKit/Models/Contact/MXKContactManager.m @@ -537,6 +537,8 @@ NSString *const MXKContactManagerDataType = @"org.matrix.kit.MXKContactManagerDa } } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" - (void)refreshLocalContacts { MXLogDebug(@"[MXKContactManager] refreshLocalContacts : Started"); @@ -721,6 +723,7 @@ NSString *const MXKContactManagerDataType = @"org.matrix.kit.MXKContactManagerDa }); } } +#pragma clang diagnostic pop - (void)updateMatrixIDsForLocalContact:(MXKContact *)contact { @@ -1582,6 +1585,9 @@ static NSString *matrixIDsDictFile = @"matrixIDsDictV2"; static NSString *localContactsFile = @"localContactsV2"; static NSString *contactsBookInfoFile = @"contactsV2"; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + - (NSString*)dataFilePathForComponent:(NSString*)component { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); @@ -1857,6 +1863,8 @@ static NSString *contactsBookInfoFile = @"contactsV2"; } } +#pragma clang diagnostic pop + - (BOOL)encryptAndSaveData:(NSData*)data toFile:(NSString*)fileName { NSError *error = nil; @@ -1865,6 +1873,10 @@ static NSString *contactsBookInfoFile = @"contactsV2"; if (error == nil) { [cipher writeToFile:[self dataFilePathForComponent:fileName] atomically:YES]; + [[NSFileManager defaultManager] excludeItemFromBackupAt:[NSURL fileURLWithPath:fileName] error:&error]; + if (error) { + MXLogDebug(@"[MXKContactManager] Cannot exclude item from backup %@", error.localizedDescription); + } } else { diff --git a/Riot/Modules/MatrixKit/Models/MXK3PID.m b/Riot/Modules/MatrixKit/Models/MXK3PID.m index 5c4657cfa..0ccf5c702 100644 --- a/Riot/Modules/MatrixKit/Models/MXK3PID.m +++ b/Riot/Modules/MatrixKit/Models/MXK3PID.m @@ -29,6 +29,10 @@ @property (nonatomic) NSString *sid; @property (nonatomic) MXIdentityService *identityService; @property (nonatomic) NSString *submitUrl; +/** + HTTP client dedicated to sending MSISDN token to custom URLs. + */ +@property (nonatomic, strong) MXHTTPClient *msisdnSubmissionHttpClient; @end @@ -255,14 +259,23 @@ @"token": token }; - MXHTTPClient *httpClient = [[MXHTTPClient alloc] initWithBaseURL:nil andOnUnrecognizedCertificateBlock:nil]; - return [httpClient requestWithMethod:@"POST" - path:url - parameters:parameters - success:^(NSDictionary *JSONResponse) { - success(); - } - failure:failure]; + self.msisdnSubmissionHttpClient = [[MXHTTPClient alloc] initWithBaseURL:nil andOnUnrecognizedCertificateBlock:nil]; + + MXWeakify(self); + return [self.msisdnSubmissionHttpClient requestWithMethod:@"POST" + path:url + parameters:parameters + success:^(NSDictionary *JSONResponse) { + success(); + MXStrongifyAndReturnIfNil(self); + self.msisdnSubmissionHttpClient = nil; + } + failure:^(NSError *error) { + failure(error); + MXStrongifyAndReturnIfNil(self); + self.msisdnSubmissionHttpClient = nil; + }]; + } - (void)add3PIDToUser:(BOOL)bind diff --git a/Riot/Modules/MatrixKit/Models/MXKAppSettings.m b/Riot/Modules/MatrixKit/Models/MXKAppSettings.m index 88aa527f7..b31fc0658 100644 --- a/Riot/Modules/MatrixKit/Models/MXKAppSettings.m +++ b/Riot/Modules/MatrixKit/Models/MXKAppSettings.m @@ -19,6 +19,7 @@ #import "MXKAppSettings.h" #import "MXKTools.h" +@import MatrixSDK; // get ISO country name @@ -66,11 +67,10 @@ static NSString *const kMXAppGroupID = @"group.org.matrix"; { NSString *cacheFolder; - // Check for a potential application group id - NSString *applicationGroupIdentifier = [MXSDKOptions sharedInstance].applicationGroupIdentifier; - if (applicationGroupIdentifier) + // Check for a potential application group container + NSURL *sharedContainerURL = [[NSFileManager defaultManager] applicationGroupContainerURL]; + if (sharedContainerURL) { - NSURL *sharedContainerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:applicationGroupIdentifier]; cacheFolder = [sharedContainerURL path]; } else @@ -86,7 +86,7 @@ static NSString *const kMXAppGroupID = @"group.org.matrix"; if (cacheFolder && ![[NSFileManager defaultManager] fileExistsAtPath:cacheFolder]) { NSError *error; - [[NSFileManager defaultManager] createDirectoryAtPath:cacheFolder withIntermediateDirectories:YES attributes:nil error:&error]; + [[NSFileManager defaultManager] createDirectoryExcludedFromBackupAtPath:cacheFolder error:&error]; if (error) { MXLogDebug(@"[MXKAppSettings] cacheFolder: Error: Cannot create MatrixKit folder at %@. Error: %@", cacheFolder, error); @@ -670,6 +670,8 @@ static NSString *const kMXAppGroupID = @"group.org.matrix"; } } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (NSString*)phonebookCountryCode { NSString* res = phonebookCountryCode; @@ -699,6 +701,7 @@ static NSString *const kMXAppGroupID = @"group.org.matrix"; return res; } +#pragma clang diagnostic pop - (void)setPhonebookCountryCode:(NSString *)stringValue { diff --git a/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.h b/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.h index b778785e6..6ec53437e 100644 --- a/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.h +++ b/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.h @@ -19,7 +19,7 @@ #import "MXKEventFormatter.h" #import "MXKURLPreviewDataProtocol.h" -@class MXThread; +@protocol MXThreadProtocol; /** Flags to indicate if a fix is required at the display time. @@ -108,7 +108,7 @@ typedef enum : NSUInteger { /** Thread for the bubble component. Should only exist for thread root events. */ -@property (nonatomic, readonly) MXThread *thread; +@property (nonatomic, readonly) id thread; /** Create a new `MXKRoomBubbleComponent` object based on a `MXEvent` instance. diff --git a/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.m b/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.m index 9a244040e..25560044b 100644 --- a/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.m +++ b/Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleComponent.m @@ -22,7 +22,7 @@ @interface MXKRoomBubbleComponent () -@property (nonatomic, readwrite) MXThread *thread; +@property (nonatomic, readwrite) id thread; @end @@ -69,8 +69,17 @@ _showEncryptionBadge = [self shouldShowWarningBadgeForEvent:event roomState:(MXRoomState*)roomState session:session]; [self updateLinkWithRoomState:roomState]; - - self.thread = [session.threadingService threadWithId:event.eventId]; + + if (event.unsignedData.relations.thread) + { + self.thread = [[MXThreadModel alloc] initWithRootEvent:event + notificationCount:0 + highlightCount:0]; + } + else + { + self.thread = [session.threadingService threadWithId:event.eventId]; + } } return self; } diff --git a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m index 88bd02a44..146fa084d 100644 --- a/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m +++ b/Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m @@ -34,7 +34,7 @@ #import "MXKSendReplyEventStringLocalizer.h" #import "MXKSlashCommands.h" -const BOOL USE_THREAD_TIMELINE = NO; +const BOOL USE_THREAD_TIMELINE = YES; #pragma mark - Constant definitions @@ -1448,6 +1448,16 @@ typedef NS_ENUM (NSUInteger, MXKRoomDataSourceError) { return NO; } } + + if (!USE_THREAD_TIMELINE && direction == MXTimelineDirectionBackwards && self.threadId) + { + // when not using a thread timeline, data source will desperately fill the screen with events by filtering them locally. + // we can stop when we see the thread root event when paginating backwards + if ([event.eventId isEqualToString:self.threadId]) + { + self.shouldStopBackPagination = YES; + } + } return YES; } diff --git a/Riot/Modules/MatrixKit/Models/RoomList/MXKInterleavedRecentsDataSource.m b/Riot/Modules/MatrixKit/Models/RoomList/MXKInterleavedRecentsDataSource.m index 28bb4c6ae..b9b34b566 100644 --- a/Riot/Modules/MatrixKit/Models/RoomList/MXKInterleavedRecentsDataSource.m +++ b/Riot/Modules/MatrixKit/Models/RoomList/MXKInterleavedRecentsDataSource.m @@ -56,7 +56,7 @@ #pragma mark - Override MXKRecentsDataSource -- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame +- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView { UIView *sectionHeader = nil; diff --git a/Riot/Modules/MatrixKit/Models/RoomList/MXKRecentsDataSource.h b/Riot/Modules/MatrixKit/Models/RoomList/MXKRecentsDataSource.h index b9de69b4a..f37147425 100644 --- a/Riot/Modules/MatrixKit/Models/RoomList/MXKRecentsDataSource.h +++ b/Riot/Modules/MatrixKit/Models/RoomList/MXKRecentsDataSource.h @@ -88,9 +88,10 @@ @param section the section index @param frame the drawing area for the header of the specified section. + @param tableView the table view @return the section header. */ -- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame; +- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView; /** Get the data for the cell at the given index path. diff --git a/Riot/Modules/MatrixKit/Models/RoomList/MXKRecentsDataSource.m b/Riot/Modules/MatrixKit/Models/RoomList/MXKRecentsDataSource.m index 0da982a1d..fa6480b85 100644 --- a/Riot/Modules/MatrixKit/Models/RoomList/MXKRecentsDataSource.m +++ b/Riot/Modules/MatrixKit/Models/RoomList/MXKRecentsDataSource.m @@ -257,7 +257,7 @@ } } -- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame +- (UIView *)viewForHeaderInSection:(NSInteger)section withFrame:(CGRect)frame inTableView:(UITableView*)tableView { UIView *sectionHeader = nil; diff --git a/Riot/Modules/MatrixKit/Models/RoomList/MXKSessionRecentsDataSource.h b/Riot/Modules/MatrixKit/Models/RoomList/MXKSessionRecentsDataSource.h index 0748f1e62..c03c4c4de 100644 --- a/Riot/Modules/MatrixKit/Models/RoomList/MXKSessionRecentsDataSource.h +++ b/Riot/Modules/MatrixKit/Models/RoomList/MXKSessionRecentsDataSource.h @@ -30,8 +30,8 @@ extern NSString *const kMXKRecentCellIdentifier; /** The recents data source based on a unique matrix session. + Deprecated: Please see MXSession.roomListDataManager */ -MXK_DEPRECATED_ATTRIBUTE_WITH_MSG("See MXSession.roomListDataManager") @interface MXKSessionRecentsDataSource : MXKDataSource { @protected diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m index 94ee3fa09..adb2b45af 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m @@ -955,7 +955,7 @@ static NSString *const kHTMLATagRegexPattern = @"([^<]*)"; } if (event.content[@"kick"]) { - displayText = [NSString stringWithFormat:@"%@\n\u2022 kick: %@", displayText, event.content[@"kick"]]; + displayText = [NSString stringWithFormat:@"%@\n\u2022 remove: %@", displayText, event.content[@"kick"]]; } if (event.content[@"redact"]) { diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MarkdownToHTMLRenderer.swift b/Riot/Modules/MatrixKit/Utils/EventFormatter/MarkdownToHTMLRenderer.swift index 820928fb2..392e900dd 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MarkdownToHTMLRenderer.swift +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MarkdownToHTMLRenderer.swift @@ -36,7 +36,17 @@ public class MarkdownToHTMLRenderer: NSObject { extension MarkdownToHTMLRenderer: MarkdownToHTMLRendererProtocol { public func renderToHTML(markdown: String) -> String? { - return try? Down(markdownString: markdown).toHTML(options) + do { + let ast = try DownASTRenderer.stringToAST(markdown, options: options) + defer { + cmark_node_free(ast) + } + ast.repairLinks() + return try DownHTMLRenderer.astToHTML(ast, options: options) + } catch { + MXLog.error("[MarkdownToHTMLRenderer] renderToHTML failed with string: \(markdown)") + return nil + } } } @@ -50,3 +60,116 @@ public class MarkdownToHTMLRendererHardBreaks: MarkdownToHTMLRenderer { } } + +// MARK: - AST-handling private extensions +private extension CMarkNode { + /// Formatting symbol associated with given note type + /// Note: this is only defined for node types that are handled in repairLinks + var formattingSymbol: String { + switch self.type { + case CMARK_NODE_EMPH: + return "_" + case CMARK_NODE_STRONG: + return "__" + default: + return "" + } + } + + /// Repairs links that were broken down by markdown formatting. + /// Should be used on the first node of libcmark's AST + /// (e.g. the object returned by DownASTRenderer.stringToAST). + func repairLinks() { + let iterator = cmark_iter_new(self) + var text = "" + var isInParagraph = false + var previousNode: CMarkNode? + var orphanNodes: [CMarkNode] = [] + var shouldUnlinkFormattingMode = false + var event: cmark_event_type? + while event != CMARK_EVENT_DONE { + event = cmark_iter_next(iterator) + + guard let node = cmark_iter_get_node(iterator) else { return } + + if node.type == CMARK_NODE_PARAGRAPH { + if event == CMARK_EVENT_ENTER { + isInParagraph = true + } else { + isInParagraph = false + text = "" + } + } + + if isInParagraph { + switch node.type { + case CMARK_NODE_SOFTBREAK, + CMARK_NODE_LINEBREAK: + text = "" + case CMARK_NODE_TEXT: + if let literal = node.literal { + text += literal + // Reset text if it ends up with a whitespace. + if text.last?.isWhitespace == true { + text = "" + } + // Only the last part could be a link conflicting with next node. + text = String(text.split(separator: " ").last ?? "") + } + case CMARK_NODE_EMPH where previousNode?.type == CMARK_NODE_TEXT, + CMARK_NODE_STRONG where previousNode?.type == CMARK_NODE_TEXT: + if event == CMARK_EVENT_ENTER { + if !text.containedUrls.isEmpty, + let childLiteral = node.pointee.first_child.literal { + // If current text is a link, the formatted text is reverted back to a + // plain text as a part of the link. + let symbol = node.formattingSymbol + let nonFormattedText = "\(symbol)\(childLiteral)\(symbol)" + let replacementTextNode = cmark_node_new(CMARK_NODE_TEXT) + cmark_node_set_literal(replacementTextNode, nonFormattedText) + cmark_node_insert_after(previousNode, replacementTextNode) + // Set child literal to empty string so we dont read it. + // This avoids having to re-create the main + // iterator in the middle of the process. + cmark_node_set_literal(node.pointee.first_child, "") + let newIterator = cmark_iter_new(node) + _ = cmark_iter_next(newIterator) + cmark_node_unlink(node) + orphanNodes.append(node) + let nextNode = cmark_iter_get_node(newIterator) + cmark_node_insert_after(previousNode, nextNode) + shouldUnlinkFormattingMode = true + } + } else { + if shouldUnlinkFormattingMode { + cmark_node_unlink(node) + orphanNodes.append(node) + shouldUnlinkFormattingMode = false + } + } + default: + break + } + } + previousNode = node + } + + // Free all nodes removed from the AST. + // This is done as a last step to avoid messing + // up with the main itertor. + for orphanNode in orphanNodes { + cmark_node_free(orphanNode) + } + } +} + +private extension String { + /// Returns array of URLs detected inside the String. + var containedUrls: [NSTextCheckingResult] { + guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else { + return [] + } + + return detector.matches(in: self, options: [], range: NSRange(location: 0, length: self.utf16.count)) + } +} diff --git a/Riot/Modules/MatrixKit/Utils/MXKConstants.h b/Riot/Modules/MatrixKit/Utils/MXKConstants.h index b580d234e..7a5659b8c 100644 --- a/Riot/Modules/MatrixKit/Utils/MXKConstants.h +++ b/Riot/Modules/MatrixKit/Utils/MXKConstants.h @@ -17,9 +17,6 @@ #import -#define MXK_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) -#define MXK_DEPRECATED_ATTRIBUTE_WITH_MSG(msg) __attribute((deprecated((msg)))) - /** The Matrix iOS Kit version. */ diff --git a/Riot/Modules/MatrixKit/Utils/MXKDocumentPickerPresenter.swift b/Riot/Modules/MatrixKit/Utils/MXKDocumentPickerPresenter.swift index 1f9b9c89c..0ff493e30 100644 --- a/Riot/Modules/MatrixKit/Utils/MXKDocumentPickerPresenter.swift +++ b/Riot/Modules/MatrixKit/Utils/MXKDocumentPickerPresenter.swift @@ -24,7 +24,8 @@ import MobileCoreServices /// MXKDocumentPickerPresenter presents a controller that provides access to documents or destinations outside the app’s sandbox. /// Internally presents a UIDocumentPickerViewController in UIDocumentPickerMode.import. -/// Note: You must turn on the iCloud Documents capabilities in Xcode (see https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/DocumentPickerProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40014451) +/// Note: You must turn on the iCloud Documents capabilities in Xcode +/// (see https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/DocumentPickerProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40014451) @objcMembers public class MXKDocumentPickerPresenter: NSObject { diff --git a/Riot/Modules/MatrixKit/Utils/MXKTools.m b/Riot/Modules/MatrixKit/Utils/MXKTools.m index c941fbc98..7e3231324 100644 --- a/Riot/Modules/MatrixKit/Utils/MXKTools.m +++ b/Riot/Modules/MatrixKit/Utils/MXKTools.m @@ -1112,7 +1112,7 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo // Caution: We need here to escape the non-ASCII characters (like '#' in room alias) // to convert the link into a legal URL string. NSString *link = [attributedString.string substringWithRange:match.range]; - link = [link stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + link = [link stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; [*mutableAttributedString addAttribute:NSLinkAttributeName value:link range:match.range]; } }]; diff --git a/Riot/Modules/MatrixKit/Utils/MXKUTI.swift b/Riot/Modules/MatrixKit/Utils/MXKUTI.swift index 911beec65..87822cd7d 100644 --- a/Riot/Modules/MatrixKit/Utils/MXKUTI.swift +++ b/Riot/Modules/MatrixKit/Utils/MXKUTI.swift @@ -20,7 +20,7 @@ import MobileCoreServices // We do not use the SwiftUTI pod anymore // The library is embedded in MatrixKit. See Libs/SwiftUTI/README.md for more details -//import SwiftUTI +// import SwiftUTI /// MXKUTI represents a Universal Type Identifier (e.g. kUTTypePNG). /// See https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html#//apple_ref/doc/uid/TP40001319-CH202-SW5 for more information. @@ -134,6 +134,8 @@ extension MXKUTI { self.init(rawValue: uti as String) } + // swiftlint:disable unused_optional_binding + /// Initialize with local file URL. /// This method is currently applicable only to URLs for file system resources. /// @@ -153,6 +155,8 @@ extension MXKUTI { } } + // swiftlint:enable unused_optional_binding + public convenience init?(localFileURL: URL) { self.init(localFileURL: localFileURL, loadResourceValues: true) } @@ -173,6 +177,8 @@ extension MXKUTI { } } +// swiftlint:disable force_unwrapping + // MARK: - Some system defined UTIs extension MXKUTI { public static let data = MXKUTI(cfRawValue: kUTTypeData)! @@ -190,6 +196,8 @@ extension MXKUTI { public static let xml = MXKUTI(cfRawValue: kUTTypeXML)! } +// swiftlint:enable force_unwrapping + // MARK: - Convenience static methods extension MXKUTI { diff --git a/Riot/Modules/MatrixKit/Views/Authentication/MXKAuthenticationFallbackWebView.m b/Riot/Modules/MatrixKit/Views/Authentication/MXKAuthenticationFallbackWebView.m index 09c50ffc6..ad44e34d8 100644 --- a/Riot/Modules/MatrixKit/Views/Authentication/MXKAuthenticationFallbackWebView.m +++ b/Riot/Modules/MatrixKit/Views/Authentication/MXKAuthenticationFallbackWebView.m @@ -120,7 +120,7 @@ sendObjectMessage({ \ MXLogDebug(@"[MXKAuthenticationFallbackWebView] URL has js: prefix"); // Listen only to scheme of the JS-WKWebView bridge - NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding]; + NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByRemovingPercentEncoding]; NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; NSError *error; diff --git a/Riot/Modules/MatrixKit/Views/Authentication/MXKAuthenticationRecaptchaWebView.m b/Riot/Modules/MatrixKit/Views/Authentication/MXKAuthenticationRecaptchaWebView.m index 8e3bfa6fa..776214b30 100644 --- a/Riot/Modules/MatrixKit/Views/Authentication/MXKAuthenticationRecaptchaWebView.m +++ b/Riot/Modules/MatrixKit/Views/Authentication/MXKAuthenticationRecaptchaWebView.m @@ -102,7 +102,7 @@ var onloadCallback = function() { \ if ([urlString hasPrefix:@"js:"]) { // Listen only to scheme of the JS-WKWebView bridge - NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding]; + NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByRemovingPercentEncoding]; NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; NSError *error; diff --git a/Riot/Modules/MatrixKit/Views/EncryptionInfoView/MXKEncryptionInfoView.m b/Riot/Modules/MatrixKit/Views/EncryptionInfoView/MXKEncryptionInfoView.m index a6537cbd5..cc6c3175e 100644 --- a/Riot/Modules/MatrixKit/Views/EncryptionInfoView/MXKEncryptionInfoView.m +++ b/Riot/Modules/MatrixKit/Views/EncryptionInfoView/MXKEncryptionInfoView.m @@ -417,7 +417,7 @@ static NSAttributedString *verticalWhitespace = nil; [_mxSession.crypto setDeviceVerification:MXDeviceVerified forDevice:_mxDeviceInfo.deviceId ofUser:_mxDeviceInfo.userId success:^{ // Refresh data - _mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent]; + self->_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent]; if (self->_delegate) { [self->_delegate encryptionInfoView:self didDeviceInfoVerifiedChange:self.mxDeviceInfo]; @@ -473,7 +473,7 @@ static NSAttributedString *verticalWhitespace = nil; [_mxSession.crypto setDeviceVerification:verificationStatus forDevice:_mxDeviceInfo.deviceId ofUser:_mxDeviceInfo.userId success:^{ // Refresh data - _mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent]; + self->_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent]; if (self->_delegate) { diff --git a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.h b/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.h deleted file mode 100644 index b10b54fa6..000000000 --- a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright 2015 OpenMarket 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 "MXKRoomInputToolbarView.h" - -#import - -/** - `MXKRoomInputToolbarViewWithHPGrowingText` is a MXKRoomInputToolbarView-inherited class in which message - composer is based on `HPGrowingTextView`. - - Toolbar buttons are not overridden by this class. We keep the default implementation. - */ -@interface MXKRoomInputToolbarViewWithHPGrowingText : MXKRoomInputToolbarView -{ -@protected - HPGrowingTextView *growingTextView; -} - -@end diff --git a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.m b/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.m deleted file mode 100644 index fe5f87ff7..000000000 --- a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.m +++ /dev/null @@ -1,187 +0,0 @@ -/* - Copyright 2015 OpenMarket Ltd - Copyright 2017 Vector Creations 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 "MXKRoomInputToolbarViewWithHPGrowingText.h" - -@interface MXKRoomInputToolbarViewWithHPGrowingText() -{ - // HPGrowingTextView triggers growingTextViewDidChange event when it recomposes itself - // Save the last edited text to prevent unexpected typing events - NSString* lastEditedText; -} - -/** - Message composer defined in `messageComposerContainer`. - */ -@property (nonatomic) IBOutlet HPGrowingTextView *growingTextView; - -@end - -@implementation MXKRoomInputToolbarViewWithHPGrowingText -@synthesize growingTextView; - -+ (UINib *)nib -{ - return [UINib nibWithNibName:NSStringFromClass([MXKRoomInputToolbarViewWithHPGrowingText class]) - bundle:[NSBundle bundleForClass:[MXKRoomInputToolbarViewWithHPGrowingText class]]]; -} - -- (void)awakeFromNib -{ - [super awakeFromNib]; - - // Handle message composer based on HPGrowingTextView use - growingTextView.delegate = self; - - [growingTextView setTranslatesAutoresizingMaskIntoConstraints: NO]; - - // Add an accessory view to the text view in order to retrieve keyboard view. - inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero]; - growingTextView.internalTextView.inputAccessoryView = self.inputAccessoryView; - - // on IOS 8, the growing textview animation could trigger weird UI animations - // indeed, the messages tableView can be refreshed while its height is updated (e.g. when setting a message) - growingTextView.animateHeightChange = NO; - - lastEditedText = nil; -} - -- (void)dealloc -{ - [self destroy]; -} - --(void)customizeViewRendering -{ - [super customizeViewRendering]; - - // set text input font - growingTextView.font = [UIFont systemFontOfSize:14]; - - // draw a rounded border around the textView - growingTextView.layer.cornerRadius = 5; - growingTextView.layer.borderWidth = 1; - growingTextView.layer.borderColor = [UIColor lightGrayColor].CGColor; - growingTextView.clipsToBounds = YES; - growingTextView.backgroundColor = [UIColor whiteColor]; -} - -- (void)destroy -{ - if (growingTextView) - { - growingTextView.delegate = nil; - growingTextView = nil; - } - - [super destroy]; -} - -- (void)setMaxHeight:(CGFloat)maxHeight -{ - growingTextView.maxHeight = maxHeight - (self.messageComposerContainerTopConstraint.constant + self.messageComposerContainerBottomConstraint.constant); - [growingTextView refreshHeight]; - - super.maxHeight = maxHeight; -} - -- (NSString*)textMessage -{ - return growingTextView.text; -} - -- (void)setTextMessage:(NSString *)textMessage -{ - growingTextView.text = textMessage; - self.rightInputToolbarButton.enabled = textMessage.length; -} - -- (void)pasteText:(NSString *)text -{ - self.textMessage = [growingTextView.text stringByReplacingCharactersInRange:growingTextView.selectedRange withString:text]; -} - -- (void)setPlaceholder:(NSString *)inPlaceholder -{ - [super setPlaceholder:inPlaceholder]; - growingTextView.placeholder = inPlaceholder; -} - -- (BOOL)becomeFirstResponder -{ - return [growingTextView becomeFirstResponder]; -} - -- (void)dismissKeyboard -{ - [growingTextView resignFirstResponder]; -} - -#pragma mark - HPGrowingTextView delegate - -- (void)growingTextViewDidEndEditing:(HPGrowingTextView *)sender -{ - if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:isTyping:)]) - { - [self.delegate roomInputToolbarView:self isTyping:NO]; - } -} - -- (void)growingTextViewDidChange:(HPGrowingTextView *)sender -{ - NSString *msg = growingTextView.text; - - // HPGrowingTextView triggers growingTextViewDidChange event when it recomposes itself. - // Save the last edited text to prevent unexpected typing events - if (![lastEditedText isEqualToString:msg]) - { - lastEditedText = msg; - if (msg.length) - { - if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:isTyping:)]) - { - [self.delegate roomInputToolbarView:self isTyping:YES]; - } - self.rightInputToolbarButton.enabled = YES; - } - else - { - if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:isTyping:)]) - { - [self.delegate roomInputToolbarView:self isTyping:NO]; - } - self.rightInputToolbarButton.enabled = NO; - } - } -} - -- (void)growingTextView:(HPGrowingTextView *)growingTextView willChangeHeight:(float)height -{ - // Update growing text's superview (toolbar view) - CGFloat updatedHeight = height + (self.messageComposerContainerTopConstraint.constant + self.messageComposerContainerBottomConstraint.constant); - if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:heightDidChanged:completion:)]) - { - [self.delegate roomInputToolbarView:self heightDidChanged:updatedHeight completion:nil]; - } -} - -- (BOOL)growingTextView:(HPGrowingTextView *)growingTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text -{ - return self.isEditable; -} - -@end diff --git a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.xib b/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.xib deleted file mode 100644 index 3f4117499..000000000 --- a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarViewWithHPGrowingText.xib +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Riot/Modules/MediaPicker/Library/MediaAlbumContentViewController.m b/Riot/Modules/MediaPicker/Library/MediaAlbumContentViewController.m index c01c845e6..440aeb027 100644 --- a/Riot/Modules/MediaPicker/Library/MediaAlbumContentViewController.m +++ b/Riot/Modules/MediaPicker/Library/MediaAlbumContentViewController.m @@ -268,10 +268,10 @@ }]; - cell.bottomLeftIcon.image = [UIImage imageNamed:@"video_icon"]; + cell.bottomLeftIcon.image = AssetImages.videoIcon.image; cell.bottomLeftIcon.hidden = (asset.mediaType == PHAssetMediaTypeImage); - cell.bottomRightIcon.image = [UIImage imageNamed:@"selection_tick"]; + cell.bottomRightIcon.image = AssetImages.selectionTick.image; cell.bottomRightIcon.tintColor = ThemeService.shared.theme.tintColor; cell.bottomRightIcon.hidden = !selectedAssets || (NSNotFound == [selectedAssets indexOfObject:asset]); diff --git a/Riot/Modules/MediaPicker/MediaPickerViewController.m b/Riot/Modules/MediaPicker/MediaPickerViewController.m index 48ff40d19..8905b926b 100644 --- a/Riot/Modules/MediaPicker/MediaPickerViewController.m +++ b/Riot/Modules/MediaPicker/MediaPickerViewController.m @@ -729,8 +729,8 @@ videoPlayerControl = [UIButton buttonWithType:UIButtonTypeCustom]; [videoPlayerControl addTarget:self action:@selector(controlVideoPlayer) forControlEvents:UIControlEventTouchUpInside]; videoPlayerControl.frame = CGRectMake(0, 0, 44, 44); - [videoPlayerControl setImage:[UIImage imageNamed:@"camera_play"] forState:UIControlStateNormal]; - [videoPlayerControl setImage:[UIImage imageNamed:@"camera_play"] forState:UIControlStateHighlighted]; + [videoPlayerControl setImage:AssetImages.cameraPlay.image forState:UIControlStateNormal]; + [videoPlayerControl setImage:AssetImages.cameraPlay.image forState:UIControlStateHighlighted]; [validationView addSubview:videoPlayerControl]; videoPlayerControl.center = validationView.imageView.center; @@ -796,8 +796,8 @@ [videoPlayer.player seekToTime:kCMTimeZero]; [videoPlayer.view removeFromSuperview]; - [videoPlayerControl setImage:[UIImage imageNamed:@"camera_play"] forState:UIControlStateNormal]; - [videoPlayerControl setImage:[UIImage imageNamed:@"camera_play"] forState:UIControlStateHighlighted]; + [videoPlayerControl setImage:AssetImages.cameraPlay.image forState:UIControlStateNormal]; + [videoPlayerControl setImage:AssetImages.cameraPlay.image forState:UIControlStateHighlighted]; } else { @@ -815,8 +815,8 @@ [videoPlayer.player play]; - [videoPlayerControl setImage:[UIImage imageNamed:@"camera_stop"] forState:UIControlStateNormal]; - [videoPlayerControl setImage:[UIImage imageNamed:@"camera_stop"] forState:UIControlStateHighlighted]; + [videoPlayerControl setImage:AssetImages.cameraStop.image forState:UIControlStateNormal]; + [videoPlayerControl setImage:AssetImages.cameraStop.image forState:UIControlStateHighlighted]; [validationView bringSubviewToFront:videoPlayerControl]; } } @@ -860,7 +860,7 @@ }]; - cell.bottomLeftIcon.image = [UIImage imageNamed:@"video_icon"]; + cell.bottomLeftIcon.image = AssetImages.videoIcon.image; cell.bottomLeftIcon.hidden = (asset.mediaType == PHAssetMediaTypeImage); // Disable user interaction in mxkImageView, in order to let collection handle user selection @@ -968,7 +968,7 @@ if (collection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumVideos) { - cell.bottomLeftIcon.image = [UIImage imageNamed:@"video_icon"]; + cell.bottomLeftIcon.image = AssetImages.videoIcon.image; cell.bottomLeftIcon.hidden = NO; } else diff --git a/Riot/Modules/Onboarding/OnboardingCoordinator.swift b/Riot/Modules/Onboarding/OnboardingCoordinator.swift index 8a6dcc7c4..dd7f088e7 100644 --- a/Riot/Modules/Onboarding/OnboardingCoordinator.swift +++ b/Riot/Modules/Onboarding/OnboardingCoordinator.swift @@ -54,12 +54,13 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { private var navigationRouter: NavigationRouterType { parameters.router } - // Keep a strong ref as we need to init authVC early to preload its view (it is *really* slow to do in realtime) - private var authenticationCoordinator: AuthenticationCoordinatorProtocol = AuthenticationCoordinator() + // Keep a strong ref as we need to init authVC early to preload its view + private let authenticationCoordinator: AuthenticationCoordinatorProtocol private var isShowingAuthentication = false // MARK: Screen results private var splashScreenResult: OnboardingSplashScreenViewModelResult? + private var useCaseResult: OnboardingUseCaseViewModelResult? // MARK: Public @@ -71,6 +72,11 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { init(parameters: OnboardingCoordinatorParameters) { self.parameters = parameters + + // Preload the authVC (it is *really* slow to load in realtime) + let authenticationParameters = AuthenticationCoordinatorParameters(navigationRouter: parameters.router) + authenticationCoordinator = AuthenticationCoordinator(parameters: authenticationParameters) + super.init() } @@ -126,9 +132,47 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { self.navigationRouter.setRootModule(coordinator, popCompletion: nil) } + @available(iOS 14.0, *) /// Displays the next view in the flow after the splash screen. private func splashScreenCoordinator(_ coordinator: OnboardingSplashScreenCoordinator, didCompleteWith result: OnboardingSplashScreenViewModelResult) { splashScreenResult = result + + // Set the auth type early to allow network requests to finish during display of the use case screen. + let mxkAuthenticationType = splashScreenResult == .register ? MXKAuthenticationTypeRegister : MXKAuthenticationTypeLogin + authenticationCoordinator.update(authenticationType: mxkAuthenticationType) + + switch result { + case .register: + showUseCaseSelectionScreen() + case .login: + showAuthenticationScreen() + } + } + + @available(iOS 14.0, *) + /// Show the use case screen for new users. + private func showUseCaseSelectionScreen() { + let coordinator = OnboardingUseCaseSelectionCoordinator() + coordinator.completion = { [weak self, weak coordinator] result in + guard let self = self, let coordinator = coordinator else { return } + self.useCaseSelectionCoordinator(coordinator, didCompleteWith: result) + } + + coordinator.start() + add(childCoordinator: coordinator) + + if self.navigationRouter.modules.isEmpty { + self.navigationRouter.setRootModule(coordinator, popCompletion: nil) + } else { + self.navigationRouter.push(coordinator, animated: true) { [weak self] in + self?.remove(childCoordinator: coordinator) + } + } + } + + /// Displays the next view in the flow after the use case screen. + private func useCaseSelectionCoordinator(_ coordinator: OnboardingUseCaseSelectionCoordinator, didCompleteWith result: OnboardingUseCaseViewModelResult) { + useCaseResult = result showAuthenticationScreen() } @@ -139,28 +183,35 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { MXLog.debug("[OnboardingCoordinator] showAuthenticationScreen") let coordinator = authenticationCoordinator - coordinator.completion = { [weak self, weak coordinator] in + coordinator.completion = { [weak self, weak coordinator] result in guard let self = self, let coordinator = coordinator else { return } - self.authenticationCoordinatorDidComplete(coordinator) + + switch result { + case .didLogin(let session): + self.authenticationCoordinator(coordinator, didLoginWith: session) + case .didComplete(let authenticationType): + self.authenticationCoordinator(coordinator, didCompleteWith: authenticationType) + } + } // Due to needing to preload the authVC, this breaks the Coordinator init/start pattern. // This can be re-assessed once we re-write a native flow for authentication. - // Set authType first as registration parameters or soft logout credentials will modify this. - let mxkAuthenticationType = splashScreenResult == .register ? MXKAuthenticationTypeRegister : MXKAuthenticationTypeLogin - coordinator.update(authenticationType: mxkAuthenticationType) - if let externalRegistrationParameters = externalRegistrationParameters { coordinator.update(externalRegistrationParameters: externalRegistrationParameters) } + + if useCaseResult == .customServer { + coordinator.showCustomServer() + } + if let softLogoutCredentials = parameters.softLogoutCredentials { coordinator.update(softLogoutCredentials: softLogoutCredentials) } coordinator.start() add(childCoordinator: coordinator) - authenticationCoordinator = coordinator if customHomeserver != nil || customIdentityServer != nil { coordinator.updateHomeserver(customHomeserver, andIdentityServer: customIdentityServer) @@ -177,9 +228,43 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { isShowingAuthentication = true } + private func authenticationCoordinator(_ coordinator: AuthenticationCoordinatorProtocol, didLoginWith session: MXSession) { + // TODO: Show next screens whilst waiting for the everything to load. + // May need to move the spinner and key verification up to here in order to coordinate properly. + } + /// Displays the next view in the flow after the authentication screen. - private func authenticationCoordinatorDidComplete(_ coordinator: AuthenticationCoordinatorProtocol) { + private func authenticationCoordinator(_ coordinator: AuthenticationCoordinatorProtocol, didCompleteWith authenticationType: MXKAuthenticationType) { completion?() isShowingAuthentication = false + + // Handle the chosen use case where applicable + if authenticationType == MXKAuthenticationTypeRegister, + let useCase = useCaseResult?.userSessionPropertyValue, + let userSession = UserSessionsService.shared.mainUserSession { + // Store the value in the user's session + userSession.userProperties.useCase = useCase + + // Update the analytics user properties with the use case + Analytics.shared.updateUserProperties(ftueUseCase: useCase) + } + } +} + +extension OnboardingUseCaseViewModelResult { + /// The result converted into the type stored in the user session. + var userSessionPropertyValue: UserSessionProperties.UseCase? { + switch self { + case .personalMessaging: + return .personalMessaging + case .workMessaging: + return .workMessaging + case .communityMessaging: + return .communityMessaging + case .skipped: + return .skipped + case .customServer: + return nil + } } } diff --git a/Riot/Modules/Onboarding/OnboardingCoordinatorBridgePresenter.swift b/Riot/Modules/Onboarding/OnboardingCoordinatorBridgePresenter.swift index 9cc43c1b1..32f433936 100644 --- a/Riot/Modules/Onboarding/OnboardingCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Onboarding/OnboardingCoordinatorBridgePresenter.swift @@ -28,7 +28,8 @@ class OnboardingCoordinatorBridgePresenterParameters: NSObject { /// OnboardingCoordinatorBridgePresenter enables to start OnboardingCoordinator from a view controller. /// This bridge is used while waiting for global usage of coordinator pattern. -/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator. +/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). Each bridge should be removed +/// once the underlying Coordinator has been integrated by another Coordinator. @objcMembers final class OnboardingCoordinatorBridgePresenter: NSObject { diff --git a/Riot/Modules/People/PeopleViewController.m b/Riot/Modules/People/PeopleViewController.m index 43bb1754c..028ec8eef 100644 --- a/Riot/Modules/People/PeopleViewController.m +++ b/Riot/Modules/People/PeopleViewController.m @@ -69,7 +69,7 @@ self.recentsTableView.tag = RecentsDataSourceModePeople; // Add the (+) button programmatically - plusButtonImageView = [self vc_addFABWithImage:[UIImage imageNamed:@"people_floating_action"] + plusButtonImageView = [self vc_addFABWithImage:AssetImages.peopleFloatingAction.image target:self action:@selector(onPlusButtonPressed)]; } @@ -177,11 +177,11 @@ { if (ThemeService.shared.isCurrentThemeDark) { - return [UIImage imageNamed:@"people_empty_screen_artwork_dark"]; + return AssetImages.peopleEmptyScreenArtworkDark.image; } else { - return [UIImage imageNamed:@"people_empty_screen_artwork"]; + return AssetImages.peopleEmptyScreenArtwork.image; } } diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 3ba7dab34..085bc137d 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -701,7 +701,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat // component is not a thread root return 0; } - return RoomBubbleCellLayout.threadSummaryViewTopMargin + + return PlainRoomCellLayoutConstants.threadSummaryViewTopMargin + [ThreadSummaryView contentViewHeightForThread:component.thread fitting:self.maxTextViewWidth]; } @@ -728,7 +728,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat // event is not in a thread return 0; } - return RoomBubbleCellLayout.fromAThreadViewTopMargin + + return PlainRoomCellLayoutConstants.fromAThreadViewTopMargin + [FromAThreadView contentViewHeightForEvent:component.event fitting:self.maxTextViewWidth]; } @@ -740,7 +740,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat return 0; } - return RoomBubbleCellLayout.urlPreviewViewTopMargin + [URLPreviewView contentViewHeightFor:component.urlPreviewData + return PlainRoomCellLayoutConstants.urlPreviewViewTopMargin + [URLPreviewView contentViewHeightFor:component.urlPreviewData fitting:self.maxTextViewWidth]; } @@ -765,7 +765,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat BOOL showAllReactions = [self.eventsToShowAllReactions containsObject:eventId]; BubbleReactionsViewModel *viewModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:eventId showAll:showAllReactions]; - height = [bubbleReactionsViewSizer heightForViewModel:viewModel fittingWidth:bubbleReactionsViewWidth] + RoomBubbleCellLayout.reactionsViewTopMargin; + height = [bubbleReactionsViewSizer heightForViewModel:viewModel fittingWidth:bubbleReactionsViewWidth] + PlainRoomCellLayoutConstants.reactionsViewTopMargin; } return height; @@ -777,7 +777,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat if (self.readReceipts[eventId].count) { - height = RoomBubbleCellLayout.readReceiptsViewHeight + RoomBubbleCellLayout.readReceiptsViewTopMargin; + height = PlainRoomCellLayoutConstants.readReceiptsViewHeight + PlainRoomCellLayoutConstants.readReceiptsViewTopMargin; } return height; diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.h b/Riot/Modules/Room/DataSources/RoomDataSource.h index 76d55375e..1ce04fc08 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.h +++ b/Riot/Modules/Room/DataSources/RoomDataSource.h @@ -130,6 +130,6 @@ @param roomDataSource room data source instance */ - (void)roomDataSource:(RoomDataSource * _Nonnull)roomDataSource - didTapThread:(MXThread * _Nonnull)thread; + didTapThread:(id _Nonnull)thread; @end diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index f4561638f..2341e692a 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -466,7 +466,8 @@ const CGFloat kTypingCellHeight = 24; // display thread summary view if the component has a thread in the room timeline if (RiotSettings.shared.enableThreads && component.thread && !self.threadId) { - threadSummaryView = [[ThreadSummaryView alloc] initWithThread:component.thread]; + threadSummaryView = [[ThreadSummaryView alloc] initWithThread:component.thread + session:self.mxSession]; threadSummaryView.delegate = self; threadSummaryView.tag = index; @@ -512,7 +513,7 @@ const CGFloat kTypingCellHeight = 24; if (roomMembers.count) { // Define the read receipts container, positioned on the right border of the bubble cell (Note the right margin 6 pts). - avatarsContainer = [[MXKReceiptSendersContainer alloc] initWithFrame:CGRectMake(bubbleCell.frame.size.width - RoomBubbleCellLayout.readReceiptsViewWidth + RoomBubbleCellLayout.readReceiptsViewRightMargin, bottomPositionY + RoomBubbleCellLayout.readReceiptsViewTopMargin, RoomBubbleCellLayout.readReceiptsViewWidth, RoomBubbleCellLayout.readReceiptsViewHeight) andMediaManager:self.mxSession.mediaManager]; + avatarsContainer = [[MXKReceiptSendersContainer alloc] initWithFrame:CGRectMake(bubbleCell.frame.size.width - PlainRoomCellLayoutConstants.readReceiptsViewWidth + PlainRoomCellLayoutConstants.readReceiptsViewRightMargin, bottomPositionY + PlainRoomCellLayoutConstants.readReceiptsViewTopMargin, PlainRoomCellLayoutConstants.readReceiptsViewWidth, PlainRoomCellLayoutConstants.readReceiptsViewHeight) andMediaManager:self.mxSession.mediaManager]; // Custom avatar display avatarsContainer.maxDisplayedAvatars = 5; @@ -557,48 +558,17 @@ const CGFloat kTypingCellHeight = 24; if ([componentEventId isEqualToString:self.room.accountData.readMarkerEventId]) { - bubbleCell.readMarkerView = [[UIView alloc] initWithFrame:CGRectMake(0, bottomPositionY - RoomBubbleCellLayout.readMarkerViewHeight, bubbleCell.bubbleOverlayContainer.frame.size.width, RoomBubbleCellLayout.readMarkerViewHeight)]; - bubbleCell.readMarkerView.backgroundColor = ThemeService.shared.theme.tintColor; + UIView *readMarkerView = [[UIView alloc] initWithFrame:CGRectMake(0, bottomPositionY - PlainRoomCellLayoutConstants.readMarkerViewHeight, bubbleCell.bubbleOverlayContainer.frame.size.width, PlainRoomCellLayoutConstants.readMarkerViewHeight)]; + readMarkerView.backgroundColor = ThemeService.shared.theme.tintColor; // Hide by default the marker, it will be shown and animated when the cell will be rendered. - bubbleCell.readMarkerView.hidden = YES; - bubbleCell.readMarkerView.tag = index; + readMarkerView.hidden = YES; + readMarkerView.tag = index; + readMarkerView.accessibilityIdentifier = @"readMarker"; - bubbleCell.readMarkerView.translatesAutoresizingMaskIntoConstraints = NO; - bubbleCell.readMarkerView.accessibilityIdentifier = @"readMarker"; - [bubbleCell.bubbleOverlayContainer addSubview:bubbleCell.readMarkerView]; - - // Force read marker constraints - bubbleCell.readMarkerViewTopConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.readMarkerView - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:bubbleCell.bubbleOverlayContainer - attribute:NSLayoutAttributeTop - multiplier:1.0 - constant:bottomPositionY - RoomBubbleCellLayout.readMarkerViewHeight]; - bubbleCell.readMarkerViewLeadingConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.readMarkerView - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:bubbleCell.bubbleOverlayContainer - attribute:NSLayoutAttributeLeading - multiplier:1.0 - constant:0]; - bubbleCell.readMarkerViewTrailingConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.bubbleOverlayContainer - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:bubbleCell.readMarkerView - attribute:NSLayoutAttributeTrailing - multiplier:1.0 - constant:0]; - - bubbleCell.readMarkerViewHeightConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.readMarkerView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0 - constant:RoomBubbleCellLayout.readMarkerViewHeight]; - - [NSLayoutConstraint activateConstraints:@[bubbleCell.readMarkerViewTopConstraint, bubbleCell.readMarkerViewLeadingConstraint, bubbleCell.readMarkerViewTrailingConstraint, bubbleCell.readMarkerViewHeightConstraint]]; + [cellDecorator addReadMarkerView:readMarkerView + toCell:bubbleCell + cellData:cellData + contentViewPositionY:bottomPositionY]; } } } diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift index 1e0dab2f8..b908e5cb0 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift @@ -58,7 +58,6 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject { func present(from viewController: UIViewController, animated: Bool) { guard let formatter = self.createEventFormatter(session: self.session) else { - //s das return } diff --git a/Riot/Modules/Room/EmojiPicker/EmojiPickerViewController.swift b/Riot/Modules/Room/EmojiPicker/EmojiPickerViewController.swift index 47349e326..b9d2beb85 100644 --- a/Riot/Modules/Room/EmojiPicker/EmojiPickerViewController.swift +++ b/Riot/Modules/Room/EmojiPicker/EmojiPickerViewController.swift @@ -164,7 +164,7 @@ final class EmojiPickerViewController: UIViewController { private func setupSearchController() { let searchController = UISearchController(searchResultsController: nil) - searchController.dimsBackgroundDuringPresentation = false + searchController.obscuresBackgroundDuringPresentation = false searchController.searchResultsUpdater = self searchController.searchBar.placeholder = VectorL10n.searchDefaultPlaceholder searchController.hidesNavigationBarDuringPresentation = false diff --git a/Riot/Modules/Room/Location/RoomTimelineLocationView.swift b/Riot/Modules/Room/Location/RoomTimelineLocationView.swift index 882a277c9..5ffb88e15 100644 --- a/Riot/Modules/Room/Location/RoomTimelineLocationView.swift +++ b/Riot/Modules/Room/Location/RoomTimelineLocationView.swift @@ -35,6 +35,7 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat @IBOutlet private var descriptionContainerView: UIView! @IBOutlet private var descriptionLabel: UILabel! @IBOutlet private var descriptionIcon: UIImageView! + @IBOutlet private var attributionLabel: UILabel! private var mapView: MGLMapView! private var annotationView: LocationMarkerView? @@ -101,6 +102,7 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat descriptionLabel.textColor = theme.colors.primaryContent descriptionLabel.font = theme.fonts.footnote descriptionIcon.tintColor = theme.colors.accent + attributionLabel.textColor = theme.colors.accent layer.borderColor = theme.colors.quinaryContent.cgColor } diff --git a/Riot/Modules/Room/Location/RoomTimelineLocationView.xib b/Riot/Modules/Room/Location/RoomTimelineLocationView.xib index acc469b74..fb2e22a29 100644 --- a/Riot/Modules/Room/Location/RoomTimelineLocationView.xib +++ b/Riot/Modules/Room/Location/RoomTimelineLocationView.xib @@ -1,9 +1,9 @@ - + - + @@ -15,48 +15,72 @@ - - + + - - + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + - - - + + + + diff --git a/Riot/Modules/Room/MXKRoomViewController.m b/Riot/Modules/Room/MXKRoomViewController.m index 62199c34d..1123358e1 100644 --- a/Riot/Modules/Room/MXKRoomViewController.m +++ b/Riot/Modules/Room/MXKRoomViewController.m @@ -228,6 +228,8 @@ [[[self class] nib] instantiateWithOwner:self options:nil]; } + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" // Adjust bottom constraint of the input toolbar container in order to take into account potential tabBar _roomInputToolbarContainerBottomConstraint.active = NO; _roomInputToolbarContainerBottomConstraint = [NSLayoutConstraint constraintWithItem:self.bottomLayoutGuide @@ -237,6 +239,8 @@ attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]; + #pragma clang diagnostic pop + _roomInputToolbarContainerBottomConstraint.active = YES; [self.view setNeedsUpdateConstraints]; @@ -419,6 +423,8 @@ }); } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" // The 2 following methods are deprecated since iOS 8 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { @@ -446,6 +452,7 @@ self->isSizeTransitionInProgress = NO; }); } +#pragma clang diagnostic pop - (void)viewDidLayoutSubviews { @@ -488,6 +495,8 @@ self.keyboardView = keyboardView; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" - (void)setKeyboardHeight:(CGFloat)keyboardHeight { // Deduce the bottom constraint for the input toolbar view (Don't forget the potential tabBar) @@ -531,6 +540,7 @@ super.keyboardHeight = keyboardHeight; } +#pragma clang diagnostic pop - (void)destroy { @@ -1046,7 +1056,7 @@ [titleView destroy]; } - titleView = self.navigationItem.titleView = [roomTitleViewClass roomTitleView]; + self.navigationItem.titleView = titleView = [roomTitleViewClass roomTitleView]; titleView.delegate = self; // Define directly the navigation titleView with the custom title view instance. Do not use anymore a container. @@ -2116,6 +2126,8 @@ self->eventDetailsView = eventDetailsView; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" [self.view addConstraint:[NSLayoutConstraint constraintWithItem:eventDetailsView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual @@ -2131,6 +2143,7 @@ attribute:NSLayoutAttributeTop multiplier:1.0f constant:-10.0f]]; + #pragma clang diagnostic pop [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeLeading @@ -2335,6 +2348,12 @@ { // Do a full reload [_bubblesTableView reloadData]; + if (shouldScrollToBottom) { + // If we need to scroll to the bottom after the reload, layout refresh needs to be triggered, + // otherwise contentSize of the table view will not be up-to-date + // e.g. https://stackoverflow.com/a/31324129 + [_bubblesTableView layoutIfNeeded]; + } } if (shouldScrollToBottom) diff --git a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m index 687587dfe..b5928f009 100644 --- a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m +++ b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m @@ -350,7 +350,7 @@ return [AvatarGenerator generateAvatarForMatrixItem:self.mxRoomMember.userId withDisplayName:self.mxRoomMember.displayname]; } - return [MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + return [MXKTools paintImage:AssetImages.placeholder.image withColor:ThemeService.shared.theme.tintColor]; } @@ -984,7 +984,7 @@ } else { - roomCell.avatarImageView.image = [UIImage imageNamed:@"start_chat"]; + roomCell.avatarImageView.image = AssetImages.startChat.image; roomCell.avatarImageView.defaultBackgroundColor = [UIColor clearColor]; roomCell.avatarImageView.userInteractionEnabled = NO; roomCell.titleLabel.text = [VectorL10n roomParticipantsActionStartNewChat]; diff --git a/Riot/Modules/Room/Members/RoomParticipantsViewController.m b/Riot/Modules/Room/Members/RoomParticipantsViewController.m index 0c8cee07f..5eadaa9b7 100644 --- a/Riot/Modules/Room/Members/RoomParticipantsViewController.m +++ b/Riot/Modules/Room/Members/RoomParticipantsViewController.m @@ -102,6 +102,8 @@ // Adjust Top and Bottom constraints to take into account potential navBar and tabBar. [NSLayoutConstraint deactivateConstraints:@[_searchBarTopConstraint]]; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" _searchBarTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual @@ -109,6 +111,7 @@ attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f]; + #pragma clang diagnostic pop [NSLayoutConstraint activateConstraints:@[_searchBarTopConstraint]]; @@ -142,7 +145,7 @@ // Add invite members button programmatically - [self vc_addFABWithImage:[UIImage imageNamed:@"add_member_floating_action"] + [self vc_addFABWithImage:AssetImages.addMemberFloatingAction.image target:self action:@selector(onAddParticipantButtonPressed)]; @@ -559,7 +562,7 @@ contactsDataSource.displaySearchInputInContactsList = YES; contactsDataSource.forceMatrixIdInDisplayName = YES; // Add a plus icon to the contact cell in the contacts picker, in order to make it more understandable for the end user. - contactsDataSource.contactCellAccessoryImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]; + contactsDataSource.contactCellAccessoryImage = [AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]; // List all the participants matrix user id to ignore them during the contacts search. for (Contact *contact in actualParticipants) @@ -850,7 +853,7 @@ pendingMaskSpinnerView.alpha = 0; [UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ - pendingMaskSpinnerView.alpha = 1; + self->pendingMaskSpinnerView.alpha = 1; } completion:^(BOOL finished) { }]; diff --git a/Riot/Modules/Room/RoomCoordinatorBridgePresenter.swift b/Riot/Modules/Room/RoomCoordinatorBridgePresenter.swift index bd6b70d77..017b8b6e8 100644 --- a/Riot/Modules/Room/RoomCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Room/RoomCoordinatorBridgePresenter.swift @@ -62,7 +62,8 @@ class RoomCoordinatorBridgePresenterParameters: NSObject { /// RoomCoordinatorBridgePresenter enables to start RoomCoordinator from a view controller. /// This bridge is used while waiting for global usage of coordinator pattern. -/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator. +/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). Each bridge should be removed +/// once the underlying Coordinator has been integrated by another Coordinator. @objcMembers final class RoomCoordinatorBridgePresenter: NSObject { diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 966a76e45..331c07ba4 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -90,7 +90,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo NSNotificationName const RoomGroupCallTileTappedNotification = @"RoomGroupCallTileTappedNotification"; const NSTimeInterval kResizeComposerAnimationDuration = .05; -@interface RoomViewController () @@ -442,7 +442,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (ThemeService.shared.isCurrentThemeDark) { - [self.scrollToBottomButton setImage:[UIImage imageNamed:@"scrolldown_dark"] forState:UIControlStateNormal]; + [self.scrollToBottomButton setImage:AssetImages.scrolldownDark.image forState:UIControlStateNormal]; self.jumpToLastUnreadBanner.backgroundColor = ThemeService.shared.theme.colors.navigation; [self.jumpToLastUnreadBanner vc_removeShadow]; @@ -450,7 +450,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; } else { - [self.scrollToBottomButton setImage:[UIImage imageNamed:@"scrolldown"] forState:UIControlStateNormal]; + [self.scrollToBottomButton setImage:AssetImages.scrolldown.image forState:UIControlStateNormal]; self.jumpToLastUnreadBanner.backgroundColor = ThemeService.shared.theme.colors.background; [self.jumpToLastUnreadBanner vc_addShadowWithColor:ThemeService.shared.theme.shadowColor @@ -1429,7 +1429,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; - (UIBarButtonItem *)videoCallBarButtonItem { - UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"video_call"] + UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:AssetImages.videoCall.image style:UIBarButtonItemStylePlain target:self action:@selector(onVideoCallPressed:)]; @@ -1440,7 +1440,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; - (UIBarButtonItem *)threadMoreBarButtonItem { - UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"room_context_menu_more"] + UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:AssetImages.roomContextMenuMore.image style:UIBarButtonItemStylePlain target:self action:@selector(onButtonPressed:)]; @@ -1452,7 +1452,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; - (BadgedBarButtonItem *)threadListBarButtonItem { UIButton *button = [UIButton new]; - UIImage *icon = [[UIImage imageNamed:@"threads_icon"] vc_resizedWith:CGSizeMake(21, 21)]; + UIImage *icon = [AssetImages.threadsIcon.image vc_resizedWith:CGSizeMake(21, 21)]; button.contentEdgeInsets = UIEdgeInsetsMake(4, 8, 4, 8); [button setImage:icon forState:UIControlStateNormal]; @@ -1609,7 +1609,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (self.roomDataSource.room.summary.membersCount.joined == 2 && self.roomDataSource.room.isDirect) { // voice call button for Matrix call - UIBarButtonItem *itemVoice = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"voice_call_hangon_icon"] + UIBarButtonItem *itemVoice = [[UIBarButtonItem alloc] initWithImage:AssetImages.voiceCallHangonIcon.image style:UIBarButtonItemStylePlain target:self action:@selector(onVoiceCallPressed:)]; @@ -1638,7 +1638,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; { // show Join button CallTileActionButton *button = [CallTileActionButton new]; - [button setImage:[UIImage imageNamed:@"call_video_icon"] + [button setImage:AssetImages.callVideoIcon.image forState:UIControlStateNormal]; [button setTitle:[VectorL10n roomJoinGroupCall] forState:UIControlStateNormal]; @@ -1660,7 +1660,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; UIBarButtonItem *item = [self videoCallBarButtonItem]; if (!self.canEditJitsiWidget) { - item.image = [[UIImage imageNamed:@"video_call"] vc_withAlpha:0.3]; + item.image = [AssetImages.videoCall.image vc_withAlpha:0.3]; } [rightBarButtonItems addObject:item]; } @@ -1669,7 +1669,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if ([self widgetsCount:NO]) { - UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"integrations_icon"] + UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:AssetImages.integrationsIcon.image style:UIBarButtonItemStylePlain target:self action:@selector(onIntegrationsPressed:)]; @@ -1986,7 +1986,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; NSMutableArray *actionItems = [NSMutableArray new]; if (RiotSettings.shared.roomScreenAllowMediaLibraryAction) { - [actionItems addObject:[[RoomActionItem alloc] initWithImage:[UIImage imageNamed:@"action_media_library"] andAction:^{ + [actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionMediaLibrary.image andAction:^{ MXStrongifyAndReturnIfNil(self); if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; @@ -1996,7 +1996,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; } if (RiotSettings.shared.roomScreenAllowStickerAction) { - [actionItems addObject:[[RoomActionItem alloc] initWithImage:[UIImage imageNamed:@"action_sticker"] andAction:^{ + [actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionSticker.image andAction:^{ MXStrongifyAndReturnIfNil(self); if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; @@ -2006,7 +2006,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; } if (RiotSettings.shared.roomScreenAllowFilesAction) { - [actionItems addObject:[[RoomActionItem alloc] initWithImage:[UIImage imageNamed:@"action_file"] andAction:^{ + [actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionFile.image andAction:^{ MXStrongifyAndReturnIfNil(self); if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; @@ -2016,7 +2016,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; } if (BuildSettings.pollsEnabled && self.displayConfiguration.sendingPollsEnabled) { - [actionItems addObject:[[RoomActionItem alloc] initWithImage:[UIImage imageNamed:@"action_poll"] andAction:^{ + [actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionPoll.image andAction:^{ MXStrongifyAndReturnIfNil(self); if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; @@ -2026,7 +2026,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; } if (BuildSettings.locationSharingEnabled) { - [actionItems addObject:[[RoomActionItem alloc] initWithImage:[UIImage imageNamed:@"action_location"] andAction:^{ + [actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionLocation.image andAction:^{ MXStrongifyAndReturnIfNil(self); if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; @@ -2036,7 +2036,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; } if (RiotSettings.shared.roomScreenAllowCameraAction) { - [actionItems addObject:[[RoomActionItem alloc] initWithImage:[UIImage imageNamed:@"action_camera"] andAction:^{ + [actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionCamera.image andAction:^{ MXStrongifyAndReturnIfNil(self); if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { ((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO; @@ -2553,7 +2553,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; } else { - previewHeader.roomAvatarPlaceholder = [MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + previewHeader.roomAvatarPlaceholder = [MXKTools paintImage:AssetImages.placeholder.image withColor:ThemeService.shared.theme.tintColor]; } } @@ -2585,7 +2585,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; self.bubblesTableViewTopConstraint.constant = self.previewHeaderContainerHeightConstraint.constant - self.bubblesTableView.adjustedContentInset.top; - previewHeader.roomAvatar.alpha = 1; + self->previewHeader.roomAvatar.alpha = 1; // Force to render the view [self forceLayoutRefresh]; @@ -3717,7 +3717,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; { MXKPasteboardManager.shared.pasteboard.URL = url; [self.view vc_toastWithMessage:VectorL10n.roomEventCopyLinkInfo - image:[UIImage imageNamed:@"link_icon"] + image:AssetImages.linkIcon.image duration:2.0 position:ToastPositionBottom additionalMargin:self.roomInputToolbarContainerHeightConstraint.constant]; @@ -4283,7 +4283,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; [self updateTitleViewEncryptionDecoration]; } -- (void)roomDataSource:(RoomDataSource *)roomDataSource didTapThread:(MXThread *)thread +- (void)roomDataSource:(RoomDataSource *)roomDataSource didTapThread:(id)thread { [self openThreadWithId:thread.id]; } @@ -5744,6 +5744,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; self->encryptionInfoView = encryptionInfoView; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated" [self.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual @@ -5759,6 +5761,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; attribute:NSLayoutAttributeTop multiplier:1.0f constant:-10.0f]]; + #pragma clang diagnostic pop [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeLeading @@ -5818,44 +5821,23 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; - (void)animateReadMarkerView { // Check whether the cell with the read marker is known and if the marker is not animated yet. - if (readMarkerTableViewCell && readMarkerTableViewCell.readMarkerView.isHidden) + + if (!readMarkerTableViewCell || readMarkerTableViewCell.readMarkerView.isHidden == NO) { - RoomBubbleCellData *cellData = (RoomBubbleCellData*)readMarkerTableViewCell.bubbleData; - - // Do not display the marker if this is the last message. - if (cellData.containsLastMessage && readMarkerTableViewCell.readMarkerView.tag == cellData.mostRecentComponentIndex) - { - readMarkerTableViewCell.readMarkerView.hidden = YES; - readMarkerTableViewCell = nil; - } - else - { - readMarkerTableViewCell.readMarkerView.hidden = NO; - - // Animate the layout to hide the read marker - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - - [UIView animateWithDuration:1.5 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn - animations:^{ - - readMarkerTableViewCell.readMarkerViewLeadingConstraint.constant = readMarkerTableViewCell.readMarkerViewTrailingConstraint.constant = readMarkerTableViewCell.bubbleOverlayContainer.frame.size.width / 2; - readMarkerTableViewCell.readMarkerView.alpha = 0; - - // Force to render the view - [readMarkerTableViewCell.bubbleOverlayContainer layoutIfNeeded]; - - } - completion:^(BOOL finished){ - - readMarkerTableViewCell.readMarkerView.hidden = YES; - readMarkerTableViewCell.readMarkerView.alpha = 1; - - readMarkerTableViewCell = nil; - }]; - - }); - } + return; } + + RoomBubbleCellData *cellData = (RoomBubbleCellData*)readMarkerTableViewCell.bubbleData; + + id cellDecorator = [RoomTimelineConfiguration shared].currentStyle.cellDecorator; + + [cellDecorator dissmissReadMarkerViewForCell:readMarkerTableViewCell + cellData:cellData + animated:YES + completion:^{ + + self->readMarkerTableViewCell = nil; + }]; } - (void)refreshRemoveJitsiWidgetView diff --git a/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m b/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m index 5b99eea2d..8be145b51 100644 --- a/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m +++ b/Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m @@ -96,6 +96,10 @@ { continueBlock(); } + else if (result.result.unsignedData.relations.thread) + { + continueBlock(); + } else { [roomDataSource.room liveTimeline:^(id liveTimeline) { @@ -143,14 +147,15 @@ { if (cellData.hasThreadRoot) { - MXThread *thread = cellData.bubbleComponents.firstObject.thread; - ThreadSummaryView *threadSummaryView = [[ThreadSummaryView alloc] initWithThread:thread]; + id thread = cellData.bubbleComponents.firstObject.thread; + ThreadSummaryView *threadSummaryView = [[ThreadSummaryView alloc] initWithThread:thread + session:self.mxSession]; [bubbleCell.tmpSubviews addObject:threadSummaryView]; threadSummaryView.translatesAutoresizingMaskIntoConstraints = NO; [bubbleCell.contentView addSubview:threadSummaryView]; - CGFloat leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin; + CGFloat leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin; CGFloat height = [ThreadSummaryView contentViewHeightForThread:thread fitting:cellData.maxTextViewWidth]; CGRect bubbleComponentFrame = [bubbleCell componentFrameInContentViewForIndex:0]; @@ -161,9 +166,9 @@ [threadSummaryView.leadingAnchor constraintEqualToAnchor:threadSummaryView.superview.leadingAnchor constant:leftMargin], [threadSummaryView.topAnchor constraintEqualToAnchor:threadSummaryView.superview.topAnchor - constant:bottomPositionY + RoomBubbleCellLayout.threadSummaryViewTopMargin], + constant:bottomPositionY + PlainRoomCellLayoutConstants.threadSummaryViewTopMargin], [threadSummaryView.heightAnchor constraintEqualToConstant:height], - [threadSummaryView.trailingAnchor constraintLessThanOrEqualToAnchor:threadSummaryView.superview.trailingAnchor constant:-RoomBubbleCellLayout.reactionsViewRightMargin] + [threadSummaryView.trailingAnchor constraintLessThanOrEqualToAnchor:threadSummaryView.superview.trailingAnchor constant:-PlainRoomCellLayoutConstants.reactionsViewRightMargin] ]]; } else if (event.isInThread) @@ -174,7 +179,7 @@ fromAThreadView.translatesAutoresizingMaskIntoConstraints = NO; [bubbleCell.contentView addSubview:fromAThreadView]; - CGFloat leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin; + CGFloat leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin; CGFloat height = [FromAThreadView contentViewHeightForEvent:event fitting:cellData.maxTextViewWidth]; CGRect bubbleComponentFrame = [bubbleCell componentFrameInContentViewForIndex:0]; @@ -185,9 +190,9 @@ [fromAThreadView.leadingAnchor constraintEqualToAnchor:fromAThreadView.superview.leadingAnchor constant:leftMargin], [fromAThreadView.topAnchor constraintEqualToAnchor:fromAThreadView.superview.topAnchor - constant:bottomPositionY + RoomBubbleCellLayout.fromAThreadViewTopMargin], + constant:bottomPositionY + PlainRoomCellLayoutConstants.fromAThreadViewTopMargin], [fromAThreadView.heightAnchor constraintEqualToConstant:height], - [fromAThreadView.trailingAnchor constraintLessThanOrEqualToAnchor:fromAThreadView.superview.trailingAnchor constant:-RoomBubbleCellLayout.reactionsViewRightMargin] + [fromAThreadView.trailingAnchor constraintLessThanOrEqualToAnchor:fromAThreadView.superview.trailingAnchor constant:-PlainRoomCellLayoutConstants.reactionsViewRightMargin] ]]; } } diff --git a/Riot/Modules/Room/Search/RoomSearchViewController.m b/Riot/Modules/Room/Search/RoomSearchViewController.m index 7d830ecb3..8a16c463d 100644 --- a/Riot/Modules/Room/Search/RoomSearchViewController.m +++ b/Riot/Modules/Room/Search/RoomSearchViewController.m @@ -73,10 +73,7 @@ [self initWithTitles:titles viewControllers:viewControllers defaultSelected:0]; [super viewDidLoad]; - - // Add the Riot background image when search bar is empty - [self addBackgroundImageViewToView:self.view]; - + // Initialize here the data sources if a matrix session has been already set. [self initializeDataSources]; @@ -87,13 +84,6 @@ { [super userInterfaceThemeDidChange]; - UIImageView *backgroundImageView = self.backgroundImageView; - if (backgroundImageView) - { - UIImage *image = [MXKTools paintImage:backgroundImageView.image withColor:ThemeService.shared.theme.matrixSearchBackgroundImageTintColor]; - backgroundImageView.image = image; - } - // Match the search bar color to the navigation bar color as it extends slightly outside the frame. self.searchBar.backgroundColor = ThemeService.shared.theme.baseColor; } @@ -165,7 +155,7 @@ threadParameters = [[ThreadParameters alloc] initWithThreadId:event.threadId stackRoomScreen:NO]; } - else if ([self.mainSession.threadingService isEventThreadRoot:event]) + else if (event.unsignedData.relations.thread || [self.mainSession.threadingService isEventThreadRoot:event]) { threadParameters = [[ThreadParameters alloc] initWithThreadId:event.eventId stackRoomScreen:NO]; @@ -227,15 +217,6 @@ #pragma mark - Override MXKViewController -- (void)setKeyboardHeight:(CGFloat)keyboardHeight -{ - [self setKeyboardHeightForBackgroundImage:keyboardHeight]; - - [super setKeyboardHeight:keyboardHeight]; - - [self checkAndShowBackgroundImage]; -} - - (void)startActivityIndicator { // Redirect the operation to the currently displayed VC @@ -283,48 +264,6 @@ [self.navigationController popViewControllerAnimated:YES]; } -- (void)setKeyboardHeightForBackgroundImage:(CGFloat)keyboardHeight -{ - [super setKeyboardHeightForBackgroundImage:keyboardHeight]; - - if (keyboardHeight > 0) - { - [self checkAndShowBackgroundImage]; - } -} - -// Check conditions before displaying the background -- (void)checkAndShowBackgroundImage -{ - // Note: This background is hidden when keyboard is dismissed. - // The other conditions depend on the current selected view controller. - if (self.selectedViewController == messagesSearchViewController) - { - self.backgroundImageView.hidden = ((messagesSearchDataSource.serverCount != 0) || !messagesSearchViewController.noResultsLabel.isHidden || (self.keyboardHeight == 0)); - } - else if (self.selectedViewController == filesSearchViewController) - { - self.backgroundImageView.hidden = ((filesSearchDataSource.serverCount != 0) || !filesSearchViewController.noResultsLabel.isHidden || (self.keyboardHeight == 0)); - } - else - { - self.backgroundImageView.hidden = (self.keyboardHeight == 0); - } - - if (!self.backgroundImageView.hidden) - { - [self.backgroundImageView layoutIfNeeded]; - [self.selectedViewController.view layoutIfNeeded]; - - // Check whether there is enough space to display this background - // For example, in landscape with the iPhone 5 & 6 screen size, the backgroundImageView must be hidden. - if (self.backgroundImageView.frame.origin.y < 0 || (self.selectedViewController.view.frame.size.height - self.backgroundImageViewBottomConstraint.constant) < self.backgroundImageView.frame.size.height) - { - self.backgroundImageView.hidden = YES; - } - } -} - #pragma mark - Override SegmentedViewController - (void)setSelectedIndex:(NSUInteger)selectedIndex @@ -341,8 +280,6 @@ { if (self.searchBar.text.length) { - self.backgroundImageView.hidden = YES; - // Forward the search request to the data source if (self.selectedViewController == messagesSearchViewController) { @@ -352,8 +289,8 @@ // Do it asynchronously to give time to messagesSearchViewController to be set up // so that it can display its loading wheel dispatch_async(dispatch_get_main_queue(), ^{ - [messagesSearchDataSource searchMessages:self.searchBar.text force:NO]; - messagesSearchViewController.shouldScrollToBottomOnRefresh = YES; + [self->messagesSearchDataSource searchMessages:self.searchBar.text force:NO]; + self->messagesSearchViewController.shouldScrollToBottomOnRefresh = YES; }); } } @@ -365,8 +302,8 @@ // Do it asynchronously to give time to filesSearchViewController to be set up // so that it can display its loading wheel dispatch_async(dispatch_get_main_queue(), ^{ - [filesSearchDataSource searchMessages:self.searchBar.text force:NO]; - filesSearchViewController.shouldScrollToBottomOnRefresh = YES; + [self->filesSearchDataSource searchMessages:self.searchBar.text force:NO]; + self->filesSearchViewController.shouldScrollToBottomOnRefresh = YES; }); } } @@ -383,8 +320,6 @@ [filesSearchDataSource searchMessages:nil force:NO]; } } - - [self checkAndShowBackgroundImage]; } @end diff --git a/Riot/Modules/Room/Settings/RoomSettingsViewController.m b/Riot/Modules/Room/Settings/RoomSettingsViewController.m index d844deaee..ff018f732 100644 --- a/Riot/Modules/Room/Settings/RoomSettingsViewController.m +++ b/Riot/Modules/Room/Settings/RoomSettingsViewController.m @@ -165,9 +165,6 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti // The pending http operation MXHTTPOperation* pendingOperation; - // the updating spinner - UIActivityIndicatorView* updatingSpinner; - UIAlertController *currentAlert; // listen to more events than the mother class @@ -1047,7 +1044,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { MXKPasteboardManager.shared.pasteboard.URL = url; [self.view vc_toastWithMessage:VectorL10n.roomEventCopyLinkInfo - image:[UIImage imageNamed:@"link_icon"] + image:AssetImages.linkIcon.image duration:2.0 position:ToastPositionBottom additionalMargin:0.0]; @@ -2640,7 +2637,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti addAddressCell.mxkLabel.text = nil; addAddressCell.accessoryType = UITableViewCellAccessoryNone; - addAddressCell.accessoryView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]]; + addAddressCell.accessoryView = [[UIImageView alloc] initWithImage:[AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]]; addAddressTextField = addAddressCell.mxkTextField; addAddressTextField.placeholder = [VectorL10n roomDetailsNewAddressPlaceholder:self.mainSession.matrixRestClient.homeserverSuffix]; @@ -2717,7 +2714,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { if ([alias isEqualToString:canonicalAlias]) { - addressCell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"main_alias_icon"]]; + addressCell.accessoryView = [[UIImageView alloc] initWithImage:AssetImages.mainAliasIcon.image]; } } } @@ -2741,7 +2738,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti addCommunityCell.mxkLabel.text = nil; addCommunityCell.accessoryType = UITableViewCellAccessoryNone; - addCommunityCell.accessoryView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]]; + addCommunityCell.accessoryView = [[UIImageView alloc] initWithImage:[AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]]; addGroupTextField = addCommunityCell.mxkTextField; addGroupTextField.placeholder = [VectorL10n roomDetailsNewFlairPlaceholder:self.mainSession.matrixRestClient.homeserverSuffix]; @@ -3270,7 +3267,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti completionHandler(YES); }]; removeAddressAction.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; - removeAddressAction.image = [[UIImage imageNamed:@"remove_icon"] vc_notRenderedImage]; + removeAddressAction.image = [AssetImages.removeIcon.image vc_notRenderedImage]; // Create swipe action configuration @@ -3291,7 +3288,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti completionHandler(YES); }]; removeAddressAction.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; - removeAddressAction.image = [[UIImage imageNamed:@"remove_icon"] vc_notRenderedImage]; + removeAddressAction.image = [AssetImages.removeIcon.image vc_notRenderedImage]; // Create swipe action configuration diff --git a/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxAndLabel.m b/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxAndLabel.m index b623ffc7d..718cce122 100644 --- a/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxAndLabel.m +++ b/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxAndLabel.m @@ -34,11 +34,11 @@ { if (enabled) { - _checkBox.image = [UIImage imageNamed:@"selection_tick"]; + _checkBox.image = AssetImages.selectionTick.image; } else { - _checkBox.image = [UIImage imageNamed:@"selection_untick"]; + _checkBox.image = AssetImages.selectionUntick.image; } _enabled = enabled; diff --git a/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxes.m b/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxes.m index 9504214a1..c3a683e7c 100644 --- a/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxes.m +++ b/Riot/Modules/Room/Settings/Views/TableViewCellWithCheckBoxes.m @@ -131,7 +131,7 @@ [checkboxContainer addSubview:checkbox]; // Store the new check box unselected by default - checkbox.image = [UIImage imageNamed:@"selection_untick"]; + checkbox.image = AssetImages.selectionUntick.image; checkbox.tintColor = ThemeService.shared.theme.tintColor; checkbox.tag = 0; [checkBoxesArray addObject:checkbox]; @@ -263,7 +263,7 @@ if (isSelected && !checkBox.tag) { - checkBox.image = [UIImage imageNamed:@"selection_tick"]; + checkBox.image = AssetImages.selectionTick.image; checkBox.tag = 1; if (!self.allowsMultipleSelection) @@ -276,7 +276,7 @@ checkBox = checkBoxesArray[k]; if (checkBox.tag) { - checkBox.image = [UIImage imageNamed:@"selection_untick"]; + checkBox.image = AssetImages.selectionUntick.image; checkBox.tag = 0; } } @@ -285,7 +285,7 @@ } else if (!isSelected && checkBox.tag) { - checkBox.image = [UIImage imageNamed:@"selection_untick"]; + checkBox.image = AssetImages.selectionUntick.image; checkBox.tag = 0; } } diff --git a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BaseBubbleCell.swift b/Riot/Modules/Room/TimelineCells/BaseRoomCell/BaseRoomCell.swift similarity index 50% rename from Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BaseBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/BaseRoomCell/BaseRoomCell.swift index 1365d0e18..f3a9ddbea 100644 --- a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BaseBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/BaseRoomCell/BaseRoomCell.swift @@ -16,13 +16,13 @@ limitations under the License. import UIKit -@objc protocol BaseBubbleCellType: Themable { - var bubbleCellContentView: BubbleCellContentView? { get } +@objc protocol BaseRoomCellProtocol: Themable { + var roomCellContentView: RoomCellContentView? { get } } -/// `BaseBubbleCell` allows a bubble cell that inherits from this class to embed and manage the default room message outer views and add an inner content view. +/// `BaseRoomCell` allows a room cell that inherits from this class to embed and manage the default room message outer views and add an inner content view. @objcMembers -class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { +class BaseRoomCell: MXKRoomBubbleTableViewCell, BaseRoomCellProtocol { // MARK: - Constants @@ -32,7 +32,7 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { // MARK: Public - weak var bubbleCellContentView: BubbleCellContentView? + weak var roomCellContentView: RoomCellContentView? private(set) var theme: Theme? @@ -40,8 +40,8 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { override var bubbleInfoContainer: UIView! { get { - guard let infoContainer = self.bubbleCellContentView?.bubbleInfoContainer else { - fatalError("[BaseBubbleCell] bubbleInfoContainer should not be used before set") + guard let infoContainer = self.roomCellContentView?.bubbleInfoContainer else { + fatalError("[BaseRoomCell] bubbleInfoContainer should not be used before set") } return infoContainer } @@ -52,8 +52,8 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { override var bubbleOverlayContainer: UIView! { get { - guard let overlayContainer = self.bubbleCellContentView?.bubbleOverlayContainer else { - fatalError("[BaseBubbleCell] bubbleOverlayContainer should not be used before set") + guard let overlayContainer = self.roomCellContentView?.bubbleOverlayContainer else { + fatalError("[BaseRoomCell] bubbleOverlayContainer should not be used before set") } return overlayContainer } @@ -64,8 +64,8 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { override var bubbleInfoContainerTopConstraint: NSLayoutConstraint! { get { - guard let infoContainerTopConstraint = self.bubbleCellContentView?.bubbleInfoContainerTopConstraint else { - fatalError("[BaseBubbleCell] bubbleInfoContainerTopConstraint should not be used before set") + guard let infoContainerTopConstraint = self.roomCellContentView?.bubbleInfoContainerTopConstraint else { + fatalError("[BaseRoomCell] bubbleInfoContainerTopConstraint should not be used before set") } return infoContainerTopConstraint } @@ -76,13 +76,13 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { override var pictureView: MXKImageView! { get { - guard let bubbleCellContentView = self.bubbleCellContentView, - bubbleCellContentView.showSenderAvatar else { + guard let roomCellContentView = self.roomCellContentView, + roomCellContentView.showSenderAvatar else { return nil } - guard let pictureView = self.bubbleCellContentView?.avatarImageView else { - fatalError("[BaseBubbleCell] pictureView should not be used before set") + guard let pictureView = self.roomCellContentView?.avatarImageView else { + fatalError("[BaseRoomCell] pictureView should not be used before set") } return pictureView } @@ -93,12 +93,12 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { override var userNameLabel: UILabel! { get { - guard let bubbleCellContentView = self.bubbleCellContentView, bubbleCellContentView.showSenderName else { + guard let roomCellContentView = self.roomCellContentView, roomCellContentView.showSenderName else { return nil } - guard let userNameLabel = bubbleCellContentView.userNameLabel else { - fatalError("[BaseBubbleCell] userNameLabel should not be used before set") + guard let userNameLabel = roomCellContentView.userNameLabel else { + fatalError("[BaseRoomCell] userNameLabel should not be used before set") } return userNameLabel } @@ -109,13 +109,13 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { override var userNameTapGestureMaskView: UIView! { get { - guard let bubbleCellContentView = self.bubbleCellContentView, - bubbleCellContentView.showSenderName else { + guard let roomCellContentView = self.roomCellContentView, + roomCellContentView.showSenderName else { return nil } - guard let userNameTapGestureMaskView = self.bubbleCellContentView?.userNameTouchMaskView else { - fatalError("[BaseBubbleCell] userNameTapGestureMaskView should not be used before set") + guard let userNameTapGestureMaskView = self.roomCellContentView?.userNameTouchMaskView else { + fatalError("[BaseRoomCell] userNameTapGestureMaskView should not be used before set") } return userNameTapGestureMaskView } @@ -124,6 +124,40 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { } } + override var readMarkerViewLeadingConstraint: NSLayoutConstraint! { + get { + if self is RoomCellReadMarkerDisplayable { + return self.roomCellContentView?.readMarkerViewLeadingConstraint + } else { + return super.readMarkerViewLeadingConstraint + } + } + set { + if self is RoomCellReadMarkerDisplayable { + self.roomCellContentView?.readMarkerViewLeadingConstraint = newValue + } else { + super.readMarkerViewLeadingConstraint = newValue + } + } + } + + override var readMarkerViewTrailingConstraint: NSLayoutConstraint! { + get { + if self is RoomCellReadMarkerDisplayable { + return self.roomCellContentView?.readMarkerViewTrailingConstraint + } else { + return super.readMarkerViewTrailingConstraint + } + } + set { + if self is RoomCellReadMarkerDisplayable { + self.roomCellContentView?.readMarkerViewTrailingConstraint = newValue + } else { + super.readMarkerViewTrailingConstraint = newValue + } + } + } + // MARK: - Setup required override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { @@ -133,6 +167,7 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) + self.commonInit() } private func commonInit() { @@ -144,16 +179,16 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { // MARK: - Public func removeDecorationViews() { - if let bubbleCellReadReceiptsDisplayable = self as? BubbleCellReadReceiptsDisplayable { - bubbleCellReadReceiptsDisplayable.removeReadReceiptsView() + if let roomCellReadReceiptsDisplayable = self as? RoomCellReadReceiptsDisplayable { + roomCellReadReceiptsDisplayable.removeReadReceiptsView() } - if let bubbleCellReactionsDisplayable = self as? BubbleCellReactionsDisplayable { - bubbleCellReactionsDisplayable.removeReactionsView() + if let roomCellReactionsDisplayable = self as? RoomCellReactionsDisplayable { + roomCellReactionsDisplayable.removeReactionsView() } - if let bubbleCellThreadSummaryDisplayable = self as? BubbleCellThreadSummaryDisplayable { - bubbleCellThreadSummaryDisplayable.removeThreadSummaryView() + if let roomCellThreadSummaryDisplayable = self as? RoomCellThreadSummaryDisplayable { + roomCellThreadSummaryDisplayable.removeThreadSummaryView() } if let timestampDisplayable = self as? TimestampDisplayable { @@ -174,13 +209,48 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { override func setupViews() { super.setupViews() - let showEncryptionStatus = bubbleCellContentView?.showEncryptionStatus ?? false + let showEncryptionStatus = roomCellContentView?.showEncryptionStatus ?? false if showEncryptionStatus { self.setupEncryptionStatusViewTapGestureRecognizer() } } + override func setupSenderNameLabel() { + + guard let userNameTouchMaskView = self.roomCellContentView?.userNameTouchMaskView else { + return + } + + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onSenderNameTap(_:))) + tapGesture.numberOfTouchesRequired = 1 + tapGesture.numberOfTapsRequired = 1 + tapGesture.delegate = self + + userNameTouchMaskView.addGestureRecognizer(tapGesture) + } + + override func setupAvatarView() { + + guard let avatarImageView = self.roomCellContentView?.avatarImageView else { + return + } + + avatarImageView.mediaFolder = kMXMediaManagerAvatarThumbnailFolder + + // Listen to avatar tap + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(onAvatarTap(_:))) + tapGesture.numberOfTouchesRequired = 1 + tapGesture.numberOfTapsRequired = 1 + tapGesture.delegate = self + avatarImageView.addGestureRecognizer(tapGesture) + avatarImageView.isUserInteractionEnabled = true + + // Add a long gesture recognizer on avatar (in order to display for example the member details) + let longPress = UILongPressGestureRecognizer(target: self, action: #selector(onLongPressGesture(_:))) + avatarImageView.addGestureRecognizer(longPress) + } + override class func defaultReuseIdentifier() -> String! { return String(describing: self) } @@ -197,17 +267,17 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { super.render(cellData) - guard let bubbleCellContentView = self.bubbleCellContentView else { + guard let roomCellContentView = self.roomCellContentView else { return } if let bubbleData = self.bubbleData, let paginationDate = bubbleData.date, - bubbleCellContentView.showPaginationTitle { - bubbleCellContentView.paginationLabel.text = bubbleData.eventFormatter.dateString(from: paginationDate, withTime: false)?.uppercased() + roomCellContentView.showPaginationTitle { + roomCellContentView.paginationLabel.text = bubbleData.eventFormatter.dateString(from: paginationDate, withTime: false)?.uppercased() } - if bubbleCellContentView.showEncryptionStatus { + if roomCellContentView.showEncryptionStatus { self.updateEncryptionStatusViewImage() } @@ -223,7 +293,7 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { func update(theme: Theme) { self.theme = theme - self.bubbleCellContentView?.update(theme: theme) + self.roomCellContentView?.update(theme: theme) } // MARK: - Private @@ -237,67 +307,80 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { } private func setupContentView() { - guard self.bubbleCellContentView == nil else { + guard self.roomCellContentView == nil else { return } - let bubbleCellContentView = BubbleCellContentView.instantiate() - self.contentView.vc_addSubViewMatchingParent(bubbleCellContentView) - self.bubbleCellContentView = bubbleCellContentView + let roomCellContentView = RoomCellContentView.instantiate() + self.contentView.vc_addSubViewMatchingParent(roomCellContentView) + self.roomCellContentView = roomCellContentView } // MARK: - RoomCellURLPreviewDisplayable - // Cannot use default implementation with ObjC protocol, if self conforms to BubbleCellReadReceiptsDisplayable method below will be used + // Cannot use default implementation with ObjC protocol, if self conforms to RoomCellURLPreviewDisplayable method below will be used func addURLPreviewView(_ urlPreviewView: UIView) { - self.bubbleCellContentView?.addURLPreviewView(urlPreviewView) + self.roomCellContentView?.addURLPreviewView(urlPreviewView) // tmpSubviews is used for touch detection in MXKRoomBubbleTableViewCell self.addTemporarySubview(urlPreviewView) } func removeURLPreviewView() { - self.bubbleCellContentView?.removeURLPreviewView() + self.roomCellContentView?.removeURLPreviewView() } - // MARK: - BubbleCellReadReceiptsDisplayable - // Cannot use default implementation with ObjC protocol, if self conforms to BubbleCellReadReceiptsDisplayable method below will be used + // MARK: - RoomCellReadReceiptsDisplayable + // Cannot use default implementation with ObjC protocol, if self conforms to RoomCellReadReceiptsDisplayable method below will be used func addReadReceiptsView(_ readReceiptsView: UIView) { - self.bubbleCellContentView?.addReadReceiptsView(readReceiptsView) + self.roomCellContentView?.addReadReceiptsView(readReceiptsView) // tmpSubviews is used for touch detection in MXKRoomBubbleTableViewCell self.addTemporarySubview(readReceiptsView) } func removeReadReceiptsView() { - self.bubbleCellContentView?.removeReadReceiptsView() + self.roomCellContentView?.removeReadReceiptsView() } - // MARK: - BubbleCellReactionsDisplayable - // Cannot use default implementation with ObjC protocol, if self conforms to BubbleCellReactionsDisplayable method below will be used + // MARK: - RoomCellReactionsDisplayable + // Cannot use default implementation with ObjC protocol, if self conforms to RoomCellReactionsDisplayable method below will be used func addReactionsView(_ reactionsView: UIView) { - self.bubbleCellContentView?.addReactionsView(reactionsView) + self.roomCellContentView?.addReactionsView(reactionsView) // tmpSubviews is used for touch detection in MXKRoomBubbleTableViewCell self.addTemporarySubview(reactionsView) } func removeReactionsView() { - self.bubbleCellContentView?.removeReactionsView() + self.roomCellContentView?.removeReactionsView() } - // MARK: - BubbleCellThreadSummaryDisplayable + // MARK: - RoomCellThreadSummaryDisplayable func addThreadSummaryView(_ threadSummaryView: ThreadSummaryView) { - self.bubbleCellContentView?.addThreadSummaryView(threadSummaryView) + self.roomCellContentView?.addThreadSummaryView(threadSummaryView) // tmpSubviews is used for touch detection in MXKRoomBubbleTableViewCell self.addTemporarySubview(threadSummaryView) } func removeThreadSummaryView() { - self.bubbleCellContentView?.removeThreadSummaryView() + self.roomCellContentView?.removeThreadSummaryView() + } + + // MARK: - RoomCellReadMarkerDisplayable + + func addReadMarkerView(_ readMarkerView: UIView) { + self.roomCellContentView?.addReadMarkerView(readMarkerView) + self.readMarkerView = readMarkerView + } + + override func removeReadMarkerView() { + self.roomCellContentView?.removeReadMarkerView() + + super.removeReadMarkerView() } // Encryption status @@ -306,13 +389,13 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType { guard let component = self.bubbleData.getFirstBubbleComponentWithDisplay() else { return } - self.bubbleCellContentView?.encryptionImageView.image = RoomEncryptedDataBubbleCell.encryptionIcon(for: component) + self.roomCellContentView?.encryptionImageView.image = RoomEncryptedDataBubbleCell.encryptionIcon(for: component) } private func setupEncryptionStatusViewTapGestureRecognizer() { let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleEncryptionStatusContainerViewTap(_:))) tapGestureRecognizer.delegate = self - self.bubbleCellContentView?.encryptionImageView.isUserInteractionEnabled = true + self.roomCellContentView?.encryptionImageView.isUserInteractionEnabled = true } @objc private func handleEncryptionStatusContainerViewTap(_ gestureRecognizer: UITapGestureRecognizer) { diff --git a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellContentView.swift b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellContentView.swift similarity index 75% rename from Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellContentView.swift rename to Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellContentView.swift index a0f7fbd22..e8cc17639 100644 --- a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellContentView.swift +++ b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellContentView.swift @@ -17,9 +17,9 @@ import UIKit import Reusable -/// `BubbleCellContentView` is a container view that display the default room message outer views and enables to manage them. Like pagination title, sender info, read receipts, reactions, encryption status. +/// `RoomCellContentView` is a container view that display the default room message outer views and enables to manage them. Like pagination title, sender info, read receipts, reactions, encryption status. @objcMembers -final class BubbleCellContentView: UIView, NibLoadable { +final class RoomCellContentView: UIView, NibLoadable { // MARK: - Properties @@ -33,11 +33,15 @@ final class BubbleCellContentView: UIView, NibLoadable { @IBOutlet weak var userNameLabel: UILabel! @IBOutlet weak var userNameTouchMaskView: UIView! + @IBOutlet weak var userNameLabelTopConstraint: NSLayoutConstraint! + @IBOutlet weak var userNameLabelBottomConstraint: NSLayoutConstraint! + @IBOutlet weak var avatarContainerView: UIView! @IBOutlet weak var avatarImageView: MXKImageView! @IBOutlet weak var innerContentView: UIView! + @IBOutlet weak var innerContentViewTopConstraint: NSLayoutConstraint! @IBOutlet weak var innerContentViewLeadingConstraint: NSLayoutConstraint! @IBOutlet weak var innerContentViewTrailingConstraint: NSLayoutConstraint! @IBOutlet weak var innerContentViewBottomContraint: NSLayoutConstraint! @@ -56,6 +60,12 @@ final class BubbleCellContentView: UIView, NibLoadable { @IBOutlet weak var readReceiptsContainerView: UIView! @IBOutlet weak var readReceiptsContentView: UIView! + @IBOutlet weak var readMarkerContainerView: UIView! + @IBOutlet weak var readMarkerContentView: UIView! + + var readMarkerViewLeadingConstraint: NSLayoutConstraint? + var readMarkerViewTrailingConstraint: NSLayoutConstraint? + @IBOutlet weak var reactionsContainerView: UIView! @IBOutlet weak var reactionsContentView: UIView! @IBOutlet weak var reactionsContentViewLeadingConstraint: NSLayoutConstraint! @@ -154,12 +164,21 @@ final class BubbleCellContentView: UIView, NibLoadable { } } + var showReadMarker: Bool { + get { + return !self.readMarkerContainerView.isHidden + } + set { + self.readMarkerContainerView.isHidden = !newValue + } + } + var decorationViewsAlignment: RoomCellDecorationAlignment = .left // MARK: - Setup - class func instantiate() -> BubbleCellContentView { - return BubbleCellContentView.loadFromNib() + class func instantiate() -> RoomCellContentView { + return RoomCellContentView.loadFromNib() } // MARK: - Public @@ -171,8 +190,8 @@ final class BubbleCellContentView: UIView, NibLoadable { } } -// MARK: - BubbleCellReadReceiptsDisplayable -extension BubbleCellContentView: BubbleCellReadReceiptsDisplayable { +// MARK: - RoomCellReadReceiptsDisplayable +extension RoomCellContentView: RoomCellReadReceiptsDisplayable { func addReadReceiptsView(_ readReceiptsView: UIView) { self.readReceiptsContentView.vc_removeAllSubviews() @@ -186,8 +205,8 @@ extension BubbleCellContentView: BubbleCellReadReceiptsDisplayable { } } -// MARK: - BubbleCellReactionsDisplayable -extension BubbleCellContentView: BubbleCellReactionsDisplayable { +// MARK: - RoomCellReactionsDisplayable +extension RoomCellContentView: RoomCellReactionsDisplayable { func addReactionsView(_ reactionsView: UIView) { self.reactionsContentView.vc_removeAllSubviews() @@ -218,8 +237,8 @@ extension BubbleCellContentView: BubbleCellReactionsDisplayable { } } -// MARK: - BubbleCellThreadSummaryDisplayable -extension BubbleCellContentView: BubbleCellThreadSummaryDisplayable { +// MARK: - RoomCellThreadSummaryDisplayable +extension RoomCellContentView: RoomCellThreadSummaryDisplayable { func addThreadSummaryView(_ threadSummaryView: ThreadSummaryView) { @@ -246,7 +265,7 @@ extension BubbleCellContentView: BubbleCellThreadSummaryDisplayable { NSLayoutConstraint.activate([ leadingConstraint, threadSummaryView.topAnchor.constraint(equalTo: containerView.topAnchor), - threadSummaryView.heightAnchor.constraint(equalToConstant: RoomBubbleCellLayout.threadSummaryViewHeight), + threadSummaryView.heightAnchor.constraint(equalToConstant: PlainRoomCellLayoutConstants.threadSummaryViewHeight), threadSummaryView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), trailingConstraint ]) @@ -261,7 +280,7 @@ extension BubbleCellContentView: BubbleCellThreadSummaryDisplayable { } // MARK: - RoomCellURLPreviewDisplayable -extension BubbleCellContentView: RoomCellURLPreviewDisplayable { +extension RoomCellContentView: RoomCellURLPreviewDisplayable { func addURLPreviewView(_ urlPreviewView: UIView) { @@ -304,3 +323,46 @@ extension BubbleCellContentView: RoomCellURLPreviewDisplayable { self.urlPreviewContentView.vc_removeAllSubviews() } } + +// MARK: - RoomCellReadMarkerDisplayable +extension RoomCellContentView: RoomCellReadMarkerDisplayable { + + func addReadMarkerView(_ readMarkerView: UIView) { + guard let containerView = self.readMarkerContainerView else { + return + } + + self.readMarkerContentView.vc_removeAllSubviews() + + readMarkerView.translatesAutoresizingMaskIntoConstraints = false + + self.readMarkerContentView.addSubview(readMarkerView) + + // Force read marker constraints + let topConstraint = readMarkerView.topAnchor.constraint(equalTo: containerView.topAnchor) + + let leadingConstraint = readMarkerView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor) + + let trailingConstraint = readMarkerView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor) + + let heightConstraint = readMarkerView.heightAnchor.constraint(equalToConstant: PlainRoomCellLayoutConstants.readMarkerViewHeight) + + let bottomContraint = readMarkerView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) + + NSLayoutConstraint.activate([topConstraint, + leadingConstraint, + trailingConstraint, + heightConstraint, + bottomContraint]) + + self.readMarkerViewLeadingConstraint = leadingConstraint + self.readMarkerViewTrailingConstraint = trailingConstraint + + self.showReadMarker = true + } + + func removeReadMarkerView() { + self.showReadMarker = false + self.readMarkerContentView.vc_removeAllSubviews() + } +} diff --git a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellContentView.xib b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellContentView.xib similarity index 91% rename from Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellContentView.xib rename to Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellContentView.xib index cdaf09edd..c05e9823a 100644 --- a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellContentView.xib +++ b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellContentView.xib @@ -4,13 +4,12 @@ - - + @@ -68,7 +67,7 @@ - + @@ -76,7 +75,7 @@ - + @@ -87,7 +86,7 @@ - + + @@ -310,8 +333,5 @@ - - - diff --git a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/RoomCellDecorationAlignment.swift b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellDecorationAlignment.swift similarity index 92% rename from Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/RoomCellDecorationAlignment.swift rename to Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellDecorationAlignment.swift index 4aa7f7d53..f5fc67ae2 100644 --- a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/RoomCellDecorationAlignment.swift +++ b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellDecorationAlignment.swift @@ -16,7 +16,7 @@ import Foundation -/// BubbleCellContentView decoration view items alignment +/// RoomCellContentView decoration view items alignment enum RoomCellDecorationAlignment { case left case right diff --git a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellReactionsDisplayable.swift b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellReactionsDisplayable.swift similarity index 81% rename from Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellReactionsDisplayable.swift rename to Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellReactionsDisplayable.swift index 91eb3de41..ff8671f7f 100644 --- a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellReactionsDisplayable.swift +++ b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellReactionsDisplayable.swift @@ -16,8 +16,8 @@ import Foundation -/// `BubbleCellReactionsDisplayable` is a protocol indicating that a cell support displaying reactions. -@objc protocol BubbleCellReactionsDisplayable { +/// `RoomCellReactionsDisplayable` is a protocol indicating that a cell support displaying reactions. +@objc protocol RoomCellReactionsDisplayable { func addReactionsView(_ reactionsView: UIView) func removeReactionsView() } diff --git a/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellReadMarkerDisplayable.swift b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellReadMarkerDisplayable.swift new file mode 100644 index 000000000..cde2ceff4 --- /dev/null +++ b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellReadMarkerDisplayable.swift @@ -0,0 +1,23 @@ +// +// Copyright 2021 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 UIKit + +/// `RoomCellReadMarkerDisplayable` is a protocol indicating that a cell support displaying read marker. +@objc protocol RoomCellReadMarkerDisplayable { + func addReadMarkerView(_ readMarkerView: UIView) + func removeReadMarkerView() +} diff --git a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellReadReceiptsDisplayable.swift b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellReadReceiptsDisplayable.swift similarity index 80% rename from Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellReadReceiptsDisplayable.swift rename to Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellReadReceiptsDisplayable.swift index 4184944fd..6e443832a 100644 --- a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellReadReceiptsDisplayable.swift +++ b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellReadReceiptsDisplayable.swift @@ -16,8 +16,8 @@ import Foundation -/// `BubbleCellReadReceiptsDisplayable` is a protocol indicating that a cell support displaying read receipts. -@objc protocol BubbleCellReadReceiptsDisplayable { +/// `RoomCellReadReceiptsDisplayable` is a protocol indicating that a cell support displaying read receipts. +@objc protocol RoomCellReadReceiptsDisplayable { func addReadReceiptsView(_ readReceiptsView: UIView) func removeReadReceiptsView() } diff --git a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellThreadSummaryDisplayable.swift b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellThreadSummaryDisplayable.swift similarity index 81% rename from Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellThreadSummaryDisplayable.swift rename to Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellThreadSummaryDisplayable.swift index 6c2848e27..c84416050 100644 --- a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/BubbleCellThreadSummaryDisplayable.swift +++ b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellThreadSummaryDisplayable.swift @@ -16,8 +16,8 @@ import Foundation -/// `BubbleCellThreadSummaryDisplayable` is a protocol indicating that a cell support displaying a thread summary. -@objc protocol BubbleCellThreadSummaryDisplayable { +/// `RoomCellThreadSummaryDisplayable` is a protocol indicating that a cell support displaying a thread summary. +@objc protocol RoomCellThreadSummaryDisplayable { func addThreadSummaryView(_ threadSummaryView: ThreadSummaryView) func removeThreadSummaryView() } diff --git a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/RoomCellURLPreviewDisplayable.swift b/Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellURLPreviewDisplayable.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/RoomCellURLPreviewDisplayable.swift rename to Riot/Modules/Room/TimelineCells/BaseRoomCell/RoomCellURLPreviewDisplayable.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/TimestampDisplayable.swift b/Riot/Modules/Room/TimelineCells/BaseRoomCell/TimestampDisplayable.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/BaseBubbleCell/TimestampDisplayable.swift rename to Riot/Modules/Room/TimelineCells/BaseRoomCell/TimestampDisplayable.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Call/CallBubbleCellBaseContentView.swift b/Riot/Modules/Room/TimelineCells/Call/CallBubbleCellBaseContentView.swift similarity index 97% rename from Riot/Modules/Room/Views/BubbleCells/Call/CallBubbleCellBaseContentView.swift rename to Riot/Modules/Room/TimelineCells/Call/CallBubbleCellBaseContentView.swift index 5fbf6e51d..e150ac729 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Call/CallBubbleCellBaseContentView.swift +++ b/Riot/Modules/Room/TimelineCells/Call/CallBubbleCellBaseContentView.swift @@ -112,9 +112,9 @@ extension CallBubbleCellBaseContentView: Themable { } -// MARK: - BubbleCellReadReceiptsDisplayable +// MARK: - RoomCellReadReceiptsDisplayable -extension CallBubbleCellBaseContentView: BubbleCellReadReceiptsDisplayable { +extension CallBubbleCellBaseContentView: RoomCellReadReceiptsDisplayable { func addReadReceiptsView(_ readReceiptsView: UIView) { self.readReceiptsContentView.vc_removeAllSubviews() diff --git a/Riot/Modules/Room/Views/BubbleCells/Call/CallBubbleCellBaseContentView.xib b/Riot/Modules/Room/TimelineCells/Call/CallBubbleCellBaseContentView.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Call/CallBubbleCellBaseContentView.xib rename to Riot/Modules/Room/TimelineCells/Call/CallBubbleCellBaseContentView.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/Call/Common/CallTileActionButton.swift b/Riot/Modules/Room/TimelineCells/Call/Common/CallTileActionButton.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Call/Common/CallTileActionButton.swift rename to Riot/Modules/Room/TimelineCells/Call/Common/CallTileActionButton.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Call/Common/HorizontalButtonsContainerView.swift b/Riot/Modules/Room/TimelineCells/Call/Common/HorizontalButtonsContainerView.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Call/Common/HorizontalButtonsContainerView.swift rename to Riot/Modules/Room/TimelineCells/Call/Common/HorizontalButtonsContainerView.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Call/Common/HorizontalButtonsContainerView.xib b/Riot/Modules/Room/TimelineCells/Call/Common/HorizontalButtonsContainerView.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Call/Common/HorizontalButtonsContainerView.xib rename to Riot/Modules/Room/TimelineCells/Call/Common/HorizontalButtonsContainerView.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/Call/Direct/RoomDirectCallStatusBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Call/Direct/RoomDirectCallStatusBubbleCell.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Call/Direct/RoomDirectCallStatusBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Call/Direct/RoomDirectCallStatusBubbleCell.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Call/Group/RoomGroupCallStatusBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Call/Group/RoomGroupCallStatusBubbleCell.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Call/Group/RoomGroupCallStatusBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Call/Group/RoomGroupCallStatusBubbleCell.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Call/RoomBaseCallBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Call/RoomBaseCallBubbleCell.swift similarity index 98% rename from Riot/Modules/Room/Views/BubbleCells/Call/RoomBaseCallBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Call/RoomBaseCallBubbleCell.swift index d760d83bd..b7efc965b 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Call/RoomBaseCallBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Call/RoomBaseCallBubbleCell.swift @@ -144,7 +144,7 @@ class RoomBaseCallBubbleCell: MXKRoomBubbleTableViewCell { } -extension RoomBaseCallBubbleCell: BubbleCellReadReceiptsDisplayable { +extension RoomBaseCallBubbleCell: RoomCellReadReceiptsDisplayable { func addReadReceiptsView(_ readReceiptsView: UIView) { innerContentView.addReadReceiptsView(readReceiptsView) diff --git a/Riot/Modules/Room/Views/BubbleCells/Call/RoomBaseCallBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Call/RoomBaseCallBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Call/RoomBaseCallBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Call/RoomBaseCallBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomBubbleTableViewCell.h b/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.h similarity index 94% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomBubbleTableViewCell.h rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.h index baf53c7ba..e79c2b721 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomBubbleTableViewCell.h +++ b/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.h @@ -337,10 +337,34 @@ extern NSString *const kMXKRoomBubbleCellUrlItemInteraction; */ - (void)setupViews; +/// Setup sender name label if needed +- (void)setupSenderNameLabel; + +/// Setup avatar view if needed +- (void)setupAvatarView; + +/// Setup message text view if needed +- (void)setupMessageTextView; + +/// Setup message text view long press gesture if needed +- (void)setupMessageTextViewLongPressGesture; + /// Add temporary subview to `tmpSubviews` property. - (void)addTemporarySubview:(UIView*)subview; /// Called when content view cell is tapped - (IBAction)onContentViewTap:(UITapGestureRecognizer*)sender; +/// Called when sender name is tapped +- (IBAction)onSenderNameTap:(UITapGestureRecognizer*)sender; + +/// Called when avatar view is tapped +- (IBAction)onAvatarTap:(UITapGestureRecognizer*)sender; + +/// Called when a UI component is long pressed +- (IBAction)onLongPressGesture:(UILongPressGestureRecognizer*)longPressGestureRecognizer; + +/// Remove marker view if present +- (void)removeReadMarkerView; + @end diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomBubbleTableViewCell.m b/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.m similarity index 94% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomBubbleTableViewCell.m rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.m index b0a2fba49..c4367d598 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomBubbleTableViewCell.m +++ b/Riot/Modules/Room/TimelineCells/Common/MXKRoomBubbleTableViewCell.m @@ -136,86 +136,11 @@ static BOOL _disableLongPressGestureOnEvent; - (void)setupViews { - if (self.userNameLabel) - { - // Listen to name tap - UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSenderNameTap:)]; - [tapGesture setNumberOfTouchesRequired:1]; - [tapGesture setNumberOfTapsRequired:1]; - [tapGesture setDelegate:self]; - - if (self.userNameTapGestureMaskView) - { - [self.userNameTapGestureMaskView addGestureRecognizer:tapGesture]; - } - else - { - [self.userNameLabel addGestureRecognizer:tapGesture]; - self.userNameLabel.userInteractionEnabled = YES; - } - } + [self setupSenderNameLabel]; - if (self.pictureView) - { - self.pictureView.mediaFolder = kMXMediaManagerAvatarThumbnailFolder; - - // Listen to avatar tap - UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onAvatarTap:)]; - [tapGesture setNumberOfTouchesRequired:1]; - [tapGesture setNumberOfTapsRequired:1]; - [tapGesture setDelegate:self]; - [self.pictureView addGestureRecognizer:tapGesture]; - self.pictureView.userInteractionEnabled = YES; - - // Add a long gesture recognizer on avatar (in order to display for example the member details) - UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPressGesture:)]; - [self.pictureView addGestureRecognizer:longPress]; - } + [self setupAvatarView]; - if (self.messageTextView) - { - // Listen to textView tap - UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onMessageTap:)]; - [tapGesture setNumberOfTouchesRequired:1]; - [tapGesture setNumberOfTapsRequired:1]; - [tapGesture setDelegate:self]; - [self.messageTextView addGestureRecognizer:tapGesture]; - self.messageTextView.userInteractionEnabled = YES; - - // Recognise and make tappable phone numbers, address, etc. - self.messageTextView.dataDetectorTypes = UIDataDetectorTypeAll; - - // Listen to link click - self.messageTextView.delegate = self; - - if (_disableLongPressGestureOnEvent == NO) - { - // Add a long gesture recognizer on text view (in order to display for example the event details) - UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPressGesture:)]; - longPress.delegate = self; - - // MXKMessageTextView does not catch touches outside of links. Add a background view to handle long touch. - if ([self.messageTextView isKindOfClass:[MXKMessageTextView class]]) - { - UIView *messageTextBackgroundView = [[UIView alloc] initWithFrame:self.messageTextView.frame]; - messageTextBackgroundView.backgroundColor = [UIColor clearColor]; - [self.contentView insertSubview:messageTextBackgroundView belowSubview:self.messageTextView]; - messageTextBackgroundView.translatesAutoresizingMaskIntoConstraints = NO; - [messageTextBackgroundView.leftAnchor constraintEqualToAnchor:self.messageTextView.leftAnchor].active = YES; - [messageTextBackgroundView.rightAnchor constraintEqualToAnchor:self.messageTextView.rightAnchor].active = YES; - [messageTextBackgroundView.topAnchor constraintEqualToAnchor:self.messageTextView.topAnchor].active = YES; - [messageTextBackgroundView.bottomAnchor constraintEqualToAnchor:self.messageTextView.bottomAnchor].active = YES; - - [messageTextBackgroundView addGestureRecognizer:longPress]; - - self.messageTextBackgroundView = messageTextBackgroundView; - } - else - { - [self.messageTextView addGestureRecognizer:longPress]; - } - } - } + [self setupMessageTextView]; if (self.playIconView) { @@ -250,6 +175,109 @@ static BOOL _disableLongPressGestureOnEvent; [self setupConstraintsConstantDefaultValues]; } +- (void)setupSenderNameLabel +{ + if (!self.userNameLabel) + { + return; + } + + // Listen to name tap + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSenderNameTap:)]; + [tapGesture setNumberOfTouchesRequired:1]; + [tapGesture setNumberOfTapsRequired:1]; + [tapGesture setDelegate:self]; + + if (self.userNameTapGestureMaskView) + { + [self.userNameTapGestureMaskView addGestureRecognizer:tapGesture]; + } + else + { + [self.userNameLabel addGestureRecognizer:tapGesture]; + self.userNameLabel.userInteractionEnabled = YES; + } +} + +- (void)setupAvatarView +{ + if (!self.pictureView) + { + return; + } + + self.pictureView.mediaFolder = kMXMediaManagerAvatarThumbnailFolder; + + // Listen to avatar tap + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onAvatarTap:)]; + [tapGesture setNumberOfTouchesRequired:1]; + [tapGesture setNumberOfTapsRequired:1]; + [tapGesture setDelegate:self]; + [self.pictureView addGestureRecognizer:tapGesture]; + self.pictureView.userInteractionEnabled = YES; + + // Add a long gesture recognizer on avatar (in order to display for example the member details) + UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPressGesture:)]; + [self.pictureView addGestureRecognizer:longPress]; +} + +- (void)setupMessageTextView +{ + if (!self.messageTextView) + { + return; + } + + // Listen to textView tap + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onMessageTap:)]; + [tapGesture setNumberOfTouchesRequired:1]; + [tapGesture setNumberOfTapsRequired:1]; + [tapGesture setDelegate:self]; + [self.messageTextView addGestureRecognizer:tapGesture]; + self.messageTextView.userInteractionEnabled = YES; + + // Recognise and make tappable phone numbers, address, etc. + self.messageTextView.dataDetectorTypes = UIDataDetectorTypeAll; + + // Listen to link click + self.messageTextView.delegate = self; + + [self setupMessageTextViewLongPressGesture]; +} + +- (void)setupMessageTextViewLongPressGesture +{ + if (_disableLongPressGestureOnEvent) + { + return; + } + + // Add a long gesture recognizer on text view (in order to display for example the event details) + UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPressGesture:)]; + longPress.delegate = self; + + // MXKMessageTextView does not catch touches outside of links. Add a background view to handle long touch. + if ([self.messageTextView isKindOfClass:[MXKMessageTextView class]]) + { + UIView *messageTextBackgroundView = [[UIView alloc] initWithFrame:self.messageTextView.frame]; + messageTextBackgroundView.backgroundColor = [UIColor clearColor]; + [self.contentView insertSubview:messageTextBackgroundView belowSubview:self.messageTextView]; + messageTextBackgroundView.translatesAutoresizingMaskIntoConstraints = NO; + [messageTextBackgroundView.leftAnchor constraintEqualToAnchor:self.messageTextView.leftAnchor].active = YES; + [messageTextBackgroundView.rightAnchor constraintEqualToAnchor:self.messageTextView.rightAnchor].active = YES; + [messageTextBackgroundView.topAnchor constraintEqualToAnchor:self.messageTextView.topAnchor].active = YES; + [messageTextBackgroundView.bottomAnchor constraintEqualToAnchor:self.messageTextView.bottomAnchor].active = YES; + + [messageTextBackgroundView addGestureRecognizer:longPress]; + + self.messageTextBackgroundView = messageTextBackgroundView; + } + else + { + [self.messageTextView addGestureRecognizer:longPress]; + } +} + - (void)customizeTableViewCellRendering { [super customizeTableViewCellRendering]; @@ -1549,6 +1577,8 @@ static NSMutableDictionary *childClasses; return shouldInteractWithURL; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" // Delegate method only called on iOS 9. iOS 10+ use method above. - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange { @@ -1571,6 +1601,7 @@ static NSMutableDictionary *childClasses; return shouldInteractWithURL; } +#pragma clang diagnostic pop #pragma mark - WKNavigationDelegate diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSBubbleTableViewCell.h b/Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSBubbleTableViewCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSBubbleTableViewCell.h rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSBubbleTableViewCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSBubbleTableViewCell.m b/Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSBubbleTableViewCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSBubbleTableViewCell.m rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSBubbleTableViewCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.h b/Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.h rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.m b/Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.m rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.xib b/Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.xib rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomIOSOutgoingBubbleTableViewCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIncomingBubbleTableViewCell.h b/Riot/Modules/Room/TimelineCells/Common/MXKRoomIncomingBubbleTableViewCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIncomingBubbleTableViewCell.h rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomIncomingBubbleTableViewCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIncomingBubbleTableViewCell.m b/Riot/Modules/Room/TimelineCells/Common/MXKRoomIncomingBubbleTableViewCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomIncomingBubbleTableViewCell.m rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomIncomingBubbleTableViewCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomOutgoingBubbleTableViewCell.h b/Riot/Modules/Room/TimelineCells/Common/MXKRoomOutgoingBubbleTableViewCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomOutgoingBubbleTableViewCell.h rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomOutgoingBubbleTableViewCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomOutgoingBubbleTableViewCell.m b/Riot/Modules/Room/TimelineCells/Common/MXKRoomOutgoingBubbleTableViewCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Common/MXKRoomOutgoingBubbleTableViewCell.m rename to Riot/Modules/Room/TimelineCells/Common/MXKRoomOutgoingBubbleTableViewCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.h b/Riot/Modules/Room/TimelineCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.h rename to Riot/Modules/Room/TimelineCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.m b/Riot/Modules/Room/TimelineCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.m rename to Riot/Modules/Room/TimelineCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.xib b/Riot/Modules/Room/TimelineCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.xib rename to Riot/Modules/Room/TimelineCells/EmptyContent/MXKRoomEmptyBubbleTableViewCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/EmptyContent/RoomEmptyBubbleCell.h b/Riot/Modules/Room/TimelineCells/EmptyContent/RoomEmptyBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/EmptyContent/RoomEmptyBubbleCell.h rename to Riot/Modules/Room/TimelineCells/EmptyContent/RoomEmptyBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/EmptyContent/RoomEmptyBubbleCell.m b/Riot/Modules/Room/TimelineCells/EmptyContent/RoomEmptyBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/EmptyContent/RoomEmptyBubbleCell.m rename to Riot/Modules/Room/TimelineCells/EmptyContent/RoomEmptyBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/EmptyContent/RoomEmptyBubbleCell.xib b/Riot/Modules/Room/TimelineCells/EmptyContent/RoomEmptyBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/EmptyContent/RoomEmptyBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/EmptyContent/RoomEmptyBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.h b/Riot/Modules/Room/TimelineCells/Encryption/RoomEncryptedDataBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Encryption/RoomEncryptedDataBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m b/Riot/Modules/Room/TimelineCells/Encryption/RoomEncryptedDataBubbleCell.m similarity index 96% rename from Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Encryption/RoomEncryptedDataBubbleCell.m index 6958e3ed5..7a5e50ff9 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m +++ b/Riot/Modules/Room/TimelineCells/Encryption/RoomEncryptedDataBubbleCell.m @@ -16,6 +16,7 @@ */ #import "RoomEncryptedDataBubbleCell.h" +#import "GeneratedInterface-Swift.h" NSString *const kRoomEncryptedDataBubbleCellTapOnEncryptionIcon = @"kRoomEncryptedDataBubbleCellTapOnEncryptionIcon"; @@ -28,7 +29,7 @@ NSString *const kRoomEncryptedDataBubbleCellTapOnEncryptionIcon = @"kRoomEncrypt return nil; } - return [UIImage imageNamed:@"encryption_warning"]; + return AssetImages.encryptionWarning.image; } + (void)addEncryptionStatusFromBubbleData:(MXKRoomBubbleCellData *)bubbleData inContainerView:(UIView *)containerView diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationBaseBubbleCell.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationBaseBubbleCell.swift similarity index 84% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationBaseBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationBaseBubbleCell.swift index 0344b20b9..81b3d851a 100644 --- a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationBaseBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationBaseBubbleCell.swift @@ -31,11 +31,11 @@ class KeyVerificationBaseBubbleCell: MXKRoomBubbleTableViewCell { weak var keyVerificationCellInnerContentView: KeyVerificationCellInnerContentView? - weak var bubbleCellContentView: BubbleCellContentView? + weak var roomCellContentView: RoomCellContentView? override var bubbleInfoContainer: UIView! { get { - guard let infoContainer = self.bubbleCellContentView?.bubbleInfoContainer else { + guard let infoContainer = self.roomCellContentView?.bubbleInfoContainer else { fatalError("[KeyVerificationBaseBubbleCell] bubbleInfoContainer should not be used before set") } return infoContainer @@ -47,7 +47,7 @@ class KeyVerificationBaseBubbleCell: MXKRoomBubbleTableViewCell { override var bubbleOverlayContainer: UIView! { get { - guard let overlayContainer = self.bubbleCellContentView?.bubbleOverlayContainer else { + guard let overlayContainer = self.roomCellContentView?.bubbleOverlayContainer else { fatalError("[KeyVerificationBaseBubbleCell] bubbleOverlayContainer should not be used before set") } return overlayContainer @@ -59,7 +59,7 @@ class KeyVerificationBaseBubbleCell: MXKRoomBubbleTableViewCell { override var bubbleInfoContainerTopConstraint: NSLayoutConstraint! { get { - guard let infoContainerTopConstraint = self.bubbleCellContentView?.bubbleInfoContainerTopConstraint else { + guard let infoContainerTopConstraint = self.roomCellContentView?.bubbleInfoContainerTopConstraint else { fatalError("[KeyVerificationBaseBubbleCell] bubbleInfoContainerTopConstraint should not be used before set") } return infoContainerTopConstraint @@ -92,7 +92,7 @@ class KeyVerificationBaseBubbleCell: MXKRoomBubbleTableViewCell { // MARK: - Public func update(theme: Theme) { - self.bubbleCellContentView?.update(theme: theme) + self.roomCellContentView?.update(theme: theme) self.keyVerificationCellInnerContentView?.update(theme: theme) } @@ -190,27 +190,27 @@ class KeyVerificationBaseBubbleCell: MXKRoomBubbleTableViewCell { super.render(cellData) if let bubbleData = self.bubbleData, - let bubbleCellContentView = self.bubbleCellContentView, + let roomCellContentView = self.roomCellContentView, let paginationDate = bubbleData.date, - bubbleCellContentView.showPaginationTitle { - bubbleCellContentView.paginationLabel.text = bubbleData.eventFormatter.dateString(from: paginationDate, withTime: false)?.uppercased() + roomCellContentView.showPaginationTitle { + roomCellContentView.paginationLabel.text = bubbleData.eventFormatter.dateString(from: paginationDate, withTime: false)?.uppercased() } } // MARK: - Private private func setupContentView() { - if self.bubbleCellContentView == nil { + if self.roomCellContentView == nil { - let bubbleCellContentView = BubbleCellContentView.instantiate() + let roomCellContentView = RoomCellContentView.instantiate() let innerContentView = KeyVerificationCellInnerContentView.instantiate() - bubbleCellContentView.innerContentView.vc_addSubViewMatchingParent(innerContentView) + roomCellContentView.innerContentView.vc_addSubViewMatchingParent(innerContentView) - self.contentView.vc_addSubViewMatchingParent(bubbleCellContentView) + self.contentView.vc_addSubViewMatchingParent(roomCellContentView) - self.bubbleCellContentView = bubbleCellContentView + self.roomCellContentView = roomCellContentView self.keyVerificationCellInnerContentView = innerContentView } } @@ -241,21 +241,21 @@ class KeyVerificationBaseBubbleCell: MXKRoomBubbleTableViewCell { var height = sizingView.systemLayoutSizeFitting(fittingSize).height if let roomBubbleCellData = cellData as? RoomBubbleCellData, let readReceipts = roomBubbleCellData.readReceipts, readReceipts.count > 0 { - height+=RoomBubbleCellLayout.readReceiptsViewHeight + height+=PlainRoomCellLayoutConstants.readReceiptsViewHeight } return height } } -// MARK: - BubbleCellReadReceiptsDisplayable -extension KeyVerificationBaseBubbleCell: BubbleCellReadReceiptsDisplayable { +// MARK: - RoomCellReadReceiptsDisplayable +extension KeyVerificationBaseBubbleCell: RoomCellReadReceiptsDisplayable { func addReadReceiptsView(_ readReceiptsView: UIView) { - self.bubbleCellContentView?.addReadReceiptsView(readReceiptsView) + self.roomCellContentView?.addReadReceiptsView(readReceiptsView) } func removeReadReceiptsView() { - self.bubbleCellContentView?.removeReadReceiptsView() + self.roomCellContentView?.removeReadReceiptsView() } } diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationCellInnerContentView.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationCellInnerContentView.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationCellInnerContentView.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationCellInnerContentView.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationCellInnerContentView.xib b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationCellInnerContentView.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationCellInnerContentView.xib rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationCellInnerContentView.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationConclusionBubbleCell.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationConclusionBubbleCell.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationConclusionBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationConclusionBubbleCell.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationConclusionViewData.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationConclusionViewData.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationConclusionViewData.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationConclusionViewData.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationConclusionWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationConclusionWithPaginationTitleBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationConclusionWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationConclusionWithPaginationTitleBubbleCell.swift index 7cc1b8277..386d12e81 100644 --- a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationConclusionWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationConclusionWithPaginationTitleBubbleCell.swift @@ -37,11 +37,11 @@ final class KeyVerificationConclusionWithPaginationTitleBubbleCell: KeyVerificat } private func commonInit() { - guard let bubbleCellContentView = self.bubbleCellContentView else { + guard let roomCellContentView = self.roomCellContentView else { fatalError("[KeyVerificationConclusionWithPaginationTitleBubbleCell] bubbleCellContentView should not be nil") } - bubbleCellContentView.showPaginationTitle = true + roomCellContentView.showPaginationTitle = true } // MARK: - Overrides diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationIncomingRequestApprovalBubbleCell.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationIncomingRequestApprovalBubbleCell.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationIncomingRequestApprovalBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationIncomingRequestApprovalBubbleCell.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationIncomingRequestApprovalViewData.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationIncomingRequestApprovalViewData.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationIncomingRequestApprovalViewData.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationIncomingRequestApprovalViewData.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift index 6b4b529f1..8a13a24c7 100644 --- a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell.swift @@ -37,11 +37,11 @@ final class KeyVerificationIncomingRequestApprovalWithPaginationTitleBubbleCell: } private func commonInit() { - guard let bubbleCellContentView = self.bubbleCellContentView else { + guard let roomCellContentView = self.roomCellContentView else { fatalError("[KeyVerificationRequestStatusWithPaginationTitleBubbleCell] bubbleCellContentView should not be nil") } - bubbleCellContentView.showPaginationTitle = true + roomCellContentView.showPaginationTitle = true } // MARK: - Overrides diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationRequestStatusBubbleCell.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationRequestStatusBubbleCell.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationRequestStatusBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationRequestStatusBubbleCell.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationRequestStatusViewData.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationRequestStatusViewData.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationRequestStatusViewData.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationRequestStatusViewData.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift index f9c0e6465..1e2b7f5b6 100644 --- a/Riot/Modules/Room/Views/BubbleCells/KeyVerification/KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/KeyVerification/KeyVerificationRequestStatusWithPaginationTitleBubbleCell.swift @@ -37,11 +37,11 @@ final class KeyVerificationRequestStatusWithPaginationTitleBubbleCell: KeyVerifi } private func commonInit() { - guard let bubbleCellContentView = self.bubbleCellContentView else { + guard let roomCellContentView = self.roomCellContentView else { fatalError("[KeyVerificationRequestStatusWithPaginationTitleBubbleCell] bubbleCellContentView should not be nil") } - bubbleCellContentView.showPaginationTitle = true + roomCellContentView.showPaginationTitle = true } // MARK: - Overrides diff --git a/Riot/Modules/Room/Views/BubbleCells/MessageTyping/RoomTypingBubbleCell.swift b/Riot/Modules/Room/TimelineCells/MessageTyping/RoomTypingBubbleCell.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/MessageTyping/RoomTypingBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/MessageTyping/RoomTypingBubbleCell.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/MessageTyping/RoomTypingBubbleCell.xib b/Riot/Modules/Room/TimelineCells/MessageTyping/RoomTypingBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/MessageTyping/RoomTypingBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/MessageTyping/RoomTypingBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationCollapsedBubbleCell.h b/Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationCollapsedBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationCollapsedBubbleCell.h rename to Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationCollapsedBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationCollapsedBubbleCell.m b/Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationCollapsedBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationCollapsedBubbleCell.m rename to Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationCollapsedBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationCollapsedBubbleCell.xib b/Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationCollapsedBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationCollapsedBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationCollapsedBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.h b/Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.h rename to Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.m b/Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.m rename to Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.xib b/Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/RoomCreation/RoomCreationWithPaginationCollapsedBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreationIntro/RoomCreationIntroCell.swift b/Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift similarity index 89% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreationIntro/RoomCreationIntroCell.swift rename to Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift index f53c943c2..dbe4f7520 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomCreationIntro/RoomCreationIntroCell.swift +++ b/Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift @@ -32,7 +32,7 @@ class RoomCreationIntroCell: MXKRoomBubbleTableViewCell { // MARK: - Properties - private weak var bubbleCellContentView: RoomCreationIntroCellContentView? + private weak var roomCellContentView: RoomCreationIntroCellContentView? // MARK: - Setup @@ -57,7 +57,7 @@ class RoomCreationIntroCell: MXKRoomBubbleTableViewCell { // MARK: - Public func update(theme: Theme) { - self.bubbleCellContentView?.update(theme: theme) + self.roomCellContentView?.update(theme: theme) } // MARK: - Overrides @@ -92,7 +92,7 @@ class RoomCreationIntroCell: MXKRoomBubbleTableViewCell { override func render(_ cellData: MXKCellData!) { super.render(cellData) - guard let bubbleCellContentView = self.bubbleCellContentView else { + guard let roomCellContentView = self.roomCellContentView else { MXLog.debug("[RoomCreationIntroCell] Fail to load content view") return } @@ -103,7 +103,7 @@ class RoomCreationIntroCell: MXKRoomBubbleTableViewCell { return } - bubbleCellContentView.fill(with: viewData) + roomCellContentView.fill(with: viewData) } // MARK: - Private @@ -140,23 +140,23 @@ class RoomCreationIntroCell: MXKRoomBubbleTableViewCell { } private func setupContentView() { - guard self.bubbleCellContentView == nil else { + guard self.roomCellContentView == nil else { return } - let bubbleCellContentView = RoomCreationIntroCellContentView.instantiate() - self.contentView.vc_addSubViewMatchingParent(bubbleCellContentView) - self.bubbleCellContentView = bubbleCellContentView + let roomCellContentView = RoomCreationIntroCellContentView.instantiate() + self.contentView.vc_addSubViewMatchingParent(roomCellContentView) + self.roomCellContentView = roomCellContentView - bubbleCellContentView.roomAvatarView?.action = { [weak self] in + roomCellContentView.roomAvatarView?.action = { [weak self] in self?.notifyDelegate(with: RoomCreationIntroCell.tapOnAvatarView) } - bubbleCellContentView.didTapTopic = { [weak self] in + roomCellContentView.didTapTopic = { [weak self] in self?.notifyDelegate(with: RoomCreationIntroCell.tapOnAddTopic) } - bubbleCellContentView.didTapAddParticipants = { [weak self] in + roomCellContentView.didTapAddParticipants = { [weak self] in self?.notifyDelegate(with: RoomCreationIntroCell.tapOnAddParticipants) } } diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift b/Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift rename to Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreationIntro/RoomCreationIntroCellContentView.xib b/Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreationIntro/RoomCreationIntroCellContentView.xib rename to Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomCreationIntro/RoomCreationIntroViewData.swift b/Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroViewData.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomCreationIntro/RoomCreationIntroViewData.swift rename to Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroViewData.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipBubbleCell.h b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipBubbleCell.h rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipBubbleCell.m b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipBubbleCell.m rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipBubbleCell.xib b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedBubbleCell.h b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedBubbleCell.h rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedBubbleCell.m b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedBubbleCell.m rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedBubbleCell.xib b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipCollapsedWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedBubbleCell.h b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedBubbleCell.h rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedBubbleCell.m b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedBubbleCell.m rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedBubbleCell.xib b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipExpandedWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/RoomMembership/RoomMembershipWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomPredecessor/RoomPredecessorBubbleCell.h b/Riot/Modules/Room/TimelineCells/RoomPredecessor/RoomPredecessorBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomPredecessor/RoomPredecessorBubbleCell.h rename to Riot/Modules/Room/TimelineCells/RoomPredecessor/RoomPredecessorBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomPredecessor/RoomPredecessorBubbleCell.m b/Riot/Modules/Room/TimelineCells/RoomPredecessor/RoomPredecessorBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomPredecessor/RoomPredecessorBubbleCell.m rename to Riot/Modules/Room/TimelineCells/RoomPredecessor/RoomPredecessorBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomPredecessor/RoomPredecessorBubbleCell.xib b/Riot/Modules/Room/TimelineCells/RoomPredecessor/RoomPredecessorBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomPredecessor/RoomPredecessorBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/RoomPredecessor/RoomPredecessorBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomTimelineCellIdentifier.h b/Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomTimelineCellIdentifier.h rename to Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomTimelineConfiguration.swift b/Riot/Modules/Room/TimelineCells/RoomTimelineConfiguration.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/RoomTimelineConfiguration.swift rename to Riot/Modules/Room/TimelineCells/RoomTimelineConfiguration.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/SizableCell/SizableBubbleCell.swift b/Riot/Modules/Room/TimelineCells/SizableCell/SizableBaseRoomCell.swift similarity index 73% rename from Riot/Modules/Room/Views/BubbleCells/SizableCell/SizableBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/SizableCell/SizableBaseRoomCell.swift index 99aa2af4a..98eb9caa3 100644 --- a/Riot/Modules/Room/Views/BubbleCells/SizableCell/SizableBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/SizableCell/SizableBaseRoomCell.swift @@ -17,18 +17,18 @@ import UIKit import MatrixSDK -@objc protocol SizableBaseBubbleCellType: BaseBubbleCellType { +@objc protocol SizableBaseRoomCellType: BaseRoomCellProtocol { static func sizingViewHeightHashValue(from bubbleCellData: MXKRoomBubbleCellData) -> Int } -/// `SizableBaseBubbleCell` allows a cell using Auto Layout that inherits from this class to automatically return the height of the cell and cache the result. +/// `SizableBaseRoomCell` allows a cell using Auto Layout that inherits from this class to automatically return the height of the cell and cache the result. @objcMembers -class SizableBaseBubbleCell: BaseBubbleCell, SizableBaseBubbleCellType { +class SizableBaseRoomCell: BaseRoomCell, SizableBaseRoomCellType { // MARK: - Constants private static let sizingViewHeightStore = SizingViewHeightStore() - private static var sizingViews: [String: SizableBaseBubbleCell] = [:] + private static var sizingViews: [String: SizableBaseRoomCell] = [:] private static let sizingReactionsView = BubbleReactionsView() private static let reactionsViewSizer = BubbleReactionsViewSizer() @@ -36,8 +36,8 @@ class SizableBaseBubbleCell: BaseBubbleCell, SizableBaseBubbleCellType { private static let urlPreviewViewSizer = URLPreviewViewSizer() - private class var sizingView: SizableBaseBubbleCell { - let sizingView: SizableBaseBubbleCell + private class var sizingView: SizableBaseRoomCell { + let sizingView: SizableBaseRoomCell let reuseIdentifier: String = self.defaultReuseIdentifier() @@ -64,7 +64,7 @@ class SizableBaseBubbleCell: BaseBubbleCell, SizableBaseBubbleCellType { return self.height(for: roomBubbleCellData, fitting: maxWidth) } - // MARK - SizableBaseBubbleCellType + // MARK - SizableBaseRoomCellType // Each sublcass should override this method, to indicate a unique identifier for a view height. // This means that the value should change if there is some data that modify the cell height. @@ -77,7 +77,7 @@ class SizableBaseBubbleCell: BaseBubbleCell, SizableBaseBubbleCellType { // MARK: - Private - class func createSizingView() -> SizableBaseBubbleCell { + class func createSizingView() -> SizableBaseRoomCell { return self.init(style: .default, reuseIdentifier: self.defaultReuseIdentifier()) } @@ -119,29 +119,29 @@ class SizableBaseBubbleCell: BaseBubbleCell, SizableBaseBubbleCellType { var height = sizingView.systemLayoutSizeFitting(fittingSize).height // Add read receipt height if needed - if let roomBubbleCellData = cellData as? RoomBubbleCellData, let readReceipts = roomBubbleCellData.readReceipts, readReceipts.count > 0, sizingView is BubbleCellReadReceiptsDisplayable { - height+=RoomBubbleCellLayout.readReceiptsViewHeight + if let roomBubbleCellData = cellData as? RoomBubbleCellData, let readReceipts = roomBubbleCellData.readReceipts, readReceipts.count > 0, sizingView is RoomCellReadReceiptsDisplayable { + height+=PlainRoomCellLayoutConstants.readReceiptsViewHeight } // Add reactions view height if needed - if sizingView is BubbleCellReactionsDisplayable, + if sizingView is RoomCellReactionsDisplayable, let roomBubbleCellData = cellData as? RoomBubbleCellData, let bubbleReactionsViewModel = self.reactionsViewModelBuilder.buildForFirstVisibleComponent(of: roomBubbleCellData) { - let reactionWidth = sizingView.bubbleCellContentView?.reactionsContentView.frame.width ?? roomBubbleCellData.maxTextViewWidth + let reactionWidth = sizingView.roomCellContentView?.reactionsContentView.frame.width ?? roomBubbleCellData.maxTextViewWidth let reactionsHeight = self.reactionsViewSizer.height(for: bubbleReactionsViewModel, fittingWidth: reactionWidth) height+=reactionsHeight } // Add thread summary view height if needed - if sizingView is BubbleCellThreadSummaryDisplayable, + if sizingView is RoomCellThreadSummaryDisplayable, let roomBubbleCellData = cellData as? RoomBubbleCellData, roomBubbleCellData.hasThreadRoot { - let bottomMargin = sizingView.bubbleCellContentView?.threadSummaryContentViewBottomConstraint.constant ?? 0 + let bottomMargin = sizingView.roomCellContentView?.threadSummaryContentViewBottomConstraint.constant ?? 0 - height += RoomBubbleCellLayout.threadSummaryViewHeight + height += PlainRoomCellLayoutConstants.threadSummaryViewHeight height += bottomMargin } @@ -150,12 +150,22 @@ class SizableBaseBubbleCell: BaseBubbleCell, SizableBaseBubbleCellType { let roomBubbleCellData = cellData as? RoomBubbleCellData, let firstBubbleComponent = roomBubbleCellData.getFirstBubbleComponentWithDisplay(), firstBubbleComponent.showURLPreview, let urlPreviewData = firstBubbleComponent.urlPreviewData as? URLPreviewData { - let urlPreviewMaxWidth = sizingView.bubbleCellContentView?.urlPreviewContentView.frame.width ?? roomBubbleCellData.maxTextViewWidth + let urlPreviewMaxWidth = sizingView.roomCellContentView?.urlPreviewContentView.frame.width ?? roomBubbleCellData.maxTextViewWidth let urlPreviewHeight = self.urlPreviewViewSizer.height(for: urlPreviewData, fittingWidth: urlPreviewMaxWidth) height+=urlPreviewHeight } + // Add read marker view height if needed + // Note: We cannot check if readMarkerView property is set here. Extra non needed height can be added + if sizingView is RoomCellReadMarkerDisplayable, + let roomBubbleCellData = cellData as? RoomBubbleCellData, let firstBubbleComponent = + roomBubbleCellData.getFirstBubbleComponentWithDisplay(), + let eventId = firstBubbleComponent.event.eventId, let room = roomBubbleCellData.mxSession.room(withRoomId: roomBubbleCellData.roomId), let readMarkerEventId = room.accountData.readMarkerEventId, eventId == readMarkerEventId { + + height+=PlainRoomCellLayoutConstants.readMarkerViewHeight + } + return height } } diff --git a/Riot/Modules/Room/Views/BubbleCells/SizableCell/SizingViewHeight.swift b/Riot/Modules/Room/TimelineCells/SizableCell/SizingViewHeight.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/SizableCell/SizingViewHeight.swift rename to Riot/Modules/Room/TimelineCells/SizableCell/SizingViewHeight.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/SizableCell/SizingViewHeightStore.swift b/Riot/Modules/Room/TimelineCells/SizableCell/SizingViewHeightStore.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/SizableCell/SizingViewHeightStore.swift rename to Riot/Modules/Room/TimelineCells/SizableCell/SizingViewHeightStore.swift diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomCellLayoutConstants.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomCellLayoutConstants.swift new file mode 100644 index 000000000..8e483174c --- /dev/null +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomCellLayoutConstants.swift @@ -0,0 +1,62 @@ +// +// Copyright 2021 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 + + +/// Bubble style room cell layout constants +@objcMembers +final class BubbleRoomCellLayoutConstants: NSObject { + + /// Inner content view margins + static let innerContentViewMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 5.0, right: 0) + + // Sender name margins + + static let senderNameLabelMargins: UIEdgeInsets = UIEdgeInsets(top: 10, left: 0, bottom: 0.0, right: 0) + + // Text message bubbles margins from cell content view + + static let outgoingBubbleBackgroundMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 80, bottom: 0, right: 34) + + static let incomingBubbleBackgroundMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 48, bottom: 0, right: 80) + + static let bubbleTextViewInsets: UIEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 12, right: 5) + + static let bubbleCornerRadius: CGFloat = 12.0 + + // Voice message + + static let voiceMessagePlaybackViewRightMargin: CGFloat = 40 + + // Polls + + static let pollBubbleBackgroundInsets: UIEdgeInsets = UIEdgeInsets(top: 2, left: 10, bottom: 0, right: 10) + + // Decoration margins + + // Timestamp margins + + /// Timestamp margins for sticker cell, margin is relative to the image view + static let stickerTimestampViewMargins = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: -27) // Right margin is not reliable there, if the text size change this one is not working anymore + + /// Timestamp margins inside bubble + static let bubbleTimestampViewMargins = UIEdgeInsets(top: 0, left: 0, bottom: 4.0, right: 8.0) + + static let reactionsViewMargins = UIEdgeInsets(top: 4, left: 0, bottom: 0, right: 0) + + static let threadSummaryViewMargins: UIEdgeInsets = UIEdgeInsets(top: 8.0, left: 0, bottom: 5, right: 0) +} diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomCellLayoutUpdater.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomCellLayoutUpdater.swift similarity index 91% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomCellLayoutUpdater.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomCellLayoutUpdater.swift index 4cd22c10b..d1894d48b 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomCellLayoutUpdater.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomCellLayoutUpdater.swift @@ -152,11 +152,10 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating { private func getIncomingMessageTextViewInsets(from bubbleCell: MXKRoomBubbleTableViewCell) -> UIEdgeInsets { - let bubbleBgRightMargin: CGFloat = 45 let messageViewMarginTop: CGFloat = 0 - let messageViewMarginBottom: CGFloat = -0 + let messageViewMarginBottom: CGFloat = 0 let messageViewMarginLeft: CGFloat = 0 - let messageViewMarginRight: CGFloat = 80 + bubbleBgRightMargin + let messageViewMarginRight: CGFloat = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right + BubbleRoomCellLayoutConstants.bubbleTextViewInsets.right let messageViewInsets = UIEdgeInsets(top: messageViewMarginTop, left: messageViewMarginLeft, bottom: messageViewMarginBottom, right: messageViewMarginRight) @@ -180,13 +179,12 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating { } private func getOutgoingMessageTextViewMargins(from bubbleCell: MXKRoomBubbleTableViewCell) -> UIEdgeInsets { - - let innerContentLeftMargin: CGFloat = 57 let messageViewMarginTop: CGFloat = 0 let messageViewMarginBottom: CGFloat = 0 - let messageViewMarginLeft: CGFloat = 80.0 + innerContentLeftMargin - let messageViewMarginRight: CGFloat = 78.0 + let messageViewMarginLeft = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left + BubbleRoomCellLayoutConstants.bubbleTextViewInsets.left + + let messageViewMarginRight = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right + BubbleRoomCellLayoutConstants.bubbleTextViewInsets.right let messageViewInsets = UIEdgeInsets(top: messageViewMarginTop, left: messageViewMarginLeft, bottom: messageViewMarginBottom, right: messageViewMarginRight) @@ -230,10 +228,8 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating { } let contentView = cell.contentView - - // TODO: Use constants - // Same as URL preview - let rightMargin: CGFloat = 34.0 + + let rightMargin = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right if let attachViewLeadingConstraint = cell.attachViewLeadingConstraint { attachViewLeadingConstraint.isActive = false @@ -263,10 +259,9 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating { let contentView = cell.contentView - // TODO: Use constants - let leftMargin: CGFloat = 67 + let leftMargin: CGFloat = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left - let leftConstraint = attachmentView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: -leftMargin) + let leftConstraint = attachmentView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: leftMargin) NSLayoutConstraint.activate([ leftConstraint diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomTimelineCellDecorator.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellDecorator.swift similarity index 85% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomTimelineCellDecorator.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellDecorator.swift index 7feac51fb..f3d80259f 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomTimelineCellDecorator.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellDecorator.swift @@ -44,8 +44,8 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator { let attachmentView = cell.attachmentView { // Prevent overlap with send status icon - let bottomMargin: CGFloat = 20.0 - let rightMargin: CGFloat = -27.0 + let bottomMargin: CGFloat = BubbleRoomCellLayoutConstants.stickerTimestampViewMargins.bottom + let rightMargin: CGFloat = BubbleRoomCellLayoutConstants.stickerTimestampViewMargins.right self.addTimestampLabel(timestampLabel, to: cell, @@ -93,7 +93,7 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator { override func addReactionView(_ reactionsView: BubbleReactionsView, toCell cell: MXKRoomBubbleTableViewCell, cellData: RoomBubbleCellData, contentViewPositionY: CGFloat, upperDecorationView: UIView?) { - if let reactionsDisplayable = cell as? BubbleCellReactionsDisplayable { + if let reactionsDisplayable = cell as? RoomCellReactionsDisplayable { reactionsDisplayable.addReactionsView(reactionsView) return } @@ -105,43 +105,38 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator { let cellContentView = cell.contentView cellContentView.addSubview(reactionsView) - - // TODO: Use constants - let topMargin: CGFloat = 4.0 + + let topMargin: CGFloat = PlainRoomCellLayoutConstants.reactionsViewTopMargin let leftMargin: CGFloat let rightMargin: CGFloat // Incoming message if cellData.isIncoming { - var incomingLeftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin + var incomingLeftMargin = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left if cellData.containsBubbleComponentWithEncryptionBadge { - incomingLeftMargin += RoomBubbleCellLayout.encryptedContentLeftMargin + incomingLeftMargin += PlainRoomCellLayoutConstants.encryptedContentLeftMargin } - leftMargin = incomingLeftMargin - 6.0 + leftMargin = incomingLeftMargin - // TODO: Use constants - let messageViewMarginRight: CGFloat = 42.0 + rightMargin = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right - rightMargin = messageViewMarginRight } else { // Outgoing message reactionsView.alignment = .right - - // TODO: Use constants - var outgoingLeftMargin: CGFloat = 80.0 + + var outgoingLeftMargin = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left if cellData.containsBubbleComponentWithEncryptionBadge { - outgoingLeftMargin += RoomBubbleCellLayout.encryptedContentLeftMargin + outgoingLeftMargin += PlainRoomCellLayoutConstants.encryptedContentLeftMargin } leftMargin = outgoingLeftMargin - - // TODO: Use constants - rightMargin = 33 + + rightMargin = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right } let leadingConstraint = reactionsView.leadingAnchor.constraint(equalTo: cellContentView.leadingAnchor, constant: leftMargin) @@ -185,24 +180,21 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator { // Incoming message if cellData.isIncoming { - var leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin + var leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin if cellData.containsBubbleComponentWithEncryptionBadge { - leftMargin += RoomBubbleCellLayout.encryptedContentLeftMargin + leftMargin += PlainRoomCellLayoutConstants.encryptedContentLeftMargin } - leftMargin-=5.0 - leadingOrTrailingConstraint = urlPreviewView.leadingAnchor.constraint(equalTo: cellContentView.leadingAnchor, constant: leftMargin) } else { // Outgoing message - // TODO: Use constants - let rightMargin: CGFloat = 34.0 + let rightMargin: CGFloat = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right leadingOrTrailingConstraint = urlPreviewView.trailingAnchor.constraint(equalTo: cellContentView.trailingAnchor, constant: -rightMargin) } - let topMargin = contentViewPositionY + RoomBubbleCellLayout.urlPreviewViewTopMargin + RoomBubbleCellLayout.reactionsViewTopMargin + let topMargin = contentViewPositionY + PlainRoomCellLayoutConstants.urlPreviewViewTopMargin + PlainRoomCellLayoutConstants.reactionsViewTopMargin // Set the preview view's origin NSLayoutConstraint.activate([ @@ -218,7 +210,7 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator { contentViewPositionY: CGFloat, upperDecorationView: UIView?) { - if let threadSummaryDisplayable = cell as? BubbleCellThreadSummaryDisplayable { + if let threadSummaryDisplayable = cell as? RoomCellThreadSummaryDisplayable { threadSummaryDisplayable.addThreadSummaryView(threadSummaryView) } else { @@ -238,14 +230,12 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator { // Incoming message if cellData.isIncoming { - leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin + leftMargin = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left if cellData.containsBubbleComponentWithEncryptionBadge { - leftMargin += RoomBubbleCellLayout.encryptedContentLeftMargin + leftMargin += PlainRoomCellLayoutConstants.encryptedContentLeftMargin } - leftMargin-=5.0 - - rightMargin = RoomBubbleCellLayout.reactionsViewRightMargin + rightMargin = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right leadingConstraint = threadSummaryView.leadingAnchor.constraint(equalTo: cellContentView.leadingAnchor, constant: leftMargin) @@ -253,10 +243,9 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator { constant: -rightMargin) } else { // Outgoing message - - // TODO: Use constants - leftMargin = 80.0 - rightMargin = 34.0 + + leftMargin = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left + rightMargin = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right leadingConstraint = threadSummaryView.leadingAnchor.constraint(greaterThanOrEqualTo: cellContentView.leadingAnchor, constant: leftMargin) @@ -264,7 +253,8 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator { constant: -rightMargin) } - let topMargin = RoomBubbleCellLayout.threadSummaryViewTopMargin + 15.0 + let topMargin = PlainRoomCellLayoutConstants.threadSummaryViewTopMargin + let height = ThreadSummaryView.contentViewHeight(forThread: threadSummaryView.thread, fitting: cellData.maxTextViewWidth) @@ -376,8 +366,8 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator { to cell: MXKRoomBubbleTableViewCell, on containerView: UIView, constrainingView: UIView, - rightMargin: CGFloat = 8.0, - bottomMargin: CGFloat = 4.0) { + rightMargin: CGFloat = BubbleRoomCellLayoutConstants.bubbleTimestampViewMargins.right, + bottomMargin: CGFloat = BubbleRoomCellLayoutConstants.bubbleTimestampViewMargins.bottom) { timestampLabel.translatesAutoresizingMaskIntoConstraints = false cell.addTemporarySubview(timestampLabel) diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomTimelineCellProvider.h b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomTimelineCellProvider.h rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.h diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineCellProvider.m diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomTimelineStyle.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineStyle.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/BubbleRoomTimelineStyle.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/BubbleRoomTimelineStyle.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/BubbleIncomingRoomCellProtocol.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/BubbleIncomingRoomCellProtocol.swift similarity index 66% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/BubbleIncomingRoomCellProtocol.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/BubbleIncomingRoomCellProtocol.swift index 32597da8e..f166e6635 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/BubbleIncomingRoomCellProtocol.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/BubbleIncomingRoomCellProtocol.swift @@ -24,7 +24,7 @@ extension BubbleIncomingRoomCellProtocol { // MARK: - Public func setupBubbleDecorations() { - self.bubbleCellContentView?.decorationViewsAlignment = .left + self.roomCellContentView?.decorationViewsAlignment = .left self.setupDecorationConstraints() } @@ -39,16 +39,16 @@ extension BubbleIncomingRoomCellProtocol { private func setupReactionsContentViewContraints() { - self.bubbleCellContentView?.reactionsContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right + self.roomCellContentView?.reactionsContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right } private func setupThreadSummaryViewContentViewContraints() { - self.bubbleCellContentView?.threadSummaryContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right + self.roomCellContentView?.threadSummaryContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right - self.bubbleCellContentView?.threadSummaryContentViewBottomConstraint.constant = BubbleRoomCellLayoutConstants.threadSummaryViewMargins.bottom + self.roomCellContentView?.threadSummaryContentViewBottomConstraint.constant = BubbleRoomCellLayoutConstants.threadSummaryViewMargins.bottom } private func setupURLPreviewContentViewContraints() { - self.bubbleCellContentView?.urlPreviewContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right + self.roomCellContentView?.urlPreviewContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/BubbleOutgoingRoomCellProtocol.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/BubbleOutgoingRoomCellProtocol.swift similarity index 59% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/BubbleOutgoingRoomCellProtocol.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/BubbleOutgoingRoomCellProtocol.swift index 2c548f80b..435074a57 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/BubbleOutgoingRoomCellProtocol.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/BubbleOutgoingRoomCellProtocol.swift @@ -26,7 +26,7 @@ extension BubbleOutgoingRoomCellProtocol { // MARK: - Public func setupBubbleDecorations() { - self.bubbleCellContentView?.decorationViewsAlignment = .right + self.roomCellContentView?.decorationViewsAlignment = .right self.setupDecorationConstraints() } @@ -40,72 +40,72 @@ extension BubbleOutgoingRoomCellProtocol { } private func setupReactionsContentViewContraints() { - guard let bubbleCellContentView = self.bubbleCellContentView, let reactionsContentView = bubbleCellContentView.reactionsContentView, let reactionsContainerView = bubbleCellContentView.reactionsContainerView else { + guard let roomCellContentView = self.roomCellContentView, let reactionsContentView = roomCellContentView.reactionsContentView, let reactionsContainerView = roomCellContentView.reactionsContainerView else { return } // Remove leading constraint - bubbleCellContentView.reactionsContentViewLeadingConstraint.isActive = false - bubbleCellContentView.reactionsContentViewLeadingConstraint = nil + roomCellContentView.reactionsContentViewLeadingConstraint.isActive = false + roomCellContentView.reactionsContentViewLeadingConstraint = nil // Setup new leading constraint let leadingConstraint = self.setupDecorationViewLeadingContraint(containerView: reactionsContainerView, contentView: reactionsContentView) - bubbleCellContentView.reactionsContentViewLeadingConstraint = leadingConstraint + roomCellContentView.reactionsContentViewLeadingConstraint = leadingConstraint // Update trailing constraint - bubbleCellContentView.reactionsContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right + roomCellContentView.reactionsContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right } private func setupThreadSummaryViewContentViewContraints() { - guard let bubbleCellContentView = self.bubbleCellContentView, let threadSummaryContentView = bubbleCellContentView.threadSummaryContentView, let threadSummaryContainerView = bubbleCellContentView.threadSummaryContainerView else { + guard let roomCellContentView = self.roomCellContentView, let threadSummaryContentView = roomCellContentView.threadSummaryContentView, let threadSummaryContainerView = roomCellContentView.threadSummaryContainerView else { return } // Remove leading constraint - bubbleCellContentView.threadSummaryContentViewLeadingConstraint.isActive = false - bubbleCellContentView.threadSummaryContentViewLeadingConstraint = nil + roomCellContentView.threadSummaryContentViewLeadingConstraint.isActive = false + roomCellContentView.threadSummaryContentViewLeadingConstraint = nil // Setup new leading constraint let leadingConstraint = self.setupDecorationViewLeadingContraint(containerView: threadSummaryContainerView, contentView: threadSummaryContentView) - bubbleCellContentView.threadSummaryContentViewLeadingConstraint = leadingConstraint + roomCellContentView.threadSummaryContentViewLeadingConstraint = leadingConstraint // Update trailing constraint - bubbleCellContentView.threadSummaryContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right + roomCellContentView.threadSummaryContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right // Update bottom constraint - bubbleCellContentView.threadSummaryContentViewBottomConstraint.constant = BubbleRoomCellLayoutConstants.threadSummaryViewMargins.bottom + roomCellContentView.threadSummaryContentViewBottomConstraint.constant = BubbleRoomCellLayoutConstants.threadSummaryViewMargins.bottom } private func setupURLPreviewContentViewContraints() { - guard let bubbleCellContentView = self.bubbleCellContentView, let contentView = bubbleCellContentView.urlPreviewContentView, let containerView = bubbleCellContentView.urlPreviewContainerView else { + guard let roomCellContentView = self.roomCellContentView, let contentView = roomCellContentView.urlPreviewContentView, let containerView = roomCellContentView.urlPreviewContainerView else { return } // Remove leading constraint - bubbleCellContentView.urlPreviewContentViewLeadingConstraint.isActive = false - bubbleCellContentView.urlPreviewContentViewLeadingConstraint = nil + roomCellContentView.urlPreviewContentViewLeadingConstraint.isActive = false + roomCellContentView.urlPreviewContentViewLeadingConstraint = nil // Setup new leading constraint let leadingConstraint = self.setupDecorationViewLeadingContraint(containerView: containerView, contentView: contentView) - bubbleCellContentView.urlPreviewContentViewLeadingConstraint = leadingConstraint + roomCellContentView.urlPreviewContentViewLeadingConstraint = leadingConstraint // Update trailing constraint - bubbleCellContentView.urlPreviewContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right + roomCellContentView.urlPreviewContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right } private func setupDecorationViewLeadingContraint(containerView: UIView, diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/BubbleRoomCellProtocol.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/BubbleRoomCellProtocol.swift similarity index 91% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/BubbleRoomCellProtocol.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/BubbleRoomCellProtocol.swift index 8567af631..8ae817999 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/BubbleRoomCellProtocol.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/BubbleRoomCellProtocol.swift @@ -18,7 +18,7 @@ import Foundation protocol BubbleRoomCellProtocol { - var bubbleCellContentView: BubbleCellContentView? { get } + var roomCellContentView: RoomCellContentView? { get } func setupBubbleDecorations() } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift similarity index 86% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift index 32dfe9612..40b485fa0 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift @@ -16,7 +16,7 @@ import UIKit -class FileWithoutThumbnailBaseBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplayable { +class FileWithoutThumbnailBaseBubbleCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable { weak var fileAttachementView: FileWithoutThumbnailCellContentView? @@ -31,9 +31,9 @@ class FileWithoutThumbnailBaseBubbleCell: SizableBaseBubbleCell, BubbleCellReact override func setupViews() { super.setupViews() - bubbleCellContentView?.backgroundColor = .clear + roomCellContentView?.backgroundColor = .clear - guard let contentView = bubbleCellContentView?.innerContentView else { + guard let contentView = roomCellContentView?.innerContentView else { return } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift similarity index 87% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift index ce0471dad..1d2058a27 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift @@ -19,13 +19,6 @@ import Reusable final class FileWithoutThumbnailCellContentView: UIView, NibLoadable { - // MARK: - Constants - - private enum Constants { - // TODO: Reuse constants, same as bubble bg - static let cornerRadius: CGFloat = 12.0 - } - // MARK: - Properties // MARK: Outlets @@ -61,7 +54,7 @@ final class FileWithoutThumbnailCellContentView: UIView, NibLoadable { override func layoutSubviews() { super.layoutSubviews() - self.layer.cornerRadius = Constants.cornerRadius + self.layer.cornerRadius = BubbleRoomCellLayoutConstants.bubbleCornerRadius } // MARK: - Public diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.xib b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.xib rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingBubbleCell.swift similarity index 73% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingBubbleCell.swift index bcc52efa8..8a768d1de 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingBubbleCell.swift @@ -21,14 +21,10 @@ class FileWithoutThumbnailIncomingBubbleCell: FileWithoutThumbnailBaseBubbleCell override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = true + roomCellContentView?.showSenderInfo = true - // TODO: Use constants - let messageViewMarginRight: CGFloat = 80 - let messageLeftMargin: CGFloat = 48 - - bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight - bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin + roomCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left + roomCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right self.setupBubbleDecorations() } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithPaginationTitleBubbleCell.swift index 518f4fc95..7ef12d6d4 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithPaginationTitleBubbleCell.swift @@ -21,6 +21,6 @@ class FileWithoutThumbnailIncomingWithPaginationTitleBubbleCell: FileWithoutThum override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithoutSenderInfoBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithoutSenderInfoBubbleCell.swift index 1db1510ca..efc1d471b 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Incoming/FileWithoutThumbnailIncomingWithoutSenderInfoBubbleCell.swift @@ -21,6 +21,6 @@ class FileWithoutThumbnailIncomingWithoutSenderInfoBubbleCell: FileWithoutThumbn override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithPaginationTitleBubbleCell.swift index 0cf7acb6c..1a276f1f2 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithPaginationTitleBubbleCell.swift @@ -21,6 +21,6 @@ class FileWithoutThumbnailOutoingWithPaginationTitleBubbleCell: FileWithoutThumb override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell.swift similarity index 73% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell.swift index 4f2283770..d9742637b 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Outgoing/FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell.swift @@ -21,15 +21,10 @@ class FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell: FileWithoutThumbna override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false - // TODO: Use constants - // Same as outgoing message - let rightMargin: CGFloat = 34.0 - let leftMargin: CGFloat = 80.0 - - bubbleCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin - bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin + roomCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left + roomCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right self.setupBubbleDecorations() } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingBubbleCell.swift similarity index 71% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingBubbleCell.swift index 4a7fb08b5..769b2b555 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingBubbleCell.swift @@ -21,12 +21,8 @@ class LocationIncomingBubbleCell: LocationBubbleCell, BubbleIncomingRoomCellProt override func setupViews() { super.setupViews() - // TODO: Use constants - let messageViewMarginRight: CGFloat = 80 - let messageLeftMargin: CGFloat = 48 - - bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight - bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin + roomCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left + roomCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right self.setupBubbleDecorations() } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithPaginationTitleBubbleCell.swift index 77b1e17e1..f08217b3c 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithPaginationTitleBubbleCell.swift @@ -20,6 +20,6 @@ class LocationIncomingWithPaginationTitleBubbleCell: LocationIncomingBubbleCell override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithoutSenderInfoBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithoutSenderInfoBubbleCell.swift index 880e7c286..5cddbb3c7 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Incoming/LocationIncomingWithoutSenderInfoBubbleCell.swift @@ -20,6 +20,6 @@ class LocationIncomingWithoutSenderInfoBubbleCell: LocationIncomingBubbleCell { override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithPaginationTitleBubbleCell.swift index f1158043f..0199dea51 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithPaginationTitleBubbleCell.swift @@ -20,6 +20,6 @@ class LocationOutgoingWithPaginationTitleBubbleCell: LocationOutgoingWithoutSend override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithoutSenderInfoBubbleCell.swift similarity index 71% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithoutSenderInfoBubbleCell.swift index ef2061224..831a11a94 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Location/Outgoing/LocationOutgoingWithoutSenderInfoBubbleCell.swift @@ -21,11 +21,10 @@ class LocationOutgoingWithoutSenderInfoBubbleCell: LocationBubbleCell, BubbleOut override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false - bubbleCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right - - bubbleCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left + roomCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left + roomCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right self.setupBubbleDecorations() } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/MXKRoomBubbleTableViewCell+BubbleStyle.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/MXKRoomBubbleTableViewCell+BubbleStyle.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/MXKRoomBubbleTableViewCell+BubbleStyle.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/MXKRoomBubbleTableViewCell+BubbleStyle.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingBubbleCell.swift similarity index 66% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingBubbleCell.swift index c71810ef5..b8d92394e 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingBubbleCell.swift @@ -21,13 +21,11 @@ class PollIncomingBubbleCell: PollBaseBubbleCell, BubbleIncomingRoomCellProtocol override func setupViews() { super.setupViews() - // TODO: Use constants - let bubbleBackgroundSideMargin: CGFloat = 10 - let messageViewMarginRight: CGFloat = 80 + bubbleBackgroundSideMargin - let messageLeftMargin: CGFloat = 48 + bubbleBackgroundSideMargin + let leftMargin: CGFloat = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left + BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.left + let rightMargin: CGFloat = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right + BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.right - bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight - bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin + roomCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin + roomCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin self.setupBubbleDecorations() } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithPaginationTitleBubbleCell.swift index b797637a0..252308a14 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithPaginationTitleBubbleCell.swift @@ -21,7 +21,7 @@ class PollIncomingWithPaginationTitleBubbleCell: PollIncomingBubbleCell { override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithoutSenderInfoBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithoutSenderInfoBubbleCell.swift index b9cdbd633..5cb6dad1f 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Incoming/PollIncomingWithoutSenderInfoBubbleCell.swift @@ -21,7 +21,7 @@ class PollIncomingWithoutSenderInfoBubbleCell: PollIncomingBubbleCell { override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithPaginationTitleBubbleCell.swift index c8a9453e9..61b6decd0 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithPaginationTitleBubbleCell.swift @@ -21,7 +21,7 @@ class PollOutgoingWithPaginationTitleBubbleCell: PollOutgoingWithoutSenderInfoBu override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithoutSenderInfoBubbleCell.swift similarity index 64% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithoutSenderInfoBubbleCell.swift index b16150d6c..ce644496d 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/Outgoing/PollOutgoingWithoutSenderInfoBubbleCell.swift @@ -21,16 +21,13 @@ class PollOutgoingWithoutSenderInfoBubbleCell: PollBaseBubbleCell, BubbleOutgoin override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false + + let leftMargin: CGFloat = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left + BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.left + let rightMargin: CGFloat = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right + BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.right - // TODO: Use constants - // Same as outgoing message - let bubbleBackgroundSideMargin: CGFloat = 10 - let rightMargin: CGFloat = 34.0 + bubbleBackgroundSideMargin - let leftMargin: CGFloat = 80.0 + bubbleBackgroundSideMargin - - bubbleCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin - bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin + roomCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin + roomCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin self.setupBubbleDecorations() } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/PollBaseBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/PollBaseBubbleCell.swift similarity index 88% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/PollBaseBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/PollBaseBubbleCell.swift index 99a6afe56..769eb82e5 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/Poll/PollBaseBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/Poll/PollBaseBubbleCell.swift @@ -57,10 +57,10 @@ class PollBaseBubbleCell: PollBubbleCell { private func addBubbleBackgroundView(_ bubbleBackgroundView: RoomMessageBubbleBackgroundView, to pollView: UIView) { - let topMargin: CGFloat = 2.0 - let leftMargin: CGFloat = 10.0 - let rightMargin: CGFloat = 10.0 // Add extra space for timestamp - let bottomMargin: CGFloat = 0.0 // + let topMargin = BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.top + let leftMargin = BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.left + let rightMargin = BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.right + let bottomMargin = BubbleRoomCellLayoutConstants.pollBubbleBackgroundInsets.bottom let topAnchor = pollView.topAnchor let leadingAnchor = pollView.leadingAnchor @@ -77,12 +77,12 @@ class PollBaseBubbleCell: PollBubbleCell { private func setupBubbleBackgroundView() { let bubbleBackgroundView = RoomMessageBubbleBackgroundView() - self.bubbleCellContentView?.insertSubview(bubbleBackgroundView, at: 0) + self.roomCellContentView?.insertSubview(bubbleBackgroundView, at: 0) } // The extension property MXKRoomBubbleTableViewCell.messageBubbleBackgroundView is not working there even by doing recursion private func getBubbleBackgroundView() -> RoomMessageBubbleBackgroundView? { - guard let contentView = self.bubbleCellContentView else { + guard let contentView = self.roomCellContentView else { return nil } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBaseBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBaseBubbleCell.swift similarity index 50% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBaseBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBaseBubbleCell.swift index f64e0d873..5a7d8984b 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBaseBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBaseBubbleCell.swift @@ -16,7 +16,7 @@ import UIKit -class TextMessageBaseBubbleCell: SizableBaseBubbleCell, RoomCellURLPreviewDisplayable, BubbleCellReactionsDisplayable, BubbleCellThreadSummaryDisplayable, BubbleCellReadReceiptsDisplayable { +class TextMessageBaseBubbleCell: SizableBaseRoomCell, RoomCellURLPreviewDisplayable, RoomCellReactionsDisplayable, RoomCellThreadSummaryDisplayable, RoomCellReadReceiptsDisplayable, RoomCellReadMarkerDisplayable { // MARK: - Properties @@ -32,29 +32,24 @@ class TextMessageBaseBubbleCell: SizableBaseBubbleCell, RoomCellURLPreviewDispla // MARK: - Overrides override func setupViews() { - super.setupViews() - bubbleCellContentView?.backgroundColor = .clear - - guard let contentView = bubbleCellContentView?.innerContentView else { - return - } - - // TODO: Use constants - - let messageLeftMargin: CGFloat = 48 - - bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin - - self.bubbleCellContentView?.innerContentViewBottomContraint.constant = 5.0 - - self.bubbleCellContentView?.innerContentViewTrailingConstraint.constant = 34.0 + roomCellContentView?.backgroundColor = .clear + roomCellContentView?.innerContentViewBottomContraint.constant = BubbleRoomCellLayoutConstants.innerContentViewMargins.bottom + roomCellContentView?.userNameLabelBottomConstraint.constant = BubbleRoomCellLayoutConstants.senderNameLabelMargins.bottom + let textMessageContentView = TextMessageBubbleCellContentView.instantiate() - - contentView.vc_addSubViewMatchingParent(textMessageContentView) + + roomCellContentView?.innerContentView.vc_addSubViewMatchingParent(textMessageContentView) self.textMessageContentView = textMessageContentView + + // Setup messageTextView property first before running `setupMessageTextView` method + super.setupViews() + } + + override func setupMessageTextViewLongPressGesture() { + // Do nothing, otherwise default setup prevent link tap } override func update(theme: Theme) { @@ -63,32 +58,6 @@ class TextMessageBaseBubbleCell: SizableBaseBubbleCell, RoomCellURLPreviewDispla if let messageTextView = self.messageTextView { messageTextView.tintColor = theme.tintColor } - -// self.setupDebug() - } - - // MARK: - Private - - private func setupDebug() { - - self.bubbleCellContentView?.innerContentView.backgroundColor = .yellow - - self.bubbleCellContentView?.layer.borderWidth = 1.0 - self.bubbleCellContentView?.layer.borderColor = UIColor.red.cgColor - - self.textMessageContentView?.layer.borderColor = UIColor.blue.cgColor - self.textMessageContentView?.layer.borderWidth = 1.0 - - - self.bubbleCellContentView?.readReceiptsContainerView.layer.borderColor = UIColor.yellow.cgColor - self.bubbleCellContentView?.readReceiptsContainerView.layer.borderWidth = 1.0 - - self.bubbleCellContentView?.reactionsContainerView.layer.borderColor = UIColor.blue.cgColor - self.bubbleCellContentView?.reactionsContainerView.layer.borderWidth = 1.0 - self.bubbleCellContentView?.reactionsContentView.backgroundColor = .blue - - self.bubbleCellContentView?.threadSummaryContainerView.layer.borderColor = UIColor.purple.cgColor - self.bubbleCellContentView?.threadSummaryContainerView.layer.borderWidth = 1.0 } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.xib b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.xib similarity index 90% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.xib rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.xib index 90fba0291..05341b741 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.xib +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Common/TextMessageBubbleCellContentView.xib @@ -14,12 +14,15 @@ - + - + + + + @@ -27,10 +30,10 @@ - + - + @@ -39,7 +42,7 @@ - + diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingBubbleCell.swift similarity index 69% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingBubbleCell.swift index 7d133e2f2..3c54e5640 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingBubbleCell.swift @@ -23,7 +23,7 @@ class TextMessageIncomingBubbleCell: TextMessageBaseBubbleCell, BubbleIncomingRo override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = true + roomCellContentView?.showSenderInfo = true self.setupBubbleConstraints() self.setupBubbleDecorations() @@ -39,10 +39,8 @@ class TextMessageIncomingBubbleCell: TextMessageBaseBubbleCell, BubbleIncomingRo private func setupBubbleConstraints() { - self.textMessageContentView?.bubbleBackgroundViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left + self.roomCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left - let innerContentViewTrailingMargin = self.bubbleCellContentView?.innerContentViewTrailingConstraint.constant ?? 0 - - self.textMessageContentView?.bubbleBackgroundViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right - innerContentViewTrailingMargin + self.roomCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleBubbleCell.swift index 7b47ab409..2db0ee1d5 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleBubbleCell.swift @@ -21,6 +21,6 @@ class TextMessageIncomingWithPaginationTitleBubbleCell: TextMessageIncomingBubbl override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.swift index 8f2c5fcc6..c13c6856f 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.swift @@ -21,6 +21,6 @@ class TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell: TextMes override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderName = false + roomCellContentView?.showSenderName = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderInfoBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderInfoBubbleCell.swift index abeed3435..5018b34fd 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderInfoBubbleCell.swift @@ -21,6 +21,6 @@ class TextMessageIncomingWithoutSenderInfoBubbleCell: TextMessageIncomingBubbleC override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderNameBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderNameBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderNameBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderNameBubbleCell.swift index 15bbacf0c..f1724e46f 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderNameBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Incoming/TextMessageIncomingWithoutSenderNameBubbleCell.swift @@ -21,6 +21,6 @@ class TextMessageIncomingWithoutSenderNameBubbleCell: TextMessageIncomingBubbleC override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderName = false + roomCellContentView?.showSenderName = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleBubbleCell.swift index 1ffb87c80..18c7420b0 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleBubbleCell.swift @@ -21,6 +21,6 @@ class TextMessageOutgoingWithPaginationTitleBubbleCell: TextMessageOutgoingWitho override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.swift index dd9a345e3..7beb377c1 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.swift @@ -19,6 +19,6 @@ class TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell: TextMes override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderName = false + roomCellContentView?.showSenderName = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift similarity index 86% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift index 992d83ad9..de15e91d3 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/TextMessage/Outgoing/TextMessageOutgoingWithoutSenderInfoBubbleCell.swift @@ -23,7 +23,7 @@ class TextMessageOutgoingWithoutSenderInfoBubbleCell: TextMessageBaseBubbleCell, override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false self.setupBubbleConstraints() self.setupBubbleDecorations() @@ -39,6 +39,9 @@ class TextMessageOutgoingWithoutSenderInfoBubbleCell: TextMessageBaseBubbleCell, private func setupBubbleConstraints() { + self.roomCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left + self.roomCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right + guard let containerView = self.textMessageContentView, let bubbleBackgroundView = containerView.bubbleBackgroundView else { return } @@ -57,7 +60,7 @@ class TextMessageOutgoingWithoutSenderInfoBubbleCell: TextMessageBaseBubbleCell, // Setup new constraints - let leadingConstraint = bubbleBackgroundView.leadingAnchor.constraint(greaterThanOrEqualTo: containerView.leadingAnchor, constant: BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left) + let leadingConstraint = bubbleBackgroundView.leadingAnchor.constraint(greaterThanOrEqualTo: containerView.leadingAnchor) let trailingConstraint = bubbleBackgroundView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 0) diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingBubbleCell.swift similarity index 73% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingBubbleCell.swift index febaf8720..c1f2e477c 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingBubbleCell.swift @@ -20,16 +20,11 @@ class VoiceMessageIncomingBubbleCell: VoiceMessageBubbleCell, BubbleIncomingRoom override func setupViews() { super.setupViews() + + roomCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left + roomCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right - // TODO: Use constants - let messageViewMarginRight: CGFloat = 80 - let messageLeftMargin: CGFloat = 48 - let playbackViewRightMargin: CGFloat = 40 - - bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight - bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin - - playbackController.playbackView.stackViewTrailingContraint.constant = playbackViewRightMargin + playbackController.playbackView.stackViewTrailingContraint.constant = BubbleRoomCellLayoutConstants.voiceMessagePlaybackViewRightMargin self.setupBubbleDecorations() } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithPaginationTitleBubbleCell.swift index bd6f1fb38..d408e2abf 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithPaginationTitleBubbleCell.swift @@ -21,6 +21,6 @@ class VoiceMessageIncomingWithPaginationTitleBubbleCell: VoiceMessageIncomingBub override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithoutSenderInfoBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithoutSenderInfoBubbleCell.swift index f0d5385e6..191a16127 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Incoming/VoiceMessageIncomingWithoutSenderInfoBubbleCell.swift @@ -20,6 +20,6 @@ class VoiceMessageIncomingWithoutSenderInfoBubbleCell: VoiceMessageIncomingBubbl override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithPaginationTitleBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithPaginationTitleBubbleCell.swift index 9f77e7843..e80bb9b2e 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithPaginationTitleBubbleCell.swift @@ -21,6 +21,6 @@ class VoiceMessageOutgoingWithPaginationTitleBubbleCell: VoiceMessageOutgoingWit override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithoutSenderInfoBubbleCell.swift similarity index 71% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithoutSenderInfoBubbleCell.swift index 5b0bffd7e..2b76d0188 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/VoiceMessage/Outgoing/VoiceMessageOutgoingWithoutSenderInfoBubbleCell.swift @@ -21,18 +21,12 @@ class VoiceMessageOutgoingWithoutSenderInfoBubbleCell: VoiceMessageBubbleCell, B override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false - - // TODO: Use constants - // Same as outgoing message - let rightMargin: CGFloat = 34.0 - let leftMargin: CGFloat = 80.0 - let playbackViewRightMargin: CGFloat = 40 - - bubbleCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin - bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin + roomCellContentView?.showSenderInfo = false + + roomCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left + roomCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right - playbackController.playbackView.stackViewTrailingContraint.constant = playbackViewRightMargin + playbackController.playbackView.stackViewTrailingContraint.constant = BubbleRoomCellLayoutConstants.voiceMessagePlaybackViewRightMargin self.setupBubbleDecorations() } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/RoomMessageBubbleBackgroundView.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/RoomMessageBubbleBackgroundView.swift similarity index 91% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/RoomMessageBubbleBackgroundView.swift rename to Riot/Modules/Room/TimelineCells/Styles/Bubble/RoomMessageBubbleBackgroundView.swift index da79211b2..45ad8e1be 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Bubble/RoomMessageBubbleBackgroundView.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/RoomMessageBubbleBackgroundView.swift @@ -19,12 +19,6 @@ import UIKit class RoomMessageBubbleBackgroundView: UIView { - // MARK: - Constant - - private enum Constants { - static let cornerRadius: CGFloat = 12.0 - } - // MARK: - Properties private var heightConstraint: NSLayoutConstraint? @@ -49,7 +43,7 @@ class RoomMessageBubbleBackgroundView: UIView { private func commonInit() { self.translatesAutoresizingMaskIntoConstraints = false self.layer.masksToBounds = true - self.layer.cornerRadius = Constants.cornerRadius + self.layer.cornerRadius = BubbleRoomCellLayoutConstants.bubbleCornerRadius } // MARK: - Public @@ -82,7 +76,7 @@ extension RoomMessageBubbleBackgroundView: TimestampDisplayable { self.removeTimestampView() - self.addTimestampView(timestampView, rightMargin: 8.0, bottomMargin: 4.0) + self.addTimestampView(timestampView, rightMargin: BubbleRoomCellLayoutConstants.bubbleTimestampViewMargins.right, bottomMargin: BubbleRoomCellLayoutConstants.bubbleTimestampViewMargins.bottom) self.timestampView = timestampView } diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Clear/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.m similarity index 69% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.m index ecb9eaa3a..565a23c0e 100644 --- a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.m +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.m @@ -15,7 +15,17 @@ */ #import "MXKRoomIncomingAttachmentBubbleCell.h" +#import "GeneratedInterface-Swift.h" @implementation MXKRoomIncomingAttachmentBubbleCell +- (void)setupViews +{ + [super setupViews]; + + RoomTimelineConfiguration *timelineConfiguration = [RoomTimelineConfiguration shared]; + + [timelineConfiguration.currentStyle.cellLayoutUpdater setupLayoutForIncomingFileAttachmentCell:self]; +} + @end diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Common/MXKRoomIncomingAttachmentWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Incoming/Encrypted/RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithPaginationTitleWithoutSenderNameBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Clear/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Common/MXKRoomOutgoingAttachmentWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileAttachment/Outgoing/Encrypted/RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/Location/LocationBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationBubbleCell.swift similarity index 87% rename from Riot/Modules/Room/Views/BubbleCells/Location/LocationBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationBubbleCell.swift index a0c375569..1e0afe211 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Location/LocationBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationBubbleCell.swift @@ -16,7 +16,7 @@ import Foundation -class LocationBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplayable { +class LocationBubbleCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable { private var locationView: RoomTimelineLocationView! @@ -55,12 +55,12 @@ class LocationBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplayable override func setupViews() { super.setupViews() - bubbleCellContentView?.backgroundColor = .clear - bubbleCellContentView?.showSenderInfo = true - bubbleCellContentView?.showPaginationTitle = false + roomCellContentView?.backgroundColor = .clear + roomCellContentView?.showSenderInfo = true + roomCellContentView?.showPaginationTitle = false guard #available(iOS 14.0, *), - let contentView = bubbleCellContentView?.innerContentView else { + let contentView = roomCellContentView?.innerContentView else { return } diff --git a/Riot/Modules/Room/Views/BubbleCells/Location/LocationWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationWithPaginationTitleBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/Views/BubbleCells/Location/LocationWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationWithPaginationTitleBubbleCell.swift index e3f6dd84f..690bf10e1 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Location/LocationWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationWithPaginationTitleBubbleCell.swift @@ -20,6 +20,6 @@ class LocationWithPaginationTitleBubbleCell: LocationBubbleCell { override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Location/LocationWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationWithoutSenderInfoBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Location/LocationWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationWithoutSenderInfoBubbleCell.swift index 1329221dd..cda0d9899 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Location/LocationWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Location/LocationWithoutSenderInfoBubbleCell.swift @@ -20,6 +20,6 @@ class LocationWithoutSenderInfoBubbleCell: LocationBubbleCell { override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Poll/PollBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollBubbleCell.swift similarity index 82% rename from Riot/Modules/Room/Views/BubbleCells/Poll/PollBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollBubbleCell.swift index c1dc7a770..51dd798d8 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Poll/PollBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollBubbleCell.swift @@ -16,7 +16,7 @@ import Foundation -class PollBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplayable { +class PollBubbleCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable { private var pollView: UIView? private var event: MXEvent? @@ -25,7 +25,7 @@ class PollBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplayable { super.render(cellData) guard #available(iOS 14.0, *), - let contentView = bubbleCellContentView?.innerContentView, + let contentView = roomCellContentView?.innerContentView, let bubbleData = cellData as? RoomBubbleCellData, let event = bubbleData.events.last, event.eventType == __MXEventType.pollStart, @@ -40,9 +40,9 @@ class PollBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplayable { override func setupViews() { super.setupViews() - bubbleCellContentView?.backgroundColor = .clear - bubbleCellContentView?.showSenderInfo = true - bubbleCellContentView?.showPaginationTitle = false + roomCellContentView?.backgroundColor = .clear + roomCellContentView?.showSenderInfo = true + roomCellContentView?.showPaginationTitle = false } // The normal flow for tapping on cell content views doesn't work for bubbles without attributed strings @@ -62,4 +62,4 @@ class PollBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplayable { } } -extension PollBubbleCell: BubbleCellThreadSummaryDisplayable {} +extension PollBubbleCell: RoomCellThreadSummaryDisplayable {} diff --git a/Riot/Modules/Room/Views/BubbleCells/Poll/PollWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollWithPaginationTitleBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/Views/BubbleCells/Poll/PollWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollWithPaginationTitleBubbleCell.swift index de6e92041..969c7864d 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Poll/PollWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollWithPaginationTitleBubbleCell.swift @@ -21,7 +21,7 @@ class PollWithPaginationTitleBubbleCell: PollBubbleCell { override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Poll/PollWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollWithoutSenderInfoBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Poll/PollWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollWithoutSenderInfoBubbleCell.swift index 880ef0cd4..2663996a8 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Poll/PollWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Poll/PollWithoutSenderInfoBubbleCell.swift @@ -21,7 +21,7 @@ class PollWithoutSenderInfoBubbleCell: PollBubbleCell { override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Sticker/RoomSelectedStickerBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Sticker/RoomSelectedStickerBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Sticker/RoomSelectedStickerBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Sticker/RoomSelectedStickerBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/Sticker/RoomSelectedStickerBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Sticker/RoomSelectedStickerBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Sticker/RoomSelectedStickerBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Sticker/RoomSelectedStickerBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/Sticker/RoomSelectedStickerBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Sticker/RoomSelectedStickerBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Sticker/RoomSelectedStickerBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/Sticker/RoomSelectedStickerBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Clear/RoomIncomingTextMsgWithoutSenderNameBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Common/MXKRoomIncomingTextMsgWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Incoming/Encrypted/RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Clear/RoomOutgoingTextMsgWithoutSenderNameBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Common/MXKRoomOutgoingTextMsgWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.h diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.m diff --git a/Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.xib b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.xib similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.xib rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/TextMessage/Outgoing/Encrypted/RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.xib diff --git a/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceMessage/VoiceMessageBubbleCell.swift similarity index 85% rename from Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceMessage/VoiceMessageBubbleCell.swift index 55c7f2487..d11c0787d 100644 --- a/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceMessage/VoiceMessageBubbleCell.swift @@ -16,7 +16,7 @@ import Foundation -class VoiceMessageBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplayable { +class VoiceMessageBubbleCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable { private(set) var playbackController: VoiceMessagePlaybackController! @@ -41,11 +41,11 @@ class VoiceMessageBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplaya override func setupViews() { super.setupViews() - bubbleCellContentView?.backgroundColor = .clear - bubbleCellContentView?.showSenderInfo = true - bubbleCellContentView?.showPaginationTitle = false + roomCellContentView?.backgroundColor = .clear + roomCellContentView?.showSenderInfo = true + roomCellContentView?.showPaginationTitle = false - guard let contentView = bubbleCellContentView?.innerContentView else { + guard let contentView = roomCellContentView?.innerContentView else { return } diff --git a/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceMessage/VoiceMessageWithPaginationTitleBubbleCell.swift similarity index 92% rename from Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithPaginationTitleBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceMessage/VoiceMessageWithPaginationTitleBubbleCell.swift index b7e516675..f4640031e 100644 --- a/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithPaginationTitleBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceMessage/VoiceMessageWithPaginationTitleBubbleCell.swift @@ -20,6 +20,6 @@ class VoiceMessageWithPaginationTitleBubbleCell: VoiceMessageBubbleCell { override func setupViews() { super.setupViews() - bubbleCellContentView?.showPaginationTitle = true + roomCellContentView?.showPaginationTitle = true } } diff --git a/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceMessage/VoiceMessageWithoutSenderInfoBubbleCell.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithoutSenderInfoBubbleCell.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceMessage/VoiceMessageWithoutSenderInfoBubbleCell.swift index cc091b39d..84a302d5d 100644 --- a/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithoutSenderInfoBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/VoiceMessage/VoiceMessageWithoutSenderInfoBubbleCell.swift @@ -20,6 +20,6 @@ class VoiceMessageWithoutSenderInfoBubbleCell: VoiceMessageBubbleCell { override func setupViews() { super.setupViews() - bubbleCellContentView?.showSenderInfo = false + roomCellContentView?.showSenderInfo = false } } diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomBubbleCellLayout.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomCellLayoutConstants.swift similarity index 86% rename from Riot/Modules/Room/Views/BubbleCells/RoomBubbleCellLayout.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomCellLayoutConstants.swift index 641bc0677..7ae41d8ac 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomBubbleCellLayout.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomCellLayoutConstants.swift @@ -16,9 +16,12 @@ import Foundation -/// MXKRoomBubbleTableViewCell layout constants +/// Plain room cells layout constants @objcMembers -final class RoomBubbleCellLayout: NSObject { +final class PlainRoomCellLayoutConstants: NSObject { + + /// Inner content view margins + static let innerContentViewMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 57, bottom: 0.0, right: 0) // Reactions diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellDecorator.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellDecorator.swift similarity index 57% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellDecorator.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellDecorator.swift index a4e4bb8ae..00d1f0edd 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellDecorator.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellDecorator.swift @@ -54,12 +54,12 @@ class PlainRoomTimelineCellDecorator: RoomTimelineCellDecorator { urlPreviewView.availableWidth = cellData.maxTextViewWidth cellContentView.addSubview(urlPreviewView) - var leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin + var leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin if cellData.containsBubbleComponentWithEncryptionBadge { - leftMargin += RoomBubbleCellLayout.encryptedContentLeftMargin + leftMargin += PlainRoomCellLayoutConstants.encryptedContentLeftMargin } - let topMargin = contentViewPositionY + RoomBubbleCellLayout.urlPreviewViewTopMargin + RoomBubbleCellLayout.reactionsViewTopMargin + let topMargin = contentViewPositionY + PlainRoomCellLayoutConstants.urlPreviewViewTopMargin + PlainRoomCellLayoutConstants.reactionsViewTopMargin // Set the preview view's origin NSLayoutConstraint.activate([ @@ -76,7 +76,7 @@ class PlainRoomTimelineCellDecorator: RoomTimelineCellDecorator { cell.addTemporarySubview(reactionsView) - if let reactionsDisplayable = cell as? BubbleCellReactionsDisplayable { + if let reactionsDisplayable = cell as? RoomCellReactionsDisplayable { reactionsDisplayable.addReactionsView(reactionsView) } else { reactionsView.translatesAutoresizingMaskIntoConstraints = false @@ -85,14 +85,14 @@ class PlainRoomTimelineCellDecorator: RoomTimelineCellDecorator { cellContentView.addSubview(reactionsView) - var leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin + var leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin if cellData.containsBubbleComponentWithEncryptionBadge { - leftMargin += RoomBubbleCellLayout.encryptedContentLeftMargin + leftMargin += PlainRoomCellLayoutConstants.encryptedContentLeftMargin } - let rightMargin = RoomBubbleCellLayout.reactionsViewRightMargin - let topMargin = RoomBubbleCellLayout.reactionsViewTopMargin + let rightMargin = PlainRoomCellLayoutConstants.reactionsViewRightMargin + let topMargin = PlainRoomCellLayoutConstants.reactionsViewTopMargin // The top constraint may need to include the URL preview view let topConstraint: NSLayoutConstraint @@ -118,7 +118,7 @@ class PlainRoomTimelineCellDecorator: RoomTimelineCellDecorator { cell.addTemporarySubview(readReceiptsView) - if let readReceiptsDisplayable = cell as? BubbleCellReadReceiptsDisplayable { + if let readReceiptsDisplayable = cell as? RoomCellReadReceiptsDisplayable { readReceiptsDisplayable.addReadReceiptsView(readReceiptsView) } else { @@ -127,13 +127,13 @@ class PlainRoomTimelineCellDecorator: RoomTimelineCellDecorator { cellContentView.addSubview(readReceiptsView) // Force receipts container size - let widthConstraint = readReceiptsView.widthAnchor.constraint(equalToConstant: RoomBubbleCellLayout.readReceiptsViewWidth) - let heightConstraint = readReceiptsView.heightAnchor.constraint(equalToConstant: RoomBubbleCellLayout.readReceiptsViewHeight) + let widthConstraint = readReceiptsView.widthAnchor.constraint(equalToConstant: PlainRoomCellLayoutConstants.readReceiptsViewWidth) + let heightConstraint = readReceiptsView.heightAnchor.constraint(equalToConstant: PlainRoomCellLayoutConstants.readReceiptsViewHeight) // Force receipts container position - let trailingConstraint = readReceiptsView.trailingAnchor.constraint(equalTo: cellContentView.trailingAnchor, constant: -RoomBubbleCellLayout.readReceiptsViewRightMargin) + let trailingConstraint = readReceiptsView.trailingAnchor.constraint(equalTo: cellContentView.trailingAnchor, constant: -PlainRoomCellLayoutConstants.readReceiptsViewRightMargin) - let topMargin = RoomBubbleCellLayout.readReceiptsViewTopMargin + let topMargin = PlainRoomCellLayoutConstants.readReceiptsViewTopMargin let topConstraint: NSLayoutConstraint if let upperDecorationView = upperDecorationView { @@ -148,6 +148,7 @@ class PlainRoomTimelineCellDecorator: RoomTimelineCellDecorator { trailingConstraint, topConstraint ]) + } } @@ -159,7 +160,7 @@ class PlainRoomTimelineCellDecorator: RoomTimelineCellDecorator { cell.addTemporarySubview(threadSummaryView) - if let threadSummaryDisplayable = cell as? BubbleCellThreadSummaryDisplayable { + if let threadSummaryDisplayable = cell as? RoomCellThreadSummaryDisplayable { threadSummaryDisplayable.addThreadSummaryView(threadSummaryView) } else { threadSummaryView.translatesAutoresizingMaskIntoConstraints = false @@ -168,14 +169,14 @@ class PlainRoomTimelineCellDecorator: RoomTimelineCellDecorator { cellContentView.addSubview(threadSummaryView) - var leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin + var leftMargin = PlainRoomCellLayoutConstants.reactionsViewLeftMargin if cellData.containsBubbleComponentWithEncryptionBadge { - leftMargin += RoomBubbleCellLayout.encryptedContentLeftMargin + leftMargin += PlainRoomCellLayoutConstants.encryptedContentLeftMargin } - let rightMargin = RoomBubbleCellLayout.reactionsViewRightMargin - let topMargin = RoomBubbleCellLayout.threadSummaryViewTopMargin + let rightMargin = PlainRoomCellLayoutConstants.reactionsViewRightMargin + let topMargin = PlainRoomCellLayoutConstants.threadSummaryViewTopMargin let height = ThreadSummaryView.contentViewHeight(forThread: threadSummaryView.thread, fitting: cellData.maxTextViewWidth) @@ -203,4 +204,92 @@ class PlainRoomTimelineCellDecorator: RoomTimelineCellDecorator { func addSendStatusView(toCell cell: MXKRoomBubbleTableViewCell, withFailedEventIds failedEventIds: Set) { cell.updateTickView(withFailedEventIds: failedEventIds) } + + func addReadMarkerView(_ readMarkerView: UIView, + toCell cell: MXKRoomBubbleTableViewCell, + cellData: MXKRoomBubbleCellData, + contentViewPositionY: CGFloat) { + + if let readMarkerDisplayable = cell as? RoomCellReadMarkerDisplayable { + + readMarkerDisplayable.addReadMarkerView(readMarkerView) + + } else { + guard let overlayContainer = cell.bubbleOverlayContainer else { + return + } + + // The read marker is added into the overlay container. + // CAUTION: Keep disabled the user interaction on this container to not disturb tap gesture handling. + overlayContainer.backgroundColor = UIColor.clear + overlayContainer.alpha = 1 + overlayContainer.isUserInteractionEnabled = false + overlayContainer.isHidden = false + + // Add read marker to overlayContainer + readMarkerView.translatesAutoresizingMaskIntoConstraints = false + overlayContainer.addSubview(readMarkerView) + cell.readMarkerView = readMarkerView + + // Force read marker constraints + let topConstraint = readMarkerView.topAnchor.constraint(equalTo: overlayContainer.topAnchor, constant: contentViewPositionY - PlainRoomCellLayoutConstants.readMarkerViewHeight) + + let leadingConstraint = readMarkerView.leadingAnchor.constraint(equalTo: overlayContainer.leadingAnchor) + + let trailingConstraint = readMarkerView.trailingAnchor.constraint(equalTo: overlayContainer.trailingAnchor) + + let heightConstraint = readMarkerView.heightAnchor.constraint(equalToConstant: PlainRoomCellLayoutConstants.readMarkerViewHeight) + + NSLayoutConstraint.activate([topConstraint, + leadingConstraint, + trailingConstraint, + heightConstraint]) + + cell.readMarkerViewTopConstraint = topConstraint + cell.readMarkerViewLeadingConstraint = leadingConstraint + cell.readMarkerViewTrailingConstraint = trailingConstraint + cell.readMarkerViewHeightConstraint = heightConstraint + } + } + + func dissmissReadMarkerView(forCell cell: MXKRoomBubbleTableViewCell, + cellData: RoomBubbleCellData, + animated: Bool, + completion: @escaping () -> Void) { + + guard let readMarkerView = cell.readMarkerView, let readMarkerContainerView = readMarkerView.superview else { + return + } + + // Do not display the marker if this is the last message. + if animated == false || (cellData.containsLastMessage && readMarkerView.tag == cellData.mostRecentComponentIndex) { + readMarkerView.isHidden = true + completion() + } else { + readMarkerView.isHidden = false + + // Animate the layout to hide the read marker + DispatchQueue.main.async { + + let readMarkerContainerViewHalfWidth = readMarkerContainerView.frame.size.width/2 + + cell.readMarkerViewLeadingConstraint.constant = readMarkerContainerViewHalfWidth + cell.readMarkerViewTrailingConstraint.constant = -readMarkerContainerViewHalfWidth + + UIView.animate(withDuration: 1.5, + delay: 0.3, + options: [.beginFromCurrentState, .curveEaseIn]) { + + readMarkerView.alpha = 0 + readMarkerContainerView.layoutIfNeeded() + + } completion: { finished in + readMarkerView.isHidden = true + readMarkerView.alpha = 1 + + completion() + } + } + } + } } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellProvider.h b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellProvider.h rename to Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.h diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellProvider.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineCellProvider.m rename to Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineStyle.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineStyle.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/Plain/PlainRoomTimelineStyle.swift rename to Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineStyle.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/RoomCellLayoutUpdating.swift b/Riot/Modules/Room/TimelineCells/Styles/RoomCellLayoutUpdating.swift similarity index 93% rename from Riot/Modules/Room/Views/BubbleCells/Styles/RoomCellLayoutUpdating.swift rename to Riot/Modules/Room/TimelineCells/Styles/RoomCellLayoutUpdating.swift index 2c8b85e83..232253a79 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/RoomCellLayoutUpdating.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/RoomCellLayoutUpdating.swift @@ -26,6 +26,8 @@ protocol RoomCellLayoutUpdating: Themable { func setupLayout(forOutgoingTextMessageCell cell: MXKRoomBubbleTableViewCell) + func setupLayout(forIncomingFileAttachmentCell cell: MXKRoomBubbleTableViewCell) + func setupLayout(forOutgoingFileAttachmentCell cell: MXKRoomBubbleTableViewCell) func updateLayout(forSelectedStickerCell cell: RoomSelectedStickerBubbleCell) diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineCellDecorator.swift b/Riot/Modules/Room/TimelineCells/Styles/RoomTimelineCellDecorator.swift similarity index 82% rename from Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineCellDecorator.swift rename to Riot/Modules/Room/TimelineCells/Styles/RoomTimelineCellDecorator.swift index ec60bcbf4..5e383dc94 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineCellDecorator.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/RoomTimelineCellDecorator.swift @@ -50,4 +50,14 @@ protocol RoomTimelineCellDecorator { func addSendStatusView(toCell cell: MXKRoomBubbleTableViewCell, withFailedEventIds failedEventIds: Set) + + func addReadMarkerView(_ readMarkerView: UIView, + toCell cell: MXKRoomBubbleTableViewCell, + cellData: MXKRoomBubbleCellData, + contentViewPositionY: CGFloat) + + func dissmissReadMarkerView(forCell cell: MXKRoomBubbleTableViewCell, + cellData: RoomBubbleCellData, + animated: Bool, + completion: @escaping () -> Void) } diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineCellProvider.h b/Riot/Modules/Room/TimelineCells/Styles/RoomTimelineCellProvider.h similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineCellProvider.h rename to Riot/Modules/Room/TimelineCells/Styles/RoomTimelineCellProvider.h diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineStyle.swift b/Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyle.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineStyle.swift rename to Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyle.swift diff --git a/Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineStyleIdentifier.swift b/Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift similarity index 100% rename from Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineStyleIdentifier.swift rename to Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.swift b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionActionViewCell.swift similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.swift rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionActionViewCell.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.xib b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionActionViewCell.xib similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.xib rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionActionViewCell.xib diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionViewCell.swift similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionViewCell.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionViewCell.xib similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionViewCell.xib diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewData.swift b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionViewData.swift similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionViewData.swift rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionViewData.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsView.swift similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsView.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.xib b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsView.xib similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionsView.xib rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsView.xib diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewModel.swift similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewModel.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelBuilder.swift b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewModelBuilder.swift similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelBuilder.swift rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewModelBuilder.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewModelType.swift similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewModelType.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewSizer.h b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewSizer.h similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionsViewSizer.h rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewSizer.h diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewSizer.m b/Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewSizer.m similarity index 100% rename from Riot/Modules/Room/BubbleReactions/BubbleReactionsViewSizer.m rename to Riot/Modules/Room/TimelineDecorations/Reactions/BubbleReactionsViewSizer.m diff --git a/Riot/Modules/MatrixKit/Views/MXKReceiptSendersContainer.h b/Riot/Modules/Room/TimelineDecorations/ReadReceipts/MXKReceiptSendersContainer.h similarity index 100% rename from Riot/Modules/MatrixKit/Views/MXKReceiptSendersContainer.h rename to Riot/Modules/Room/TimelineDecorations/ReadReceipts/MXKReceiptSendersContainer.h diff --git a/Riot/Modules/MatrixKit/Views/MXKReceiptSendersContainer.m b/Riot/Modules/Room/TimelineDecorations/ReadReceipts/MXKReceiptSendersContainer.m similarity index 100% rename from Riot/Modules/MatrixKit/Views/MXKReceiptSendersContainer.m rename to Riot/Modules/Room/TimelineDecorations/ReadReceipts/MXKReceiptSendersContainer.m diff --git a/Riot/Modules/Room/Views/Threads/From/FromAThreadView.swift b/Riot/Modules/Room/TimelineDecorations/Threads/From/FromAThreadView.swift similarity index 100% rename from Riot/Modules/Room/Views/Threads/From/FromAThreadView.swift rename to Riot/Modules/Room/TimelineDecorations/Threads/From/FromAThreadView.swift diff --git a/Riot/Modules/Room/Views/Threads/From/FromAThreadView.xib b/Riot/Modules/Room/TimelineDecorations/Threads/From/FromAThreadView.xib similarity index 100% rename from Riot/Modules/Room/Views/Threads/From/FromAThreadView.xib rename to Riot/Modules/Room/TimelineDecorations/Threads/From/FromAThreadView.xib diff --git a/Riot/Modules/Room/Views/Threads/Summary/ThreadSummaryModel.swift b/Riot/Modules/Room/TimelineDecorations/Threads/Summary/ThreadSummaryModel.swift similarity index 100% rename from Riot/Modules/Room/Views/Threads/Summary/ThreadSummaryModel.swift rename to Riot/Modules/Room/TimelineDecorations/Threads/Summary/ThreadSummaryModel.swift diff --git a/Riot/Modules/Room/Views/Threads/Summary/ThreadSummaryView.swift b/Riot/Modules/Room/TimelineDecorations/Threads/Summary/ThreadSummaryView.swift similarity index 91% rename from Riot/Modules/Room/Views/Threads/Summary/ThreadSummaryView.swift rename to Riot/Modules/Room/TimelineDecorations/Threads/Summary/ThreadSummaryView.swift index c1da2d9dd..a57888aeb 100644 --- a/Riot/Modules/Room/Views/Threads/Summary/ThreadSummaryView.swift +++ b/Riot/Modules/Room/TimelineDecorations/Threads/Summary/ThreadSummaryView.swift @@ -38,7 +38,8 @@ class ThreadSummaryView: UIView { @IBOutlet private weak var lastMessageContentLabel: UILabel! private var theme: Theme = ThemeService.shared().theme - private(set) var thread: MXThread? + private(set) var thread: MXThreadProtocol? + private weak var session: MXSession? private lazy var tapGestureRecognizer: UITapGestureRecognizer = { return UITapGestureRecognizer(target: self, action: #selector(tapped(_:))) @@ -48,19 +49,20 @@ class ThreadSummaryView: UIView { // MARK: - Setup - init(withThread thread: MXThread) { + init(withThread thread: MXThreadProtocol, session: MXSession) { self.thread = thread + self.session = session super.init(frame: CGRect(origin: .zero, size: CGSize(width: Constants.viewDefaultWidth, - height: RoomBubbleCellLayout.threadSummaryViewHeight))) + height: PlainRoomCellLayoutConstants.threadSummaryViewHeight))) loadNibContent() update(theme: ThemeService.shared().theme) configure() translatesAutoresizingMaskIntoConstraints = false } - static func contentViewHeight(forThread thread: MXThread?, fitting maxWidth: CGFloat) -> CGFloat { - return RoomBubbleCellLayout.threadSummaryViewHeight + static func contentViewHeight(forThread thread: MXThreadProtocol?, fitting maxWidth: CGFloat) -> CGFloat { + return PlainRoomCellLayoutConstants.threadSummaryViewHeight } required init?(coder: NSCoder) { @@ -93,7 +95,7 @@ class ThreadSummaryView: UIView { guard let thread = thread, let lastMessage = thread.lastMessage, - let session = thread.session, + let session = session, let eventFormatter = session.roomSummaryUpdateDelegate as? MXKEventFormatter, let room = session.room(withRoomId: lastMessage.roomId) else { lastMessageAvatarView.avatarImageView.image = nil diff --git a/Riot/Modules/Room/Views/Threads/Summary/ThreadSummaryView.xib b/Riot/Modules/Room/TimelineDecorations/Threads/Summary/ThreadSummaryView.xib similarity index 100% rename from Riot/Modules/Room/Views/Threads/Summary/ThreadSummaryView.xib rename to Riot/Modules/Room/TimelineDecorations/Threads/Summary/ThreadSummaryView.xib diff --git a/Riot/Modules/Room/Views/URLPreviews/URLPreviewView.swift b/Riot/Modules/Room/TimelineDecorations/URLPreviews/URLPreviewView.swift similarity index 100% rename from Riot/Modules/Room/Views/URLPreviews/URLPreviewView.swift rename to Riot/Modules/Room/TimelineDecorations/URLPreviews/URLPreviewView.swift diff --git a/Riot/Modules/Room/Views/URLPreviews/URLPreviewView.xib b/Riot/Modules/Room/TimelineDecorations/URLPreviews/URLPreviewView.xib similarity index 100% rename from Riot/Modules/Room/Views/URLPreviews/URLPreviewView.xib rename to Riot/Modules/Room/TimelineDecorations/URLPreviews/URLPreviewView.xib diff --git a/Riot/Modules/Room/Views/URLPreviews/URLPreviewViewSizer.swift b/Riot/Modules/Room/TimelineDecorations/URLPreviews/URLPreviewViewSizer.swift similarity index 100% rename from Riot/Modules/Room/Views/URLPreviews/URLPreviewViewSizer.swift rename to Riot/Modules/Room/TimelineDecorations/URLPreviews/URLPreviewViewSizer.swift diff --git a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m index cb15df862..6acd41443 100644 --- a/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m +++ b/Riot/Modules/Room/Views/Activities/RoomActivitiesView.m @@ -124,7 +124,7 @@ [self.resendButton setTitle:[VectorL10n retry] forState:UIControlStateNormal]; self.resendButton.backgroundColor = ThemeService.shared.theme.tintColor; - UIImage *image = [[UIImage imageNamed:@"room_context_menu_delete"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + UIImage *image = [AssetImages.roomContextMenuDelete.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [self.deleteButton setImage:image forState:UIControlStateNormal]; self.deleteButton.tintColor = ThemeService.shared.theme.warningColor; @@ -174,7 +174,7 @@ if (labelText.length) { - self.iconImageView.image = [UIImage imageNamed:@"error"]; + self.iconImageView.image = AssetImages.error.image; self.iconImageView.tintColor = ThemeService.shared.theme.noticeColor; self.messageLabel.text = labelText; self.messageLabel.textColor = ThemeService.shared.theme.warningColor; @@ -192,7 +192,7 @@ if (labelText.length) { - self.iconImageView.image = [UIImage imageNamed:@"typing"]; + self.iconImageView.image = AssetImages.typing.image; self.iconImageView.tintColor = ThemeService.shared.theme.tintColor; self.messageLabel.text = labelText; @@ -309,7 +309,7 @@ self.messageTextView.hidden = NO; self.messageTextView.backgroundColor = [UIColor clearColor]; - self.iconImageView.image = [UIImage imageNamed:@"error"]; + self.iconImageView.image = AssetImages.error.image; self.iconImageView.tintColor = ThemeService.shared.theme.noticeColor; self.iconImageView.hidden = NO; @@ -484,6 +484,8 @@ #pragma mark - UITextViewDelegate +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange { if ([[URL absoluteString] isEqualToString:@"onResendLink"]) @@ -559,6 +561,7 @@ return YES; } +#pragma clang diagnostic pop #pragma mark - UIGestureRecognizerDelegate diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 2ce98c82c..cc00383e4 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -127,21 +127,21 @@ static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; self.attachMediaButton.accessibilityLabel = [VectorL10n roomAccessibilityUpload]; - UIImage *image = [UIImage imageNamed:@"input_text_background"]; + UIImage *image = AssetImages.inputTextBackground.image; image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(9, 15, 10, 16)]; self.inputTextBackgroundView.image = image; self.inputTextBackgroundView.tintColor = ThemeService.shared.theme.roomInputTextBorder; if ([ThemeService.shared.themeId isEqualToString:@"light"]) { - [self.attachMediaButton setImage:[UIImage imageNamed:@"upload_icon"] forState:UIControlStateNormal]; + [self.attachMediaButton setImage:AssetImages.uploadIcon.image forState:UIControlStateNormal]; } else if ([ThemeService.shared.themeId isEqualToString:@"dark"] || [ThemeService.shared.themeId isEqualToString:@"black"]) { - [self.attachMediaButton setImage:[UIImage imageNamed:@"upload_icon_dark"] forState:UIControlStateNormal]; + [self.attachMediaButton setImage:AssetImages.uploadIconDark.image forState:UIControlStateNormal]; } else if (ThemeService.shared.theme.userInterfaceStyle == UIUserInterfaceStyleDark) { - [self.attachMediaButton setImage:[UIImage imageNamed:@"upload_icon_dark"] forState:UIControlStateNormal]; + [self.attachMediaButton setImage:AssetImages.uploadIconDark.image forState:UIControlStateNormal]; } self.inputContextImageView.tintColor = ThemeService.shared.theme.textSecondaryColor; @@ -192,8 +192,8 @@ static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; switch (_sendMode) { case RoomInputToolbarViewSendModeReply: - buttonImage = [UIImage imageNamed:@"send_icon"]; - self.inputContextImageView.image = [UIImage imageNamed:@"input_reply_icon"]; + buttonImage = AssetImages.sendIcon.image; + self.inputContextImageView.image = AssetImages.inputReplyIcon.image; self.inputContextLabel.text = [VectorL10n roomMessageReplyingTo:self.eventSenderDisplayName]; self.inputContextViewHeightConstraint.constant = kContextBarHeight; @@ -201,8 +201,8 @@ static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; self.textView.maxHeight -= kContextBarHeight; break; case RoomInputToolbarViewSendModeEdit: - buttonImage = [UIImage imageNamed:@"save_icon"]; - self.inputContextImageView.image = [UIImage imageNamed:@"input_edit_icon"]; + buttonImage = AssetImages.saveIcon.image; + self.inputContextImageView.image = AssetImages.inputEditIcon.image; self.inputContextLabel.text = [VectorL10n roomMessageEditing]; self.inputContextViewHeightConstraint.constant = kContextBarHeight; @@ -210,7 +210,7 @@ static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; self.textView.maxHeight -= kContextBarHeight; break; default: - buttonImage = [UIImage imageNamed:@"send_icon"]; + buttonImage = AssetImages.sendIcon.image; if (previousMode != _sendMode) { diff --git a/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m b/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m index 81a128564..7e8456a5b 100644 --- a/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m +++ b/Riot/Modules/Room/Views/Title/Preview/PreviewRoomTitleView.m @@ -102,7 +102,7 @@ andImageOrientation:UIImageOrientationUp toFitViewSize:self.roomAvatar.frame.size withMethod:MXThumbnailingMethodCrop - previewImage:[MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + previewImage:[MXKTools paintImage:AssetImages.placeholder.image withColor:ThemeService.shared.theme.tintColor] mediaManager:self.mxRoom.mxSession.mediaManager]; } diff --git a/Riot/Modules/RoomKeyRequest/RoomKeyRequestViewController.m b/Riot/Modules/RoomKeyRequest/RoomKeyRequestViewController.m index a5bf789d6..91f62a8d6 100644 --- a/Riot/Modules/RoomKeyRequest/RoomKeyRequestViewController.m +++ b/Riot/Modules/RoomKeyRequest/RoomKeyRequestViewController.m @@ -92,7 +92,7 @@ // Accept the received requests from this device [self.mxSession.crypto acceptAllPendingKeyRequestsFromUser:self.device.userId andDevice:self.device.deviceId onComplete:^{ - onComplete(); + self->onComplete(); }]; } }]]; @@ -110,7 +110,7 @@ // Ignore all pending requests from this device [self.mxSession.crypto ignoreAllPendingKeyRequestsFromUser:self.device.userId andDevice:self.device.deviceId onComplete:^{ - onComplete(); + self->onComplete(); }]; } }]]; @@ -169,7 +169,7 @@ // As the device is now verified, all other key requests will be automatically accepted. [self.mxSession.crypto acceptAllPendingKeyRequestsFromUser:self.device.userId andDevice:self.device.deviceId onComplete:^{ - onComplete(); + self->onComplete(); }]; } else diff --git a/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerTableViewCell.m b/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerTableViewCell.m index fed334510..6ec9484e6 100644 --- a/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerTableViewCell.m +++ b/Riot/Modules/Rooms/DirectoryPicker/Views/DirectoryServerTableViewCell.m @@ -63,7 +63,7 @@ [self.iconImageView setImageURI:iconURL withType:nil andImageOrientation:UIImageOrientationUp - previewImage:[MXKTools paintImage:[UIImage imageNamed:@"placeholder"] + previewImage:[MXKTools paintImage:AssetImages.placeholder.image withColor:ThemeService.shared.theme.tintColor] mediaManager:cellData.mediaManager]; } diff --git a/Riot/Modules/Rooms/RoomsViewController.m b/Riot/Modules/Rooms/RoomsViewController.m index 71975e939..6e2adfc62 100644 --- a/Riot/Modules/Rooms/RoomsViewController.m +++ b/Riot/Modules/Rooms/RoomsViewController.m @@ -58,7 +58,7 @@ self.recentsTableView.tag = RecentsDataSourceModeRooms; // Add the (+) button programmatically - plusButtonImageView = [self vc_addFABWithImage:[UIImage imageNamed:@"rooms_floating_action"] + plusButtonImageView = [self vc_addFABWithImage:AssetImages.roomsFloatingAction.image target:self action:@selector(onPlusButtonPressed)]; } @@ -157,11 +157,11 @@ { if (ThemeService.shared.isCurrentThemeDark) { - return [UIImage imageNamed:@"rooms_empty_screen_artwork_dark"]; + return AssetImages.roomsEmptyScreenArtworkDark.image; } else { - return [UIImage imageNamed:@"rooms_empty_screen_artwork"]; + return AssetImages.roomsEmptyScreenArtwork.image; } } diff --git a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift index 911f08302..23b48397e 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift @@ -103,7 +103,7 @@ final class SecretsRecoveryWithKeyViewController: UIViewController { switch self.viewModel.recoveryGoal { case .default, .keyBackup, .restoreSecureBackup: informationText = VectorL10n.secretsRecoveryWithKeyInformationDefault - case .unlockSecureBackup(_): + case .unlockSecureBackup: informationText = VectorL10n.secretsRecoveryWithKeyInformationUnlockSecureBackupWithKey case .verifyDevice: informationText = VectorL10n.secretsRecoveryWithKeyInformationVerifyDevice diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift index 5c64fd51b..0578f2b37 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift @@ -106,7 +106,7 @@ final class SecretsRecoveryWithPassphraseViewController: UIViewController { switch self.viewModel.recoveryGoal { case .default, .keyBackup, .restoreSecureBackup: informationText = VectorL10n.secretsRecoveryWithPassphraseInformationDefault - case .unlockSecureBackup(_): + case .unlockSecureBackup: informationText = VectorL10n.secretsRecoveryWithKeyInformationUnlockSecureBackupWithPhrase case .verifyDevice: informationText = VectorL10n.secretsRecoveryWithPassphraseInformationVerifyDevice diff --git a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m index 42920094f..ab7edff15 100644 --- a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m +++ b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m @@ -383,12 +383,12 @@ enum { if (deviceInfo.trustLevel.isVerified) { cell.textLabel.text = [VectorL10n manageSessionTrusted]; - cell.imageView.image = [UIImage imageNamed:@"encryption_trusted"]; + cell.imageView.image = AssetImages.encryptionTrusted.image; } else { cell.textLabel.text = [VectorL10n manageSessionNotTrusted]; - cell.imageView.image = [UIImage imageNamed:@"encryption_warning"]; + cell.imageView.image = AssetImages.encryptionWarning.image; } return cell; diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 99b7f7c93..7082ba3bf 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -1097,19 +1097,19 @@ TableViewSectionsDelegate> { if ([deviceId isEqualToString:self.mainSession.myDeviceId]) { - return [UIImage imageNamed:@"encryption_warning"]; + return AssetImages.encryptionWarning.image; } else { - return [UIImage imageNamed:@"encryption_normal"]; + return AssetImages.encryptionNormal.image; } } - UIImage* shieldImageForDevice = [UIImage imageNamed:@"encryption_warning"]; + UIImage* shieldImageForDevice = AssetImages.encryptionWarning.image; MXDeviceInfo *device = [self.mainSession.crypto deviceWithDeviceId:deviceId ofUser:self.mainSession.myUser.userId]; if (device.trustLevel.isVerified) { - shieldImageForDevice = [UIImage imageNamed:@"encryption_trusted"]; + shieldImageForDevice = AssetImages.encryptionTrusted.image; } return shieldImageForDevice; diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index d1e1f163d..00088a4f7 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -259,7 +259,9 @@ TableViewSectionsDelegate> @property (nonatomic) UNNotificationSettings *systemNotificationSettings; @property (nonatomic, weak) DeactivateAccountViewController *deactivateAccountViewController; + @property (nonatomic, strong) NotificationSettingsCoordinatorBridgePresenter *notificationSettingsBridgePresenter; + @property (nonatomic, strong) SignOutAlertPresenter *signOutAlertPresenter; @property (nonatomic, weak) UIButton *signOutButton; @property (nonatomic, strong) SingleImagePickerPresenter *imagePickerPresenter; @@ -1836,7 +1838,7 @@ TableViewSectionsDelegate> newEmailCell.mxkLabel.text = [VectorL10n settingsAddEmailAddress]; newEmailCell.mxkTextField.text = nil; newEmailCell.mxkTextField.userInteractionEnabled = NO; - newEmailCell.accessoryView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]]; + newEmailCell.accessoryView = [[UIImageView alloc] initWithImage:[AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]]; } else { @@ -1871,7 +1873,7 @@ TableViewSectionsDelegate> newEmailTextField = newEmailCell.mxkTextField; } - UIImage *accessoryViewImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.tintColor]; + UIImage *accessoryViewImage = [AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.tintColor]; newEmailCell.accessoryView = [[UIImageView alloc] initWithImage:accessoryViewImage]; } @@ -1889,7 +1891,7 @@ TableViewSectionsDelegate> newPhoneCell.mxkLabel.text = [VectorL10n settingsAddPhoneNumber]; newPhoneCell.mxkTextField.text = nil; newPhoneCell.mxkTextField.userInteractionEnabled = NO; - newPhoneCell.accessoryView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]]; + newPhoneCell.accessoryView = [[UIImageView alloc] initWithImage:[AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]]; cell = newPhoneCell; } @@ -1951,7 +1953,7 @@ TableViewSectionsDelegate> newPhoneNumberCell = newPhoneCell; } - UIImage *accessoryViewImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.tintColor]; + UIImage *accessoryViewImage = [AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.tintColor]; newPhoneCell.accessoryView = [[UIImageView alloc] initWithImage:accessoryViewImage]; cell = newPhoneCell; diff --git a/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewController.swift b/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewController.swift index 60c942176..bbd8b5a90 100644 --- a/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewController.swift +++ b/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewController.swift @@ -219,7 +219,11 @@ class SpaceDetailViewController: UIViewController { self.inviterPanelHeight.constant = 0 } - let avatarViewData = AvatarViewData(matrixItemId: parameters.spaceId, displayName: parameters.displayName, avatarUrl: parameters.avatarUrl, mediaManager: self.mediaManager, fallbackImage: .matrixItem(parameters.spaceId, parameters.displayName)) + let avatarViewData = AvatarViewData(matrixItemId: parameters.spaceId, + displayName: parameters.displayName, + avatarUrl: parameters.avatarUrl, + mediaManager: self.mediaManager, + fallbackImage: .matrixItem(parameters.spaceId, parameters.displayName)) self.titleLabel.text = parameters.displayName self.avatarView.fill(with: avatarViewData) diff --git a/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewModel.swift b/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewModel.swift index 46bb2b1f1..df57aeba0 100644 --- a/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewModel.swift +++ b/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewModel.swift @@ -76,14 +76,30 @@ class SpaceDetailViewModel: SpaceDetailViewModelType { private func loadData() { if let publicRoom = self.publicRoom { - self.update(viewState: .loaded(SpaceDetailLoadedParameters(spaceId: publicRoom.roomId, displayName: publicRoom.displayname(), topic: publicRoom.topic, avatarUrl: publicRoom.avatarUrl, joinRule: publicRoom.worldReadable ? .public : .private, membership: .unknown, inviterId: nil, inviter: nil, membersCount: UInt(publicRoom.numJoinedMembers)))) + self.update(viewState: .loaded(SpaceDetailLoadedParameters(spaceId: publicRoom.roomId, + displayName: publicRoom.displayname(), + topic: publicRoom.topic, + avatarUrl: publicRoom.avatarUrl, + joinRule: publicRoom.worldReadable ? .public : .private, + membership: .unknown, + inviterId: nil, + inviter: nil, + membersCount: UInt(publicRoom.numJoinedMembers)))) } else { guard let space = self.session.spaceService.getSpace(withId: self.spaceId), let summary = space.summary else { MXLog.error("[SpaceDetailViewModel] setupViews: no space found") return } - let parameters = SpaceDetailLoadedParameters(spaceId: space.spaceId, displayName: summary.displayname, topic: summary.topic, avatarUrl: summary.avatar, joinRule: nil, membership: summary.membership, inviterId: nil, inviter: nil, membersCount: 0) + let parameters = SpaceDetailLoadedParameters(spaceId: space.spaceId, + displayName: summary.displayname, + topic: summary.topic, + avatarUrl: summary.avatar, + joinRule: nil, + membership: summary.membership, + inviterId: nil, + inviter: nil, + membersCount: 0) self.update(viewState: .loaded(parameters)) self.update(viewState: .loading) @@ -103,7 +119,15 @@ class SpaceDetailViewModel: SpaceDetailViewModelType { } }) - let parameters = SpaceDetailLoadedParameters(spaceId: space.spaceId, displayName: summary.displayname, topic: summary.topic, avatarUrl: summary.avatar, joinRule: joinRule, membership: summary.membership, inviterId: inviterId, inviter: inviter, membersCount: membersCount) + let parameters = SpaceDetailLoadedParameters(spaceId: space.spaceId, + displayName: summary.displayname, + topic: summary.topic, + avatarUrl: summary.avatar, + joinRule: joinRule, + membership: summary.membership, + inviterId: inviterId, + inviter: inviter, + membersCount: membersCount) self.update(viewState: .loaded(parameters)) } } diff --git a/Riot/Modules/Spaces/SpaceList/SpaceListViewModel.swift b/Riot/Modules/Spaces/SpaceList/SpaceListViewModel.swift index 62770e05e..4d82d8368 100644 --- a/Riot/Modules/Spaces/SpaceList/SpaceListViewModel.swift +++ b/Riot/Modules/Spaces/SpaceList/SpaceListViewModel.swift @@ -204,7 +204,11 @@ final class SpaceListViewModel: SpaceListViewModelType { let homeNotificationState = session.spaceService.notificationCounter.homeNotificationState let homeViewData = SpaceListItemViewData(spaceId: Constants.homeSpaceId, - title: VectorL10n.spacesHomeSpaceTitle, avatarViewData: avatarViewData, isInvite: false, notificationCount: homeNotificationState.allCount, highlightedNotificationCount: homeNotificationState.allHighlightCount) + title: VectorL10n.spacesHomeSpaceTitle, + avatarViewData: avatarViewData, + isInvite: false, + notificationCount: homeNotificationState.allCount, + highlightedNotificationCount: homeNotificationState.allHighlightCount) return homeViewData } @@ -214,7 +218,11 @@ final class SpaceListViewModel: SpaceListViewModelType { session.spaceService.rootSpaceSummaries.forEach { summary in let avatarViewData = AvatarViewData(matrixItemId: summary.roomId, displayName: summary.displayname, avatarUrl: summary.avatar, mediaManager: session.mediaManager, fallbackImage: .matrixItem(summary.roomId, summary.displayname)) let notificationState = session.spaceService.notificationCounter.notificationState(forSpaceWithId: summary.roomId) - let viewData = SpaceListItemViewData(spaceId: summary.roomId, title: summary.displayname, avatarViewData: avatarViewData, isInvite: summary.membership == .invite, notificationCount: notificationState?.groupMissedDiscussionsCount ?? 0, highlightedNotificationCount: notificationState?.groupMissedDiscussionsHighlightedCount ?? 0) + let viewData = SpaceListItemViewData(spaceId: summary.roomId, title: summary.displayname, + avatarViewData: avatarViewData, + isInvite: summary.membership == .invite, + notificationCount: notificationState?.groupMissedDiscussionsCount ?? 0, + highlightedNotificationCount: notificationState?.groupMissedDiscussionsHighlightedCount ?? 0) if viewData.isInvite { invites.append(viewData) } else { diff --git a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift index 7b4ea93c6..056a591b1 100644 --- a/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Spaces/SpaceMembers/SpaceMembersCoordinatorBridgePresenter.swift @@ -24,7 +24,8 @@ import Foundation /// SpaceMembersCoordinatorBridgePresenter enables to start SpaceMemberListCoordinator from a view controller. /// This bridge is used while waiting for global usage of coordinator pattern. -/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (mainly for integration in legacy view controllers). Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator. +/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (mainly for integration in legacy view controllers). +/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator. @objcMembers final class SpaceMembersCoordinatorBridgePresenter: NSObject { diff --git a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinatorBridgePresenter.swift b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinatorBridgePresenter.swift index 824801fb4..bbbcedfac 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/ExploreRoomCoordinatorBridgePresenter.swift @@ -24,7 +24,8 @@ import Foundation /// ExploreRoomCoordinatorBridgePresenter enables to start ExploreRoomCoordinator from a view controller. /// This bridge is used while waiting for global usage of coordinator pattern. -/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (mainly for integration in legacy view controllers). Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator. +/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (mainly for integration in legacy view controllers). Each bridge should be removed once the underlying Coordinator has +/// been integrated by another Coordinator. @objcMembers final class ExploreRoomCoordinatorBridgePresenter: NSObject { diff --git a/Riot/Modules/Spaces/SpaceRoomList/SpaceChildRoomDetail/SpaceChildRoomDetailViewModel.swift b/Riot/Modules/Spaces/SpaceRoomList/SpaceChildRoomDetail/SpaceChildRoomDetailViewModel.swift index 299c42d4a..c5989e23b 100644 --- a/Riot/Modules/Spaces/SpaceRoomList/SpaceChildRoomDetail/SpaceChildRoomDetailViewModel.swift +++ b/Riot/Modules/Spaces/SpaceRoomList/SpaceChildRoomDetail/SpaceChildRoomDetailViewModel.swift @@ -71,7 +71,11 @@ final class SpaceChildRoomDetailViewModel: SpaceChildRoomDetailViewModelType { // MARK: - Private private func loadData() { - let avatarViewData = AvatarViewData(matrixItemId: self.childInfo.childRoomId, displayName: self.childInfo.displayName, avatarUrl: self.childInfo.avatarUrl, mediaManager: self.session.mediaManager, fallbackImage: .matrixItem(self.childInfo.childRoomId, self.childInfo.name)) + let avatarViewData = AvatarViewData(matrixItemId: self.childInfo.childRoomId, + displayName: self.childInfo.displayName, + avatarUrl: self.childInfo.avatarUrl, + mediaManager: self.session.mediaManager, + fallbackImage: .matrixItem(self.childInfo.childRoomId, self.childInfo.name)) self.update(viewState: .loaded(self.childInfo, avatarViewData, self.isRoomJoined)) } diff --git a/Riot/Modules/SplitView/SplitViewCoordinator.swift b/Riot/Modules/SplitView/SplitViewCoordinator.swift index 442da486e..2157cbd31 100644 --- a/Riot/Modules/SplitView/SplitViewCoordinator.swift +++ b/Riot/Modules/SplitView/SplitViewCoordinator.swift @@ -266,7 +266,8 @@ extension SplitViewCoordinator: UISplitViewControllerDelegate { func splitViewController(_ splitViewController: UISplitViewController, separateSecondaryFrom primaryViewController: UIViewController) -> UIViewController? { // If the primary root controller of the UISplitViewController is a UINavigationController, - // it's possible to have nested navigation controllers due to private property `_allowNestedNavigationControllers` set to true (https://blog.malcolmhall.com/2017/01/27/default-behaviour-of-uisplitviewcontroller-collapsesecondaryviewcontroller/). + // it's possible to have nested navigation controllers due to private property `_allowNestedNavigationControllers` set to true + // (https://blog.malcolmhall.com/2017/01/27/default-behaviour-of-uisplitviewcontroller-collapsesecondaryviewcontroller/). // So if the top view controller of the primary navigation controller is a navigation controller and it corresponds to the existing `detailNavigationController` instance. // Return `detailNavigationController` as is, it will be used as the secondary view of the split view controller. if let topMostNavigationController = self.selectedNavigationRouter?.modules.last as? UINavigationController, topMostNavigationController == self.detailNavigationController { @@ -302,7 +303,8 @@ extension SplitViewCoordinator: UISplitViewControllerDelegate { // Return false to let the split view controller try to incorporate the secondary view controller's content into the collapsed interface. // If the primary root controller of a UISplitViewController is a UINavigationController, - // it's possible to have nested navigation controllers due to private property `_allowNestedNavigationControllers` set to true (https://blog.malcolmhall.com/2017/01/27/default-behaviour-of-uisplitviewcontroller-collapsesecondaryviewcontroller/). + // it's possible to have nested navigation controllers due to private property `_allowNestedNavigationControllers` set to true + // (https://blog.malcolmhall.com/2017/01/27/default-behaviour-of-uisplitviewcontroller-collapsesecondaryviewcontroller/). // So in this case returning false here will push the `detailNavigationController` on top of the `primaryNavigationController`. // Sample primary view stack: // primaryNavigationController[ @@ -344,7 +346,8 @@ extension SplitViewCoordinator: SplitViewMasterPresentableDelegate { // - If the split view controller is collpased (one column visible): // The `detailNavigationController` will be pushed on top of the primary navigation controller. // In fact if the primary root controller of a UISplitViewController is a UINavigationController, - // it's possible to have nested navigation controllers due to private property `_allowNestedNavigationControllers` set to true (https://blog.malcolmhall.com/2017/01/27/default-behaviour-of-uisplitviewcontroller-collapsesecondaryviewcontroller/). + // it's possible to have nested navigation controllers due to private property `_allowNestedNavigationControllers` set to true + // (https://blog.malcolmhall.com/2017/01/27/default-behaviour-of-uisplitviewcontroller-collapsesecondaryviewcontroller/). // - Else if the split view controller is not collpased (two column visible) // It will set the `detailNavigationController` as the secondary view of the split view controller self.splitViewController.showDetailViewController(detailNavigationController, sender: nil) diff --git a/Riot/Modules/StartChat/StartChatViewController.m b/Riot/Modules/StartChat/StartChatViewController.m index 4f2d71dc1..9d8da5e52 100644 --- a/Riot/Modules/StartChat/StartChatViewController.m +++ b/Riot/Modules/StartChat/StartChatViewController.m @@ -105,7 +105,7 @@ dataSource.forceMatrixIdInDisplayName = YES; // Add a plus icon to the contact cell when a search session is in progress, // in order to make it more understandable for the end user. - dataSource.contactCellAccessoryImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]; + dataSource.contactCellAccessoryImage = [AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]; [self displayList:dataSource]; diff --git a/Riot/Modules/TabBar/MasterTabBarController.m b/Riot/Modules/TabBar/MasterTabBarController.m index 9b01d88d3..65ed41122 100644 --- a/Riot/Modules/TabBar/MasterTabBarController.m +++ b/Riot/Modules/TabBar/MasterTabBarController.m @@ -119,9 +119,9 @@ self.delegate = self; - _isOnboardingInProgress = NO; + self.isOnboardingInProgress = NO; - // Note: UITabBarViewController shoud not be embed in a UINavigationController (https://github.com/vector-im/riot-ios/issues/3086) + // Note: UITabBarViewController should not be embed in a UINavigationController (https://github.com/vector-im/riot-ios/issues/3086) [self vc_removeBackTitle]; [self setupTitleView]; @@ -520,7 +520,7 @@ [self.onboardingCoordinatorBridgePresenter dismissWithAnimated:YES completion:nil]; self.onboardingCoordinatorBridgePresenter = nil; - self.isOnboardingInProgress = NO; + self.isOnboardingInProgress = NO; // Must be set before calling didCompleteAuthentication [self.masterTabBarDelegate masterTabBarControllerDidCompleteAuthentication:self]; }; @@ -531,7 +531,8 @@ self.addAccountObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidAddAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { MXStrongifyAndReturnIfNil(self); -#warning What was this doing? This should probably happen elsewhere + + // What was this doing? This should probably happen elsewhere // self.onboardingCoordinatorBridgePresenter = nil; [[NSNotificationCenter defaultCenter] removeObserver:self.addAccountObserver]; @@ -541,7 +542,8 @@ self.removeAccountObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidRemoveAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { MXStrongifyAndReturnIfNil(self); // The user has cleared data for their soft logged out account -#warning What was this doing? This should probably happen elsewhere + + // What was this doing? This should probably happen elsewhere // self.onboardingCoordinatorBridgePresenter = nil; [[NSNotificationCenter defaultCenter] removeObserver:self.removeAccountObserver]; diff --git a/Riot/Modules/TabBar/TabBarCoordinator.swift b/Riot/Modules/TabBar/TabBarCoordinator.swift index c0454e86c..30ce90bc0 100644 --- a/Riot/Modules/TabBar/TabBarCoordinator.swift +++ b/Riot/Modules/TabBar/TabBarCoordinator.swift @@ -227,6 +227,10 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { homeViewController.tabBarItem.image = homeViewController.tabBarItem.image homeViewController.accessibilityLabel = VectorL10n.titleHome + if BuildSettings.appActivityIndicators { + homeViewController.activityPresenter = AppActivityIndicatorPresenter(appNavigator: parameters.appNavigator) + } + let wrapperViewController = HomeViewControllerWithBannerWrapperViewController(viewController: homeViewController) return wrapperViewController } diff --git a/Riot/Modules/Threads/ThreadList/ThreadListCoordinator.swift b/Riot/Modules/Threads/ThreadList/ThreadListCoordinator.swift index a870d8808..618d967c5 100644 --- a/Riot/Modules/Threads/ThreadList/ThreadListCoordinator.swift +++ b/Riot/Modules/Threads/ThreadList/ThreadListCoordinator.swift @@ -65,11 +65,11 @@ extension ThreadListCoordinator: ThreadListViewModelCoordinatorDelegate { self.delegate?.threadListCoordinatorDidLoadThreads(self) } - func threadListViewModelDidSelectThread(_ viewModel: ThreadListViewModelProtocol, thread: MXThread) { + func threadListViewModelDidSelectThread(_ viewModel: ThreadListViewModelProtocol, thread: MXThreadProtocol) { self.delegate?.threadListCoordinatorDidSelectThread(self, thread: thread) } - func threadListViewModelDidSelectThreadViewInRoom(_ viewModel: ThreadListViewModelProtocol, thread: MXThread) { + func threadListViewModelDidSelectThreadViewInRoom(_ viewModel: ThreadListViewModelProtocol, thread: MXThreadProtocol) { self.delegate?.threadListCoordinatorDidSelectRoom(self, roomId: thread.roomId, eventId: thread.id) } diff --git a/Riot/Modules/Threads/ThreadList/ThreadListCoordinatorProtocol.swift b/Riot/Modules/Threads/ThreadList/ThreadListCoordinatorProtocol.swift index e23505480..fb9b02bee 100644 --- a/Riot/Modules/Threads/ThreadList/ThreadListCoordinatorProtocol.swift +++ b/Riot/Modules/Threads/ThreadList/ThreadListCoordinatorProtocol.swift @@ -20,7 +20,7 @@ import Foundation protocol ThreadListCoordinatorDelegate: AnyObject { func threadListCoordinatorDidLoadThreads(_ coordinator: ThreadListCoordinatorProtocol) - func threadListCoordinatorDidSelectThread(_ coordinator: ThreadListCoordinatorProtocol, thread: MXThread) + func threadListCoordinatorDidSelectThread(_ coordinator: ThreadListCoordinatorProtocol, thread: MXThreadProtocol) func threadListCoordinatorDidSelectRoom(_ coordinator: ThreadListCoordinatorProtocol, roomId: String, eventId: String) func threadListCoordinatorDidCancel(_ coordinator: ThreadListCoordinatorProtocol) } diff --git a/Riot/Modules/Threads/ThreadList/ThreadListViewModel.swift b/Riot/Modules/Threads/ThreadList/ThreadListViewModel.swift index 381c19f52..e63a63703 100644 --- a/Riot/Modules/Threads/ThreadList/ThreadListViewModel.swift +++ b/Riot/Modules/Threads/ThreadList/ThreadListViewModel.swift @@ -26,12 +26,12 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { private let session: MXSession private let roomId: String - private var threads: [MXThread] = [] + private var threads: [MXThreadProtocol] = [] private var eventFormatter: MXKEventFormatter? private var roomState: MXRoomState? private var currentOperation: MXHTTPOperation? - private var longPressedThread: MXThread? + private var longPressedThread: MXThreadProtocol? // MARK: Public @@ -144,7 +144,7 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { // MARK: - Private - private func model(forThread thread: MXThread) -> ThreadModel { + private func model(forThread thread: MXThreadProtocol) -> ThreadModel { let rootAvatarViewData: AvatarViewData? let rootMessageSender: MXUser? let lastAvatarViewData: AvatarViewData? @@ -199,7 +199,7 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { notificationStatus: notificationStatus) } - private func rootMessageText(forThread thread: MXThread) -> NSAttributedString? { + private func rootMessageText(forThread thread: MXThreadProtocol) -> NSAttributedString? { guard let eventFormatter = eventFormatter else { return nil } @@ -229,7 +229,7 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { error: formatterError) } - private func lastMessageTextAndTime(forThread thread: MXThread) -> (NSAttributedString?, String?) { + private func lastMessageTextAndTime(forThread thread: MXThreadProtocol) -> (NSAttributedString?, String?) { guard let eventFormatter = eventFormatter else { return (nil, nil) } @@ -250,23 +250,36 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { if showLoading { viewState = .loading } + + let onlyParticipated: Bool switch selectedFilterType { case .all: - threads = session.threadingService.threads(inRoom: roomId) + onlyParticipated = false case .myThreads: - threads = session.threadingService.participatedThreads(inRoom: roomId) + onlyParticipated = true } + session.threadingService.allThreads(inRoom: roomId, + onlyParticipated: onlyParticipated) { [weak self] response in + guard let self = self else { return } + switch response { + case .success(let threads): + self.threads = threads + self.threadsLoaded() + case .failure(let error): + MXLog.error("[ThreadListViewModel] loadData: error: \(error)") + self.viewState = .error(error) + } + } + } + + private func threadsLoaded() { if threads.isEmpty { viewState = .empty(emptyViewModel) return } - - threadsLoaded() - } - - private func threadsLoaded() { + guard let eventFormatter = session.roomSummaryUpdateDelegate as? MXKEventFormatter, let room = session.room(withRoomId: roomId) else { // go into loaded state @@ -323,7 +336,7 @@ final class ThreadListViewModel: ThreadListViewModelProtocol { private func actionShare() { guard let thread = longPressedThread, - let index = threads.firstIndex(of: thread) else { + let index = threads.firstIndex(where: { thread.id == $0.id }) else { return } if let permalink = MXTools.permalink(toEvent: thread.id, inRoom: thread.roomId), diff --git a/Riot/Modules/Threads/ThreadList/ThreadListViewModelProtocol.swift b/Riot/Modules/Threads/ThreadList/ThreadListViewModelProtocol.swift index 3720f0c35..09db9be20 100644 --- a/Riot/Modules/Threads/ThreadList/ThreadListViewModelProtocol.swift +++ b/Riot/Modules/Threads/ThreadList/ThreadListViewModelProtocol.swift @@ -24,8 +24,8 @@ protocol ThreadListViewModelViewDelegate: AnyObject { protocol ThreadListViewModelCoordinatorDelegate: AnyObject { func threadListViewModelDidLoadThreads(_ viewModel: ThreadListViewModelProtocol) - func threadListViewModelDidSelectThread(_ viewModel: ThreadListViewModelProtocol, thread: MXThread) - func threadListViewModelDidSelectThreadViewInRoom(_ viewModel: ThreadListViewModelProtocol, thread: MXThread) + func threadListViewModelDidSelectThread(_ viewModel: ThreadListViewModelProtocol, thread: MXThreadProtocol) + func threadListViewModelDidSelectThreadViewInRoom(_ viewModel: ThreadListViewModelProtocol, thread: MXThreadProtocol) func threadListViewModelDidCancel(_ viewModel: ThreadListViewModelProtocol) } diff --git a/Riot/Modules/Threads/ThreadList/Views/Cell/ThreadModel.swift b/Riot/Modules/Threads/ThreadList/Views/Cell/ThreadModel.swift index 1692b9f6c..6667d5bfc 100644 --- a/Riot/Modules/Threads/ThreadList/Views/Cell/ThreadModel.swift +++ b/Riot/Modules/Threads/ThreadList/Views/Cell/ThreadModel.swift @@ -32,7 +32,7 @@ enum ThreadNotificationStatus { case notified case highlighted - init(withThread thread: MXThread) { + init(withThread thread: MXThreadProtocol) { if thread.highlightCount > 0 { self = .highlighted } else if thread.isParticipated && thread.notificationCount > 0 { diff --git a/Riot/Modules/Threads/ThreadsCoordinator.swift b/Riot/Modules/Threads/ThreadsCoordinator.swift index f5857dcc9..d645decd3 100644 --- a/Riot/Modules/Threads/ThreadsCoordinator.swift +++ b/Riot/Modules/Threads/ThreadsCoordinator.swift @@ -153,7 +153,7 @@ extension ThreadsCoordinator: ThreadListCoordinatorDelegate { } - func threadListCoordinatorDidSelectThread(_ coordinator: ThreadListCoordinatorProtocol, thread: MXThread) { + func threadListCoordinatorDidSelectThread(_ coordinator: ThreadListCoordinatorProtocol, thread: MXThreadProtocol) { let roomCoordinator = createThreadCoordinator(forThreadId: thread.id) selectedThreadCoordinator = roomCoordinator roomCoordinator.start() diff --git a/Riot/Modules/Threads/ThreadsCoordinatorBridgePresenter.swift b/Riot/Modules/Threads/ThreadsCoordinatorBridgePresenter.swift index 43879b292..2fb6fc14b 100644 --- a/Riot/Modules/Threads/ThreadsCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Threads/ThreadsCoordinatorBridgePresenter.swift @@ -28,7 +28,8 @@ import Foundation /// ThreadsCoordinatorBridgePresenter enables to start ThreadsCoordinator from a view controller. /// This bridge is used while waiting for global usage of coordinator pattern. -/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator. +/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). Each bridge should be removed +/// once the underlying Coordinator has been integrated by another Coordinator. @objcMembers final class ThreadsCoordinatorBridgePresenter: NSObject { diff --git a/Riot/Modules/UserDevices/UsersDevicesViewController.m b/Riot/Modules/UserDevices/UsersDevicesViewController.m index 308f0afcd..6e2145c4c 100644 --- a/Riot/Modules/UserDevices/UsersDevicesViewController.m +++ b/Riot/Modules/UserDevices/UsersDevicesViewController.m @@ -279,9 +279,9 @@ [self stopActivityIndicator]; [self dismissViewControllerAnimated:YES completion:nil]; - if (onCompleteBlock) + if (self->onCompleteBlock) { - onCompleteBlock(YES); + self->onCompleteBlock(YES); } }]; } diff --git a/Riot/Modules/UserDevices/Views/DeviceTableViewCell.m b/Riot/Modules/UserDevices/Views/DeviceTableViewCell.m index 7fde39ca4..d2f3a045c 100644 --- a/Riot/Modules/UserDevices/Views/DeviceTableViewCell.m +++ b/Riot/Modules/UserDevices/Views/DeviceTableViewCell.m @@ -55,7 +55,7 @@ case MXDeviceUnknown: case MXDeviceUnverified: { - self.deviceStatus.image = [UIImage imageNamed:@"e2e_warning"]; + self.deviceStatus.image = AssetImages.e2eWarning.image; [_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateNormal]; [_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateHighlighted]; @@ -65,7 +65,7 @@ } case MXDeviceVerified: { - self.deviceStatus.image = [UIImage imageNamed:@"e2e_verified"]; + self.deviceStatus.image = AssetSharedImages.e2eVerified.image; [_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnverify] forState:UIControlStateNormal]; [_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnverify] forState:UIControlStateHighlighted]; @@ -76,7 +76,7 @@ } case MXDeviceBlocked: { - self.deviceStatus.image = [UIImage imageNamed:@"e2e_blocked"]; + self.deviceStatus.image = AssetImages.e2eBlocked.image; [_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateNormal]; [_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateHighlighted]; diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index efb79d40e..88c504bf0 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -82,7 +82,7 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; CGSize imageSize = CGSizeMake(20, 20); NSTextAttachment *attachment = [[NSTextAttachment alloc] init]; - attachment.image = [[[UIImage imageNamed:@"room_context_menu_delete"] vc_resizedWith:imageSize] vc_tintedImageUsingColor:color]; + attachment.image = [[AssetImages.roomContextMenuDelete.image vc_resizedWith:imageSize] vc_tintedImageUsingColor:color]; attachment.bounds = CGRectMake(0, font.descender, imageSize.width, imageSize.height); NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:attachment]; @@ -247,7 +247,7 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; { // Track e2e failures dispatch_async(dispatch_get_main_queue(), ^{ - [[DecryptionFailureTracker sharedInstance] reportUnableToDecryptErrorForEvent:event withRoomState:roomState myUser:mxSession.myUser.userId]; + [[DecryptionFailureTracker sharedInstance] reportUnableToDecryptErrorForEvent:event withRoomState:roomState myUser:self->mxSession.myUser.userId]; }); if (event.decryptionError.code == MXDecryptingErrorUnknownInboundSessionIdCode) diff --git a/Riot/target.yml b/Riot/target.yml index f8f52a7da..1abcaae6c 100644 --- a/Riot/target.yml +++ b/Riot/target.yml @@ -35,6 +35,7 @@ targets: - target: SiriIntents - target: RiotNSE - target: DesignKit + - target: CommonKit - package: Mapbox configFiles: diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index 0cba64e18..533d35549 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -396,7 +396,7 @@ class NotificationService: UNNotificationServiceExtension { } if !isHighlighted { - #warning("In practice, this only hides the notification's content. An empty notification may be less useful in this instance?") + // In practice, this only hides the notification's content. An empty notification may be less useful in this instance? // Ignore this notif. MXLog.debug("[NotificationService] notificationContentForEvent: Ignore non highlighted notif in mentions only room") onComplete(nil, false) diff --git a/RiotNSE/target.yml b/RiotNSE/target.yml index 68a86ca51..77e72852f 100644 --- a/RiotNSE/target.yml +++ b/RiotNSE/target.yml @@ -63,8 +63,9 @@ targets: - path: ../Riot/PropertyWrappers/UserDefaultsBackedPropertyWrapper.swift - path: ../Riot/Modules/MatrixKit - path: ../Riot/Modules/Analytics + - path: ../Riot/Managers/UserSessions - path: ../Riot/Managers/AppInfo/ excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Generated/MatrixKitStrings.swift - - path: ../Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineStyleIdentifier.swift + - path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift diff --git a/RiotShareExtension/Shared/View/RecentRoomTableViewCell.m b/RiotShareExtension/Shared/View/RecentRoomTableViewCell.m index d5f277193..b33952e20 100644 --- a/RiotShareExtension/Shared/View/RecentRoomTableViewCell.m +++ b/RiotShareExtension/Shared/View/RecentRoomTableViewCell.m @@ -56,8 +56,8 @@ self.contentView.backgroundColor = ThemeService.shared.theme.backgroundColor; self.selectionButton.tintColor = ThemeService.shared.theme.tintColor; - [self.selectionButton setImage:[UIImage imageNamed:@"radio-button-default"] forState:UIControlStateNormal]; - [self.selectionButton setImage:[UIImage imageNamed:@"radio-button-selected"] forState:UIControlStateSelected]; + [self.selectionButton setImage:AssetSharedImages.radioButtonDefault.image forState:UIControlStateNormal]; + [self.selectionButton setImage:AssetSharedImages.radioButtonSelected.image forState:UIControlStateSelected]; [self.selectionButton setTitle:@"" forState:UIControlStateNormal]; [self.selectionButton setTitle:@"" forState:UIControlStateSelected]; diff --git a/RiotShareExtension/target.yml b/RiotShareExtension/target.yml index 494407323..44ebc5312 100644 --- a/RiotShareExtension/target.yml +++ b/RiotShareExtension/target.yml @@ -60,6 +60,7 @@ targets: - path: ../Riot/Modules/Common/Recents/Model/RecentCellData.m - path: ../Riot/PropertyWrappers/UserDefaultsBackedPropertyWrapper.swift - path: ../Riot/Generated/Strings.swift + - path: ../Riot/Generated/Images.swift - path: ../Riot/Managers/Locale/LocaleProviderType.swift - path: ../Riot/Managers/Locale/LocaleProvider.swift - path: ../Riot/Modules/Common/SegmentedViewController/SegmentedViewController.xib @@ -70,7 +71,8 @@ targets: buildPhase: resources - path: ../Riot/Modules/MatrixKit - path: ../Riot/Modules/Analytics + - path: ../Riot/Managers/UserSessions excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Generated/MatrixKitStrings.swift - - path: ../Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineStyleIdentifier.swift + - path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift index 2cfdb6e0f..6e4e34f30 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift @@ -42,18 +42,13 @@ struct AnalyticsPromptViewState: BindableState { /// A collection of strings for the UI that need to be created in /// the coordinator or mocked in the RiotSwiftUI target. protocol AnalyticsPromptStringsProtocol { - var appDisplayName: String { get } - var point1: NSAttributedString { get } var point2: NSAttributedString { get } - - var termsNewUser: NSAttributedString { get } - var termsUpgrade: NSAttributedString { get } } enum AnalyticsPromptType { - case newUser(termsString: NSAttributedString) - case upgrade(termsString: NSAttributedString) + case newUser + case upgrade } extension AnalyticsPromptType { @@ -67,11 +62,23 @@ extension AnalyticsPromptType { } } - /// The terms string that should be displayed. - var termsStrings: NSAttributedString { + /// The main part of the terms string that should be displayed. + var mainTermsString: String { switch self { - case .newUser(let termsString), .upgrade(let termsString): - return termsString + case .newUser: + return VectorL10n.analyticsPromptTermsNewUser("%@") + case .upgrade: + return VectorL10n.analyticsPromptTermsUpgrade("%@") + } + } + + /// The tappable part of the terms string that should be displayed. + var termsLinkString: String { + switch self { + case .newUser: + return VectorL10n.analyticsPromptTermsLinkNewUser + case .upgrade: + return VectorL10n.analyticsPromptTermsLinkUpgrade } } @@ -96,15 +103,7 @@ extension AnalyticsPromptType { } } -extension AnalyticsPromptType: CaseIterable { - static var allCases: [AnalyticsPromptType] { - let strings = MockAnalyticsPromptStrings() - return [ - .newUser(termsString: strings.termsNewUser), - .upgrade(termsString: strings.termsUpgrade) - ] - } -} +extension AnalyticsPromptType: CaseIterable { } extension AnalyticsPromptType: Identifiable { var id: String { diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptCoordinator.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptCoordinator.swift index 6e107b62b..32884fea0 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptCoordinator.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptCoordinator.swift @@ -52,9 +52,9 @@ final class AnalyticsPromptCoordinator: Coordinator, Presentable { let promptType: AnalyticsPromptType if Analytics.shared.promptShouldDisplayUpgradeMessage { - promptType = .upgrade(termsString: strings.termsUpgrade) + promptType = .upgrade } else { - promptType = .newUser(termsString: strings.termsNewUser) + promptType = .newUser } let viewModel = AnalyticsPromptViewModel(promptType: promptType, strings: strings, termsURL: BuildSettings.analyticsTermsURL) diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptStrings.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptStrings.swift index 4ce8ab20d..cc653f6d5 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptStrings.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptStrings.swift @@ -18,16 +18,7 @@ import Foundation @available(iOS 14.0, *) struct AnalyticsPromptStrings: AnalyticsPromptStringsProtocol { - let appDisplayName = AppInfo.current.displayName - let point1 = HTMLFormatter().formatHTML(VectorL10n.analyticsPromptPoint1, withAllowedTags: ["b", "p"], fontSize: UIFont.systemFontSize) let point2 = HTMLFormatter().formatHTML(VectorL10n.analyticsPromptPoint2, withAllowedTags: ["b", "p"], fontSize: UIFont.systemFontSize) - - let termsNewUser = HTMLFormatter().format(VectorL10n.analyticsPromptTermsNewUser("%@"), - with: VectorL10n.analyticsPromptTermsLinkNewUser, - using: BuildSettings.analyticsTermsURL) - let termsUpgrade = HTMLFormatter().format(VectorL10n.analyticsPromptTermsUpgrade("%@"), - with: VectorL10n.analyticsPromptTermsLinkUpgrade, - using: BuildSettings.analyticsTermsURL) } diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/MockAnalyticsPromptStrings.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/MockAnalyticsPromptStrings.swift index ee0e59ed0..37a38e32d 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/MockAnalyticsPromptStrings.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/MockAnalyticsPromptStrings.swift @@ -17,14 +17,9 @@ import UIKit struct MockAnalyticsPromptStrings: AnalyticsPromptStringsProtocol { - var appDisplayName = "Element" - let point1: NSAttributedString let point2: NSAttributedString - let termsNewUser: NSAttributedString - let termsUpgrade: NSAttributedString - let shortString = NSAttributedString(string: "This is a short string.") let longString = NSAttributedString(string: "This is a very long string that will be used to test the layout over multiple lines of text to ensure everything is correct.") @@ -38,15 +33,5 @@ struct MockAnalyticsPromptStrings: AnalyticsPromptStringsProtocol { point2.append(NSAttributedString(string: "don't", attributes: [.font: UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)])) point2.append(NSAttributedString(string: " share information with third parties")) self.point2 = point2 - - let termsNewUser = NSMutableAttributedString(string: "You can read all our terms ") - termsNewUser.append(NSAttributedString(string: "here", attributes: [.link: URL(string: "https://element.io/cookie-policy")!])) - termsNewUser.append(NSAttributedString(string: ".")) - self.termsNewUser = termsNewUser - - let termsUpgrade = NSMutableAttributedString(string: "Read all our terms ") - termsUpgrade.append(NSAttributedString(string: "here", attributes: [.link: URL(string: "https://element.io/cookie-policy")!])) - termsUpgrade.append(NSAttributedString(string: ". Is that OK?")) - self.termsUpgrade = termsUpgrade } } diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPrompt.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPrompt.swift index 5e622ab5d..178d1d215 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPrompt.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPrompt.swift @@ -42,12 +42,10 @@ struct AnalyticsPrompt: View { VStack { Text("\(viewModel.viewState.promptType.message)\n") - AnalyticsPromptTermsText(attributedString: viewModel.viewState.promptType.termsStrings) - .accessibilityLabel(Text(viewModel.viewState.promptType.termsStrings.string)) - .accessibilityValue(Text(VectorL10n.accessibilityButtonLabel)) - .onTapGesture { - viewModel.send(viewAction: .openTermsURL) - } + InlineTextButton(viewModel.viewState.promptType.mainTermsString, + tappableText: viewModel.viewState.promptType.termsLinkString) { + viewModel.send(viewAction: .openTermsURL) + } } } @@ -71,7 +69,7 @@ struct AnalyticsPrompt: View { Image(uiImage: Asset.Images.analyticsLogo.image) .padding(.bottom, 25) - Text(VectorL10n.analyticsPromptTitle(viewModel.viewState.strings.appDisplayName)) + Text(VectorL10n.analyticsPromptTitle(AppInfo.current.displayName)) .font(theme.fonts.title2B) .foregroundColor(theme.colors.primaryContent) .padding(.bottom, 2) @@ -125,6 +123,7 @@ struct AnalyticsPrompt: View { .padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 16) } .background(theme.colors.background.ignoresSafeArea()) + .accentColor(theme.colors.accent) } } } diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPromptTermsText.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPromptTermsText.swift deleted file mode 100644 index 7616e1084..000000000 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPromptTermsText.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright 2021 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 - -@available(iOS 14.0, *) -/// The last line of text in the description with highlighting on the link string. -struct AnalyticsPromptTermsText: View { - - // MARK: - Properties - - // MARK: Private - - @Environment(\.theme) private var theme - - /// A string with a link attribute. - private struct StringComponent { - let string: String - let isLink: Bool - } - - /// Internal representation of the string as composable parts. - private let components: [StringComponent] - - // MARK: - Setup - - init(attributedString: NSAttributedString) { - var components = [StringComponent]() - let range = NSRange(location: 0, length: attributedString.length) - let string = attributedString.string as NSString - - attributedString.enumerateAttributes(in: range, options: []) { attributes, range, stop in - let isLink = attributes.keys.contains(.link) - components.append(StringComponent(string: string.substring(with: range), isLink: isLink)) - } - - self.components = components - } - - // MARK: - Views - - var body: some View { - components.reduce(Text("")) { - $0 + Text($1.string).foregroundColor($1.isLink ? theme.colors.accent : nil) - } - } -} - -// MARK: - Previews -@available(iOS 14.0, *) -struct AnalyticsPromptTermsText_Previews: PreviewProvider { - - static let strings = MockAnalyticsPromptStrings() - - static var previews: some View { - VStack(spacing: 8) { - AnalyticsPromptTermsText(attributedString: strings.termsNewUser) - AnalyticsPromptTermsText(attributedString: strings.termsUpgrade) - } - } -} diff --git a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift index bf139307f..242e4c088 100644 --- a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift +++ b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift @@ -20,6 +20,7 @@ import Foundation @available(iOS 14.0, *) enum MockAppScreens { static let appScreens: [MockScreenState.Type] = [ + MockOnboardingUseCaseSelectionScreenState.self, MockOnboardingSplashScreenScreenState.self, MockLocationSharingScreenState.self, MockAnalyticsPromptScreenState.self, diff --git a/RiotSwiftUI/Modules/Common/Util/InlineTextButton.swift b/RiotSwiftUI/Modules/Common/Util/InlineTextButton.swift new file mode 100644 index 000000000..1b1924b0b --- /dev/null +++ b/RiotSwiftUI/Modules/Common/Util/InlineTextButton.swift @@ -0,0 +1,89 @@ +// +// Copyright 2021 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 + +@available(iOS, introduced: 14.0, deprecated: 15.0, message: "Use Text with an AttributedString instead that includes a link and handle the tap by adding an OpenURLAction to the environment.") +/// A `Button`, that fakes having a tappable string inside of a regular string. +struct InlineTextButton: View { + + private struct StringComponent { + let string: Substring + let isTinted: Bool + } + + // MARK: - Properties + + // MARK: Private + + /// The individual components of the string. + private let components: [StringComponent] + private let action: () -> Void + + + // MARK: - Setup + + /// Creates a new `InlineTextButton`. + /// - Parameters: + /// - mainText: The main text that shouldn't appear tappable. This must contain a single `%@` placeholder somewhere within. + /// - tappableText: The tappable text that will be substituted into the `%@` placeholder. + /// - action: The action to perform when tapping the button. + internal init(_ mainText: String, tappableText: String, action: @escaping () -> Void) { + guard let range = mainText.range(of: "%@") else { + self.components = [StringComponent(string: Substring(mainText), isTinted: false)] + self.action = action + return + } + + let firstComponent = StringComponent(string: mainText[.. some View { + components.reduce(Text("")) { lastValue, component in + lastValue + Text(component.string) + .foregroundColor(component.isTinted ? .accentColor.opacity(configuration.isPressed ? 0.2 : 1) : nil) + } + } + } +} + +@available(iOS 14.0, *) +struct Previews_InlineButtonText_Previews: PreviewProvider { + static var previews: some View { + InlineTextButton("Hello there this is a sentence. %@.", + tappableText: "And this is a button", + action: { }) + .padding() + } +} diff --git a/RiotSwiftUI/Modules/Common/Util/StyledText.swift b/RiotSwiftUI/Modules/Common/Util/StyledText.swift new file mode 100644 index 000000000..fbb7919f2 --- /dev/null +++ b/RiotSwiftUI/Modules/Common/Util/StyledText.swift @@ -0,0 +1,126 @@ +// +// Copyright 2021 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 +import DesignKit + +@available(iOS, introduced: 14.0, deprecated: 15.0, message: "Use Text with an AttributedString instead.") +/// A `Text` view that renders attributed strings with their `.font` and `.foregroundColor` attributes. +/// This view is a workaround for iOS 13/14 not supporting `AttributedString`. +struct StyledText: View { + + // MARK: - Properties + + // MARK: Private + + @Environment(\.theme) private var theme + + /// A string with a bold property. + private struct StringComponent { + let string: String + var font: Font? = nil + var color: Color? = nil + } + + /// Internal representation of the string as composable parts. + private let components: [StringComponent] + + // MARK: - Setup + + /// Creates a `StyledText` using the supplied attributed string. + /// - Parameter attributedString: The attributed string to display. + init(_ attributedString: NSAttributedString) { + var components = [StringComponent]() + let range = NSRange(location: 0, length: attributedString.length) + let string = attributedString.string as NSString + + attributedString.enumerateAttributes(in: range, options: []) { attributes, range, stop in + let font = attributes[.font] as? UIFont + let color = attributes[.foregroundColor] as? UIColor + + let component = StringComponent( + string: string.substring(with: range), + font: font.map { Font($0) }, + color: color.map { Color($0) } + ) + + components.append(component) + } + + self.components = components + } + + /// Creates a `StyledText` using a plain string. + /// - Parameter string: The plain string to display + init(_ string: String) { + self.components = [StringComponent(string: string, font: nil)] + } + + // MARK: - Views + + var body: some View { + components.reduce(Text("")) { lastValue, component in + lastValue + Text(component.string) + .font(component.font) + .foregroundColor(component.color) + } + } +} + + +@available(iOS 14.0, *) +struct StyledText_Previews: PreviewProvider { + static func prettyText() -> NSAttributedString { + let string = NSMutableAttributedString(string: "T", attributes: [ + .font: UIFont.boldSystemFont(ofSize: 12), + .foregroundColor: UIColor.red + ]) + string.append(NSAttributedString(string: "e", attributes: [ + .font: UIFont.boldSystemFont(ofSize: 14), + .foregroundColor: UIColor.orange + ])) + string.append(NSAttributedString(string: "s", attributes: [ + .font: UIFont.boldSystemFont(ofSize: 13), + .foregroundColor: UIColor.yellow + ])) + string.append(NSAttributedString(string: "t", attributes: [ + .font: UIFont.boldSystemFont(ofSize: 15), + .foregroundColor: UIColor.green + ])) + string.append(NSAttributedString(string: "i", attributes: [ + .font: UIFont.boldSystemFont(ofSize: 11), + .foregroundColor: UIColor.cyan + ])) + string.append(NSAttributedString(string: "n", attributes: [ + .font: UIFont.boldSystemFont(ofSize: 16), + .foregroundColor: UIColor.blue + ])) + string.append(NSAttributedString(string: "g", attributes: [ + .font: UIFont.boldSystemFont(ofSize: 14), + .foregroundColor: UIColor.purple + ])) + return string + } + + static var previews: some View { + VStack(spacing: 8) { + StyledText("Hello, World!") + StyledText(NSAttributedString(string: "Testing", + attributes: [.font: UIFont.boldSystemFont(ofSize: 64)])) + StyledText(prettyText()) + } + } +} diff --git a/RiotSwiftUI/Modules/Onboarding/Common/OnboardingButtonStyle.swift b/RiotSwiftUI/Modules/Onboarding/Common/OnboardingButtonStyle.swift new file mode 100644 index 000000000..73a591d86 --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/Common/OnboardingButtonStyle.swift @@ -0,0 +1,32 @@ +// +// Copyright 2021 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 + +@available(iOS 14.0, *) +struct OnboardingButtonStyle: ButtonStyle { + @Environment(\.theme) private var theme + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .frame(maxWidth: .infinity, alignment: .leading) + .background( + RoundedRectangle(cornerRadius: 8) + .stroke(configuration.isPressed ? theme.colors.accent : theme.colors.quinaryContent, lineWidth: configuration.isPressed ? 2 : 1.5) + ) + .contentShape(RoundedRectangle(cornerRadius: 8)) + } +} diff --git a/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPage.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPage.swift index 53a71d301..9ec08b950 100644 --- a/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPage.swift +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPage.swift @@ -48,6 +48,7 @@ struct OnboardingSplashScreenPage: View { .scaledToFit() .frame(maxWidth: 300) .padding(20) + .accessibilityHidden(true) VStack(spacing: 8) { OnboardingSplashScreenTitleText(content.title) diff --git a/RiotSwiftUI/Modules/Onboarding/UseCase/Coordinator/OnboardingUseCaseSelectionCoordinator.swift b/RiotSwiftUI/Modules/Onboarding/UseCase/Coordinator/OnboardingUseCaseSelectionCoordinator.swift new file mode 100644 index 000000000..0a2d7eae4 --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/UseCase/Coordinator/OnboardingUseCaseSelectionCoordinator.swift @@ -0,0 +1,61 @@ +// +// Copyright 2021 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 + +final class OnboardingUseCaseSelectionCoordinator: Coordinator, Presentable { + + // MARK: - Properties + + // MARK: Private + + private let onboardingUseCaseHostingController: UIViewController + private var onboardingUseCaseViewModel: OnboardingUseCaseViewModelProtocol + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + var completion: ((OnboardingUseCaseViewModelResult) -> Void)? + + // MARK: - Setup + + @available(iOS 14.0, *) + init() { + let viewModel = OnboardingUseCaseViewModel() + let view = OnboardingUseCaseSelectionScreen(viewModel: viewModel.context) + onboardingUseCaseViewModel = viewModel + + let hostingController = VectorHostingController(rootView: view) + hostingController.vc_removeBackTitle() + hostingController.enableNavigationBarScrollEdgesAppearance = true + onboardingUseCaseHostingController = hostingController + } + + // MARK: - Public + func start() { + MXLog.debug("[OnboardingUseCaseSelectionCoordinator] did start.") + onboardingUseCaseViewModel.completion = { [weak self] result in + MXLog.debug("[OnboardingUseCaseSelectionCoordinator] OnboardingUseCaseViewModel did complete with result: \(result).") + guard let self = self else { return } + self.completion?(result) + } + } + + func toPresentable() -> UIViewController { + return self.onboardingUseCaseHostingController + } +} diff --git a/RiotSwiftUI/Modules/Onboarding/UseCase/MockOnboardingUseCaseScreenState.swift b/RiotSwiftUI/Modules/Onboarding/UseCase/MockOnboardingUseCaseScreenState.swift new file mode 100644 index 000000000..1cce35b23 --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/UseCase/MockOnboardingUseCaseScreenState.swift @@ -0,0 +1,52 @@ +// +// Copyright 2021 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 SwiftUI + +/// Using an enum for the screen allows you define the different state cases with +/// the relevant associated data for each case. +@available(iOS 14.0, *) +enum MockOnboardingUseCaseSelectionScreenState: MockScreenState, CaseIterable { + // A case for each state you want to represent + // with specific, minimal associated data that will allow you + // mock that screen. + case `default` + + /// The associated screen + var screenType: Any.Type { + OnboardingUseCaseSelectionScreen.self + } + + /// A list of screen state definitions + static var allCases: [MockOnboardingUseCaseSelectionScreenState] { + // Each of the presence statuses + [.default] + } + + /// Generate the view struct for the screen state. + var screenView: ([Any], AnyView) { + let viewModel = OnboardingUseCaseViewModel() + + // can simulate service and viewModel actions here if needs be. + + return ( + [self, viewModel], + AnyView(OnboardingUseCaseSelectionScreen(viewModel: viewModel.context) + .addDependency(MockAvatarService.example)) + ) + } +} diff --git a/RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseModels.swift b/RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseModels.swift new file mode 100644 index 000000000..c67798b6d --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseModels.swift @@ -0,0 +1,41 @@ +// +// Copyright 2021 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 + +// MARK: - Coordinator + +// MARK: View model + +enum OnboardingUseCaseStateAction { + case viewAction(OnboardingUseCaseViewAction) +} + +enum OnboardingUseCaseViewModelResult { + case personalMessaging + case workMessaging + case communityMessaging + case skipped + case customServer +} + +// MARK: View + +struct OnboardingUseCaseViewState: BindableState { } + +enum OnboardingUseCaseViewAction { + case answer(OnboardingUseCaseViewModelResult) +} diff --git a/RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseViewModel.swift b/RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseViewModel.swift new file mode 100644 index 000000000..89a4ae9f1 --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseViewModel.swift @@ -0,0 +1,52 @@ +// +// Copyright 2021 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 + +@available(iOS 14, *) +typealias OnboardingUseCaseViewModelType = StateStoreViewModel +@available(iOS 14, *) +class OnboardingUseCaseViewModel: OnboardingUseCaseViewModelType, OnboardingUseCaseViewModelProtocol { + + // MARK: - Properties + + // MARK: Private + + // MARK: Public + + var completion: ((OnboardingUseCaseViewModelResult) -> Void)? + + // MARK: - Setup + + init() { + super.init(initialViewState: OnboardingUseCaseViewState()) + } + + // MARK: - Public + + override func process(viewAction: OnboardingUseCaseViewAction) { + switch viewAction { + case .answer(let result): + completion?(result) + } + } + + override class func reducer(state: inout OnboardingUseCaseViewState, action: OnboardingUseCaseStateAction) { + // There is no mutable state to reduce :) + } +} diff --git a/RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseViewModelProtocol.swift b/RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseViewModelProtocol.swift new file mode 100644 index 000000000..0c535b36c --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/UseCase/OnboardingUseCaseViewModelProtocol.swift @@ -0,0 +1,24 @@ +// +// Copyright 2021 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 OnboardingUseCaseViewModelProtocol { + + var completion: ((OnboardingUseCaseViewModelResult) -> Void)? { get set } + @available(iOS 14, *) + var context: OnboardingUseCaseViewModelType.Context { get } +} diff --git a/RiotSwiftUI/Modules/Onboarding/UseCase/Test/UI/OnboardingUseCaseUITests.swift b/RiotSwiftUI/Modules/Onboarding/UseCase/Test/UI/OnboardingUseCaseUITests.swift new file mode 100644 index 000000000..0f254dfb3 --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/UseCase/Test/UI/OnboardingUseCaseUITests.swift @@ -0,0 +1,23 @@ +// +// Copyright 2021 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 XCTest +import RiotSwiftUI + +@available(iOS 14.0, *) +class OnboardingUseCaseUITests: MockScreenTest { + // The view has no parameters or changing state to test. +} diff --git a/RiotSwiftUI/Modules/Onboarding/UseCase/Test/Unit/OnboardingUseCaseViewModelTests.swift b/RiotSwiftUI/Modules/Onboarding/UseCase/Test/Unit/OnboardingUseCaseViewModelTests.swift new file mode 100644 index 000000000..9d3883faf --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/UseCase/Test/Unit/OnboardingUseCaseViewModelTests.swift @@ -0,0 +1,24 @@ +// +// Copyright 2021 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 XCTest + +@testable import RiotSwiftUI + +@available(iOS 14.0, *) +class OnboardingUseCaseViewModelTests: XCTestCase { + // The view model has nothing to test. +} diff --git a/RiotSwiftUI/Modules/Onboarding/UseCase/View/OnboardingUseCaseButton.swift b/RiotSwiftUI/Modules/Onboarding/UseCase/View/OnboardingUseCaseButton.swift new file mode 100644 index 000000000..0fedfedb0 --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/UseCase/View/OnboardingUseCaseButton.swift @@ -0,0 +1,59 @@ +// +// Copyright 2021 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 + +@available(iOS 14.0, *) +/// A button used for the Use Case selection. +struct OnboardingUseCaseButton: View { + + // MARK: Private + + @Environment(\.theme) private var theme + + // MARK: Public + + /// The button's title. + let title: String + /// The button's image. + let image: ImageAsset + + /// The button's action when tapped. + let action: () -> Void + + var body: some View { + Button(action: action) { + HStack(spacing: 16) { + Image(image.name) + Text(title) + .font(theme.fonts.bodySB) + .foregroundColor(theme.colors.primaryContent) + } + .padding(16) + } + .buttonStyle(OnboardingButtonStyle()) + } +} + +@available(iOS 14.0, *) +struct Previews_OnboardingUseCaseButton_Previews: PreviewProvider { + static var previews: some View { + OnboardingUseCaseButton(title: VectorL10n.onboardingUseCaseWorkMessaging, + image: Asset.Images.onboardingUseCaseWork, + action: { }) + .padding(16) + } +} diff --git a/RiotSwiftUI/Modules/Onboarding/UseCase/View/OnboardingUseCaseSelectionScreen.swift b/RiotSwiftUI/Modules/Onboarding/UseCase/View/OnboardingUseCaseSelectionScreen.swift new file mode 100644 index 000000000..34c9363ed --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/UseCase/View/OnboardingUseCaseSelectionScreen.swift @@ -0,0 +1,130 @@ +// +// Copyright 2021 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 + +@available(iOS 14.0, *) +/// The screen shown to a new user to select their use case for the app. +struct OnboardingUseCaseSelectionScreen: View { + + // MARK: - Properties + + // MARK: Private + + @Environment(\.theme) private var theme + + // MARK: Public + + @ObservedObject var viewModel: OnboardingUseCaseViewModel.Context + + /// The screen's title and instructions. + var titleContent: some View { + VStack(spacing: 8) { + Image(Asset.Images.onboardingUseCaseIcon.name) + .padding(.bottom, 8) + .accessibilityHidden(true) + + Text(VectorL10n.onboardingUseCaseTitle) + .font(theme.fonts.title2B) + .foregroundColor(theme.colors.primaryContent) + + Text(VectorL10n.onboardingUseCaseMessage) + .font(theme.fonts.body) + .foregroundColor(theme.colors.secondaryContent) + } + } + + /// The buttons used to select a use case for the app. + var useCaseButtons: some View { + VStack(spacing: 8) { + OnboardingUseCaseButton(title: VectorL10n.onboardingUseCasePersonalMessaging, + image: theme.isDark ? Asset.Images.onboardingUseCasePersonalDark : Asset.Images.onboardingUseCasePersonal) { + viewModel.send(viewAction: .answer(.personalMessaging)) + } + + OnboardingUseCaseButton(title: VectorL10n.onboardingUseCaseWorkMessaging, + image: theme.isDark ? Asset.Images.onboardingUseCaseWorkDark : Asset.Images.onboardingUseCaseWork) { + viewModel.send(viewAction: .answer(.workMessaging)) + } + + OnboardingUseCaseButton(title: VectorL10n.onboardingUseCaseCommunityMessaging, + image: theme.isDark ? Asset.Images.onboardingUseCaseCommunityDark : Asset.Images.onboardingUseCaseCommunity) { + viewModel.send(viewAction: .answer(.communityMessaging)) + } + + InlineTextButton(VectorL10n.onboardingUseCaseNotSureYet("%@"), + tappableText: VectorL10n.onboardingUseCaseSkipButton) { + viewModel.send(viewAction: .answer(.skipped)) + } + .font(theme.fonts.subheadline) + .foregroundColor(theme.colors.tertiaryContent) + .padding(.top, 8) + } + } + + /// A footer showing a button to connect to a server. + var serverFooter: some View { + VStack(spacing: 14) { + Text(VectorL10n.onboardingUseCaseExistingServerMessage) + .font(theme.fonts.subheadline) + .foregroundColor(theme.colors.tertiaryContent) + + Button { viewModel.send(viewAction: .answer(.customServer)) } label: { + Text(VectorL10n.onboardingUseCaseExistingServerButton) + .font(theme.fonts.body) + } + } + } + + var body: some View { + GeometryReader { geometry in + VStack { + ScrollView { + VStack(spacing: 0) { + titleContent + .padding(.bottom, 36) + + useCaseButtons + } + .frame(maxWidth: OnboardingConstants.maxContentWidth, + maxHeight: OnboardingConstants.maxContentHeight) + .padding(16) + } + .frame(maxWidth: .infinity) + + serverFooter + .padding(.horizontal, 16) + .padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 20 : 36) + } + } + .background(theme.colors.background.ignoresSafeArea()) + .accentColor(theme.colors.accent) + } +} + +// MARK: - Previews + +@available(iOS 14.0, *) +struct OnboardingUseCase_Previews: PreviewProvider { + static let stateRenderer = MockOnboardingUseCaseSelectionScreenState.stateRenderer + static var previews: some View { + NavigationView { + stateRenderer.screenGroup() + .navigationBarTitleDisplayMode(.inline) + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/LocationSharingCoordinator.swift b/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/LocationSharingCoordinator.swift index 8edef1a65..b43f4f05d 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/LocationSharingCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/LocationSharingCoordinator.swift @@ -97,7 +97,8 @@ final class LocationSharingCoordinator: Coordinator, Presentable { static func shareLocationActivityController(_ location: CLLocationCoordinate2D) -> UIActivityViewController { return UIActivityViewController(activityItems: [ShareToMapsAppActivity.urlForMapsAppType(.apple, location: location)], applicationActivities: [ShareToMapsAppActivity(type: .apple, location: location), - ShareToMapsAppActivity(type: .google, location: location)]) + ShareToMapsAppActivity(type: .google, location: location), + ShareToMapsAppActivity(type: .osm, location: location)]) } // MARK: - Presentable diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/ShareToMapsAppActivity.swift b/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/ShareToMapsAppActivity.swift index 57dad80f8..aeac582c3 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/ShareToMapsAppActivity.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/ShareToMapsAppActivity.swift @@ -24,6 +24,7 @@ class ShareToMapsAppActivity: UIActivity { enum MapsAppType { case apple case google + case osm } private let type: MapsAppType @@ -44,6 +45,8 @@ class ShareToMapsAppActivity: UIActivity { return URL(string: "https://maps.apple.com?ll=\(location.latitude),\(location.longitude)&q=Pin")! case .google: return URL(string: "https://www.google.com/maps/search/?api=1&query=\(location.latitude),\(location.longitude)")! + case .osm: + return URL(string: "https://www.openstreetmap.org/?mlat=\(location.latitude)&mlon=\(location.longitude)")! } } @@ -53,6 +56,8 @@ class ShareToMapsAppActivity: UIActivity { return VectorL10n.locationSharingOpenAppleMaps case .google: return VectorL10n.locationSharingOpenGoogleMaps + case .osm: + return VectorL10n.locationSharingOpenOpenStreetMaps } } diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift index ca13a80f9..5c69349cc 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift @@ -32,66 +32,74 @@ struct LocationSharingView: View { var body: some View { NavigationView { - LocationSharingMapView(tileServerMapURL: context.viewState.mapStyleURL, - avatarData: context.viewState.avatarData, - location: context.viewState.location, - errorSubject: context.viewState.errorSubject, - userLocation: $context.userLocation) - .toolbar { - ToolbarItem(placement: .navigationBarLeading) { - Button(VectorL10n.cancel, action: { - context.send(viewAction: .cancel) - }) - } - ToolbarItem(placement: .principal) { - Text(VectorL10n.locationSharingTitle) - .font(.headline) - .foregroundColor(theme.colors.primaryContent) - } - ToolbarItem(placement: .navigationBarTrailing) { - if context.viewState.location != nil { - Button { - context.send(viewAction: .share) - } label: { - Image(uiImage: Asset.Images.locationShareIcon.image) - .accessibilityIdentifier("LocationSharingView.shareButton") - } - .disabled(!context.viewState.shareButtonEnabled) - } else { - Button(VectorL10n.locationSharingShareAction, action: { - context.send(viewAction: .share) - }) - .disabled(!context.viewState.shareButtonEnabled) + ZStack(alignment: .bottom) { + LocationSharingMapView(tileServerMapURL: context.viewState.mapStyleURL, + avatarData: context.viewState.avatarData, + location: context.viewState.location, + errorSubject: context.viewState.errorSubject, + userLocation: $context.userLocation) + .ignoresSafeArea() + + HStack { + Link("© MapTiler", destination: URL(string: "https://www.maptiler.com/copyright/")!) + Link("© OpenStreetMap contributors", destination: URL(string: "https://www.openstreetmap.org/copyright")!) + } + .font(theme.fonts.caption1) + } + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button(VectorL10n.cancel, action: { + context.send(viewAction: .cancel) + }) + } + ToolbarItem(placement: .principal) { + Text(VectorL10n.locationSharingTitle) + .font(.headline) + .foregroundColor(theme.colors.primaryContent) + } + ToolbarItem(placement: .navigationBarTrailing) { + if context.viewState.location != nil { + Button { + context.send(viewAction: .share) + } label: { + Image(uiImage: Asset.Images.locationShareIcon.image) + .accessibilityIdentifier("LocationSharingView.shareButton") } - } - } - .navigationBarTitleDisplayMode(.inline) - .ignoresSafeArea() - .alert(item: $context.alertInfo) { info in - if let secondaryButton = info.secondaryButton { - return Alert(title: Text(info.title), - message: subtitleTextForAlertInfo(info), - primaryButton: .default(Text(info.primaryButton.title)) { - info.primaryButton.action?() - }, - secondaryButton: .default(Text(secondaryButton.title)) { - secondaryButton.action?() - }) + .disabled(!context.viewState.shareButtonEnabled) } else { - return Alert(title: Text(info.title), - message: subtitleTextForAlertInfo(info), - dismissButton: .default(Text(info.primaryButton.title)) { - info.primaryButton.action?() - }) + Button(VectorL10n.locationSharingShareAction, action: { + context.send(viewAction: .share) + }) + .disabled(!context.viewState.shareButtonEnabled) } } + } + .navigationBarTitleDisplayMode(.inline) + .introspectNavigationController { navigationController in + ThemeService.shared().theme.applyStyle(onNavigationBar: navigationController.navigationBar) + } + .alert(item: $context.alertInfo) { info in + if let secondaryButton = info.secondaryButton { + return Alert(title: Text(info.title), + message: subtitleTextForAlertInfo(info), + primaryButton: .default(Text(info.primaryButton.title)) { + info.primaryButton.action?() + }, + secondaryButton: .default(Text(secondaryButton.title)) { + secondaryButton.action?() + }) + } else { + return Alert(title: Text(info.title), + message: subtitleTextForAlertInfo(info), + dismissButton: .default(Text(info.primaryButton.title)) { + info.primaryButton.action?() + }) + } + } } .accentColor(theme.colors.accent) .activityIndicator(show: context.viewState.showLoadingIndicator) .navigationViewStyle(StackNavigationViewStyle()) - .introspectNavigationController { navigationController in - ThemeService.shared().theme.applyStyle(onNavigationBar: navigationController.navigationBar) - } } @ViewBuilder diff --git a/RiotSwiftUI/Modules/Room/PollEditForm/Coordinator/PollEditFormCoordinator.swift b/RiotSwiftUI/Modules/Room/PollEditForm/Coordinator/PollEditFormCoordinator.swift index 2878648c8..a3fa803e2 100644 --- a/RiotSwiftUI/Modules/Room/PollEditForm/Coordinator/PollEditFormCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/PollEditForm/Coordinator/PollEditFormCoordinator.swift @@ -146,15 +146,17 @@ final class PollEditFormCoordinator: Coordinator, Presentable { } private static func pollDetailsTypeToKindKey(_ type: EditFormPollType) -> String { - let mapping = [EditFormPollType.disclosed : kMXMessageContentKeyExtensiblePollKindDisclosed, - EditFormPollType.undisclosed : kMXMessageContentKeyExtensiblePollKindUndisclosed] + let mapping = [EditFormPollType.disclosed : kMXMessageContentKeyExtensiblePollKindDisclosedMSC3381, + EditFormPollType.undisclosed : kMXMessageContentKeyExtensiblePollKindUndisclosedMSC3381] - return mapping[type] ?? kMXMessageContentKeyExtensiblePollKindDisclosed + return mapping[type] ?? kMXMessageContentKeyExtensiblePollKindDisclosedMSC3381 } private static func pollKindKeyToDetailsType(_ key: String) -> EditFormPollType { let mapping = [kMXMessageContentKeyExtensiblePollKindDisclosed : EditFormPollType.disclosed, - kMXMessageContentKeyExtensiblePollKindUndisclosed : EditFormPollType.undisclosed] + kMXMessageContentKeyExtensiblePollKindDisclosedMSC3381 : EditFormPollType.disclosed, + kMXMessageContentKeyExtensiblePollKindUndisclosed : EditFormPollType.undisclosed, + kMXMessageContentKeyExtensiblePollKindUndisclosedMSC3381 : EditFormPollType.undisclosed] return mapping[key] ?? EditFormPollType.disclosed } diff --git a/RiotSwiftUI/Modules/Room/PollEditForm/View/PollEditForm.swift b/RiotSwiftUI/Modules/Room/PollEditForm/View/PollEditForm.swift index 21bf5b189..c92acd3fa 100644 --- a/RiotSwiftUI/Modules/Room/PollEditForm/View/PollEditForm.swift +++ b/RiotSwiftUI/Modules/Room/PollEditForm/View/PollEditForm.swift @@ -35,8 +35,7 @@ struct PollEditForm: View { ScrollView { VStack(alignment: .leading, spacing: 32.0) { - // Intentionally disabled until platform parity. - // PollEditFormTypePicker(selectedType: $viewModel.type) + PollEditFormTypePicker(selectedType: $viewModel.type) VStack(alignment: .leading, spacing: 16.0) { Text(VectorL10n.pollEditFormPollQuestionOrTopic) @@ -116,14 +115,14 @@ struct PollEditForm: View { } } .navigationBarTitleDisplayMode(.inline) + .introspectNavigationController { navigationController in + ThemeService.shared().theme.applyStyle(onNavigationBar: navigationController.navigationBar) + } } } } .accentColor(theme.colors.accent) .navigationViewStyle(StackNavigationViewStyle()) - .introspectNavigationController { navigationController in - ThemeService.shared().theme.applyStyle(onNavigationBar: navigationController.navigationBar) - } } } diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift index 9502a6205..699cfec60 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift @@ -96,8 +96,7 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel } func canEditPoll() -> Bool { - return false // Intentionally disabled until platform parity. - // return pollAggregator.poll.isClosed == false && pollAggregator.poll.totalAnswerCount == 0 + return pollAggregator.poll.isClosed == false && pollAggregator.poll.totalAnswerCount == 0 } func endPoll() { diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollViewModel.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollViewModel.swift index e0574c0d7..a139442ef 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollViewModel.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/TimelinePollViewModel.swift @@ -81,11 +81,8 @@ class TimelinePollViewModel: TimelinePollViewModelType, TimelinePollViewModelPro state.poll.answerOptions.updateEach { answerOption in if answerOption.selected { answerOption.selected = false - - if(state.poll.answerOptions.count > 0) { - answerOption.count = answerOption.count - 1 - state.poll.totalAnswerCount -= 1 - } + answerOption.count = UInt(max(0, Int(answerOption.count) - 1)) + state.poll.totalAnswerCount = UInt(max(0, Int(state.poll.totalAnswerCount) - 1)) } if answerOption.id == selectedAnswerIdentifier { @@ -114,8 +111,8 @@ class TimelinePollViewModel: TimelinePollViewModelType, TimelinePollViewModelPro if answerOption.selected { answerOption.selected = false - answerOption.count -= 1 - state.poll.totalAnswerCount -= 1 + answerOption.count = UInt(max(0, Int(answerOption.count) - 1)) + state.poll.totalAnswerCount = UInt(max(0, Int(state.poll.totalAnswerCount) - 1)) } else { answerOption.selected = true answerOption.count += 1 diff --git a/RiotTests/AnalyticsTests.swift b/RiotTests/AnalyticsTests.swift index 5e15bf523..148a23bc8 100644 --- a/RiotTests/AnalyticsTests.swift +++ b/RiotTests/AnalyticsTests.swift @@ -16,6 +16,7 @@ import XCTest @testable import Riot +import AnalyticsEvents class AnalyticsTests: XCTestCase { func testAnalyticsPromptNewUser() { @@ -70,4 +71,68 @@ class AnalyticsTests: XCTestCase { // Then no prompt should be shown. XCTAssertFalse(showPrompt, "A prompt should not be shown any more.") } + + func testAddingUserProperties() { + // Given a client with no user properties set + let client = PostHogAnalyticsClient() + XCTAssertNil(client.pendingUserProperties, "No user properties should have been set yet.") + + // When updating the user properties + client.updateUserProperties(AnalyticsEvent.UserProperties(ftueUseCaseSelection: .PersonalMessaging, numSpaces: 5)) + + // Then the properties should be cached + XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.") + XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.") + XCTAssertEqual(client.pendingUserProperties?.numSpaces, 5, "The number of spaces should match.") + } + + func testMergingUserProperties() { + // Given a client with a cached use case user properties + let client = PostHogAnalyticsClient() + client.updateUserProperties(AnalyticsEvent.UserProperties(ftueUseCaseSelection: .PersonalMessaging, numSpaces: nil)) + + XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.") + XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.") + XCTAssertNil(client.pendingUserProperties?.numSpaces, "The number of spaces should not be set.") + + // When updating the number of spaced + client.updateUserProperties(AnalyticsEvent.UserProperties(ftueUseCaseSelection: nil, numSpaces: 5)) + + // Then the new properties should be updated and the existing properties should remain unchanged + XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.") + XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection shouldn't have changed.") + XCTAssertEqual(client.pendingUserProperties?.numSpaces, 5, "The number of spaces should have been updated.") + } + + func testSendingUserProperties() { + // Given a client with user properties set + let client = PostHogAnalyticsClient() + client.updateUserProperties(AnalyticsEvent.UserProperties(ftueUseCaseSelection: .PersonalMessaging, numSpaces: nil)) + client.start() + + XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.") + XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.") + + // When sending an event (tests run under Debug configuration so this is sent to the development instance) + client.screen(AnalyticsEvent.Screen(durationMs: nil, screenName: .Home)) + + // Then the properties should be cleared + XCTAssertNil(client.pendingUserProperties, "The user properties should be cleared.") + } + + func testSendingUserPropertiesWithIdentify() { + // Given a client with user properties set + let client = PostHogAnalyticsClient() + client.updateUserProperties(AnalyticsEvent.UserProperties(ftueUseCaseSelection: .PersonalMessaging, numSpaces: nil)) + client.start() + + XCTAssertNotNil(client.pendingUserProperties, "The user properties should be cached.") + XCTAssertEqual(client.pendingUserProperties?.ftueUseCaseSelection, .PersonalMessaging, "The use case selection should match.") + + // When calling identify (tests run under Debug configuration so this is sent to the development instance) + client.identify(id: UUID().uuidString) + + // Then the properties should be cleared + XCTAssertNil(client.pendingUserProperties, "The user properties should be cleared.") + } } diff --git a/RiotTests/MarkdownToHTMLRendererTests.swift b/RiotTests/MarkdownToHTMLRendererTests.swift new file mode 100644 index 000000000..496308b4a --- /dev/null +++ b/RiotTests/MarkdownToHTMLRendererTests.swift @@ -0,0 +1,170 @@ +// +// 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 XCTest + +@testable import Riot + +final class MarkdownToHTMLRendererTests: XCTestCase { + // MARK: - Tests + /// Test autolinks HTML render. + func testRenderAutolinks() { + let input = [ + "Test1:", + "<#_foonetic_xkcd:matrix.org>", + "", + "", + "<#_foonetic_xkcd:matrix.org>", + "", + "Test1A:", + "<#_foonetic_xkcd:matrix.org>", + "", + "", + "<#_foonetic_xkcd:matrix.org>", + "", + "Test2:", + "", + "", + "", + "Test3:", + "", + "", + ].joined(separator: "\n") + let expectedOutput = [ + "

Test1:\n<#_foonetic_xkcd:matrix.org>\nhttp://google.com/_thing_\nhttps://matrix.org/_matrix/client/foo/123_\n<#_foonetic_xkcd:matrix.org>

", + "

Test1A:\n<#_foonetic_xkcd:matrix.org>\nhttp://google.com/_thing_\nhttps://matrix.org/_matrix/client/foo/123_\n<#_foonetic_xkcd:matrix.org>

", + "

Test2:\nhttp://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg\nhttp://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg

", + "

Test3:\nhttps://riot.im/app/#/room/#_foonetic_xkcd:matrix.org\nhttps://riot.im/app/#/room/#_foonetic_xkcd:matrix.org

", + "", + ].joined(separator: "\n") + testRenderHTML(input: input, expectedOutput: expectedOutput) + } + + /// Test links with markdown formatting conflict. + func testRenderRepairedLinks() { + let input = [ + "Test1:", + "#_foonetic_xkcd:matrix.org", + "http://google.com/_thing_", + "https://matrix.org/_matrix/client/foo/123_", + "#_foonetic_xkcd:matrix.org", + "", + "Test1A:", + "#_foonetic_xkcd:matrix.org", + "http://google.com/_thing_", + "https://matrix.org/_matrix/client/foo/123_", + "#_foonetic_xkcd:matrix.org", + "", + "Test2:", + "http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg", + "http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg", + "", + "Test3:", + "https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org", + "https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org", + ].joined(separator: "\n") + let expectedOutput = [ + "

Test1:\n#_foonetic_xkcd:matrix.org\nhttp://google.com/_thing_\nhttps://matrix.org/_matrix/client/foo/123_\n#_foonetic_xkcd:matrix.org

", + "

Test1A:\n#_foonetic_xkcd:matrix.org\nhttp://google.com/_thing_\nhttps://matrix.org/_matrix/client/foo/123_\n#_foonetic_xkcd:matrix.org

", + "

Test2:\nhttp://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg\nhttp://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg

", + "

Test3:\nhttps://riot.im/app/#/room/#_foonetic_xkcd:matrix.org\nhttps://riot.im/app/#/room/#_foonetic_xkcd:matrix.org

", + "", + ].joined(separator: "\n") + testRenderHTML(input: input, expectedOutput: expectedOutput) + } + + /// Test links with markdown strong formatting conflict. + func testRenderRepairedLinksWithStrongFormatting() { + let input = "https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py" + + " " + + "https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py" + let expectedOutput = "

https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py" + + " " + + "https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py

" + + "\n" + testRenderHTML(input: input, expectedOutput: expectedOutput) + } + + /// Test links with markdown formatting conflict and actual markdown in between. + func testRenderRepairedLinksWithMarkdownInBetween() { + let input = "__Some bold text__ " + + "https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py" + + " _some emphased text_ " + + "http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg" + let expectedOutput = "

Some bold text " + + "https://github.com/matrix-org/synapse/blob/develop/synapse/module_api/__init__.py" + + " some emphased text " + + "http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg

" + + "\n" + testRenderHTML(input: input, expectedOutput: expectedOutput) + } + + /// Test links inside codeblocks. + func testRenderLinksInCodeblock() { + let input = "```" + + [ + "Test1:", + "#_foonetic_xkcd:matrix.org", + "http://google.com/_thing_", + "https://matrix.org/_matrix/client/foo/123_", + "#_foonetic_xkcd:matrix.org", + "", + "Test1A:", + "#_foonetic_xkcd:matrix.org", + "http://google.com/_thing_", + "https://matrix.org/_matrix/client/foo/123_", + "#_foonetic_xkcd:matrix.org", + "", + "Test2:", + "http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg", + "http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg", + "", + "Test3:", + "https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org", + "https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org", + ].joined(separator: "\n") + + "```" + let expectedOutput = [ + "
#_foonetic_xkcd:matrix.org",
+            "http://google.com/_thing_",
+            "https://matrix.org/_matrix/client/foo/123_",
+            "#_foonetic_xkcd:matrix.org",
+            "",
+            "Test1A:",
+            "#_foonetic_xkcd:matrix.org",
+            "http://google.com/_thing_",
+            "https://matrix.org/_matrix/client/foo/123_",
+            "#_foonetic_xkcd:matrix.org",
+            "",
+            "Test2:",
+            "http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg",
+            "http://domain.xyz/foo/bar-_stuff-like-this_-in-it.jpg",
+            "",
+            "Test3:",
+            "https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org",
+            "https://riot.im/app/#/room/#_foonetic_xkcd:matrix.org```",
+            "
", + "", + ].joined(separator: "\n") + testRenderHTML(input: input, expectedOutput: expectedOutput) + } + + // MARK: - Private + private func testRenderHTML(input: String, expectedOutput: String) { + let output = MarkdownToHTMLRenderer().renderToHTML(markdown: input) + XCTAssertEqual(output, expectedOutput) + } +} diff --git a/RiotTests/OnboardingTests.swift b/RiotTests/OnboardingTests.swift new file mode 100644 index 000000000..4c43fd0bd --- /dev/null +++ b/RiotTests/OnboardingTests.swift @@ -0,0 +1,102 @@ +// +// Copyright 2021 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 XCTest +@testable import Riot + +class OnboardingTests: XCTestCase { + + let userId = "@test:matrix.org" + + override func setUp() { + // Clear any properties for the test + UserSessionProperties(userId: userId).delete() + } + + func testEmptyUseCase() { + // Given an empty set of user properties + let properties = UserSessionProperties(userId: userId) + + // Then the use case property should return nil + XCTAssertNil(properties.useCase, "A use case has not been set") + } + + func testPersonalMessagingUseCase() { + // Given an empty set of user properties + let properties = UserSessionProperties(userId: userId) + + // When storing a use case result of personal messaging + let result = OnboardingUseCaseViewModelResult.personalMessaging + properties.useCase = result.userSessionPropertyValue + + // Then the use case property should return personal messaging + XCTAssertEqual(properties.useCase, .personalMessaging, "The use case should be Personal Messaging") + } + + func testSkippedUseCase() { + // Given an empty set of user properties + let properties = UserSessionProperties(userId: userId) + + // When storing a skipped use case result + let result = OnboardingUseCaseViewModelResult.skipped + properties.useCase = result.userSessionPropertyValue + + // Then the use case property should return skipped + XCTAssertEqual(properties.useCase, .skipped) + } + + func testCustomServerUseCase() { + // Given an empty set of user properties + let properties = UserSessionProperties(userId: userId) + + // When storing a custom server case result + let result = OnboardingUseCaseViewModelResult.customServer + properties.useCase = result.userSessionPropertyValue + + // Then the use case property should return nil + XCTAssertNil(properties.useCase) + } + + func testUseCaseAfterDeletingProperties() { + // Given a set of user properties with the Work Messaging use case + let properties = UserSessionProperties(userId: userId) + let result = OnboardingUseCaseViewModelResult.workMessaging + properties.useCase = result.userSessionPropertyValue + XCTAssertEqual(properties.useCase, .workMessaging, "The use case should be Work Messaging") + + // When deleting the user properties + properties.delete() + + // Then the use case property should return nil + XCTAssertNil(properties.useCase) + } + + func testUseCasePersistence() { + // Given a set of user properties with the Personal Messaging use case + var properties: UserSessionProperties? = UserSessionProperties(userId: userId) + let result = OnboardingUseCaseViewModelResult.personalMessaging + properties?.useCase = result.userSessionPropertyValue + XCTAssertEqual(properties?.useCase, .personalMessaging, "The use case should be Personal Messaging") + + // When the app is relaunched and a new user properties instance is creates + properties = nil + let newProperties = UserSessionProperties(userId: userId) + + // Then the use case property should still return Personal Messaging + XCTAssertEqual(newProperties.useCase, .personalMessaging, "The use case should be Personal Messaging") + } + +} diff --git a/RiotTests/target.yml b/RiotTests/target.yml index ad56a52c5..089ce208f 100644 --- a/RiotTests/target.yml +++ b/RiotTests/target.yml @@ -64,6 +64,6 @@ targets: - path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift - path: ../Riot/Managers/KeyValueStorage/ - path: ../Riot/PropertyWrappers/UserDefaultsBackedPropertyWrapper.swift - - path: ../Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineStyleIdentifier.swift + - path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift - path: ../Riot/Modules/Room/EventMenu/EventMenuBuilder.swift - path: ../Riot/Modules/Room/EventMenu/EventMenuItemType.swift diff --git a/SiriIntents/target.yml b/SiriIntents/target.yml index ca60c3bc1..a048b5c46 100644 --- a/SiriIntents/target.yml +++ b/SiriIntents/target.yml @@ -52,10 +52,11 @@ targets: - path: ../Riot/Managers/Locale/LocaleProvider.swift - path: ../Riot/Modules/MatrixKit - path: ../Riot/Modules/Analytics + - path: ../Riot/Managers/UserSessions - path: ../Riot/Managers/AppInfo/ - path: ../Riot/Managers/Locale/LocaleProviderType.swift - path: ../Riot/Generated/Strings.swift excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Generated/MatrixKitStrings.swift - - path: ../Riot/Modules/Room/Views/BubbleCells/Styles/RoomTimelineStyleIdentifier.swift + - path: ../Riot/Modules/Room/TimelineCells/Styles/RoomTimelineStyleIdentifier.swift diff --git a/Tools/SwiftGen/Templates/Assets/swift4-element.stencil b/Tools/SwiftGen/Templates/Assets/swift4-element.stencil new file mode 100644 index 000000000..a614d8715 --- /dev/null +++ b/Tools/SwiftGen/Templates/Assets/swift4-element.stencil @@ -0,0 +1,337 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +{% if catalogs %} +{% set className %}{{param.className|default:"Asset"}}{% endset %} +{% set arResourceGroupType %}{{param.arResourceGroupTypeName|default:"ARResourceGroupAsset"}}{% endset %} +{% set colorType %}{{param.colorTypeName|default:"ColorAsset"}}{% endset %} +{% set dataType %}{{param.dataTypeName|default:"DataAsset"}}{% endset %} +{% set imageType %}{{param.imageTypeName|default:"ImageAsset"}}{% endset %} +{% set symbolType %}{{param.symbolTypeName|default:"SymbolAsset"}}{% endset %} +{% set forceNamespaces %}{{param.forceProvidesNamespaces|default:"false"}}{% endset %} +{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %} +#if os(macOS) + import AppKit +#elseif os(iOS) +{% if resourceCount.arresourcegroup > 0 %} + import ARKit +{% endif %} + import UIKit +#elseif os(tvOS) || os(watchOS) + import UIKit +#endif + +// Deprecated typealiases +{% if resourceCount.color > 0 %} +@available(*, deprecated, renamed: "{{colorType}}.Color", message: "This typealias will be removed in SwiftGen 7.0") +{{accessModifier}} typealias {{param.colorAliasName|default:"AssetColorTypeAlias"}} = {{colorType}}.Color +{% endif %} +{% if resourceCount.image > 0 %} +@available(*, deprecated, renamed: "{{imageType}}.Image", message: "This typealias will be removed in SwiftGen 7.0") +{{accessModifier}} typealias {{param.imageAliasName|default:"AssetImageTypeAlias"}} = {{imageType}}.Image +{% endif %} + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Asset Catalogs + +{% macro enumBlock assets %} + {% call casesBlock assets %} + {% if param.allValues %} + + // swiftlint:disable trailing_comma + {% if resourceCount.arresourcegroup > 0 %} + {{accessModifier}} static let allResourceGroups: [{{arResourceGroupType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "arresourcegroup" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.color > 0 %} + {{accessModifier}} static let allColors: [{{colorType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "color" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.data > 0 %} + {{accessModifier}} static let allDataAssets: [{{dataType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "data" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.image > 0 %} + {{accessModifier}} static let allImages: [{{imageType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "image" "" %}{% endfilter %} + ] + {% endif %} + {% if resourceCount.symbol > 0 %} + {{accessModifier}} static let allSymbols: [{{symbolType}}] = [ + {% filter indent:2 %}{% call allValuesBlock assets "symbol" "" %}{% endfilter %} + ] + {% endif %} + // swiftlint:enable trailing_comma + {% endif %} +{% endmacro %} +{% macro casesBlock assets %} + {% for asset in assets %} + {% if asset.type == "arresourcegroup" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{arResourceGroupType}}(name: "{{asset.value}}") + {% elif asset.type == "color" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{colorType}}(name: "{{asset.value}}") + {% elif asset.type == "data" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{dataType}}(name: "{{asset.value}}") + {% elif asset.type == "image" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{imageType}}(name: "{{asset.value}}") + {% elif asset.type == "symbol" %} + {{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{symbolType}}(name: "{{asset.value}}") + {% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %} + {{accessModifier}} enum {{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} { + {% filter indent:2 %}{% call casesBlock asset.items %}{% endfilter %} + } + {% elif asset.items %} + {% call casesBlock asset.items %} + {% endif %} + {% endfor %} +{% endmacro %} +{% macro allValuesBlock assets filter prefix %} + {% for asset in assets %} + {% if asset.type == filter %} + {{prefix}}{{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}, + {% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %} + {% set prefix2 %}{{prefix}}{{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}.{% endset %} + {% call allValuesBlock asset.items filter prefix2 %} + {% elif asset.items %} + {% call allValuesBlock asset.items filter prefix %} + {% endif %} + {% endfor %} +{% endmacro %} +// swiftlint:disable identifier_name line_length nesting type_body_length type_name +@objcMembers +{{accessModifier}} class {{className}}: NSObject { + {% if catalogs.count > 1 or param.forceFileNameEnum %} + {% for catalog in catalogs %} + @objcMembers + @objc({{className}}{{catalog.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}) {{accessModifier}} class {{catalog.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}: NSObject { + {% filter indent:2 %}{% call enumBlock catalog.assets %}{% endfilter %} + } + {% endfor %} + {% else %} + {% call enumBlock catalogs.first.assets %} + {% endif %} +} +// swiftlint:enable identifier_name line_length nesting type_body_length type_name + +// MARK: - Implementation Details +{% if resourceCount.arresourcegroup > 0 %} + +{{accessModifier}} struct {{arResourceGroupType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(iOS) + @available(iOS 11.3, *) + {{accessModifier}} var referenceImages: Set { + return ARReferenceImage.referenceImages(in: self) + } + + @available(iOS 12.0, *) + {{accessModifier}} var referenceObjects: Set { + return ARReferenceObject.referenceObjects(in: self) + } + #endif +} + +#if os(iOS) +@available(iOS 11.3, *) +{{accessModifier}} extension ARReferenceImage { + static func referenceImages(in asset: {{arResourceGroupType}}) -> Set { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + return referenceImages(inGroupNamed: asset.name, bundle: bundle) ?? Set() + } +} + +@available(iOS 12.0, *) +{{accessModifier}} extension ARReferenceObject { + static func referenceObjects(in asset: {{arResourceGroupType}}) -> Set { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + return referenceObjects(inGroupNamed: asset.name, bundle: bundle) ?? Set() + } +} +#endif +{% endif %} +{% if resourceCount.color > 0 %} + +{{accessModifier}} final class {{colorType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(macOS) + {{accessModifier}} typealias Color = NSColor + #elseif os(iOS) || os(tvOS) || os(watchOS) + {{accessModifier}} typealias Color = UIColor + #endif + + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + {{accessModifier}} private(set) lazy var color: Color = Color(asset: self) + + #if os(iOS) || os(tvOS) + @available(iOS 11.0, tvOS 11.0, *) + {{accessModifier}} func color(compatibleWith traitCollection: UITraitCollection) -> Color { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + guard let color = Color(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load color asset named \(name).") + } + return color + } + #endif + + fileprivate init(name: String) { + self.name = name + } +} + +{{accessModifier}} extension {{colorType}}.Color { + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + convenience init!(asset: {{colorType}}) { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSColor.Name(asset.name), bundle: bundle) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} +{% endif %} +{% if resourceCount.data > 0 %} + +{{accessModifier}} struct {{dataType}} { + {{accessModifier}} fileprivate(set) var name: String + + @available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *) + {{accessModifier}} var data: NSDataAsset { + return NSDataAsset(asset: self) + } +} + +@available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *) +{{accessModifier}} extension NSDataAsset { + convenience init!(asset: {{dataType}}) { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) || os(watchOS) + self.init(name: asset.name, bundle: bundle) + #elseif os(macOS) + self.init(name: NSDataAsset.Name(asset.name), bundle: bundle) + #endif + } +} +{% endif %} +{% if resourceCount.image > 0 %} + +@objcMembers +{{accessModifier}} class {{imageType}}: NSObject { + {{accessModifier}} fileprivate(set) var name: String + + #if os(macOS) + {{accessModifier}} typealias Image = NSImage + #elseif os(iOS) || os(tvOS) || os(watchOS) + {{accessModifier}} typealias Image = UIImage + #endif + + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, macOS 10.7, *) + {{accessModifier}} var image: Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + let name = NSImage.Name(self.name) + let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + + {{accessModifier}} init(name: String) { + self.name = name + } + + #if os(iOS) || os(tvOS) + @available(iOS 8.0, tvOS 9.0, *) + {{accessModifier}} func image(compatibleWith traitCollection: UITraitCollection) -> Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + #endif +} + +{{accessModifier}} extension {{imageType}}.Image { + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, *) + @available(macOS, deprecated, + message: "This initializer is unsafe on macOS, please use the {{imageType}}.image property") + convenience init!(asset: {{imageType}}) { + #if os(iOS) || os(tvOS) + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSImage.Name(asset.name)) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} +{% endif %} +{% if resourceCount.symbol > 0 %} + +{{accessModifier}} struct {{symbolType}} { + {{accessModifier}} fileprivate(set) var name: String + + #if os(iOS) || os(tvOS) || os(watchOS) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + {{accessModifier}} typealias Configuration = UIImage.SymbolConfiguration + {{accessModifier}} typealias Image = UIImage + + @available(iOS 12.0, tvOS 12.0, watchOS 5.0, *) + {{accessModifier}} var image: Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load symbol asset named \(name).") + } + return result + } + + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + {{accessModifier}} func image(with configuration: Configuration) -> Image { + let bundle = {{param.bundle|default:"BundleToken.bundle"}} + guard let result = Image(named: name, in: bundle, with: configuration) else { + fatalError("Unable to load symbol asset named \(name).") + } + return result + } + #endif +} +{% endif %} +{% if not param.bundle %} + +// swiftlint:disable convenience_type +private final class BundleToken { + static let bundle: Bundle = { + #if SWIFT_PACKAGE + return Bundle.module + #else + return Bundle(for: BundleToken.self) + #endif + }() +} +// swiftlint:enable convenience_type +{% endif %} +{% else %} +// No assets found +{% endif %} + diff --git a/Tools/SwiftGen/swiftgen-config.yml b/Tools/SwiftGen/swiftgen-config.yml index 3d1cd6693..a561f43ae 100755 --- a/Tools/SwiftGen/swiftgen-config.yml +++ b/Tools/SwiftGen/swiftgen-config.yml @@ -28,7 +28,7 @@ xcassets: - Assets/Images.xcassets - Assets/SharedImages.xcassets outputs: - templateName: swift4 + templatePath: Templates/Assets/swift4-element.stencil output: Images.swift plist: inputs: Assets/Riot-Defaults.plist diff --git a/project.yml b/project.yml index dcd15f63b..7bb2ef0a0 100644 --- a/project.yml +++ b/project.yml @@ -35,9 +35,11 @@ include: - path: RiotSwiftUI/target.yml - path: RiotSwiftUI/targetUnitTests.yml - path: RiotSwiftUI/targetUITests.yml + - path: CommonKit/target.yml + - path: CommonKit/targetUnitTests.yml packages: Mapbox: url: https://github.com/maplibre/maplibre-gl-native-distribution minVersion: 5.12.2 - maxVersion: 5.13.0 \ No newline at end of file + maxVersion: 5.13.0