feat: Enhance message notifications with history and actions (#4133)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2026-01-04 15:46:07 -06:00 committed by GitHub
parent 43aca3c558
commit 49f6ffe6e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 496 additions and 43 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Meshtastic LLC
* Copyright (c) 2025-2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.data.repository
import androidx.paging.Pager
@ -46,7 +45,7 @@ constructor(
private val dispatchers: CoroutineDispatchers,
) {
fun getWaypoints(): Flow<List<Packet>> =
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getAllPackets(PortNum.WAYPOINT_APP_VALUE) }
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getAllWaypointsFlow() }
fun getContacts(): Flow<Map<String, Packet>> =
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getContactKeys() }
@ -97,18 +96,21 @@ constructor(
suspend fun insert(packet: Packet) =
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().insert(packet) }
suspend fun getMessagesFrom(contact: String, getNode: suspend (String?) -> Node) = withContext(dispatchers.io) {
dbManager.currentDb.value.packetDao().getMessagesFrom(contact).mapLatest { packets ->
packets.map { packet ->
val message = packet.toMessage(getNode)
message.replyId
.takeIf { it != null && it != 0 }
?.let { getPacketByPacketId(it) }
?.toMessage(getNode)
?.let { originalMessage -> message.copy(originalMessage = originalMessage) } ?: message
suspend fun getMessagesFrom(contact: String, limit: Int? = null, getNode: suspend (String?) -> Node) =
withContext(dispatchers.io) {
val dao = dbManager.currentDb.value.packetDao()
val flow = if (limit != null) dao.getMessagesFrom(contact, limit) else dao.getMessagesFrom(contact)
flow.mapLatest { packets ->
packets.map { packet ->
val message = packet.toMessage(getNode)
message.replyId
.takeIf { it != null && it != 0 }
?.let { getPacketByPacketId(it) }
?.toMessage(getNode)
?.let { originalMessage -> message.copy(originalMessage = originalMessage) } ?: message
}
}
}
}
fun getMessagesFromPaged(contact: String, getNode: suspend (String?) -> Node): Flow<PagingData<Message>> = Pager(
config = PagingConfig(pageSize = 50, enablePlaceholders = false, initialLoadSize = 50),
@ -176,4 +178,7 @@ constructor(
withContext(dispatchers.io) {
dbManager.currentDb.value.packetDao().migrateChannelsByPSK(oldSettings, newSettings)
}
private fun org.meshtastic.core.database.dao.PacketDao.getAllWaypointsFlow(): Flow<List<Packet>> =
getAllPackets(PortNum.WAYPOINT_APP_VALUE)
}

View file

@ -149,6 +149,18 @@ interface PacketDao {
)
fun getMessagesFrom(contact: String): Flow<List<PacketEntity>>
@Transaction
@Query(
"""
SELECT * FROM packet
WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node))
AND port_num = 1 AND contact_key = :contact
ORDER BY received_time DESC
LIMIT :limit
""",
)
fun getMessagesFrom(contact: String, limit: Int): Flow<List<PacketEntity>>
@Transaction
@Query(
"""

View file

@ -31,7 +31,7 @@ interface MeshServiceNotifications {
fun updateServiceStateNotification(summaryString: String?, telemetry: TelemetryProtos.Telemetry?): Notification
fun updateMessageNotification(
suspend fun updateMessageNotification(
contactKey: String,
name: String,
message: String,
@ -39,7 +39,15 @@ interface MeshServiceNotifications {
channelName: String?,
)
fun updateWaypointNotification(contactKey: String, name: String, message: String, waypointId: Int)
suspend fun updateWaypointNotification(contactKey: String, name: String, message: String, waypointId: Int)
suspend fun updateReactionNotification(
contactKey: String,
name: String,
emoji: String,
isBroadcast: Boolean,
channelName: String?,
)
fun showAlertNotification(contactKey: String, name: String, alert: String)

View file

@ -16,6 +16,8 @@
-->
<resources>
<string name="meshtastic_app_name">Meshtastic</string>
<!-- Language tags native names (not available via .getDisplayLanguage) -->
<string name="fr_HT" translatable="false">Kreyòl ayisyen</string>
<string name="pt_BR" translatable="false">Português do Brasil</string>
@ -357,6 +359,7 @@
<string name="remove">Remove</string>
<string name="remove_node_text">This node will be removed from your list until your node receives data from it again.</string>
<string name="mute_notifications">Mute notifications</string>
<string name="mute_1_hour">1 hour</string>
<string name="mute_8_hours">8 hours</string>
<string name="mute_1_week">1 week</string>
<string name="mute_always">Always</string>
@ -575,7 +578,7 @@
<string name="output_led_gpio">Output LED (GPIO)</string>
<string name="output_led_active_high">Output LED active high</string>
<string name="output_buzzer_gpio">Output buzzer (GPIO)</string>
<string name="use_pwm_buzzer">Use PWM buzzer</string>
<string name="use_view_buzzer">Use VIEW buzzer</string>
<string name="output_vibra_gpio">Output vibra (GPIO)</string>
<string name="output_duration_milliseconds">Output duration (milliseconds)</string>
<string name="nag_timeout_seconds">Nag timeout (seconds)</string>
@ -1076,4 +1079,5 @@
<string name="compass_no_location_fix">Waiting for a GPS fix to calculate distance and bearing.</string>
<string name="compass_uncertainty">Estimated area: \u00b1%1$s (\u00b1%2$s)</string>
<string name="compass_uncertainty_unknown">Estimated area: unknown accuracy</string>
<string name="mark_as_read">Mark as read</string>
</resources>