Skip to content

Commit

Permalink
Merge pull request #7127 from vector-im/mauroromito/design_improvemen…
Browse files Browse the repository at this point in the history
…ts_2

Rich Text Editor: Design Improvements
  • Loading branch information
Velin92 authored Nov 30, 2022
2 parents ef8a9be + 1c28ba3 commit aa43d74
Show file tree
Hide file tree
Showing 18 changed files with 111 additions and 100 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"images" : [
{
"filename" : "action_formatting_disabled.png",
"filename" : "Frame 143.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "action_formatting_disabled@2x.png",
"filename" : "Frame 143@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "action_formatting_disabled@3x.png",
"filename" : "Frame 143@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
10 changes: 9 additions & 1 deletion Riot/Modules/Room/RoomViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,15 @@ extension RoomViewController {
optionalTextView?.becomeFirstResponder()
originalRect = wysiwygInputToolbar.convert(wysiwygInputToolbar.frame, to: view)
}
wysiwygInputToolbar.showKeyboard()
// This tirggers a SwiftUI update that is handled correctly on iOS 16, but needs to be dispatchted async on older versions
// Dispatching on iOS 16 instead causes some weird SwiftUI update behaviours
if #available(iOS 16, *) {
wysiwygInputToolbar.showKeyboard()
} else {
DispatchQueue.main.async {
wysiwygInputToolbar.showKeyboard()
}
}
roomInputToolbarContainer.removeFromSuperview()
let dimmingView = UIView()
dimmingView.translatesAutoresizingMaskIntoConstraints = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
guard let self = self else { return }
self.toolbarViewDelegate?.didChangeMaximisedState(value)
self.hostingViewController.view.layer.cornerRadius = value ? 20 : 0
if !value {
self.voiceMessageBottomConstraint?.constant = 2
}
}
]

Expand Down Expand Up @@ -215,23 +218,17 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
keyboardHeight = keyboardRectangle.height
UIView.performWithoutAnimation {
if self.isMaximised {
self.voiceMessageBottomConstraint?.constant = keyboardHeight - (window?.safeAreaInsets.bottom ?? 0) + 4
} else {
self.voiceMessageBottomConstraint?.constant = 4
}
self.layoutIfNeeded()
if self.isMaximised {
self.voiceMessageBottomConstraint?.constant = keyboardHeight - (window?.safeAreaInsets.bottom ?? 0) + 2
} else {
self.voiceMessageBottomConstraint?.constant = 2
}
}
}

@objc private func keyboardWillHide(_ notification: Notification) {
if self.isMaximised {
UIView.performWithoutAnimation {
self.voiceMessageBottomConstraint?.constant = 4
self.layoutIfNeeded()
}
self.voiceMessageBottomConstraint?.constant = 2
}
}

Expand Down Expand Up @@ -277,7 +274,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
}
)
}

