mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Rich text editor "expanded mode" (#1656)
* Fix composer icon in dark mode * Add RTE poc * Amend cornerRadius * Add snaps * Fix composer top spacing * Fix clipping * Refine UX * Fix animation * Add constants + iPad hide bars logics * Polish clamping * Fix UT * Cleanup * Add grabber color * Add UI tests * Rename handle -> grabber * Fix resize composer when RTE is off * Fix project file
This commit is contained in:
parent
89c4786af7
commit
bd4ee92124
@ -103,6 +103,7 @@
|
||||
237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55D7E514F9DE4E3D72FDCAD /* SessionVerificationControllerProxy.swift */; };
|
||||
245F7FE5961BD10C145A26E0 /* UITimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA689E792E679F5E3956F21 /* UITimelineView.swift */; };
|
||||
24A75F72EEB7561B82D726FD /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2141693488CE5446BB391964 /* Date.swift */; };
|
||||
24B7CD41342C143117ADA768 /* Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B1CC9AA154F4D5435BF60A /* Comparable.swift */; };
|
||||
24BDDD09A90B8BFE3793F3AA /* ClientProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6033779EB37259F27F938937 /* ClientProxyProtocol.swift */; };
|
||||
25618589E0DE0F1E95FC7B5C /* EmojiProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */; };
|
||||
256D76972BA3254F7CB7F88B /* LocationAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD8234D0E9C9B12BF9F240B /* LocationAnnotation.swift */; };
|
||||
@ -1491,6 +1492,7 @@
|
||||
E1E0B4A34E69BD2132BEC521 /* MessageText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageText.swift; sourceTree = "<group>"; };
|
||||
E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSize.swift; sourceTree = "<group>"; };
|
||||
E26747B3154A5DBC3A7E24A5 /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = "<group>"; };
|
||||
E2B1CC9AA154F4D5435BF60A /* Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comparable.swift; sourceTree = "<group>"; };
|
||||
E2DCA495ED42D2463DDAA94D /* TimelineBubbleLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineBubbleLayout.swift; sourceTree = "<group>"; };
|
||||
E3059CFA00C67D8787273B20 /* ServerSelectionScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = "<group>"; };
|
||||
@ -2205,6 +2207,7 @@
|
||||
52BD6ED18E2EB61E28C340AD /* AttributedString.swift */,
|
||||
B6E89E530A8E92EC44301CA1 /* Bundle.swift */,
|
||||
A9FAFE1C2149E6AC8156ED2B /* Collection.swift */,
|
||||
E2B1CC9AA154F4D5435BF60A /* Comparable.swift */,
|
||||
B383DCD3DCB19E00FD478A5F /* ConfirmationDialog.swift */,
|
||||
2141693488CE5446BB391964 /* Date.swift */,
|
||||
BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */,
|
||||
@ -4460,6 +4463,7 @@
|
||||
9FAF6DA7E8E85C9699757764 /* CollapsibleRoomTimelineView.swift in Sources */,
|
||||
0DC815CA24E1BD7F408F37D3 /* CollapsibleTimelineItem.swift in Sources */,
|
||||
663E198678778F7426A9B27D /* Collection.swift in Sources */,
|
||||
24B7CD41342C143117ADA768 /* Comparable.swift in Sources */,
|
||||
0B57C2399B9E1CE5CE0D8005 /* ComposerToolbar.swift in Sources */,
|
||||
56BAB81A0D03C2EF09B86294 /* ComposerToolbarModels.swift in Sources */,
|
||||
5995C63B1C61DE1373AA2BCE /* ComposerToolbarViewModel.swift in Sources */,
|
||||
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "199",
|
||||
"green" : "197",
|
||||
"red" : "197"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "89",
|
||||
"green" : "83",
|
||||
"red" : "81"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
"preserves-vector-representation" : true,
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ internal enum Asset {
|
||||
internal enum Colors {
|
||||
internal static let accentColor = ColorAsset(name: "colors/accent-color")
|
||||
internal static let backgroundColor = ColorAsset(name: "colors/background-color")
|
||||
internal static let grabber = ColorAsset(name: "colors/grabber")
|
||||
}
|
||||
internal enum Images {
|
||||
internal static let appLogo = ImageAsset(name: "images/app-logo")
|
||||
|
21
ElementX/Sources/Other/Extensions/Comparable.swift
Normal file
21
ElementX/Sources/Other/Extensions/Comparable.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
extension Comparable {
|
||||
func clamped(to limits: ClosedRange<Self>) -> Self {
|
||||
min(max(self, limits.lowerBound), limits.upperBound)
|
||||
}
|
||||
}
|
@ -68,6 +68,7 @@ struct ComposerToolbarViewStateBindings {
|
||||
var composerPlainText = ""
|
||||
var composerFocused = false
|
||||
var composerActionsEnabled = false
|
||||
var composerExpanded = false
|
||||
var formatItems: [FormatItem] = .init()
|
||||
var alertInfo: AlertInfo<UUID>?
|
||||
|
||||
|
@ -90,7 +90,6 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
|
||||
actionsSubject.send(.sendPlainTextMessage(message: context.composerPlainText,
|
||||
mode: state.composerMode))
|
||||
}
|
||||
state.bindings.composerActionsEnabled = false
|
||||
case .cancelReply:
|
||||
set(mode: .default)
|
||||
case .cancelEdit:
|
||||
|
@ -43,6 +43,10 @@ struct ComposerToolbar: View {
|
||||
}
|
||||
messageComposer
|
||||
.environmentObject(context)
|
||||
.onTapGesture {
|
||||
guard !composerFocused else { return }
|
||||
composerFocused = true
|
||||
}
|
||||
if !context.composerActionsEnabled {
|
||||
sendButton
|
||||
}
|
||||
@ -53,6 +57,7 @@ struct ComposerToolbar: View {
|
||||
HStack(alignment: .bottom, spacing: 10) {
|
||||
Button {
|
||||
context.composerActionsEnabled = false
|
||||
context.composerExpanded = false
|
||||
} label: {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.compound.headingLG)
|
||||
@ -89,7 +94,9 @@ struct ComposerToolbar: View {
|
||||
private var messageComposer: some View {
|
||||
MessageComposer(plainText: $context.composerPlainText,
|
||||
composerView: composerView,
|
||||
mode: context.viewState.composerMode) {
|
||||
mode: context.viewState.composerMode,
|
||||
showResizeGrabber: context.viewState.bindings.composerActionsEnabled,
|
||||
isExpanded: $context.composerExpanded) {
|
||||
context.send(viewAction: .sendMessage)
|
||||
} pasteAction: { provider in
|
||||
context.send(viewAction: .handlePasteOrDrop(provider: provider))
|
||||
|
@ -24,6 +24,8 @@ struct MessageComposer: View {
|
||||
@Binding var plainText: String
|
||||
let composerView: WysiwygComposerView
|
||||
let mode: RoomScreenComposerMode
|
||||
let showResizeGrabber: Bool
|
||||
@Binding var isExpanded: Bool
|
||||
let sendAction: EnterKeyHandler
|
||||
let pasteAction: PasteHandler
|
||||
let replyCancellationAction: () -> Void
|
||||
@ -32,14 +34,43 @@ struct MessageComposer: View {
|
||||
@FocusState private var focused: Bool
|
||||
|
||||
@State private var isMultiline = false
|
||||
@State private var composerTranslation: CGFloat = 0
|
||||
|
||||
var body: some View {
|
||||
let roundedRectangle = RoundedRectangle(cornerRadius: borderRadius)
|
||||
VStack(spacing: 0) {
|
||||
if showResizeGrabber {
|
||||
resizeGrabber
|
||||
}
|
||||
|
||||
mainContent
|
||||
.padding(.horizontal, 12.0)
|
||||
.clipShape(RoundedRectangle(cornerRadius: borderRadius))
|
||||
.background {
|
||||
let roundedRectangle = RoundedRectangle(cornerRadius: borderRadius)
|
||||
ZStack {
|
||||
roundedRectangle
|
||||
.fill(Color.compound.bgSubtleSecondary)
|
||||
roundedRectangle
|
||||
.stroke(Color.compound._borderTextFieldFocused, lineWidth: 1)
|
||||
.opacity(focused ? 1 : 0)
|
||||
}
|
||||
}
|
||||
// Explicitly disable all animations to fix weirdness with the header immediately
|
||||
// appearing whilst the text field and keyboard are still animating up to it.
|
||||
.animation(.noAnimation, value: mode)
|
||||
}
|
||||
.gesture(showResizeGrabber ? dragGesture : nil)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private var mainContent: some View {
|
||||
VStack(alignment: .leading, spacing: -6) {
|
||||
header
|
||||
HStack(alignment: .bottom) {
|
||||
if ServiceLocator.shared.settings.richTextEditorEnabled {
|
||||
composerView
|
||||
.frame(minHeight: composerHeight, alignment: .top)
|
||||
.tint(.compound.iconAccentTertiary)
|
||||
.padding(.vertical, 10)
|
||||
.focused($focused)
|
||||
@ -59,20 +90,11 @@ struct MessageComposer: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 12.0)
|
||||
.clipped()
|
||||
.background {
|
||||
ZStack {
|
||||
roundedRectangle
|
||||
.fill(Color.compound.bgSubtleSecondary)
|
||||
roundedRectangle
|
||||
.stroke(Color.compound._borderTextFieldFocused, lineWidth: 1)
|
||||
.opacity(focused ? 1 : 0)
|
||||
}
|
||||
}
|
||||
// Explicitly disable all animations to fix weirdness with the header immediately
|
||||
// appearing whilst the text field and keyboard are still animating up to it.
|
||||
.animation(.noAnimation, value: mode)
|
||||
}
|
||||
|
||||
private var composerHeight: CGFloat {
|
||||
let baseHeight = isExpanded ? ComposerConstant.maxHeight : ComposerConstant.minHeight
|
||||
return (baseHeight - composerTranslation).clamped(to: ComposerConstant.allowedHeightRange)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
@ -95,6 +117,31 @@ struct MessageComposer: View {
|
||||
return 20
|
||||
}
|
||||
}
|
||||
|
||||
private var resizeGrabber: some View {
|
||||
Capsule()
|
||||
.foregroundColor(Asset.Colors.grabber.swiftUIColor)
|
||||
.frame(width: 36, height: 5)
|
||||
.padding(.vertical, 8)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
private var dragGesture: some Gesture {
|
||||
DragGesture()
|
||||
.onChanged { value in
|
||||
composerTranslation += value.translation.height
|
||||
}
|
||||
.onEnded { _ in
|
||||
withElementAnimation(.easeIn(duration: 0.3)) {
|
||||
if composerTranslation > ComposerConstant.translationThreshold {
|
||||
isExpanded = false
|
||||
} else if composerTranslation < -ComposerConstant.translationThreshold {
|
||||
isExpanded = true
|
||||
}
|
||||
composerTranslation = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct MessageComposerReplyHeader: View {
|
||||
@ -169,6 +216,8 @@ struct MessageComposer_Previews: PreviewProvider {
|
||||
return MessageComposer(plainText: .constant(content),
|
||||
composerView: composerView,
|
||||
mode: mode,
|
||||
showResizeGrabber: false,
|
||||
isExpanded: .constant(false),
|
||||
sendAction: { },
|
||||
pasteAction: { _ in },
|
||||
replyCancellationAction: { },
|
||||
|
@ -61,7 +61,9 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
|
||||
wysiwygViewModel = WysiwygComposerViewModel(minHeight: 22, maxExpandedHeight: 250)
|
||||
wysiwygViewModel = WysiwygComposerViewModel(minHeight: ComposerConstant.minHeight,
|
||||
maxCompressedHeight: ComposerConstant.maxHeight,
|
||||
maxExpandedHeight: ComposerConstant.maxHeight)
|
||||
composerViewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel)
|
||||
}
|
||||
|
||||
@ -124,3 +126,10 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
return AnyView(RoomScreen(context: viewModel.context, composerToolbar: composerToolbar))
|
||||
}
|
||||
}
|
||||
|
||||
enum ComposerConstant {
|
||||
static let minHeight: CGFloat = 22
|
||||
static let maxHeight: CGFloat = 250
|
||||
static let allowedHeightRange = minHeight...maxHeight
|
||||
static let translationThreshold: CGFloat = 60
|
||||
}
|
||||
|
@ -19,11 +19,18 @@ import WysiwygComposer
|
||||
|
||||
struct RoomScreen: View {
|
||||
@ObservedObject var context: RoomScreenViewModel.Context
|
||||
@ObservedObject private var composerToolbarContext: ComposerToolbarViewModel.Context
|
||||
@State private var dragOver = false
|
||||
let composerToolbar: ComposerToolbar
|
||||
|
||||
private let attachmentButtonPadding = 10.0
|
||||
|
||||
init(context: RoomScreenViewModel.Context, composerToolbar: ComposerToolbar) {
|
||||
self.context = context
|
||||
self.composerToolbar = composerToolbar
|
||||
composerToolbarContext = composerToolbar.context
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
timeline
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
@ -31,12 +38,20 @@ struct RoomScreen: View {
|
||||
composerToolbar
|
||||
.padding(.leading, attachmentButtonPadding)
|
||||
.padding(.trailing, 12)
|
||||
.padding(.top, 8)
|
||||
.padding(.bottom)
|
||||
.background {
|
||||
if composerToolbarContext.composerActionsEnabled {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.stroke(Color.compound._borderTextFieldFocused, lineWidth: 1)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
.padding(.top, 8)
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.environmentObject(context)
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarHidden(isNavigationBarHidden)
|
||||
.toolbar { toolbar }
|
||||
.toolbarBackground(.visible, for: .navigationBar) // Fix the toolbar's background.
|
||||
.overlay { loadingIndicator }
|
||||
@ -142,6 +157,10 @@ struct RoomScreen: View {
|
||||
RoomHeaderView(context: context)
|
||||
}
|
||||
}
|
||||
|
||||
private var isNavigationBarHidden: Bool {
|
||||
composerToolbarContext.composerActionsEnabled && composerToolbarContext.composerExpanded && UIDevice.current.userInterfaceIdiom == .pad
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
@ -372,7 +372,9 @@ class MockScreen: Identifiable {
|
||||
var sessionVerificationControllerProxy = SessionVerificationControllerProxyMock.configureMock(requestDelay: .seconds(5))
|
||||
let parameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationControllerProxy)
|
||||
return SessionVerificationScreenCoordinator(parameters: parameters)
|
||||
case .userSessionScreen, .userSessionScreenReply:
|
||||
case .userSessionScreen, .userSessionScreenReply, .userSessionScreenRTE:
|
||||
let appSettings: AppSettings = ServiceLocator.shared.settings
|
||||
appSettings.richTextEditorEnabled = id == .userSessionScreenRTE
|
||||
let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator())
|
||||
|
||||
let clientProxy = MockClientProxy(userID: "@mock:client.com", roomSummaryProvider: MockRoomSummaryProvider(state: .loaded(.mockRooms)))
|
||||
@ -383,7 +385,7 @@ class MockScreen: Identifiable {
|
||||
navigationSplitCoordinator: navigationSplitCoordinator,
|
||||
bugReportService: BugReportServiceMock(),
|
||||
roomTimelineControllerFactory: MockRoomTimelineControllerFactory(),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
appSettings: appSettings,
|
||||
analytics: ServiceLocator.shared.analytics)
|
||||
|
||||
coordinator.start()
|
||||
|
@ -51,6 +51,7 @@ enum UITestsScreenIdentifier: String {
|
||||
case sessionVerification
|
||||
case userSessionScreen
|
||||
case userSessionScreenReply
|
||||
case userSessionScreenRTE
|
||||
case roomDetailsScreen
|
||||
case roomDetailsScreenWithRoomAvatar
|
||||
case roomDetailsScreenWithEmptyTopic
|
||||
|
@ -45,4 +45,19 @@ class UserSessionScreenTests: XCTestCase {
|
||||
|
||||
try await app.assertScreenshot(.userSessionScreenReply)
|
||||
}
|
||||
|
||||
func testUserSessionRTE() async throws {
|
||||
let roomName = "First room"
|
||||
let app = Application.launch(.userSessionScreenRTE)
|
||||
|
||||
app.buttons[A11yIdentifiers.homeScreen.roomName(roomName)].tap()
|
||||
XCTAssert(app.staticTexts[roomName].waitForExistence(timeout: 5.0))
|
||||
try await Task.sleep(for: .seconds(1))
|
||||
|
||||
app.buttons[A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions].tap()
|
||||
try await app.assertScreenshot(.userSessionScreenRTE, step: 1)
|
||||
|
||||
app.buttons[A11yIdentifiers.roomScreen.attachmentPickerTextFormatting].tap()
|
||||
try await app.assertScreenshot(.userSessionScreenRTE, step: 2)
|
||||
}
|
||||
}
|
||||
|
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreenRTE-1.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreenRTE-1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreenRTE-2.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.userSessionScreenRTE-2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreenRTE-1.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreenRTE-1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreenRTE-2.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.userSessionScreenRTE-2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreenRTE-1.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreenRTE-1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreenRTE-2.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.userSessionScreenRTE-2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreenRTE-1.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreenRTE-1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreenRTE-2.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.userSessionScreenRTE-2.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -78,12 +78,12 @@ class ComposerToolbarViewModelTests: XCTestCase {
|
||||
XCTAssertTrue(viewModel.state.bindings.composerFocused)
|
||||
}
|
||||
|
||||
func testRTEDisabledAfterSendingMessage() {
|
||||
func testRTEEnabledAfterSendingMessage() {
|
||||
viewModel.process(viewAction: .enableTextFormatting)
|
||||
XCTAssertTrue(viewModel.state.bindings.composerFocused)
|
||||
viewModel.state.composerEmpty = false
|
||||
viewModel.process(viewAction: .sendMessage)
|
||||
XCTAssertFalse(viewModel.state.bindings.composerActionsEnabled)
|
||||
XCTAssertTrue(viewModel.state.bindings.composerActionsEnabled)
|
||||
}
|
||||
|
||||
func testAlertIsShownAfterLinkAction() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user