diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index 95c5c4a02..f886276fd 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -88,7 +88,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp let subView: UIView = hostingViewController.view self.addSubview(subView) - hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false + self.translatesAutoresizingMaskIntoConstraints = false subView.translatesAutoresizingMaskIntoConstraints = false heightConstraint = subView.heightAnchor.constraint(equalToConstant: height) NSLayoutConstraint.activate([ @@ -103,7 +103,13 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp .sink(receiveValue: { [weak self] idealHeight in guard let self = self else { return } self.updateToolbarHeight(wysiwygHeight: idealHeight) - }) + }), + // Required to update the view constraints after minimise/maximise is tapped + wysiwygViewModel.$idealHeight + .removeDuplicates() + .sink { [weak hostingViewController] _ in + hostingViewController?.view.setNeedsLayout() + } ] update(theme: ThemeService.shared().theme) diff --git a/RiotSwiftUI/Modules/Room/Composer/Test/UI/ComposerUITests.swift b/RiotSwiftUI/Modules/Room/Composer/Test/UI/ComposerUITests.swift index 08ce71fc0..c80bea819 100644 --- a/RiotSwiftUI/Modules/Room/Composer/Test/UI/ComposerUITests.swift +++ b/RiotSwiftUI/Modules/Room/Composer/Test/UI/ComposerUITests.swift @@ -30,6 +30,19 @@ final class ComposerUITests: MockScreenTestCase { wysiwygTextView.typeText("test") XCTAssertTrue(sendButton.exists) XCTAssertFalse(app.buttons["editButton"].exists) + + let maximiseButton = app.buttons["maximiseButton"] + let minimiseButton = app.buttons["minimiseButton"] + XCTAssertFalse(minimiseButton.exists) + XCTAssertTrue(maximiseButton.exists) + + maximiseButton.tap() + XCTAssertTrue(minimiseButton.exists) + XCTAssertFalse(maximiseButton.exists) + + minimiseButton.tap() + XCTAssertFalse(minimiseButton.exists) + XCTAssertTrue(maximiseButton.exists) } func testReplyMode() throws { @@ -56,6 +69,19 @@ final class ComposerUITests: MockScreenTestCase { let textViewContent = wysiwygTextView.value as! String XCTAssertFalse(textViewContent.isEmpty) XCTAssertFalse(cancelButton.exists) + + let maximiseButton = app.buttons["maximiseButton"] + let minimiseButton = app.buttons["minimiseButton"] + XCTAssertFalse(minimiseButton.exists) + XCTAssertTrue(maximiseButton.exists) + + maximiseButton.tap() + XCTAssertTrue(minimiseButton.exists) + XCTAssertFalse(maximiseButton.exists) + + minimiseButton.tap() + XCTAssertFalse(minimiseButton.exists) + XCTAssertTrue(maximiseButton.exists) } func testEditMode() throws { @@ -82,5 +108,18 @@ final class ComposerUITests: MockScreenTestCase { let textViewContent = wysiwygTextView.value as! String XCTAssertTrue(textViewContent.isEmpty) XCTAssertFalse(cancelButton.exists) + + let maximiseButton = app.buttons["maximiseButton"] + let minimiseButton = app.buttons["minimiseButton"] + XCTAssertFalse(minimiseButton.exists) + XCTAssertTrue(maximiseButton.exists) + + maximiseButton.tap() + XCTAssertTrue(minimiseButton.exists) + XCTAssertFalse(maximiseButton.exists) + + minimiseButton.tap() + XCTAssertFalse(minimiseButton.exists) + XCTAssertTrue(maximiseButton.exists) } } diff --git a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift index be7577e12..624c84638 100644 --- a/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift +++ b/RiotSwiftUI/Modules/Room/Composer/View/Composer.swift @@ -51,6 +51,14 @@ struct Composer: View { viewModel.viewState.sendMode == .edit ? "editButton" : "sendButton" } + private var toggleButtonAcccessibilityIdentifier: String { + wysiwygViewModel.maximised ? "minimiseButton" : "maximiseButton" + } + + private var toggleButtonImageName: String { + wysiwygViewModel.maximised ? Asset.Images.minimiseComposer.name : Asset.Images.maximiseComposer.name + } + private var borderColor: Color { focused ? theme.colors.quarterlyContent : theme.colors.quinaryContent } @@ -76,8 +84,6 @@ struct Composer: View { var body: some View { VStack(spacing: 8) { let rect = RoundedRectangle(cornerRadius: cornerRadius) - // TODO: Fix maximise animation bugs before re-enabling - // ZStack(alignment: .topTrailing) { VStack(spacing: 12) { if viewModel.viewState.shouldDisplayContext { HStack { @@ -103,36 +109,39 @@ struct Composer: View { .padding(.top, 8) .padding(.horizontal, horizontalPadding) } - WysiwygComposerView( - focused: $focused, - content: wysiwygViewModel.content, - replaceText: wysiwygViewModel.replaceText, - select: wysiwygViewModel.select, - didUpdateText: wysiwygViewModel.didUpdateText - ) - .tintColor(theme.colors.accent) - .placeholder(viewModel.viewState.placeholder, color: theme.colors.tertiaryContent) - .frame(height: wysiwygViewModel.idealHeight) - .padding(.horizontal, horizontalPadding) - .onAppear { - wysiwygViewModel.setup() + HStack(alignment: .top, spacing: 0) { + WysiwygComposerView( + focused: $focused, + content: wysiwygViewModel.content, + replaceText: wysiwygViewModel.replaceText, + select: wysiwygViewModel.select, + didUpdateText: wysiwygViewModel.didUpdateText + ) + .tintColor(theme.colors.accent) + .placeholder(viewModel.viewState.placeholder, color: theme.colors.tertiaryContent) + .frame(height: wysiwygViewModel.idealHeight) + .onAppear { + wysiwygViewModel.setup() + } + Button { + wysiwygViewModel.maximised.toggle() + } label: { + Image(toggleButtonImageName) + .resizable() + .foregroundColor(theme.colors.tertiaryContent) + .frame(width: 16, height: 16) + } + .accessibilityIdentifier(toggleButtonAcccessibilityIdentifier) + .padding(.leading, 12) + .padding(.trailing, 4) } - // Button { - // withAnimation(.easeInOut(duration: 0.25)) { - // viewModel.maximised.toggle() - // } - // } label: { - // Image(viewModel.maximised ? Asset.Images.minimiseComposer.name : Asset.Images.maximiseComposer.name) - // .foregroundColor(theme.colors.tertiaryContent) - // } - // .padding(.top, 4) - // .padding(.trailing, 12) - // } + .padding(.horizontal, horizontalPadding) .padding(.top, topPadding) .padding(.bottom, verticalPadding) } .clipShape(rect) .overlay(rect.stroke(borderColor, lineWidth: 1)) + .animation(.easeInOut(duration: 0.1), value: wysiwygViewModel.idealHeight) .padding(.horizontal, horizontalPadding) .padding(.top, 8) .onTapGesture { @@ -148,7 +157,6 @@ struct Composer: View { .resizable() .foregroundColor(theme.colors.tertiaryContent) .frame(width: 14, height: 14) - } .frame(width: 36, height: 36) .background(Circle().fill(theme.colors.system)) diff --git a/changelog.d/6954.change b/changelog.d/6954.change new file mode 100644 index 000000000..40c49aac0 --- /dev/null +++ b/changelog.d/6954.change @@ -0,0 +1 @@ +Added the maximise/minimise toggle button to the Rich Text Composer \ No newline at end of file