mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Refactors both the channel and user message views to isolate typing state which prevents excessive re-rendering of large message lists on every new character typed. Also consolidates typing view code of both lists into the new TextMessageField and related sub views.
170 lines
4.2 KiB
Swift
170 lines
4.2 KiB
Swift
import SwiftUI
|
|
|
|
struct TextMessageField: View {
|
|
static let maxbytes = 228
|
|
@EnvironmentObject var bleManager: BLEManager
|
|
|
|
let destination: Destination
|
|
@Binding var replyMessageId: Int64
|
|
@FocusState.Binding var isFocused: Bool
|
|
let onSubmit: () -> Void
|
|
|
|
enum Destination {
|
|
case user(Int64)
|
|
case channel(Int32)
|
|
}
|
|
|
|
@State private var typingMessage: String = ""
|
|
@State private var totalBytes = 0
|
|
@State private var sendPositionWithMessage = false
|
|
|
|
var body: some View {
|
|
#if targetEnvironment(macCatalyst)
|
|
HStack {
|
|
if destination.showAlertButton {
|
|
Spacer()
|
|
AlertButton { typingMessage += "🔔 Alert Bell Character! \u{7}" }
|
|
}
|
|
Spacer()
|
|
RequestPositionButton(action: requestPosition)
|
|
TextMessageSize(maxbytes: Self.maxbytes, totalBytes: totalBytes).padding(.trailing)
|
|
}
|
|
#endif
|
|
|
|
HStack(alignment: .top) {
|
|
ZStack {
|
|
TextField("message", text: $typingMessage, axis: .vertical)
|
|
.onChange(of: typingMessage, perform: { value in
|
|
totalBytes = value.utf8.count
|
|
// Only mess with the value if it is too big
|
|
if totalBytes > Self.maxbytes {
|
|
let firstNBytes = Data(typingMessage.utf8.prefix(Self.maxbytes))
|
|
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
|
|
// Set the message back to the last place where it was the right size
|
|
typingMessage = maxBytesString
|
|
} else {
|
|
print("not a valid UTF-8 sequence")
|
|
}
|
|
}
|
|
})
|
|
.keyboardType(.default)
|
|
.toolbar {
|
|
ToolbarItemGroup(placement: .keyboard) {
|
|
Button("dismiss.keyboard") {
|
|
isFocused = false
|
|
}
|
|
.font(.subheadline)
|
|
|
|
if destination.showAlertButton {
|
|
Spacer()
|
|
AlertButton { typingMessage += "🔔 Alert Bell Character! \u{7}" }
|
|
}
|
|
|
|
Spacer()
|
|
RequestPositionButton(action: requestPosition)
|
|
TextMessageSize(maxbytes: Self.maxbytes, totalBytes: totalBytes)
|
|
}
|
|
}
|
|
.padding(.horizontal, 8)
|
|
.focused($isFocused)
|
|
.multilineTextAlignment(.leading)
|
|
.frame(minHeight: 50)
|
|
.keyboardShortcut(.defaultAction)
|
|
.onSubmit {
|
|
#if targetEnvironment(macCatalyst)
|
|
sendMessage()
|
|
#endif
|
|
}
|
|
|
|
Text(typingMessage)
|
|
.opacity(0)
|
|
.padding(.all, 0)
|
|
}
|
|
.overlay(RoundedRectangle(cornerRadius: 20).stroke(.tertiary, lineWidth: 1))
|
|
.padding(.bottom, 15)
|
|
|
|
Button(action: sendMessage) {
|
|
Image(systemName: "arrow.up.circle.fill")
|
|
.font(.largeTitle)
|
|
.foregroundColor(.accentColor)
|
|
}
|
|
}
|
|
.padding(.all, 15)
|
|
}
|
|
|
|
private func requestPosition() {
|
|
let userLongName = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown"
|
|
sendPositionWithMessage = true
|
|
typingMessage = "📍 " + userLongName + " \(destination.positionShareMessage)."
|
|
}
|
|
|
|
private func sendMessage() {
|
|
let messageSent = bleManager.sendMessage(
|
|
message: typingMessage,
|
|
toUserNum: destination.userNum,
|
|
channel: destination.channelNum,
|
|
isEmoji: false,
|
|
replyID: replyMessageId
|
|
)
|
|
if messageSent {
|
|
typingMessage = ""
|
|
isFocused = false
|
|
replyMessageId = 0
|
|
onSubmit()
|
|
if sendPositionWithMessage {
|
|
let positionSent = bleManager.sendPosition(
|
|
channel: destination.channelNum,
|
|
destNum: destination.positionDestNum,
|
|
wantResponse: destination.wantPositionResponse
|
|
)
|
|
if positionSent {
|
|
print("Location Sent")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension TextMessageField.Destination {
|
|
var positionShareMessage: String {
|
|
switch self {
|
|
case .user: return "has shared their position and requested a response with your position"
|
|
case .channel: return "has shared their position with you"
|
|
}
|
|
}
|
|
|
|
var userNum: Int64 {
|
|
switch self {
|
|
case let .user(num): return num
|
|
case .channel: return 0
|
|
}
|
|
}
|
|
|
|
var channelNum: Int32 {
|
|
switch self {
|
|
case .user: return 0
|
|
case let .channel(num): return num
|
|
}
|
|
}
|
|
|
|
var positionDestNum: Int64 {
|
|
switch self {
|
|
case let .user(num): return num
|
|
case .channel: return Int64(BLEManager.emptyNodeNum)
|
|
}
|
|
}
|
|
|
|
var showAlertButton: Bool {
|
|
switch self {
|
|
case .user: return false
|
|
case .channel: return true
|
|
}
|
|
}
|
|
|
|
var wantPositionResponse: Bool {
|
|
switch self {
|
|
case .user: return true
|
|
case .channel: return false
|
|
}
|
|
}
|
|
}
|