feat: per device DB manager (#3641)

This commit is contained in:
Mac DeCourcy 2025-11-09 08:54:21 -08:00 committed by GitHub
parent f0b9a0ff75
commit cb8d1871c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 643 additions and 86 deletions

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2025 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.datasource
import kotlinx.coroutines.flow.Flow
import org.meshtastic.core.database.entity.MyNodeEntity
import org.meshtastic.core.database.entity.NodeEntity
import org.meshtastic.core.database.entity.NodeWithRelations
interface NodeInfoReadDataSource {
fun myNodeInfoFlow(): Flow<MyNodeEntity?>
fun nodeDBbyNumFlow(): Flow<Map<Int, NodeWithRelations>>
fun getNodesFlow(
sort: String,
filter: String,
includeUnknown: Boolean,
hopsAwayMax: Int,
lastHeardMin: Int,
): Flow<List<NodeWithRelations>>
suspend fun getNodesOlderThan(lastHeard: Int): List<NodeEntity>
suspend fun getUnknownNodes(): List<NodeEntity>
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2025 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.datasource
import org.meshtastic.core.database.entity.MetadataEntity
import org.meshtastic.core.database.entity.MyNodeEntity
import org.meshtastic.core.database.entity.NodeEntity
interface NodeInfoWriteDataSource {
suspend fun upsert(node: NodeEntity)
suspend fun installConfig(mi: MyNodeEntity, nodes: List<NodeEntity>)
suspend fun clearNodeDB()
suspend fun deleteNode(num: Int)
suspend fun deleteNodes(nodeNums: List<Int>)
suspend fun deleteMetadata(num: Int)
suspend fun upsert(metadata: MetadataEntity)
suspend fun setNodeNotes(num: Int, notes: String)
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2025 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.datasource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import org.meshtastic.core.database.DatabaseManager
import org.meshtastic.core.database.entity.MyNodeEntity
import org.meshtastic.core.database.entity.NodeEntity
import org.meshtastic.core.database.entity.NodeWithRelations
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SwitchingNodeInfoReadDataSource @Inject constructor(private val dbManager: DatabaseManager) :
NodeInfoReadDataSource {
override fun myNodeInfoFlow(): Flow<MyNodeEntity?> =
dbManager.currentDb.flatMapLatest { db -> db.nodeInfoDao().getMyNodeInfo() }
override fun nodeDBbyNumFlow(): Flow<Map<Int, NodeWithRelations>> =
dbManager.currentDb.flatMapLatest { db -> db.nodeInfoDao().nodeDBbyNum() }
override fun getNodesFlow(
sort: String,
filter: String,
includeUnknown: Boolean,
hopsAwayMax: Int,
lastHeardMin: Int,
): Flow<List<NodeWithRelations>> = dbManager.currentDb.flatMapLatest { db ->
db.nodeInfoDao()
.getNodes(
sort = sort,
filter = filter,
includeUnknown = includeUnknown,
hopsAwayMax = hopsAwayMax,
lastHeardMin = lastHeardMin,
)
}
override suspend fun getNodesOlderThan(lastHeard: Int): List<NodeEntity> =
dbManager.withDb { it.nodeInfoDao().getNodesOlderThan(lastHeard) }
override suspend fun getUnknownNodes(): List<NodeEntity> = dbManager.withDb { it.nodeInfoDao().getUnknownNodes() }
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2025 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.datasource
import kotlinx.coroutines.withContext
import org.meshtastic.core.database.DatabaseManager
import org.meshtastic.core.database.entity.MetadataEntity
import org.meshtastic.core.database.entity.MyNodeEntity
import org.meshtastic.core.database.entity.NodeEntity
import org.meshtastic.core.di.CoroutineDispatchers
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SwitchingNodeInfoWriteDataSource
@Inject
constructor(
private val dbManager: DatabaseManager,
private val dispatchers: CoroutineDispatchers,
) : NodeInfoWriteDataSource {
override suspend fun upsert(node: NodeEntity) =
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().upsert(node) } }
override suspend fun installConfig(mi: MyNodeEntity, nodes: List<NodeEntity>) =
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().installConfig(mi, nodes) } }
override suspend fun clearNodeDB() =
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().clearNodeInfo() } }
override suspend fun deleteNode(num: Int) =
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().deleteNode(num) } }
override suspend fun deleteNodes(nodeNums: List<Int>) =
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().deleteNodes(nodeNums) } }
override suspend fun deleteMetadata(num: Int) =
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().deleteMetadata(num) } }
override suspend fun upsert(metadata: MetadataEntity) =
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().upsert(metadata) } }
override suspend fun setNodeNotes(num: Int, notes: String) =
withContext(dispatchers.io) { dbManager.withDb { it.nodeInfoDao().setNodeNotes(num, notes) } }
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2025 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.meshtastic.core.data.datasource.NodeInfoReadDataSource
import org.meshtastic.core.data.datasource.NodeInfoWriteDataSource
import org.meshtastic.core.data.datasource.SwitchingNodeInfoReadDataSource
import org.meshtastic.core.data.datasource.SwitchingNodeInfoWriteDataSource
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
interface NodeDataSourceModule {
@Binds @Singleton
fun bindNodeInfoReadDataSource(impl: SwitchingNodeInfoReadDataSource): NodeInfoReadDataSource
@Binds @Singleton
fun bindNodeInfoWriteDataSource(impl: SwitchingNodeInfoWriteDataSource): NodeInfoWriteDataSource
}

