mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Merge branch 'develop' into andy/4829_activity_indicators
This commit is contained in:
commit
18af47ad34
66 changed files with 902 additions and 104 deletions
12
CHANGES.md
12
CHANGES.md
|
@ -1,3 +1,15 @@
|
|||
## Changes in 1.8.1 (2022-02-16)
|
||||
|
||||
🙌 Improvements
|
||||
|
||||
- Upgrade MatrixSDK version ([v0.22.1](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.22.1)).
|
||||
|
||||
🐛 Bugfixes
|
||||
|
||||
- Settings: Fix a bug where tapping a toggle could change multiple settings. ([#5463](https://github.com/vector-im/element-ios/issues/5463))
|
||||
- Fix for images sometimes being sent unencrypted inside an encrypted room. ([#5564](https://github.com/vector-im/element-ios/issues/5564))
|
||||
|
||||
|
||||
## Changes in 1.8.0 (2022-02-09)
|
||||
|
||||
✨ Features
|
||||
|
|
28
CommonKit/Common.xcconfig
Normal file
28
CommonKit/Common.xcconfig
Normal file
|
@ -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
|
27
CommonKit/CommonKit.h
Normal file
27
CommonKit/CommonKit.h
Normal file
|
@ -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 <Foundation/Foundation.h>
|
||||
|
||||
//! 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 <CommonKit/PublicHeader.h>
|
||||
|
||||
|
20
CommonKit/Debug.xcconfig
Normal file
20
CommonKit/Debug.xcconfig
Normal file
|
@ -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"
|
22
CommonKit/Info.plist
Normal file
22
CommonKit/Info.plist
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
</dict>
|
||||
</plist>
|
20
CommonKit/Release.xcconfig
Normal file
20
CommonKit/Release.xcconfig
Normal file
|
@ -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"
|
94
CommonKit/Source/Activity/Activity.swift
Normal file
94
CommonKit/Source/Activity/Activity.swift
Normal file
|
@ -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<C>(in collection: inout C) where C: RangeReplaceableCollection, C.Element == Activity {
|
||||
collection.append(self)
|
||||
}
|
||||
}
|
60
CommonKit/Source/Activity/ActivityCenter.swift
Normal file
60
CommonKit/Source/Activity/ActivityCenter.swift
Normal file
|
@ -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<T: AnyObject> {
|
||||
weak var element: T?
|
||||
init(_ element: T) {
|
||||
self.element = element
|
||||
}
|
||||
}
|
||||
|
||||
public static let shared = ActivityCenter()
|
||||
private var queue = [Weak<Activity>]()
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
}
|
25
CommonKit/Source/Activity/ActivityDismissal.swift
Normal file
25
CommonKit/Source/Activity/ActivityDismissal.swift
Normal file
|
@ -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)
|
||||
}
|
25
CommonKit/Source/Activity/ActivityPresentable.swift
Normal file
25
CommonKit/Source/Activity/ActivityPresentable.swift
Normal file
|
@ -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()
|
||||
}
|
28
CommonKit/Source/Activity/ActivityRequest.swift
Normal file
28
CommonKit/Source/Activity/ActivityRequest.swift
Normal file
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// 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 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
|
||||
|
||||
public init(presenter: ActivityPresentable, dismissal: ActivityDismissal) {
|
||||
self.presenter = presenter
|
||||
self.dismissal = dismissal
|
||||
}
|
||||
}
|
55
CommonKit/Source/Activity/Tests/ActivityCenterTests.swift
Normal file
55
CommonKit/Source/Activity/Tests/ActivityCenterTests.swift
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
29
CommonKit/Source/Activity/Tests/ActivityPresenterSpy.swift
Normal file
29
CommonKit/Source/Activity/Tests/ActivityPresenterSpy.swift
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// 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 ActivityPresenterSpy: ActivityPresentable {
|
||||
var intel = [String]()
|
||||
|
||||
func present() {
|
||||
intel.append(#function)
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
intel.append(#function)
|
||||
}
|
||||
}
|
127
CommonKit/Source/Activity/Tests/ActivityTests.swift
Normal file
127
CommonKit/Source/Activity/Tests/ActivityTests.swift
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
40
CommonKit/target.yml
Normal file
40
CommonKit/target.yml
Normal file
|
@ -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/**"
|
42
CommonKit/targetUnitTests.yml
Normal file
42
CommonKit/targetUnitTests.yml
Normal file
|
@ -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: .
|
||||
|
|
@ -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
|
||||
|
|
6
Podfile
6
Podfile
|
@ -13,9 +13,9 @@ 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.0'
|
||||
$matrixSDKVersion = '= 0.22.1'
|
||||
# $matrixSDKVersion = :local
|
||||
$matrixSDKVersion = { :branch => 'develop'}
|
||||
# $matrixSDKVersion = { :branch => 'develop'}
|
||||
# $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } }
|
||||
|
||||
########################################
|
||||
|
@ -154,4 +154,4 @@ post_install do |installer|
|
|||
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)', '-Xcc', '-Wno-nullability-completeness']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
27
Podfile.lock
27
Podfile.lock
|
@ -45,6 +45,7 @@ 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)
|
||||
|
@ -56,16 +57,16 @@ PODS:
|
|||
- LoggerAPI (1.9.200):
|
||||
- Logging (~> 1.1)
|
||||
- Logging (1.4.0)
|
||||
- MatrixSDK (0.22.0):
|
||||
- MatrixSDK/Core (= 0.22.0)
|
||||
- MatrixSDK/Core (0.22.0):
|
||||
- MatrixSDK (0.22.1):
|
||||
- MatrixSDK/Core (= 0.22.1)
|
||||
- MatrixSDK/Core (0.22.1):
|
||||
- AFNetworking (~> 4.0.0)
|
||||
- GZIP (~> 1.3.0)
|
||||
- libbase58 (~> 0.1.4)
|
||||
- OLMKit (~> 3.2.5)
|
||||
- Realm (= 10.16.0)
|
||||
- SwiftyBeaver (= 1.9.5)
|
||||
- MatrixSDK/JingleCallStack (0.22.0):
|
||||
- MatrixSDK/JingleCallStack (0.22.1):
|
||||
- JitsiMeetSDK (= 3.10.2)
|
||||
- MatrixSDK/Core
|
||||
- OLMKit (3.2.5):
|
||||
|
@ -111,12 +112,13 @@ 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)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (from `https://github.com/matrix-org/matrix-ios-sdk.git`, branch `develop`)
|
||||
- MatrixSDK/JingleCallStack (from `https://github.com/matrix-org/matrix-ios-sdk.git`, branch `develop`)
|
||||
- MatrixSDK (= 0.22.1)
|
||||
- MatrixSDK/JingleCallStack (= 0.22.1)
|
||||
- OLMKit
|
||||
- PostHog (~> 1.4.4)
|
||||
- ReadMoreTextView (~> 3.0.1)
|
||||
|
@ -147,6 +149,7 @@ SPEC REPOS:
|
|||
- FlowCommoniOS
|
||||
- GBDeviceInfo
|
||||
- GZIP
|
||||
- HPGrowingTextView
|
||||
- Introspect
|
||||
- JitsiMeetSDK
|
||||
- KeychainAccess
|
||||
|
@ -156,6 +159,7 @@ SPEC REPOS:
|
|||
- libPhoneNumber-iOS
|
||||
- LoggerAPI
|
||||
- Logging
|
||||
- MatrixSDK
|
||||
- OLMKit
|
||||
- PostHog
|
||||
- ReadMoreTextView
|
||||
|
@ -176,17 +180,11 @@ EXTERNAL SOURCES:
|
|||
AnalyticsEvents:
|
||||
:branch: release/swift
|
||||
:git: https://github.com/matrix-org/matrix-analytics-events.git
|
||||
MatrixSDK:
|
||||
:branch: develop
|
||||
:git: https://github.com/matrix-org/matrix-ios-sdk.git
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
AnalyticsEvents:
|
||||
:commit: 8058dc6ec07ce0acfe5fdb19eb7e309b0c13845c
|
||||
:git: https://github.com/matrix-org/matrix-analytics-events.git
|
||||
MatrixSDK:
|
||||
:commit: 7be07981c3f2932b0205797f234982ca32da7dff
|
||||
:git: https://github.com/matrix-org/matrix-ios-sdk.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
|
||||
|
@ -204,6 +202,7 @@ SPEC CHECKSUMS:
|
|||
FlowCommoniOS: ca92071ab526dc89905495a37844fd7e78d1a7f2
|
||||
GBDeviceInfo: ed0db16230d2fa280e1cbb39a5a7f60f6946aaec
|
||||
GZIP: 416858efbe66b41b206895ac6dfd5493200d95b3
|
||||
HPGrowingTextView: 88a716d97fb853bcb08a4a08e4727da17efc9b19
|
||||
Introspect: 2be020f30f084ada52bb4387fff83fa52c5c400e
|
||||
JitsiMeetSDK: 2f118fa770f23e518f3560fc224fae3ac7062223
|
||||
KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51
|
||||
|
@ -213,7 +212,7 @@ SPEC CHECKSUMS:
|
|||
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
|
||||
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
|
||||
Logging: beeb016c9c80cf77042d62e83495816847ef108b
|
||||
MatrixSDK: 21201cd007145d96beff24cc7f9727ced497c3fd
|
||||
MatrixSDK: 12c1a56e037f629e493cbcd615fd13cfc58cee3a
|
||||
OLMKit: 9fb4799c4a044dd2c06bda31ec31a12191ad30b5
|
||||
PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f
|
||||
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
|
||||
|
@ -230,6 +229,6 @@ SPEC CHECKSUMS:
|
|||
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
|
||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||
|
||||
PODFILE CHECKSUM: 9b4be35779b652e3d0ad333d84069240ec58dc96
|
||||
PODFILE CHECKSUM: ae70a46e98aae87f130ad3d246711fc6b6ae7286
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
|
23
Riot/Assets/Images.xcassets/Common/filter_off.imageset/Contents.json
vendored
Normal file
23
Riot/Assets/Images.xcassets/Common/filter_off.imageset/Contents.json
vendored
Normal file
|
@ -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
|
||||
}
|
||||
}
|
BIN
Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 201 B |
BIN
Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@2x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 297 B |
BIN
Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@3x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Common/filter_off.imageset/filter_off@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 397 B |
23
Riot/Assets/Images.xcassets/Common/filter_on.imageset/Contents.json
vendored
Normal file
23
Riot/Assets/Images.xcassets/Common/filter_on.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "filter_on.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "filter_on@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "filter_on@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 204 B |
BIN
Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@2x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 295 B |
BIN
Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@3x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Common/filter_on.imageset/filter_on@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 377 B |
|
@ -250,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…";
|
||||
|
@ -396,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";
|
||||
|
|
|
@ -63,6 +63,8 @@ internal class Asset: NSObject {
|
|||
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")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -3259,7 +3259,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")
|
||||
}
|
||||
|
@ -4023,6 +4023,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")
|
||||
|
|
|
@ -123,7 +123,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];
|
||||
|
@ -173,8 +177,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) {
|
||||
|
||||
|
@ -2197,6 +2204,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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -89,6 +89,10 @@
|
|||
{
|
||||
continueBlock();
|
||||
}
|
||||
else if (result.result.unsignedData.relations.thread)
|
||||
{
|
||||
continueBlock();
|
||||
}
|
||||
else if (room)
|
||||
{
|
||||
[room liveTimeline:^(id<MXEventTimeline> liveTimeline) {
|
||||
|
@ -164,8 +168,9 @@
|
|||
{
|
||||
if (cellData.hasThreadRoot)
|
||||
{
|
||||
MXThread *thread = cellData.bubbleComponents.firstObject.thread;
|
||||
ThreadSummaryView *threadSummaryView = [[ThreadSummaryView alloc] initWithThread:thread];
|
||||
id<MXThreadProtocol> thread = cellData.bubbleComponents.firstObject.thread;
|
||||
ThreadSummaryView *threadSummaryView = [[ThreadSummaryView alloc] initWithThread:thread
|
||||
session:self.mxSession];
|
||||
[bubbleCell.tmpSubviews addObject:threadSummaryView];
|
||||
|
||||
threadSummaryView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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<MXThreadProtocol> thread;
|
||||
|
||||
/**
|
||||
Create a new `MXKRoomBubbleComponent` object based on a `MXEvent` instance.
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
@interface MXKRoomBubbleComponent ()
|
||||
|
||||
@property (nonatomic, readwrite) MXThread *thread;
|
||||
@property (nonatomic, readwrite) id<MXThreadProtocol> 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;
|
||||
}
|
||||
|
|
|
@ -955,7 +955,7 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=\"(.*?)\">([^<]*)</a>";
|
|||
}
|
||||
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"])
|
||||
{
|
||||
|
|
|
@ -130,6 +130,6 @@
|
|||
@param roomDataSource room data source instance
|
||||
*/
|
||||
- (void)roomDataSource:(RoomDataSource * _Nonnull)roomDataSource
|
||||
didTapThread:(MXThread * _Nonnull)thread;
|
||||
didTapThread:(id<MXThreadProtocol> _Nonnull)thread;
|
||||
|
||||
@end
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -438,10 +438,17 @@ typedef NS_ENUM(NSUInteger, MXKRoomViewControllerJoinRoomResult) {
|
|||
- (void)setBubbleTableViewContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;
|
||||
|
||||
/**
|
||||
Handle typing notification.
|
||||
Sends a typing notification with the specified timeout.
|
||||
|
||||
@param typing Flag indicating whether the user is typing or not.
|
||||
@param notificationTimeoutMS The length of time the typing notification is valid for
|
||||
*/
|
||||
- (void)handleTypingNotification:(BOOL)typing;
|
||||
- (void)sendTypingNotification:(BOOL)typing timeout:(NSUInteger)notificationTimeoutMS;
|
||||
|
||||
|
||||
/**
|
||||
Share encryption keys in this room.
|
||||
*/
|
||||
- (void)shareEncryptionKeys;
|
||||
|
||||
@end
|
||||
|
|
|
@ -3326,7 +3326,7 @@
|
|||
roomDataSource.partialTextMessage = inputToolbarView.textMessage;
|
||||
}
|
||||
|
||||
[self handleTypingNotification:typing];
|
||||
[self handleTypingState:typing];
|
||||
}
|
||||
|
||||
- (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView heightDidChanged:(CGFloat)height completion:(void (^)(BOOL finished))completion
|
||||
|
@ -3447,7 +3447,7 @@
|
|||
}
|
||||
# pragma mark - Typing notification
|
||||
|
||||
- (void)handleTypingNotification:(BOOL)typing
|
||||
- (void)handleTypingState:(BOOL)typing
|
||||
{
|
||||
NSUInteger notificationTimeoutMS = -1;
|
||||
if (typing)
|
||||
|
@ -3509,6 +3509,11 @@
|
|||
lastTypingDate = nil;
|
||||
}
|
||||
|
||||
[self sendTypingNotification:typing timeout:notificationTimeoutMS];
|
||||
}
|
||||
|
||||
- (void)sendTypingNotification:(BOOL)typing timeout:(NSUInteger)notificationTimeoutMS
|
||||
{
|
||||
MXWeakify(self);
|
||||
|
||||
// Send typing notification to server
|
||||
|
@ -3539,7 +3544,7 @@
|
|||
// Check whether a new typing event has been observed
|
||||
BOOL typing = (lastTypingDate != nil);
|
||||
// Post a new typing notification
|
||||
[self handleTypingNotification:typing];
|
||||
[self handleTypingState:typing];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4283,7 +4283,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
|
|||
[self updateTitleViewEncryptionDecoration];
|
||||
}
|
||||
|
||||
- (void)roomDataSource:(RoomDataSource *)roomDataSource didTapThread:(MXThread *)thread
|
||||
- (void)roomDataSource:(RoomDataSource *)roomDataSource didTapThread:(id<MXThreadProtocol>)thread
|
||||
{
|
||||
[self openThreadWithId:thread.id];
|
||||
}
|
||||
|
@ -4564,6 +4564,15 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
|
|||
[self.userSuggestionCoordinator processTextMessage:toolbarView.textMessage];
|
||||
}
|
||||
|
||||
- (void)roomInputToolbarViewDidOpenActionMenu:(MXKRoomInputToolbarView*)toolbarView
|
||||
{
|
||||
// Consider opening the action menu as beginning to type and share encryption keys if requested.
|
||||
if ([MXKAppSettings standardAppSettings].outboundGroupSessionKeyPreSharingStrategy == MXKKeyPreSharingWhenTyping)
|
||||
{
|
||||
[self shareEncryptionKeys];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MXKRoomMemberDetailsViewControllerDelegate
|
||||
|
||||
- (void)roomMemberDetailsViewController:(MXKRoomMemberDetailsViewController *)roomMemberDetailsViewController startChatWithMemberId:(NSString *)matrixId completion:(void (^)(void))completion
|
||||
|
|
|
@ -96,6 +96,10 @@
|
|||
{
|
||||
continueBlock();
|
||||
}
|
||||
else if (result.result.unsignedData.relations.thread)
|
||||
{
|
||||
continueBlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
[roomDataSource.room liveTimeline:^(id<MXEventTimeline> liveTimeline) {
|
||||
|
@ -143,8 +147,9 @@
|
|||
{
|
||||
if (cellData.hasThreadRoot)
|
||||
{
|
||||
MXThread *thread = cellData.bubbleComponents.firstObject.thread;
|
||||
ThreadSummaryView *threadSummaryView = [[ThreadSummaryView alloc] initWithThread:thread];
|
||||
id<MXThreadProtocol> thread = cellData.bubbleComponents.firstObject.thread;
|
||||
ThreadSummaryView *threadSummaryView = [[ThreadSummaryView alloc] initWithThread:thread
|
||||
session:self.mxSession];
|
||||
[bubbleCell.tmpSubviews addObject:threadSummaryView];
|
||||
|
||||
threadSummaryView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
|
|
@ -165,7 +165,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];
|
||||
|
|
|
@ -47,6 +47,13 @@ typedef enum : NSUInteger
|
|||
*/
|
||||
- (void)roomInputToolbarViewDidChangeTextMessage:(MXKRoomInputToolbarView*)toolbarView;
|
||||
|
||||
/**
|
||||
Inform the delegate that the action menu was opened.
|
||||
|
||||
@param toolbarView the room input toolbar view
|
||||
*/
|
||||
- (void)roomInputToolbarViewDidOpenActionMenu:(MXKRoomInputToolbarView*)toolbarView;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
|
|
|
@ -429,6 +429,7 @@ static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3;
|
|||
if (_actionMenuOpened) {
|
||||
self.actionsBar.hidden = NO;
|
||||
[self.actionsBar animateWithShowIn:_actionMenuOpened completion:nil];
|
||||
[self.delegate roomInputToolbarViewDidOpenActionMenu:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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,8 +49,9 @@ 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)))
|
||||
|
@ -59,7 +61,7 @@ class ThreadSummaryView: UIView {
|
|||
translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
|
||||
static func contentViewHeight(forThread thread: MXThread?, fitting maxWidth: CGFloat) -> CGFloat {
|
||||
static func contentViewHeight(forThread thread: MXThreadProtocol?, fitting maxWidth: CGFloat) -> CGFloat {
|
||||
return RoomBubbleCellLayout.threadSummaryViewHeight
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -1997,7 +1997,7 @@ TableViewSectionsDelegate>
|
|||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
labelAndSwitchCell.mxkSwitch.enabled = YES;
|
||||
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableURLPreviews:) forControlEvents:UIControlEventValueChanged];
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableURLPreviews:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
|
@ -2442,7 +2442,7 @@ TableViewSectionsDelegate>
|
|||
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableRingingForGroupCalls;
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableRingingForGroupCalls:) forControlEvents:UIControlEventValueChanged];
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableRingingForGroupCalls:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
|
@ -2454,7 +2454,7 @@ TableViewSectionsDelegate>
|
|||
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableThreads;
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableThreads:) forControlEvents:UIControlEventValueChanged];
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableThreads:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
|
|
|
@ -70,8 +70,8 @@ class ThreadViewController: RoomViewController {
|
|||
super.onButtonPressed(sender)
|
||||
}
|
||||
|
||||
override func handleTypingNotification(_ typing: Bool) {
|
||||
// no-op
|
||||
override func sendTypingNotification(_ typing: Bool, timeout notificationTimeoutMS: UInt) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
private func showThreadActions() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -35,6 +35,7 @@ targets:
|
|||
- target: SiriIntents
|
||||
- target: RiotNSE
|
||||
- target: DesignKit
|
||||
- target: CommonKit
|
||||
- package: Mapbox
|
||||
|
||||
configFiles:
|
||||
|
|
1
changelog.d/4103.bugfix
Normal file
1
changelog.d/4103.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fix proximity sensor staying on and sleep timer staying disabled after call ends
|
1
changelog.d/4829.change
Normal file
1
changelog.d/4829.change
Normal file
|
@ -0,0 +1 @@
|
|||
CommonKit: Create a new framework with common functionality and create Activity and ActivityCenter
|
1
changelog.d/5250.change
Normal file
1
changelog.d/5250.change
Normal file
|
@ -0,0 +1 @@
|
|||
Filter: update placeholder text and icon
|
1
changelog.d/5346.bugfix
Normal file
1
changelog.d/5346.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Wordings: Replace "kick" and all affiliate word by "remove"
|
1
changelog.d/5540.change
Normal file
1
changelog.d/5540.change
Normal file
|
@ -0,0 +1 @@
|
|||
ThreadListViewModel: Use new apis to fetch threads.
|
1
changelog.d/5562.change
Normal file
1
changelog.d/5562.change
Normal file
|
@ -0,0 +1 @@
|
|||
Search: Use bundled aggregations if provided.
|
|
@ -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
|
||||
maxVersion: 5.13.0
|
||||
|
|
Loading…
Reference in a new issue