mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
284 lines
8.3 KiB
Swift
284 lines
8.3 KiB
Swift
//
|
|
// 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.
|
|
//
|
|
|
|
import SwiftUI
|
|
import UIKit
|
|
import WysiwygComposer
|
|
|
|
enum ComposerToolbarViewModelAction {
|
|
case sendMessage(plain: String, html: String?, mode: RoomScreenComposerMode, intentionalMentions: IntentionalMentions)
|
|
case displayCameraPicker
|
|
case displayMediaPicker
|
|
case displayDocumentPicker
|
|
case displayLocationPicker
|
|
case displayPollForm
|
|
|
|
case handlePasteOrDrop(provider: NSItemProvider)
|
|
|
|
case composerModeChanged(mode: RoomScreenComposerMode)
|
|
case composerFocusedChanged(isFocused: Bool)
|
|
|
|
case startVoiceMessageRecording
|
|
case stopVoiceMessageRecording
|
|
case cancelVoiceMessageRecording
|
|
case deleteVoiceMessageRecording
|
|
case startVoiceMessagePlayback
|
|
case pauseVoiceMessagePlayback
|
|
case scrubVoiceMessagePlayback(scrubbing: Bool)
|
|
case seekVoiceMessagePlayback(progress: Double)
|
|
case sendVoiceMessage
|
|
}
|
|
|
|
enum ComposerToolbarViewAction {
|
|
case composerAppeared
|
|
case sendMessage
|
|
case cancelReply
|
|
case cancelEdit
|
|
case displayCameraPicker
|
|
case displayMediaPicker
|
|
case displayDocumentPicker
|
|
case displayLocationPicker
|
|
case displayPollForm
|
|
case handlePasteOrDrop(provider: NSItemProvider)
|
|
case enableTextFormatting
|
|
case composerAction(action: ComposerAction)
|
|
case selectedSuggestion(_ suggestion: SuggestionItem)
|
|
case startVoiceMessageRecording
|
|
case stopVoiceMessageRecording
|
|
case cancelVoiceMessageRecording
|
|
case deleteVoiceMessageRecording
|
|
case startVoiceMessagePlayback
|
|
case pauseVoiceMessagePlayback
|
|
case scrubVoiceMessagePlayback(scrubbing: Bool)
|
|
case seekVoiceMessagePlayback(progress: Double)
|
|
}
|
|
|
|
struct ComposerToolbarViewState: BindableState {
|
|
var composerMode: RoomScreenComposerMode = .default
|
|
var composerEmpty = true
|
|
var suggestions: [SuggestionItem] = []
|
|
var audioPlayerState: AudioPlayerState
|
|
var audioRecorderState: AudioRecorderState
|
|
|
|
var bindings: ComposerToolbarViewStateBindings
|
|
|
|
var isUploading: Bool {
|
|
switch composerMode {
|
|
case .previewVoiceMessage(_, _, let isUploading):
|
|
return isUploading
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
var showSendButton: Bool {
|
|
switch composerMode {
|
|
case .recordVoiceMessage:
|
|
return false
|
|
case .previewVoiceMessage:
|
|
return true
|
|
default:
|
|
return !composerEmpty
|
|
}
|
|
}
|
|
|
|
var sendButtonDisabled: Bool {
|
|
if case .previewVoiceMessage = composerMode {
|
|
return false
|
|
}
|
|
return composerEmpty
|
|
}
|
|
|
|
var isVoiceMessageModeActivated: Bool {
|
|
switch composerMode {
|
|
case .recordVoiceMessage, .previewVoiceMessage:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ComposerToolbarViewStateBindings {
|
|
var composerFocused = false
|
|
var composerActionsEnabled = false
|
|
var composerExpanded = false
|
|
var formatItems: [FormatItem] = .init()
|
|
var alertInfo: AlertInfo<UUID>?
|
|
|
|
var showAttachmentPopover = false {
|
|
didSet {
|
|
if showAttachmentPopover {
|
|
composerFocused = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An item in the toolbar
|
|
struct FormatItem {
|
|
/// The type of the item
|
|
let type: FormatType
|
|
/// The state of the item
|
|
let state: ActionState
|
|
}
|
|
|
|
/// The types of formatting actions
|
|
enum FormatType {
|
|
case bold
|
|
case italic
|
|
case underline
|
|
case strikeThrough
|
|
case link
|
|
case unorderedList
|
|
case orderedList
|
|
case indent
|
|
case unindent
|
|
case inlineCode
|
|
case codeBlock
|
|
case quote
|
|
}
|
|
|
|
extension FormatType: CaseIterable, Identifiable {
|
|
var id: Self { self }
|
|
}
|
|
|
|
extension FormatItem: Identifiable {
|
|
var id: FormatType { type }
|
|
}
|
|
|
|
extension FormatItem {
|
|
/// The icon to display in the formatting toolbar.
|
|
var icon: Image {
|
|
switch type {
|
|
case .bold:
|
|
return Image(asset: Asset.Images.bold)
|
|
case .italic:
|
|
return Image(asset: Asset.Images.italic)
|
|
case .underline:
|
|
return Image(asset: Asset.Images.underline)
|
|
case .strikeThrough:
|
|
return Image(asset: Asset.Images.strikethrough)
|
|
case .unorderedList:
|
|
return Image(asset: Asset.Images.bulletList)
|
|
case .orderedList:
|
|
return Image(asset: Asset.Images.numberedList)
|
|
case .indent:
|
|
return Image(asset: Asset.Images.indent)
|
|
case .unindent:
|
|
return Image(asset: Asset.Images.unindent)
|
|
case .inlineCode:
|
|
return Image(asset: Asset.Images.inlineCode)
|
|
case .codeBlock:
|
|
return Image(asset: Asset.Images.codeBlock)
|
|
case .quote:
|
|
return Image(asset: Asset.Images.quote)
|
|
case .link:
|
|
return Image(asset: Asset.Images.link)
|
|
}
|
|
}
|
|
|
|
var accessibilityIdentifier: String {
|
|
switch type {
|
|
case .bold:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.bold
|
|
case .italic:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.italic
|
|
case .underline:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.underline
|
|
case .strikeThrough:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.strikethrough
|
|
case .unorderedList:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.unorderedList
|
|
case .orderedList:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.orderedList
|
|
case .indent:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.indent
|
|
case .unindent:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.unindent
|
|
case .inlineCode:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.inlineCode
|
|
case .codeBlock:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.codeBlock
|
|
case .quote:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.quote
|
|
case .link:
|
|
return A11yIdentifiers.roomScreen.composerToolbar.link
|
|
}
|
|
}
|
|
|
|
var accessibilityLabel: String {
|
|
switch type {
|
|
case .bold:
|
|
return L10n.richTextEditorFormatBold
|
|
case .italic:
|
|
return L10n.richTextEditorFormatItalic
|
|
case .underline:
|
|
return L10n.richTextEditorFormatUnderline
|
|
case .strikeThrough:
|
|
return L10n.richTextEditorFormatStrikethrough
|
|
case .unorderedList:
|
|
return L10n.richTextEditorBulletList
|
|
case .orderedList:
|
|
return L10n.richTextEditorNumberedList
|
|
case .indent:
|
|
return L10n.richTextEditorIndent
|
|
case .unindent:
|
|
return L10n.richTextEditorUnindent
|
|
case .inlineCode:
|
|
return L10n.richTextEditorInlineCode
|
|
case .codeBlock:
|
|
return L10n.richTextEditorCodeBlock
|
|
case .quote:
|
|
return L10n.richTextEditorQuote
|
|
case .link:
|
|
return L10n.richTextEditorLink
|
|
}
|
|
}
|
|
}
|
|
|
|
extension FormatType {
|
|
/// The associated library composer action.
|
|
var composerAction: ComposerAction {
|
|
switch self {
|
|
case .bold:
|
|
return .bold
|
|
case .italic:
|
|
return .italic
|
|
case .underline:
|
|
return .underline
|
|
case .strikeThrough:
|
|
return .strikeThrough
|
|
case .unorderedList:
|
|
return .unorderedList
|
|
case .orderedList:
|
|
return .orderedList
|
|
case .indent:
|
|
return .indent
|
|
case .unindent:
|
|
return .unindent
|
|
case .inlineCode:
|
|
return .inlineCode
|
|
case .codeBlock:
|
|
return .codeBlock
|
|
case .quote:
|
|
return .quote
|
|
case .link:
|
|
return .link
|
|
}
|
|
}
|
|
}
|