mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
* Remove extra want config call when adding a contact * App badge and unnecessary notification fixes (#1455) * - Fix for app badge not going to zero if a message arrives while you have that chat open - Fix for push notifications popping up when a message is received while that chat is open * Fix for cancelling notifications, works now. And scroll to bottom of conversation upon new message * Fix: Channels help grammer fix (#1452) * remove outdated TCP not available on Apple devices (#1450) * Update initial onboarding view * remove toggle gating for mac * Update crash reporting opt out in real time * Update onboarding text --------- Co-authored-by: Gnome Adrift <646322+gnomeadrift@users.noreply.github.com> Co-authored-by: Zain Kergaye <62440012+ZainKergaye@users.noreply.github.com> Co-authored-by: NillRudd <102033730+NillRudd@users.noreply.github.com>
141 lines
4.6 KiB
Swift
141 lines
4.6 KiB
Swift
//
|
|
// ChannelMessageList.swift
|
|
// Meshtastic
|
|
//
|
|
// Created by Garth Vander Houwen on 12/24/21.
|
|
//
|
|
|
|
import CoreData
|
|
import MeshtasticProtobufs
|
|
import OSLog
|
|
import SwiftUI
|
|
|
|
struct ChannelMessageList: View {
|
|
@EnvironmentObject var appState: AppState
|
|
@EnvironmentObject var router: Router
|
|
@Environment(\.managedObjectContext) var context
|
|
@EnvironmentObject var accessoryManager: AccessoryManager
|
|
@FocusState var messageFieldFocused: Bool
|
|
@ObservedObject var myInfo: MyInfoEntity
|
|
@ObservedObject var channel: ChannelEntity
|
|
@State private var replyMessageId: Int64 = 0
|
|
@State private var redrawTapbacksTrigger = UUID()
|
|
@AppStorage("preferredPeripheralNum") private var preferredPeripheralNum = -1
|
|
@State private var messageToHighlight: Int64 = 0
|
|
@FetchRequest private var allPrivateMessages: FetchedResults<MessageEntity>
|
|
|
|
init(myInfo: MyInfoEntity, channel: ChannelEntity) {
|
|
self.myInfo = myInfo
|
|
self.channel = channel
|
|
|
|
// Configure fetch request here
|
|
let request: NSFetchRequest<MessageEntity> = MessageEntity.fetchRequest()
|
|
request.sortDescriptors = [
|
|
NSSortDescriptor(keyPath: \MessageEntity.messageTimestamp, ascending: true)
|
|
]
|
|
request.predicate = NSPredicate(
|
|
format: "channel == %ld AND toUser == nil AND isEmoji == false",
|
|
channel.index
|
|
)
|
|
_allPrivateMessages = FetchRequest(fetchRequest: request)
|
|
}
|
|
|
|
func handleInteractionComplete() {
|
|
markMessagesAsRead()
|
|
redrawTapbacksTrigger = UUID()
|
|
}
|
|
|
|
func markMessagesAsRead() {
|
|
do {
|
|
for unreadMessage in allPrivateMessages.filter({ !$0.read }) {
|
|
unreadMessage.read = true
|
|
}
|
|
try context.save()
|
|
Logger.data.info("📖 [App] All unread messages marked as read.")
|
|
appState.unreadChannelMessages = myInfo.unreadMessages
|
|
context.refresh(myInfo, mergeChanges: true)
|
|
} catch {
|
|
Logger.data.error("Failed to read messages: \(error.localizedDescription, privacy: .public)")
|
|
}
|
|
}
|
|
|
|
var body: some View {
|
|
ScrollViewReader { scrollView in
|
|
ScrollView {
|
|
LazyVStack {
|
|
ForEach(allPrivateMessages.indices, id: \.self) { index in
|
|
let message = allPrivateMessages[index]
|
|
let previousMessage = index > 0 ? allPrivateMessages[index - 1] : nil
|
|
|
|
ChannelMessageRow(
|
|
message: message,
|
|
allMessages: allPrivateMessages,
|
|
previousMessage: previousMessage,
|
|
preferredPeripheralNum: preferredPeripheralNum,
|
|
channel: channel,
|
|
replyMessageId: $replyMessageId,
|
|
messageFieldFocused: $messageFieldFocused,
|
|
messageToHighlight: $messageToHighlight,
|
|
scrollView: scrollView,
|
|
onInteractionComplete: handleInteractionComplete
|
|
)
|
|
.onAppear {
|
|
// Only mark as read if the app is in the foreground
|
|
if !message.read && UIApplication.shared.applicationState == .active {
|
|
message.read = true
|
|
LocalNotificationManager().cancelNotificationForMessageId(message.messageId)
|
|
// Race condition, sometimes the app doesn't update unread count if we run this too early
|
|
// So, run it in the main queue after everything saves and stabilizes
|
|
DispatchQueue.main.async {
|
|
markMessagesAsRead()
|
|
scrollView.scrollTo("bottomAnchor", anchor: .bottom)
|
|
}
|
|
}
|
|
}
|
|
.id(redrawTapbacksTrigger)
|
|
}
|
|
Color.clear
|
|
.frame(height: 1)
|
|
.id("bottomAnchor")
|
|
}
|
|
}
|
|
.defaultScrollAnchor(.bottom)
|
|
.defaultScrollAnchorTopAlignment()
|
|
.defaultScrollAnchorBottomSizeChanges()
|
|
.scrollDismissesKeyboard(.immediately)
|
|
.onChange(of: messageFieldFocused) {
|
|
if messageFieldFocused {
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
scrollView.scrollTo("bottomAnchor", anchor: .bottom)
|
|
}
|
|
}
|
|
}
|
|
TextMessageField(
|
|
destination: .channel(channel),
|
|
replyMessageId: $replyMessageId,
|
|
isFocused: $messageFieldFocused
|
|
)
|
|
}
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .principal) {
|
|
HStack {
|
|
CircleText(text: String(channel.index), color: .accentColor, circleSize: 44).fixedSize()
|
|
Text(String(channel.name ?? "Unknown").camelCaseToWords()).font(.headline)
|
|
}
|
|
}
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
ZStack {
|
|
ConnectedDevice(
|
|
deviceConnected: accessoryManager.isConnected,
|
|
name: accessoryManager.activeConnection?.device.shortName ?? "?",
|
|
mqttProxyConnected: accessoryManager.mqttProxyConnected && (channel.uplinkEnabled || channel.downlinkEnabled),
|
|
mqttUplinkEnabled: channel.uplinkEnabled,
|
|
mqttDownlinkEnabled: channel.downlinkEnabled,
|
|
mqttTopic: accessoryManager.mqttManager.topic
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|