private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
Expand All @@ -294,7 +291,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
private func updateTextViewHeight() {
let height = UIScreen.main.bounds.height
let barOffset: CGFloat = 68
let toolbarHeight: CGFloat = 96
let toolbarHeight: CGFloat = sendMode == .send ? 96 : 110
let finalHeight = height - keyboardHeight - toolbarHeight - barOffset
wysiwygViewModel.maxExpandedHeight = finalHeight
if finalHeight < 200 {
Expand Down Expand Up @@ -339,9 +336,10 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
set {
viewModel.sendMode = ComposerSendMode(from: newValue)
updatePlaceholderText()
updateTextViewHeight()
}
}

/// Whether text formatting is currently enabled in the composer.
var textFormattingEnabled: Bool {
get {
Expand All @@ -361,7 +359,7 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp
voiceMessageToolbarView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.deactivate(voiceMessageToolbarView.containersTopConstraints)
addSubview(voiceMessageToolbarView)
let bottomConstraint = hostingViewController.view.bottomAnchor.constraint(equalTo: voiceMessageToolbarView.bottomAnchor, constant: 4)
let bottomConstraint = hostingViewController.view.bottomAnchor.constraint(equalTo: voiceMessageToolbarView.bottomAnchor, constant: 2)
voiceMessageBottomConstraint = bottomConstraint
NSLayoutConstraint.activate(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,31 @@ final class ComposerCreateActionListCoordinator: NSObject, Coordinator, Presenta
// MARK: - Setup

init(actions: [ComposerCreateAction], wysiwygEnabled: Bool, textFormattingEnabled: Bool) {
let isScrollingEnabled: Bool
if #available(iOS 16, *) {
isScrollingEnabled = false
} else {
isScrollingEnabled = true
}
viewModel = ComposerCreateActionListViewModel(initialViewState: ComposerCreateActionListViewState(
actions: actions,
wysiwygEnabled: wysiwygEnabled,
isScrollingEnabled: isScrollingEnabled,
bindings: ComposerCreateActionListBindings(textFormattingEnabled: textFormattingEnabled)))
view = ComposerCreateActionList(viewModel: viewModel.context)
let hostingVC = VectorHostingController(rootView: view)
let height = hostingVC.sizeThatFits(in: CGSize(width: hostingVC.view.frame.width, height: UIView.layoutFittingCompressedSize.height)).height
hostingVC.bottomSheetPreferences = VectorHostingBottomSheetPreferences(
detents: [.custom(height: 470)],
// on iOS 15 custom will be replaced by medium which may require some scrolling
detents: [.custom(height: height)],
prefersGrabberVisible: true,
cornerRadius: 20,
prefersScrollingExpandsWhenScrolledToEdge: false
)
hostingController = hostingVC
super.init()
hostingVC.presentationController?.delegate = self
hostingVC.bottomSheetPreferences?.setup(viewController: hostingVC)
}

// MARK: - Public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enum MockComposerCreateActionListScreenState: MockScreenState, CaseIterable {
let viewModel = ComposerCreateActionListViewModel(initialViewState: ComposerCreateActionListViewState(
actions: actions,
wysiwygEnabled: true,
isScrollingEnabled: false,
bindings: ComposerCreateActionListBindings(textFormattingEnabled: true)))

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct ComposerCreateActionListViewState: BindableState {
/// The list of composer create actions to display to the user
let actions: [ComposerCreateAction]
let wysiwygEnabled: Bool
let isScrollingEnabled: Bool

var bindings: ComposerCreateActionListBindings
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class ComposerCreateActionListTests: XCTestCase {
initialViewState: ComposerCreateActionListViewState(
actions: ComposerCreateAction.allCases,
wysiwygEnabled: true,
isScrollingEnabled: false,
bindings: ComposerCreateActionListBindings(textFormattingEnabled: true)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,56 +32,71 @@ struct ComposerCreateActionList: View {
// MARK: Public

@ObservedObject var viewModel: ComposerCreateActionListViewModel.Context

var body: some View {
ScrollView {
VStack(alignment: .leading) {
ForEach(viewModel.viewState.actions) { action in
HStack(spacing: 16) {
Image(action.icon)
.renderingMode(.template)
.foregroundColor(theme.colors.accent)
Text(action.title)
.foregroundColor(theme.colors.primaryContent)
.font(theme.fonts.body)
.accessibilityIdentifier(action.accessibilityIdentifier)
Spacer()
}
.contentShape(Rectangle())
.onTapGesture {
viewModel.send(viewAction: .selectAction(action))
}
.padding(.horizontal, 16)
.padding(.vertical, 12)

private var internalView: some View {
VStack(alignment: .leading) {
ForEach(viewModel.viewState.actions) { action in
HStack(spacing: 16) {
Image(action.icon)
.renderingMode(.template)
.foregroundColor(theme.colors.accent)
Text(action.title)
.foregroundColor(theme.colors.primaryContent)
.font(theme.fonts.body)
.accessibilityIdentifier(action.accessibilityIdentifier)
Spacer()
}
if viewModel.viewState.wysiwygEnabled {
SeparatorLine()
HStack(spacing: 16) {
Image(textFormattingIcon)
.renderingMode(.template)
.foregroundColor(theme.colors.accent)
Text(VectorL10n.wysiwygComposerStartActionTextFormatting)
.foregroundColor(theme.colors.primaryContent)
.font(theme.fonts.body)
.accessibilityIdentifier("textFormatting")
Spacer()
Toggle("", isOn: $viewModel.textFormattingEnabled)
.toggleStyle(ComposerToggleActionStyle())
.labelsHidden()
.onChange(of: viewModel.textFormattingEnabled) { isOn in
viewModel.send(viewAction: .toggleTextFormatting(isOn))
}
}
.contentShape(Rectangle())
.padding(.horizontal, 16)
.padding(.vertical, 12)

.contentShape(Rectangle())
.onTapGesture {
viewModel.send(viewAction: .selectAction(action))
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
}
if viewModel.viewState.wysiwygEnabled {
SeparatorLine()
HStack(spacing: 16) {
Image(textFormattingIcon)
.renderingMode(.template)
.foregroundColor(theme.colors.accent)
Text(VectorL10n.wysiwygComposerStartActionTextFormatting)
.foregroundColor(theme.colors.primaryContent)
.font(theme.fonts.body)
.accessibilityIdentifier("textFormatting")
Spacer()
Toggle("", isOn: $viewModel.textFormattingEnabled)
.labelsHidden()
.toggleStyle(SwitchToggleStyle(tint: theme.colors.accent))
.onChange(of: viewModel.textFormattingEnabled) { isOn in
viewModel.send(viewAction: .toggleTextFormatting(isOn))
}
}
.contentShape(Rectangle())
.onTapGesture {
viewModel.textFormattingEnabled.toggle()
}
.padding(.horizontal, 16)
.padding(.vertical, 12)

}
}
}

var body: some View {
if viewModel.viewState.isScrollingEnabled {
ScrollView {
internalView
}
Spacer()
.padding(.top, 23)
.background(theme.colors.background.ignoresSafeArea())
} else {
VStack {
internalView
Spacer()
}
.padding(.top, 23)
.background(theme.colors.background.ignoresSafeArea())
}
.padding(.top, 23)
.background(theme.colors.background.ignoresSafeArea())
}
}

Expand All @@ -93,35 +108,3 @@ struct ComposerCreateActionList_Previews: PreviewProvider {
stateRenderer.screenGroup()
}
}

struct ComposerToggleActionStyle: ToggleStyle {
@Environment(\.theme) private var theme

func makeBody(configuration: Configuration) -> some View {
HStack {
Rectangle()
.foregroundColor(.clear)
.frame(width: 50, height: 30, alignment: .center)
.overlay(
Rectangle()
.foregroundColor(configuration.isOn
? theme.colors.accent.opacity(0.5)
: theme.colors.primaryContent.opacity(0.25))
.cornerRadius(7)
.padding(.all, 8)
)
.overlay(
Circle()
.foregroundColor(configuration.isOn
? theme.colors.accent
: theme.colors.background)
.padding(.all, 3)
.offset(x: configuration.isOn ? 11 : -11, y: 0)
.shadow(radius: configuration.isOn ? 0.0 : 2.0)
.animation(Animation.linear(duration: 0.1))

).cornerRadius(20)
.onTapGesture { configuration.isOn.toggle() }
}
}
}
10 changes: 8 additions & 2 deletions RiotSwiftUI/Modules/Room/Composer/View/Composer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,17 @@ struct Composer: View {
}

private var cornerRadius: CGFloat {
if viewModel.viewState.shouldDisplayContext || wysiwygViewModel.idealHeight > wysiwygViewModel.minHeight {
if shouldFixRoundCorner {
return 14
} else {
return borderHeight / 2
}
}

private var shouldFixRoundCorner: Bool {
viewModel.viewState.shouldDisplayContext || wysiwygViewModel.idealHeight > wysiwygViewModel.minHeight
}

private var actionButtonAccessibilityIdentifier: String {
viewModel.viewState.sendMode == .edit ? "editButton" : "sendButton"
}
Expand Down Expand Up @@ -103,7 +107,7 @@ struct Composer: View {
.padding(.top, 8)
.padding(.horizontal, horizontalPadding)
}
HStack(alignment: .top, spacing: 0) {
HStack(alignment: shouldFixRoundCorner ? .top : .center, spacing: 0) {
WysiwygComposerView(
focused: $viewModel.focused,
viewModel: wysiwygViewModel
Expand Down Expand Up @@ -210,10 +214,12 @@ struct Composer: View {
HStack(alignment: .bottom, spacing: 0) {
if !viewModel.viewState.textFormattingEnabled {
sendMediaButton
.padding(.bottom, 1)
}
composerContainer
if !viewModel.viewState.textFormattingEnabled {
sendButton
.padding(.bottom, 1)
}
}
if viewModel.viewState.textFormattingEnabled {
Expand Down
1 change: 1 addition & 0 deletions changelog.d/7118.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rich Text Editor: Fixed a bug that prevented fullscreen mode to work on iOS 15.
1 change: 1 addition & 0 deletions changelog.d/7130.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rich Text Composer: Fix for fullscreen mode breaking sometimes when opening it when keyboard is not showing.
1 change: 1 addition & 0 deletions changelog.d/pr-7127.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rich Text Editor: Design Improvements.

0 comments on commit aa43d74

Please sign in to comment.