View file

@ -17,14 +17,14 @@
package org.meshtastic.core.data.repository
import dagger.Lazy
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.withContext
import org.meshtastic.core.database.dao.MeshLogDao
import org.meshtastic.core.database.DatabaseManager
import org.meshtastic.core.database.entity.MeshLog
import org.meshtastic.core.di.CoroutineDispatchers
import org.meshtastic.proto.MeshProtos
@ -37,16 +37,16 @@ import javax.inject.Inject
class MeshLogRepository
@Inject
constructor(
private val meshLogDaoLazy: Lazy<MeshLogDao>,
private val dbManager: DatabaseManager,
private val dispatchers: CoroutineDispatchers,
) {
private val meshLogDao by lazy { meshLogDaoLazy.get() }
fun getAllLogs(maxItems: Int = MAX_ITEMS): Flow<List<MeshLog>> =
meshLogDao.getAllLogs(maxItems).flowOn(dispatchers.io).conflate()
dbManager.currentDb.flatMapLatest { it.meshLogDao().getAllLogs(maxItems) }.flowOn(dispatchers.io).conflate()
fun getAllLogsInReceiveOrder(maxItems: Int = MAX_ITEMS): Flow<List<MeshLog>> =
meshLogDao.getAllLogsInReceiveOrder(maxItems).flowOn(dispatchers.io).conflate()
fun getAllLogsInReceiveOrder(maxItems: Int = MAX_ITEMS): Flow<List<MeshLog>> = dbManager.currentDb
.flatMapLatest { it.meshLogDao().getAllLogsInReceiveOrder(maxItems) }
.flowOn(dispatchers.io)
.conflate()
private fun parseTelemetryLog(log: MeshLog): Telemetry? = runCatching {
Telemetry.parseFrom(log.fromRadio.packet.decoded.payload)
@ -106,8 +106,10 @@ constructor(
}
.getOrNull()
fun getTelemetryFrom(nodeNum: Int): Flow<List<Telemetry>> = meshLogDao
.getLogsFrom(nodeNum, Portnums.PortNum.TELEMETRY_APP_VALUE, MAX_MESH_PACKETS)
fun getTelemetryFrom(nodeNum: Int): Flow<List<Telemetry>> = dbManager.currentDb
.flatMapLatest {
it.meshLogDao().getLogsFrom(nodeNum, Portnums.PortNum.TELEMETRY_APP_VALUE, MAX_MESH_PACKETS)
}
.distinctUntilChanged()
.mapLatest { list -> list.mapNotNull(::parseTelemetryLog) }
.flowOn(dispatchers.io)
@ -116,8 +118,10 @@ constructor(
nodeNum: Int,
portNum: Int = Portnums.PortNum.UNKNOWN_APP_VALUE,
maxItem: Int = MAX_MESH_PACKETS,
): Flow<List<MeshLog>> =
meshLogDao.getLogsFrom(nodeNum, portNum, maxItem).distinctUntilChanged().flowOn(dispatchers.io)
): Flow<List<MeshLog>> = dbManager.currentDb
.flatMapLatest { it.meshLogDao().getLogsFrom(nodeNum, portNum, maxItem) }
.distinctUntilChanged()
.flowOn(dispatchers.io)
/*
* Retrieves MeshPackets matching 'nodeNum' and 'portNum'.
@ -130,14 +134,16 @@ constructor(
.mapLatest { list -> list.firstOrNull { it.myNodeInfo != null }?.myNodeInfo }
.flowOn(dispatchers.io)
suspend fun insert(log: MeshLog) = withContext(dispatchers.io) { meshLogDao.insert(log) }
suspend fun insert(log: MeshLog) =
withContext(dispatchers.io) { dbManager.currentDb.value.meshLogDao().insert(log) }
suspend fun deleteAll() = withContext(dispatchers.io) { meshLogDao.deleteAll() }
suspend fun deleteAll() = withContext(dispatchers.io) { dbManager.currentDb.value.meshLogDao().deleteAll() }
suspend fun deleteLog(uuid: String) = withContext(dispatchers.io) { meshLogDao.deleteLog(uuid) }
suspend fun deleteLog(uuid: String) =
withContext(dispatchers.io) { dbManager.currentDb.value.meshLogDao().deleteLog(uuid) }
suspend fun deleteLogs(nodeNum: Int, portNum: Int) =
withContext(dispatchers.io) { meshLogDao.deleteLogs(nodeNum, portNum) }
withContext(dispatchers.io) { dbManager.currentDb.value.meshLogDao().deleteLogs(nodeNum, portNum) }
companion object {
private const val MAX_ITEMS = 500

View file

@ -30,7 +30,8 @@ import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
import org.meshtastic.core.database.dao.NodeInfoDao
import org.meshtastic.core.data.datasource.NodeInfoReadDataSource
import org.meshtastic.core.data.datasource.NodeInfoWriteDataSource
import org.meshtastic.core.database.entity.MetadataEntity
import org.meshtastic.core.database.entity.MyNodeEntity
import org.meshtastic.core.database.entity.NodeEntity
@ -49,13 +50,14 @@ class NodeRepository
@Inject
constructor(
processLifecycle: Lifecycle,
private val nodeInfoDao: NodeInfoDao,
private val nodeInfoReadDataSource: NodeInfoReadDataSource,
private val nodeInfoWriteDataSource: NodeInfoWriteDataSource,
private val dispatchers: CoroutineDispatchers,
) {
// hardware info about our local device (can be null)
val myNodeInfo: StateFlow<MyNodeEntity?> =
nodeInfoDao
.getMyNodeInfo()
nodeInfoReadDataSource
.myNodeInfoFlow()
.flowOn(dispatchers.io)
.stateIn(processLifecycle.coroutineScope, SharingStarted.Eagerly, null)
@ -69,12 +71,13 @@ constructor(
val myId: StateFlow<String?>
get() = _myId
fun getNodeDBbyNum() = nodeInfoDao.nodeDBbyNum().map { map -> map.mapValues { (_, it) -> it.toEntity() } }
fun getNodeDBbyNum() =
nodeInfoReadDataSource.nodeDBbyNumFlow().map { map -> map.mapValues { (_, it) -> it.toEntity() } }
// A map from nodeNum to Node
val nodeDBbyNum: StateFlow<Map<Int, Node>> =
nodeInfoDao
.nodeDBbyNum()
nodeInfoReadDataSource
.nodeDBbyNumFlow()
.mapLatest { map -> map.mapValues { (_, it) -> it.toModel() } }
.onEach {
val ourNodeInfo = it.values.firstOrNull()
@ -116,8 +119,8 @@ constructor(
includeUnknown: Boolean = true,
onlyOnline: Boolean = false,
onlyDirect: Boolean = false,
) = nodeInfoDao
.getNodes(
) = nodeInfoReadDataSource
.getNodesFlow(
sort = sort.sqlValue,
filter = filter,
includeUnknown = includeUnknown,
@ -128,40 +131,46 @@ constructor(
.flowOn(dispatchers.io)
.conflate()
suspend fun upsert(node: NodeEntity) = withContext(dispatchers.io) { nodeInfoDao.upsert(node) }
suspend fun upsert(node: NodeEntity) = withContext(dispatchers.io) { nodeInfoWriteDataSource.upsert(node) }
suspend fun installConfig(mi: MyNodeEntity, nodes: List<NodeEntity>) =
withContext(dispatchers.io) { nodeInfoDao.installConfig(mi, nodes) }
withContext(dispatchers.io) { nodeInfoWriteDataSource.installConfig(mi, nodes) }
suspend fun clearNodeDB() = withContext(dispatchers.io) { nodeInfoDao.clearNodeInfo() }
suspend fun clearNodeDB() = withContext(dispatchers.io) { nodeInfoWriteDataSource.clearNodeDB() }
suspend fun deleteNode(num: Int) = withContext(dispatchers.io) {
nodeInfoDao.deleteNode(num)
nodeInfoDao.deleteMetadata(num)
nodeInfoWriteDataSource.deleteNode(num)
nodeInfoWriteDataSource.deleteMetadata(num)
}
suspend fun deleteNodes(nodeNums: List<Int>) = withContext(dispatchers.io) {
nodeInfoDao.deleteNodes(nodeNums)
nodeNums.forEach { nodeInfoDao.deleteMetadata(it) }
nodeInfoWriteDataSource.deleteNodes(nodeNums)
nodeNums.forEach { nodeInfoWriteDataSource.deleteMetadata(it) }
}
suspend fun getNodesOlderThan(lastHeard: Int): List<NodeEntity> =
withContext(dispatchers.io) { nodeInfoDao.getNodesOlderThan(lastHeard) }
withContext(dispatchers.io) { nodeInfoReadDataSource.getNodesOlderThan(lastHeard) }
suspend fun getUnknownNodes(): List<NodeEntity> = withContext(dispatchers.io) { nodeInfoDao.getUnknownNodes() }
suspend fun getUnknownNodes(): List<NodeEntity> =
withContext(dispatchers.io) { nodeInfoReadDataSource.getUnknownNodes() }
suspend fun insertMetadata(metadata: MetadataEntity) = withContext(dispatchers.io) { nodeInfoDao.upsert(metadata) }
suspend fun insertMetadata(metadata: MetadataEntity) =
withContext(dispatchers.io) { nodeInfoWriteDataSource.upsert(metadata) }
val onlineNodeCount: Flow<Int> =
nodeInfoDao
.nodeDBbyNum()
nodeInfoReadDataSource
.nodeDBbyNumFlow()
.mapLatest { map -> map.values.count { it.node.lastHeard > onlineTimeThreshold() } }
.flowOn(dispatchers.io)
.conflate()
val totalNodeCount: Flow<Int> =
nodeInfoDao.nodeDBbyNum().mapLatest { map -> map.values.count() }.flowOn(dispatchers.io).conflate()
nodeInfoReadDataSource
.nodeDBbyNumFlow()
.mapLatest { map -> map.values.count() }
.flowOn(dispatchers.io)
.conflate()
suspend fun setNodeNotes(num: Int, notes: String) =
withContext(dispatchers.io) { nodeInfoDao.setNodeNotes(num, notes) }
withContext(dispatchers.io) { nodeInfoWriteDataSource.setNodeNotes(num, notes) }
}

View file

@ -17,45 +17,54 @@
package org.meshtastic.core.data.repository
import dagger.Lazy
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.withContext
import org.meshtastic.core.database.dao.PacketDao
import org.meshtastic.core.database.DatabaseManager
import org.meshtastic.core.database.entity.ContactSettings
import org.meshtastic.core.database.entity.Packet
import org.meshtastic.core.database.entity.ReactionEntity
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.di.CoroutineDispatchers
import org.meshtastic.core.model.DataPacket
import org.meshtastic.core.model.MessageStatus
import org.meshtastic.proto.ChannelProtos.ChannelSettings
import org.meshtastic.proto.Portnums.PortNum
import javax.inject.Inject
class PacketRepository @Inject constructor(private val packetDaoLazy: Lazy<PacketDao>) {
private val packetDao by lazy { packetDaoLazy.get() }
class PacketRepository
@Inject
constructor(
private val dbManager: DatabaseManager,
private val dispatchers: CoroutineDispatchers,
) {
fun getWaypoints(): Flow<List<Packet>> =
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getAllPackets(PortNum.WAYPOINT_APP_VALUE) }
fun getWaypoints(): Flow<List<Packet>> = packetDao.getAllPackets(PortNum.WAYPOINT_APP_VALUE)
fun getContacts(): Flow<Map<String, Packet>> = packetDao.getContactKeys()
fun getContacts(): Flow<Map<String, Packet>> =
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getContactKeys() }
suspend fun getMessageCount(contact: String): Int =
withContext(Dispatchers.IO) { packetDao.getMessageCount(contact) }
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getMessageCount(contact) }
suspend fun getUnreadCount(contact: String): Int = withContext(Dispatchers.IO) { packetDao.getUnreadCount(contact) }
suspend fun getUnreadCount(contact: String): Int =
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getUnreadCount(contact) }
fun getUnreadCountTotal(): Flow<Int> = packetDao.getUnreadCountTotal()
fun getUnreadCountTotal(): Flow<Int> =
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getUnreadCountTotal() }
suspend fun clearUnreadCount(contact: String, timestamp: Long) =
withContext(Dispatchers.IO) { packetDao.clearUnreadCount(contact, timestamp) }
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().clearUnreadCount(contact, timestamp) }
suspend fun getQueuedPackets(): List<DataPacket>? = withContext(Dispatchers.IO) { packetDao.getQueuedPackets() }
suspend fun getQueuedPackets(): List<DataPacket>? =
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getQueuedPackets() }
suspend fun insert(packet: Packet) = withContext(Dispatchers.IO) { packetDao.insert(packet) }
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) {
packetDao.getMessagesFrom(contact).mapLatest { packets ->
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
@ -68,43 +77,53 @@ class PacketRepository @Inject constructor(private val packetDaoLazy: Lazy<Packe
}
suspend fun updateMessageStatus(d: DataPacket, m: MessageStatus) =
withContext(Dispatchers.IO) { packetDao.updateMessageStatus(d, m) }
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().updateMessageStatus(d, m) }
suspend fun updateMessageId(d: DataPacket, id: Int) =
withContext(Dispatchers.IO) { packetDao.updateMessageId(d, id) }
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().updateMessageId(d, id) }
suspend fun getPacketById(requestId: Int) = withContext(Dispatchers.IO) { packetDao.getPacketById(requestId) }
suspend fun getPacketById(requestId: Int) =
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getPacketById(requestId) }
suspend fun getPacketByPacketId(packetId: Int) =
withContext(Dispatchers.IO) { packetDao.getPacketByPacketId(packetId) }
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().getPacketByPacketId(packetId) }
suspend fun deleteMessages(uuidList: List<Long>) = withContext(Dispatchers.IO) {
for (chunk in uuidList.chunked(500)) { // limit number of UUIDs per query
packetDao.deleteMessages(chunk)
suspend fun deleteMessages(uuidList: List<Long>) = withContext(dispatchers.io) {
for (chunk in uuidList.chunked(500)) {
// Fetch DAO per chunk to avoid holding a stale reference if the active DB switches
dbManager.currentDb.value.packetDao().deleteMessages(chunk)
}
}
suspend fun deleteContacts(contactList: List<String>) =
withContext(Dispatchers.IO) { packetDao.deleteContacts(contactList) }
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().deleteContacts(contactList) }
suspend fun deleteWaypoint(id: Int) = withContext(Dispatchers.IO) { packetDao.deleteWaypoint(id) }
suspend fun deleteWaypoint(id: Int) =
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().deleteWaypoint(id) }
suspend fun delete(packet: Packet) = withContext(Dispatchers.IO) { packetDao.delete(packet) }
suspend fun delete(packet: Packet) =
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().delete(packet) }
suspend fun update(packet: Packet) = withContext(Dispatchers.IO) { packetDao.update(packet) }
suspend fun update(packet: Packet) =
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().update(packet) }
fun getContactSettings(): Flow<Map<String, ContactSettings>> = packetDao.getContactSettings()
fun getContactSettings(): Flow<Map<String, ContactSettings>> =
dbManager.currentDb.flatMapLatest { db -> db.packetDao().getContactSettings() }
suspend fun getContactSettings(contact: String) =
withContext(Dispatchers.IO) { packetDao.getContactSettings(contact) ?: ContactSettings(contact) }
suspend fun getContactSettings(contact: String) = withContext(dispatchers.io) {
dbManager.currentDb.value.packetDao().getContactSettings(contact) ?: ContactSettings(contact)
}
suspend fun setMuteUntil(contacts: List<String>, until: Long) =
withContext(Dispatchers.IO) { packetDao.setMuteUntil(contacts, until) }
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().setMuteUntil(contacts, until) }
suspend fun insertReaction(reaction: ReactionEntity) = withContext(Dispatchers.IO) { packetDao.insert(reaction) }
suspend fun insertReaction(reaction: ReactionEntity) =
withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().insert(reaction) }
suspend fun clearPacketDB() = withContext(Dispatchers.IO) { packetDao.deleteAll() }
suspend fun clearPacketDB() = withContext(dispatchers.io) { dbManager.currentDb.value.packetDao().deleteAll() }
suspend fun migrateChannelsByPSK(oldSettings: List<ChannelSettings>, newSettings: List<ChannelSettings>) =
packetDao.migrateChannelsByPSK(oldSettings, newSettings)
withContext(dispatchers.io) {
dbManager.currentDb.value.packetDao().migrateChannelsByPSK(oldSettings, newSettings)
}
}

View file

@ -17,10 +17,10 @@
package org.meshtastic.core.data.repository
import dagger.Lazy
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import org.meshtastic.core.database.dao.QuickChatActionDao
import org.meshtastic.core.database.DatabaseManager
import org.meshtastic.core.database.entity.QuickChatAction
import org.meshtastic.core.di.CoroutineDispatchers
import javax.inject.Inject
@ -28,19 +28,20 @@ import javax.inject.Inject
class QuickChatActionRepository
@Inject
constructor(
private val quickChatDaoLazy: Lazy<QuickChatActionDao>,
private val dbManager: DatabaseManager,
private val dispatchers: CoroutineDispatchers,
) {
private val quickChatActionDao by lazy { quickChatDaoLazy.get() }
fun getAllActions() = dbManager.currentDb.flatMapLatest { it.quickChatActionDao().getAll() }.flowOn(dispatchers.io)
fun getAllActions() = quickChatActionDao.getAll().flowOn(dispatchers.io)
suspend fun upsert(action: QuickChatAction) =
withContext(dispatchers.io) { dbManager.currentDb.value.quickChatActionDao().upsert(action) }
suspend fun upsert(action: QuickChatAction) = withContext(dispatchers.io) { quickChatActionDao.upsert(action) }
suspend fun deleteAll() = withContext(dispatchers.io) { dbManager.currentDb.value.quickChatActionDao().deleteAll() }
suspend fun deleteAll() = withContext(dispatchers.io) { quickChatActionDao.deleteAll() }
suspend fun delete(action: QuickChatAction) =
withContext(dispatchers.io) { dbManager.currentDb.value.quickChatActionDao().delete(action) }
suspend fun delete(action: QuickChatAction) = withContext(dispatchers.io) { quickChatActionDao.delete(action) }
suspend fun setItemPosition(uuid: Long, newPos: Int) =
withContext(dispatchers.io) { quickChatActionDao.updateActionPosition(uuid, newPos) }
suspend fun setItemPosition(uuid: Long, newPos: Int) = withContext(dispatchers.io) {
dbManager.currentDb.value.quickChatActionDao().updateActionPosition(uuid, newPos)
}
}