2022-01-29 23:37:02 +00:00
|
|
|
//
|
|
|
|
// SendMessageView.swift
|
|
|
|
// SimpleX
|
|
|
|
//
|
|
|
|
// Created by Evgeny Poberezkin on 29/01/2022.
|
|
|
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import SwiftUI
|
2022-05-31 07:55:13 +01:00
|
|
|
import SimpleXChat
|
2022-01-29 23:37:02 +00:00
|
|
|
|
|
|
|
struct SendMessageView: View {
|
2022-04-25 12:44:24 +04:00
|
|
|
@Binding var composeState: ComposeState
|
2022-04-27 20:54:21 +04:00
|
|
|
var sendMessage: () -> Void
|
2022-11-25 15:16:37 +04:00
|
|
|
var voiceMessageAllowed: Bool = true
|
2022-11-24 21:18:28 +04:00
|
|
|
var startVoiceMessageRecording: (() -> Void)? = nil
|
|
|
|
var finishVoiceMessageRecording: (() -> Void)? = nil
|
|
|
|
@State private var longPressingVMR = false
|
2022-02-05 14:24:23 +00:00
|
|
|
@Namespace var namespace
|
2022-02-11 07:42:00 +00:00
|
|
|
@FocusState.Binding var keyboardVisible: Bool
|
2022-02-05 14:24:23 +00:00
|
|
|
@State private var teHeight: CGFloat = 42
|
|
|
|
@State private var teFont: Font = .body
|
|
|
|
var maxHeight: CGFloat = 360
|
|
|
|
var minHeight: CGFloat = 37
|
2022-01-29 23:37:02 +00:00
|
|
|
|
|
|
|
var body: some View {
|
2022-02-05 14:24:23 +00:00
|
|
|
ZStack {
|
|
|
|
HStack(alignment: .bottom) {
|
|
|
|
ZStack(alignment: .leading) {
|
2022-11-24 21:18:28 +04:00
|
|
|
if case .voicePreview = composeState.preview {
|
|
|
|
Text("Voice message…")
|
|
|
|
.font(teFont.italic())
|
|
|
|
.multilineTextAlignment(.leading)
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
.padding(.horizontal, 10)
|
|
|
|
.padding(.vertical, 8)
|
|
|
|
.frame(maxWidth: .infinity)
|
|
|
|
} else {
|
|
|
|
let alignment: TextAlignment = isRightToLeft(composeState.message) ? .trailing : .leading
|
|
|
|
Text(composeState.message)
|
|
|
|
.lineLimit(10)
|
|
|
|
.font(teFont)
|
|
|
|
.multilineTextAlignment(alignment)
|
|
|
|
.foregroundColor(.clear)
|
|
|
|
.padding(.horizontal, 10)
|
|
|
|
.padding(.vertical, 8)
|
|
|
|
.matchedGeometryEffect(id: "te", in: namespace)
|
|
|
|
.background(GeometryReader(content: updateHeight))
|
|
|
|
TextEditor(text: $composeState.message)
|
|
|
|
.focused($keyboardVisible)
|
|
|
|
.font(teFont)
|
|
|
|
.textInputAutocapitalization(.sentences)
|
|
|
|
.multilineTextAlignment(alignment)
|
|
|
|
.padding(.horizontal, 5)
|
|
|
|
.allowsTightening(false)
|
|
|
|
.frame(height: teHeight)
|
|
|
|
}
|
2022-02-05 14:24:23 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 12:44:24 +04:00
|
|
|
if (composeState.inProgress) {
|
2022-02-05 14:24:23 +00:00
|
|
|
ProgressView()
|
|
|
|
.scaleEffect(1.4)
|
|
|
|
.frame(width: 31, height: 31, alignment: .center)
|
|
|
|
.padding([.bottom, .trailing], 3)
|
|
|
|
} else {
|
2022-11-24 21:18:28 +04:00
|
|
|
let vmrs = composeState.voiceMessageRecordingState
|
2022-11-25 15:16:37 +04:00
|
|
|
if voiceMessageAllowed,
|
2022-11-24 21:18:28 +04:00
|
|
|
composeState.message.isEmpty,
|
|
|
|
!composeState.editing,
|
|
|
|
(composeState.noPreview && vmrs == .noRecording)
|
|
|
|
|| (vmrs == .recording && longPressingVMR) {
|
|
|
|
recordVoiceMessageButton()
|
|
|
|
} else if vmrs == .recording && !longPressingVMR {
|
|
|
|
finishVoiceMessageRecordingButton()
|
|
|
|
} else {
|
|
|
|
sendMessageButton()
|
2022-02-05 14:24:23 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-29 23:37:02 +00:00
|
|
|
}
|
2022-02-05 14:24:23 +00:00
|
|
|
|
|
|
|
RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))
|
|
|
|
.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true)
|
|
|
|
.frame(height: teHeight)
|
2022-01-29 23:37:02 +00:00
|
|
|
}
|
2022-02-05 14:24:23 +00:00
|
|
|
.padding(.vertical, 8)
|
2022-01-29 23:37:02 +00:00
|
|
|
}
|
|
|
|
|
2022-11-24 21:18:28 +04:00
|
|
|
private func sendMessageButton() -> some View {
|
|
|
|
Button(action: { sendMessage() }) {
|
|
|
|
Image(systemName: composeState.editing ? "checkmark.circle.fill" : "arrow.up.circle.fill")
|
|
|
|
.resizable()
|
|
|
|
.foregroundColor(.accentColor)
|
|
|
|
}
|
2022-11-25 15:16:37 +04:00
|
|
|
.disabled(
|
|
|
|
!composeState.sendEnabled ||
|
|
|
|
composeState.disabled ||
|
|
|
|
(!voiceMessageAllowed && composeState.voicePreview)
|
|
|
|
)
|
2022-11-24 21:18:28 +04:00
|
|
|
.frame(width: 29, height: 29)
|
|
|
|
.padding([.bottom, .trailing], 4)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func recordVoiceMessageButton() -> some View {
|
|
|
|
Button(action: {
|
|
|
|
if !longPressingVMR {
|
|
|
|
startVoiceMessageRecording?()
|
|
|
|
} else {
|
|
|
|
finishVoiceMessageRecording?()
|
|
|
|
}
|
|
|
|
longPressingVMR = false
|
|
|
|
}) {
|
|
|
|
Image(systemName: "mic")
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
}
|
|
|
|
.simultaneousGesture(
|
|
|
|
LongPressGesture()
|
|
|
|
.onEnded { _ in
|
|
|
|
longPressingVMR = true
|
|
|
|
startVoiceMessageRecording?()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
.disabled(composeState.disabled)
|
|
|
|
.frame(width: 29, height: 29)
|
|
|
|
.padding([.bottom, .trailing], 4)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private func finishVoiceMessageRecordingButton() -> some View {
|
|
|
|
Button(action: { finishVoiceMessageRecording?() }) {
|
|
|
|
Image(systemName: "stop.fill")
|
|
|
|
.foregroundColor(.accentColor)
|
|
|
|
}
|
|
|
|
.disabled(composeState.disabled)
|
|
|
|
.frame(width: 29, height: 29)
|
|
|
|
.padding([.bottom, .trailing], 4)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func updateHeight(_ g: GeometryProxy) -> Color {
|
2022-02-05 14:24:23 +00:00
|
|
|
DispatchQueue.main.async {
|
|
|
|
teHeight = min(max(g.frame(in: .local).size.height, minHeight), maxHeight)
|
2022-04-25 12:44:24 +04:00
|
|
|
teFont = isShortEmoji(composeState.message)
|
|
|
|
? composeState.message.count < 4
|
2022-11-24 21:18:28 +04:00
|
|
|
? largeEmojiFont
|
|
|
|
: mediumEmojiFont
|
|
|
|
: .body
|
2022-02-05 14:24:23 +00:00
|
|
|
}
|
|
|
|
return Color.clear
|
2022-01-29 23:37:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SendMessageView_Previews: PreviewProvider {
|
|
|
|
static var previews: some View {
|
2022-04-25 12:44:24 +04:00
|
|
|
@State var composeStateNew = ComposeState()
|
|
|
|
let ci = ChatItem.getSample(1, .directSnd, .now, "hello")
|
|
|
|
@State var composeStateEditing = ComposeState(editingItem: ci)
|
2022-02-11 07:42:00 +00:00
|
|
|
@FocusState var keyboardVisible: Bool
|
2022-04-19 12:29:03 +04:00
|
|
|
@State var sendEnabled: Bool = true
|
2022-02-11 07:42:00 +00:00
|
|
|
|
2022-03-25 22:26:05 +04:00
|
|
|
return Group {
|
|
|
|
VStack {
|
|
|
|
Text("")
|
|
|
|
Spacer(minLength: 0)
|
|
|
|
SendMessageView(
|
2022-04-25 12:44:24 +04:00
|
|
|
composeState: $composeStateNew,
|
2022-04-27 20:54:21 +04:00
|
|
|
sendMessage: {},
|
2022-04-25 12:44:24 +04:00
|
|
|
keyboardVisible: $keyboardVisible
|
2022-03-25 22:26:05 +04:00
|
|
|
)
|
|
|
|
}
|
|
|
|
VStack {
|
|
|
|
Text("")
|
|
|
|
Spacer(minLength: 0)
|
|
|
|
SendMessageView(
|
2022-04-25 12:44:24 +04:00
|
|
|
composeState: $composeStateEditing,
|
2022-04-27 20:54:21 +04:00
|
|
|
sendMessage: {},
|
2022-04-25 12:44:24 +04:00
|
|
|
keyboardVisible: $keyboardVisible
|
2022-03-25 22:26:05 +04:00
|
|
|
)
|
|
|
|
}
|
2022-02-05 14:24:23 +00:00
|
|
|
}
|
2022-01-29 23:37:02 +00:00
|
|
|
}
|
|
|
|
}
|