Merge branch 'develop' into aleksandrs/6963_multi_session_logout
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "action_voice_message.png",
|
||||
"filename" : "Microphone icon.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "action_voice_message@2x.png",
|
||||
"filename" : "Microphone icon@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "action_voice_message@3x.png",
|
||||
"filename" : "Microphone icon@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 459 B |
After Width: | Height: | Size: 818 B |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 694 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB |
|
@ -1,17 +1,17 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "voice_message_record_button_recording.png",
|
||||
"filename" : "Microphone asset.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "voice_message_record_button_recording@2x.png",
|
||||
"filename" : "Microphone asset@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "voice_message_record_button_recording@3x.png",
|
||||
"filename" : "Microphone asset@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 8.1 KiB |
|
@ -32,6 +32,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
private var voiceMessageToolbarView: VoiceMessageToolbarView?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var heightConstraint: NSLayoutConstraint!
|
||||
private var hostingViewController: VectorHostingController!
|
||||
|
@ -39,42 +40,6 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
private var viewModel: ComposerViewModelProtocol = ComposerViewModel(initialViewState: ComposerViewState())
|
||||
|
||||
// MARK: Public
|
||||
var isEncryptionEnabled = false {
|
||||
didSet {
|
||||
updatePlaceholderText()
|
||||
}
|
||||
}
|
||||
|
||||
/// The current html content of the composer
|
||||
var htmlContent: String {
|
||||
get {
|
||||
wysiwygViewModel.content.html
|
||||
}
|
||||
set {
|
||||
wysiwygViewModel.setHtmlContent(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// The display name to show when in edit/reply
|
||||
var eventSenderDisplayName: String! {
|
||||
get {
|
||||
viewModel.eventSenderDisplayName
|
||||
}
|
||||
set {
|
||||
viewModel.eventSenderDisplayName = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the composer is in send, reply or edit mode.
|
||||
var sendMode: RoomInputToolbarViewSendMode {
|
||||
get {
|
||||
viewModel.sendMode.legacySendMode
|
||||
}
|
||||
set {
|
||||
viewModel.sendMode = ComposerSendMode(from: newValue)
|
||||
updatePlaceholderText()
|
||||
}
|
||||
}
|
||||
|
||||
override var placeholder: String! {
|
||||
get {
|
||||
|
@ -169,9 +134,22 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
switch result {
|
||||
case .cancel:
|
||||
self.toolbarViewDelegate?.roomInputToolbarViewDidTapCancel(self)
|
||||
case let .contentDidChange(isEmpty):
|
||||
setVoiceMessageToolbarIsHidden(!isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
private func setVoiceMessageToolbarIsHidden(_ isHidden: Bool) {
|
||||
guard let voiceMessageToolbarView = voiceMessageToolbarView else { return }
|
||||
UIView.transition(
|
||||
with: voiceMessageToolbarView, duration: 0.15,
|
||||
options: .transitionCrossDissolve,
|
||||
animations: {
|
||||
voiceMessageToolbarView.isHidden = isHidden
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
@ -185,12 +163,64 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
|
|||
wysiwygViewModel.textColor = theme.colors.primaryContent
|
||||
}
|
||||
|
||||
// MARK: - RoomInputToolbarViewProtocol
|
||||
// MARK: - HtmlRoomInputToolbarViewProtocol
|
||||
var isEncryptionEnabled = false {
|
||||
didSet {
|
||||
updatePlaceholderText()
|
||||
}
|
||||
}
|
||||
|
||||
/// The current html content of the composer
|
||||
var htmlContent: String {
|
||||
get {
|
||||
wysiwygViewModel.content.html
|
||||
}
|
||||
set {
|
||||
wysiwygViewModel.setHtmlContent(newValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// The display name to show when in edit/reply
|
||||
var eventSenderDisplayName: String! {
|
||||
get {
|
||||
viewModel.eventSenderDisplayName
|
||||
}
|
||||
set {
|
||||
viewModel.eventSenderDisplayName = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the composer is in send, reply or edit mode.
|
||||
var sendMode: RoomInputToolbarViewSendMode {
|
||||
get {
|
||||
viewModel.sendMode.legacySendMode
|
||||
}
|
||||
set {
|
||||
viewModel.sendMode = ComposerSendMode(from: newValue)
|
||||
updatePlaceholderText()
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the voice message toolbar to the composer
|
||||
/// - Parameter voiceMessageToolbarView: the voice message toolbar UIView
|
||||
func setVoiceMessageToolbarView(_ voiceMessageToolbarView: UIView!) {
|
||||
// TODO embed the voice messages UI
|
||||
if let voiceMessageToolbarView = voiceMessageToolbarView as? VoiceMessageToolbarView {
|
||||
self.voiceMessageToolbarView = voiceMessageToolbarView
|
||||
voiceMessageToolbarView.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.deactivate(voiceMessageToolbarView.containersTopConstraints)
|
||||
addSubview(voiceMessageToolbarView)
|
||||
NSLayoutConstraint.activate(
|
||||
[
|
||||
hostingViewController.view.topAnchor.constraint(equalTo: voiceMessageToolbarView.topAnchor),
|
||||
hostingViewController.view.leftAnchor.constraint(equalTo: voiceMessageToolbarView.leftAnchor),
|
||||
hostingViewController.view.bottomAnchor.constraint(equalTo: voiceMessageToolbarView.bottomAnchor, constant: 4),
|
||||
hostingViewController.view.rightAnchor.constraint(equalTo: voiceMessageToolbarView.rightAnchor)
|
||||
]
|
||||
)
|
||||
} else {
|
||||
self.voiceMessageToolbarView?.removeFromSuperview()
|
||||
self.voiceMessageToolbarView = nil
|
||||
}
|
||||
}
|
||||
|
||||
func toolbarHeight() -> CGFloat {
|
||||
|
|
|
@ -88,6 +88,8 @@ class VoiceMessageToolbarView: PassthroughView, NibLoadable, Themable, UIGesture
|
|||
@IBOutlet private var toastNotificationContainerView: UIView!
|
||||
@IBOutlet private var toastNotificationLabel: UILabel!
|
||||
|
||||
@IBOutlet var containersTopConstraints: [NSLayoutConstraint]!
|
||||
|
||||
private var playbackView: VoiceMessagePlaybackView!
|
||||
|
||||
private var cancelLabelToRecordButtonDistance: CGFloat = 0.0
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="VoiceMessageToolbarView" customModule="Riot" customModuleProvider="target">
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="VoiceMessageToolbarView" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="544" height="72"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
|
@ -19,7 +19,7 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XRB-CY-ijK" customClass="PassthroughView" customModule="Riot" customModuleProvider="target">
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XRB-CY-ijK" customClass="PassthroughView" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="544" height="72"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8fP-9K-WTa">
|
||||
|
@ -71,7 +71,7 @@
|
|||
<constraint firstAttribute="height" constant="152" id="li1-Bd-px2"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dyu-ha-046" customClass="PassthroughView" customModule="Riot" customModuleProvider="target">
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dyu-ha-046" customClass="PassthroughView" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="544" height="72"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="6FH-4Q-Z5e">
|
||||
|
@ -267,12 +267,14 @@
|
|||
<outlet property="slideToCancelLabel" destination="Ydw-Nb-zP6" id="l4Y-Eg-Qwc"/>
|
||||
<outlet property="toastNotificationContainerView" destination="HDF-2Z-UHZ" id="8Ty-Gl-XnP"/>
|
||||
<outlet property="toastNotificationLabel" destination="gZJ-ep-9Bz" id="soa-bs-C37"/>
|
||||
<outletCollection property="containersTopConstraints" destination="XRb-zW-xdf" collectionClass="NSMutableArray" id="0JK-wT-gdZ"/>
|
||||
<outletCollection property="containersTopConstraints" destination="tEJ-94-MLM" collectionClass="NSMutableArray" id="pY3-hI-Sda"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="10.144927536231885" y="456.69642857142856"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="chevron.left" catalog="system" width="96" height="128"/>
|
||||
<image name="chevron.left" catalog="system" width="97" height="128"/>
|
||||
<image name="room_context_menu_delete" width="24" height="24"/>
|
||||
<image name="send_icon" width="36" height="36"/>
|
||||
<image name="voice_message_cancel_gradient" width="104" height="47"/>
|
||||
|
|
|
@ -40,11 +40,13 @@ enum MockComposerScreenState: MockScreenState, CaseIterable {
|
|||
|
||||
viewModel.callback = { [weak viewModel, weak wysiwygviewModel] result in
|
||||
guard let viewModel = viewModel else { return }
|
||||
if viewModel.sendMode == .edit {
|
||||
wysiwygviewModel?.setHtmlContent("")
|
||||
}
|
||||
switch result {
|
||||
case .cancel: viewModel.sendMode = .send
|
||||
case .cancel:
|
||||
if viewModel.sendMode == .edit {
|
||||
wysiwygviewModel?.setHtmlContent("")
|
||||
}
|
||||
viewModel.sendMode = .send
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,12 +127,14 @@ enum ComposerSendMode: Equatable {
|
|||
case createDM
|
||||
}
|
||||
|
||||
enum ComposerViewAction {
|
||||
enum ComposerViewAction: Equatable {
|
||||
case cancel
|
||||
case contentDidChange(isEmpty: Bool)
|
||||
}
|
||||
|
||||
enum ComposerViewModelResult {
|
||||
enum ComposerViewModelResult: Equatable {
|
||||
case cancel
|
||||
case contentDidChange(isEmpty: Bool)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,11 +25,10 @@ final class ComposerUITests: MockScreenTestCase {
|
|||
let wysiwygTextView = app.textViews.allElementsBoundByIndex[0]
|
||||
XCTAssertTrue(wysiwygTextView.exists)
|
||||
let sendButton = app.buttons["sendButton"]
|
||||
XCTAssertTrue(sendButton.exists)
|
||||
XCTAssertFalse(sendButton.isEnabled)
|
||||
XCTAssertFalse(sendButton.exists)
|
||||
wysiwygTextView.tap()
|
||||
wysiwygTextView.typeText("test")
|
||||
XCTAssertTrue(sendButton.isEnabled)
|
||||
XCTAssertTrue(sendButton.exists)
|
||||
XCTAssertFalse(app.buttons["editButton"].exists)
|
||||
}
|
||||
|
||||
|
@ -39,8 +38,7 @@ final class ComposerUITests: MockScreenTestCase {
|
|||
let wysiwygTextView = app.textViews.allElementsBoundByIndex[0]
|
||||
XCTAssertTrue(wysiwygTextView.exists)
|
||||
let sendButton = app.buttons["sendButton"]
|
||||
XCTAssertTrue(sendButton.exists)
|
||||
XCTAssertFalse(sendButton.isEnabled)
|
||||
XCTAssertFalse(sendButton.exists)
|
||||
|
||||
let cancelButton = app.buttons["cancelButton"]
|
||||
XCTAssertTrue(cancelButton.exists)
|
||||
|
@ -51,7 +49,7 @@ final class ComposerUITests: MockScreenTestCase {
|
|||
|
||||
wysiwygTextView.tap()
|
||||
wysiwygTextView.typeText("test")
|
||||
XCTAssertTrue(sendButton.isEnabled)
|
||||
XCTAssertTrue(sendButton.exists)
|
||||
XCTAssertFalse(app.buttons["editButton"].exists)
|
||||
|
||||
cancelButton.tap()
|
||||
|
@ -66,8 +64,7 @@ final class ComposerUITests: MockScreenTestCase {
|
|||
let wysiwygTextView = app.textViews.allElementsBoundByIndex[0]
|
||||
XCTAssertTrue(wysiwygTextView.exists)
|
||||
let editButton = app.buttons["editButton"]
|
||||
XCTAssertTrue(editButton.exists)
|
||||
XCTAssertFalse(editButton.isEnabled)
|
||||
XCTAssertFalse(editButton.exists)
|
||||
|
||||
let cancelButton = app.buttons["cancelButton"]
|
||||
XCTAssertTrue(cancelButton.exists)
|
||||
|
@ -78,7 +75,7 @@ final class ComposerUITests: MockScreenTestCase {
|
|||
|
||||
wysiwygTextView.tap()
|
||||
wysiwygTextView.typeText("test")
|
||||
XCTAssertTrue(editButton.isEnabled)
|
||||
XCTAssertTrue(editButton.exists)
|
||||
XCTAssertFalse(app.buttons["sendButton"].exists)
|
||||
|
||||
cancelButton.tap()
|
||||
|
|
|
@ -26,7 +26,7 @@ struct Composer: View {
|
|||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
@State private var focused = false
|
||||
@State private var isActionButtonEnabled = false
|
||||
@State private var isActionButtonShowing = false
|
||||
|
||||
private let horizontalPadding: CGFloat = 12
|
||||
private let borderHeight: CGFloat = 40
|
||||
|
@ -148,7 +148,7 @@ struct Composer: View {
|
|||
.resizable()
|
||||
.foregroundColor(theme.colors.tertiaryContent)
|
||||
.frame(width: 14, height: 14)
|
||||
|
||||
|
||||
}
|
||||
.frame(width: 36, height: 36)
|
||||
.background(Circle().fill(theme.colors.system))
|
||||
|
@ -159,16 +159,6 @@ struct Composer: View {
|
|||
}
|
||||
.frame(height: 44)
|
||||
Spacer()
|
||||
// ZStack {
|
||||
// TODO: Add support for voice messages
|
||||
// Button {
|
||||
//
|
||||
// } label: {
|
||||
// Image(Asset.Images.voiceMessageRecordButtonDefault.name)
|
||||
// .foregroundColor(theme.colors.tertiaryContent)
|
||||
// }
|
||||
// .isHidden(showSendButton)
|
||||
// .isHidden(true)
|
||||
Button {
|
||||
sendMessageAction(wysiwygViewModel.content)
|
||||
wysiwygViewModel.clearContent()
|
||||
|
@ -181,18 +171,18 @@ struct Composer: View {
|
|||
}
|
||||
.frame(width: 36, height: 36)
|
||||
.padding(.leading, 8)
|
||||
.disabled(!isActionButtonEnabled)
|
||||
.opacity(isActionButtonEnabled ? 1 : 0.3)
|
||||
.animation(.easeInOut(duration: 0.15), value: isActionButtonEnabled)
|
||||
.isHidden(!isActionButtonShowing)
|
||||
.accessibilityIdentifier(actionButtonAccessibilityIdentifier)
|
||||
.accessibilityLabel(VectorL10n.send)
|
||||
.onChange(of: wysiwygViewModel.isContentEmpty) { empty in
|
||||
isActionButtonEnabled = !empty
|
||||
.onChange(of: wysiwygViewModel.isContentEmpty) { isEmpty in
|
||||
viewModel.send(viewAction: .contentDidChange(isEmpty: isEmpty))
|
||||
withAnimation(.easeInOut(duration: 0.15)) {
|
||||
isActionButtonShowing = !isEmpty
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.bottom, 4)
|
||||
.animation(.none)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ final class ComposerViewModel: ComposerViewModelType, ComposerViewModelProtocol
|
|||
switch viewAction {
|
||||
case .cancel:
|
||||
callback?(.cancel)
|
||||
case let .contentDidChange(isEmpty):
|
||||
callback?(.contentDidChange(isEmpty: isEmpty))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .inactive, isEditModeEnabled: false)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: "Title",
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: inactiveSectionHeader,
|
||||
emptyItemsTitle: VectorL10n.userOtherSessionNoInactiveSessions,
|
||||
allItemsSelected: false)
|
||||
|
@ -74,7 +74,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .all, isEditModeEnabled: false)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: "Title",
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: allSectionHeader,
|
||||
emptyItemsTitle: "",
|
||||
allItemsSelected: false)
|
||||
|
@ -90,7 +90,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .unverified, isEditModeEnabled: false)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: "Title",
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: unverifiedSectionHeader,
|
||||
emptyItemsTitle: VectorL10n.userOtherSessionNoUnverifiedSessions,
|
||||
allItemsSelected: false)
|
||||
|
@ -106,7 +106,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .verified, isEditModeEnabled: false)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: "Title",
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: verifiedSectionHeader,
|
||||
emptyItemsTitle: VectorL10n.userOtherSessionNoVerifiedSessions,
|
||||
allItemsSelected: false)
|
||||
|
@ -120,7 +120,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .verified, isEditModeEnabled: false)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: "Title",
|
||||
items: [],
|
||||
sessionItems: [],
|
||||
header: verifiedSectionHeader,
|
||||
emptyItemsTitle: VectorL10n.userOtherSessionNoVerifiedSessions,
|
||||
allItemsSelected: false)
|
||||
|
@ -134,7 +134,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .unverified, isEditModeEnabled: false)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: "Title",
|
||||
items: [],
|
||||
sessionItems: [],
|
||||
header: unverifiedSectionHeader,
|
||||
emptyItemsTitle: VectorL10n.userOtherSessionNoUnverifiedSessions,
|
||||
allItemsSelected: false)
|
||||
|
@ -148,7 +148,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .inactive, isEditModeEnabled: false)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: "Title",
|
||||
items: [],
|
||||
sessionItems: [],
|
||||
header: inactiveSectionHeader,
|
||||
emptyItemsTitle: VectorL10n.userOtherSessionNoInactiveSessions,
|
||||
allItemsSelected: false)
|
||||
|
@ -167,7 +167,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .all, isEditModeEnabled: true)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: VectorL10n.userOtherSessionSelectedCount("2"),
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: allSectionHeader,
|
||||
emptyItemsTitle: "",
|
||||
allItemsSelected: true)
|
||||
|
@ -186,7 +186,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .all, isEditModeEnabled: true)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: VectorL10n.userOtherSessionSelectedCount("0"),
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: allSectionHeader,
|
||||
emptyItemsTitle: "",
|
||||
allItemsSelected: false)
|
||||
|
@ -204,7 +204,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .all, isEditModeEnabled: true)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: VectorL10n.userOtherSessionSelectedCount("1"),
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: allSectionHeader,
|
||||
emptyItemsTitle: "",
|
||||
allItemsSelected: false)
|
||||
|
@ -222,7 +222,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .all, isEditModeEnabled: true)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: VectorL10n.userOtherSessionSelectedCount("2"),
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: allSectionHeader,
|
||||
emptyItemsTitle: "",
|
||||
allItemsSelected: true)
|
||||
|
@ -240,7 +240,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .all, isEditModeEnabled: true)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: VectorL10n.userOtherSessionSelectedCount("0"),
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: allSectionHeader,
|
||||
emptyItemsTitle: "",
|
||||
allItemsSelected: false)
|
||||
|
@ -261,7 +261,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
|
|||
let bindings = UserOtherSessionsBindings(filter: .all, isEditModeEnabled: true)
|
||||
let expectedState = UserOtherSessionsViewState(bindings: bindings,
|
||||
title: VectorL10n.userOtherSessionSelectedCount("0"),
|
||||
items: expectedItems,
|
||||
sessionItems: expectedItems,
|
||||
header: allSectionHeader,
|
||||
emptyItemsTitle: "",
|
||||
allItemsSelected: false)
|
||||
|
|
|
@ -35,7 +35,7 @@ enum UserOtherSessionsViewModelResult: Equatable {
|
|||
struct UserOtherSessionsViewState: BindableState, Equatable {
|
||||
var bindings: UserOtherSessionsBindings
|
||||
var title: String
|
||||
var items: [UserSessionListItemViewData]
|
||||
var sessionItems: [UserSessionListItemViewData]
|
||||
var header: UserOtherSessionsHeaderViewData
|
||||
var emptyItemsTitle: String
|
||||
var allItemsSelected: Bool
|
||||
|
|
|
@ -30,10 +30,10 @@ class UserOtherSessionsViewModel: UserOtherSessionsViewModelType, UserOtherSessi
|
|||
self.sessionInfos = sessionInfos
|
||||
defaultTitle = title
|
||||
let bindings = UserOtherSessionsBindings(filter: filter, isEditModeEnabled: false)
|
||||
let items = filter.filterSessionInfos(sessionInfos: sessionInfos, selectedSessions: selectedSessions)
|
||||
let sessionItems = filter.filterSessionInfos(sessionInfos: sessionInfos, selectedSessions: selectedSessions)
|
||||
super.init(initialViewState: UserOtherSessionsViewState(bindings: bindings,
|
||||
title: title,
|
||||
items: items,
|
||||
sessionItems: sessionItems,
|
||||
header: filter.userOtherSessionsViewHeader,
|
||||
emptyItemsTitle: filter.userOtherSessionsViewEmptyResultsTitle,
|
||||
allItemsSelected: false))
|
||||
|
@ -87,7 +87,7 @@ class UserOtherSessionsViewModel: UserOtherSessionsViewModelType, UserOtherSessi
|
|||
private func updateViewState() {
|
||||
let currentFilter = state.bindings.filter
|
||||
|
||||
state.items = currentFilter.filterSessionInfos(sessionInfos: sessionInfos, selectedSessions: selectedSessions)
|
||||
state.sessionItems = currentFilter.filterSessionInfos(sessionInfos: sessionInfos, selectedSessions: selectedSessions)
|
||||
state.header = currentFilter.userOtherSessionsViewHeader
|
||||
|
||||
if state.bindings.isEditModeEnabled {
|
||||
|
|
|
@ -24,7 +24,7 @@ struct UserOtherSessions: View {
|
|||
var body: some View {
|
||||
ScrollView {
|
||||
SwiftUI.Section {
|
||||
if viewModel.viewState.items.isEmpty {
|
||||
if viewModel.viewState.sessionItems.isEmpty {
|
||||
noItemsView()
|
||||
} else {
|
||||
itemsView()
|
||||
|
@ -81,7 +81,7 @@ struct UserOtherSessions: View {
|
|||
|
||||
private func itemsView() -> some View {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(viewModel.viewState.items) { viewData in
|
||||
ForEach(viewModel.viewState.sessionItems) { viewData in
|
||||
UserSessionListItem(viewData: viewData,
|
||||
isEditModeEnabled: viewModel.isEditModeEnabled,
|
||||
onBackgroundTap: { sessionId in viewModel.send(viewAction: .userOtherSessionSelected(sessionId: sessionId)) },
|
||||
|
|
|
@ -48,7 +48,7 @@ struct UserOtherSessionsToolbar: ToolbarContent {
|
|||
} else {
|
||||
filterMenuButton()
|
||||
.offset(x: 12)
|
||||
kebabMenu()
|
||||
optionsMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ struct UserOtherSessionsToolbar: ToolbarContent {
|
|||
}
|
||||
}
|
||||
|
||||
private func kebabMenu() -> some View {
|
||||
private func optionsMenu() -> some View {
|
||||
Button { } label: {
|
||||
Menu {
|
||||
Button {
|
||||
|
|
1
changelog.d/6941.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Added voice message support to the Rich Text Composer
|