mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Initial integration of RTE (#1464)
* Initial integration of RTE * Fix `clipped`, `focused` and composer view type * Remove horizontal padding * Add `ComposerToolbar` mock * Restore `composerFocusedSubject` * Allow using HTML from RTE on message sent * Fix new message content API * Add feature flag for Rich Text Editor
This commit is contained in:
@@ -15,18 +15,23 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import WysiwygComposer
|
||||
|
||||
typealias EnterKeyHandler = () -> Void
|
||||
typealias PasteHandler = (NSItemProvider) -> Void
|
||||
|
||||
struct MessageComposer: View {
|
||||
@Binding var text: String
|
||||
var focused: FocusState<Bool>.Binding
|
||||
@Binding var plainText: String
|
||||
let composerView: WysiwygComposerView
|
||||
let sendingDisabled: Bool
|
||||
let mode: RoomScreenComposerMode
|
||||
|
||||
let sendAction: EnterKeyHandler
|
||||
let pasteAction: PasteHandler
|
||||
let replyCancellationAction: () -> Void
|
||||
let editCancellationAction: () -> Void
|
||||
|
||||
let onAppearAction: () -> Void
|
||||
@FocusState private var focused: Bool
|
||||
|
||||
@State private var isMultiline = false
|
||||
@ScaledMetric private var sendButtonIconSize = 16
|
||||
|
||||
@@ -35,15 +40,25 @@ struct MessageComposer: View {
|
||||
VStack(alignment: .leading, spacing: -6) {
|
||||
header
|
||||
HStack(alignment: .bottom) {
|
||||
MessageComposerTextField(placeholder: L10n.richTextEditorComposerPlaceholder,
|
||||
text: $text,
|
||||
focused: focused,
|
||||
isMultiline: $isMultiline,
|
||||
maxHeight: 300,
|
||||
enterKeyHandler: sendAction,
|
||||
pasteHandler: pasteAction)
|
||||
.tint(.compound.iconAccentTertiary)
|
||||
.padding(.vertical, 10)
|
||||
if ServiceLocator.shared.settings.richTextEditorEnabled {
|
||||
composerView
|
||||
.tint(.compound.iconAccentTertiary)
|
||||
.padding(.vertical, 10)
|
||||
.focused($focused)
|
||||
.onAppear {
|
||||
onAppearAction()
|
||||
}
|
||||
} else {
|
||||
MessageComposerTextField(placeholder: L10n.richTextEditorComposerPlaceholder,
|
||||
text: $plainText,
|
||||
isMultiline: $isMultiline,
|
||||
maxHeight: 300,
|
||||
enterKeyHandler: sendAction,
|
||||
pasteHandler: pasteAction)
|
||||
.tint(.compound.iconAccentTertiary)
|
||||
.padding(.vertical, 10)
|
||||
.focused($focused)
|
||||
}
|
||||
|
||||
Button {
|
||||
sendAction()
|
||||
@@ -64,13 +79,14 @@ struct MessageComposer: View {
|
||||
}
|
||||
}
|
||||
.padding(.leading, 12.0)
|
||||
.clipped()
|
||||
.background {
|
||||
ZStack {
|
||||
roundedRectangle
|
||||
.fill(Color.compound.bgSubtleSecondary)
|
||||
roundedRectangle
|
||||
.stroke(Color.compound._borderTextFieldFocused, lineWidth: 1)
|
||||
.opacity(focused.wrappedValue ? 1 : 0)
|
||||
.opacity(focused ? 1 : 0)
|
||||
}
|
||||
}
|
||||
// Explicitly disable all animations to fix weirdness with the header immediately
|
||||
@@ -174,63 +190,40 @@ private struct MessageComposerHeaderLabelStyle: LabelStyle {
|
||||
struct MessageComposer_Previews: PreviewProvider {
|
||||
static let viewModel = RoomScreenViewModel.mock
|
||||
|
||||
static func messageComposer(_ content: String = "",
|
||||
sendingDisabled: Bool = false,
|
||||
mode: RoomScreenComposerMode = .default) -> MessageComposer {
|
||||
let viewModel = WysiwygComposerViewModel(minHeight: 22,
|
||||
maxExpandedHeight: 250)
|
||||
viewModel.setMarkdownContent(content)
|
||||
|
||||
let composerView = WysiwygComposerView(placeholder: L10n.richTextEditorComposerPlaceholder,
|
||||
viewModel: viewModel,
|
||||
itemProviderHelper: nil,
|
||||
keyCommandHandler: nil,
|
||||
pasteHandler: nil)
|
||||
|
||||
return MessageComposer(plainText: .constant(content),
|
||||
composerView: composerView,
|
||||
sendingDisabled: sendingDisabled,
|
||||
mode: mode,
|
||||
sendAction: { },
|
||||
pasteAction: { _ in },
|
||||
replyCancellationAction: { },
|
||||
editCancellationAction: { },
|
||||
onAppearAction: { viewModel.setup() })
|
||||
}
|
||||
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
MessageComposer(text: .constant(""),
|
||||
focused: FocusState<Bool>().projectedValue,
|
||||
sendingDisabled: true,
|
||||
mode: .default,
|
||||
sendAction: { },
|
||||
pasteAction: { _ in },
|
||||
replyCancellationAction: { },
|
||||
editCancellationAction: { })
|
||||
messageComposer(sendingDisabled: true)
|
||||
|
||||
MessageComposer(text: .constant("This is a short message."),
|
||||
focused: FocusState<Bool>().projectedValue,
|
||||
sendingDisabled: false,
|
||||
mode: .default,
|
||||
sendAction: { },
|
||||
pasteAction: { _ in },
|
||||
replyCancellationAction: { },
|
||||
editCancellationAction: { })
|
||||
messageComposer("Some message",
|
||||
mode: .edit(originalItemId: .random))
|
||||
|
||||
MessageComposer(text: .constant("This is a very long message that will wrap to 2 lines on an iPhone 14."),
|
||||
focused: FocusState<Bool>().projectedValue,
|
||||
sendingDisabled: false,
|
||||
mode: .default,
|
||||
sendAction: { },
|
||||
pasteAction: { _ in },
|
||||
replyCancellationAction: { },
|
||||
editCancellationAction: { })
|
||||
|
||||
MessageComposer(text: .constant("This is an even longer message that will wrap to 3 lines on an iPhone 14, just to see the difference it makes."),
|
||||
focused: FocusState<Bool>().projectedValue,
|
||||
sendingDisabled: false,
|
||||
mode: .default,
|
||||
sendAction: { },
|
||||
pasteAction: { _ in },
|
||||
replyCancellationAction: { },
|
||||
editCancellationAction: { })
|
||||
|
||||
MessageComposer(text: .constant("Some message"),
|
||||
focused: FocusState<Bool>().projectedValue,
|
||||
sendingDisabled: false,
|
||||
mode: .edit(originalItemId: .random),
|
||||
sendAction: { },
|
||||
pasteAction: { _ in },
|
||||
replyCancellationAction: { },
|
||||
editCancellationAction: { })
|
||||
|
||||
MessageComposer(text: .constant(""),
|
||||
focused: FocusState<Bool>().projectedValue,
|
||||
sendingDisabled: false,
|
||||
mode: .reply(itemID: .random,
|
||||
messageComposer(mode: .reply(itemID: .random,
|
||||
replyDetails: .loaded(sender: .init(id: "Kirk"),
|
||||
contentType: .text(.init(body: "Text: Where the wild things are")))),
|
||||
sendAction: { },
|
||||
pasteAction: { _ in },
|
||||
replyCancellationAction: { },
|
||||
editCancellationAction: { })
|
||||
contentType: .text(.init(body: "Text: Where the wild things are")))))
|
||||
}
|
||||
.padding(.horizontal)
|
||||
|
||||
@@ -253,15 +246,8 @@ struct MessageComposer_Previews: PreviewProvider {
|
||||
]
|
||||
|
||||
ForEach(replyTypes, id: \.self) { replyDetails in
|
||||
MessageComposer(text: .constant(""),
|
||||
focused: FocusState<Bool>().projectedValue,
|
||||
sendingDisabled: false,
|
||||
mode: .reply(itemID: .random,
|
||||
replyDetails: replyDetails),
|
||||
sendAction: { },
|
||||
pasteAction: { _ in },
|
||||
replyCancellationAction: { },
|
||||
editCancellationAction: { })
|
||||
messageComposer(mode: .reply(itemID: .random,
|
||||
replyDetails: replyDetails))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user