Add support for messages plus API (#1945)

This commit is contained in:
Mikołaj Pich 2022-08-22 14:30:50 +02:00 committed by GitHub
parent 08a3bd77bd
commit 9d47127921
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 3065 additions and 575 deletions

View File

@ -186,7 +186,7 @@ ext {
}
dependencies {
implementation "io.github.wulkanowy:sdk:9032e33686"
implementation "io.github.wulkanowy:sdk:dbe87aac"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,6 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AppInfo
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
@ -110,7 +109,6 @@ internal class DataModule {
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context)
@OptIn(ExperimentalCoroutinesApi::class)
@Singleton
@Provides
fun provideFlowSharedPref(sharedPreferences: SharedPreferences) =
@ -197,7 +195,7 @@ internal class DataModule {
@Singleton
@Provides
fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao
fun provideMailboxesDao(database: AppDatabase) = database.mailboxDao
@Singleton
@Provides

View File

@ -30,7 +30,7 @@ import javax.inject.Singleton
Subject::class,
LuckyNumber::class,
CompletedLesson::class,
ReportingUnit::class,
Mailbox::class,
Recipient::class,
MobileDevice::class,
Teacher::class,
@ -55,7 +55,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 50
const val VERSION_SCHEMA = 51
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@ -103,7 +103,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration44(),
Migration46(),
Migration49(),
Migration50()
Migration50(),
Migration51(),
)
fun newInstance(
@ -154,7 +155,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract val completedLessonsDao: CompletedLessonsDao
abstract val reportingUnitDao: ReportingUnitDao
abstract val mailboxDao: MailboxDao
abstract val recipientDao: RecipientDao

View File

@ -0,0 +1,17 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Mailbox
import javax.inject.Singleton
@Singleton
@Dao
interface MailboxDao : BaseDao<Mailbox> {
@Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ")
suspend fun loadAll(userLoginId: Int): List<Mailbox>
@Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId AND studentName = :studentName ")
suspend fun load(userLoginId: Int, studentName: String): Mailbox?
}

View File

@ -11,9 +11,9 @@ import kotlinx.coroutines.flow.Flow
interface MessagesDao : BaseDao<Message> {
@Transaction
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId")
fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow<MessageWithAttachment?>
@Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey")
fun loadMessageWithAttachment(messageGlobalKey: String): Flow<MessageWithAttachment?>
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC")
fun loadAll(studentId: Int, folder: Int): Flow<List<Message>>
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
fun loadAll(mailboxKey: String, folder: Int): Flow<List<Message>>
}

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Recipient
import javax.inject.Singleton
@ -9,6 +10,6 @@ import javax.inject.Singleton
@Dao
interface RecipientDao : BaseDao<Recipient> {
@Query("SELECT * FROM Recipients WHERE student_id = :studentId AND unit_id = :unitId AND role = :role")
suspend fun loadAll(studentId: Int, unitId: Int, role: Int): List<Recipient>
@Query("SELECT * FROM Recipients WHERE type = :type AND studentMailboxGlobalKey = :studentMailboxGlobalKey")
suspend fun loadAll(type: MailboxType, studentMailboxGlobalKey: String): List<Recipient>
}

View File

@ -1,17 +0,0 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.ReportingUnit
import javax.inject.Singleton
@Singleton
@Dao
interface ReportingUnitDao : BaseDao<ReportingUnit> {
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId")
suspend fun load(studentId: Int): List<ReportingUnit>
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId")
suspend fun loadOne(studentId: Int, unitId: Int): ReportingUnit?
}

View File

@ -0,0 +1,25 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "Mailboxes")
data class Mailbox(
@PrimaryKey
val globalKey: String,
val fullName: String,
val userName: String,
val userLoginId: Int,
val studentName: String,
val schoolNameShort: String,
val type: MailboxType,
)
enum class MailboxType {
STUDENT,
PARENT,
GUARDIAN,
EMPLOYEE,
UNKNOWN,
}

View File

@ -9,23 +9,16 @@ import java.time.Instant
@Entity(tableName = "Messages")
data class Message(
@ColumnInfo(name = "student_id")
val studentId: Long,
@ColumnInfo(name = "message_global_key")
val messageGlobalKey: String,
@ColumnInfo(name = "real_id")
val realId: Int,
@ColumnInfo(name = "mailbox_key")
val mailboxKey: String,
@ColumnInfo(name = "message_id")
val messageId: Int,
@ColumnInfo(name = "sender_name")
val sender: String,
@ColumnInfo(name = "sender_id")
val senderId: Int,
@ColumnInfo(name = "recipient_name")
val recipient: String,
val correspondents: String,
val subject: String,
@ -36,8 +29,6 @@ data class Message(
var unread: Boolean,
val removed: Boolean,
@ColumnInfo(name = "has_attachments")
val hasAttachments: Boolean
) : Serializable {
@ -48,11 +39,7 @@ data class Message(
@ColumnInfo(name = "is_notified")
var isNotified: Boolean = true
@ColumnInfo(name = "unread_by")
var unreadBy: Int = 0
@ColumnInfo(name = "read_by")
var readBy: Int = 0
var content: String = ""
var sender: String? = null
var recipients: String? = null
}

View File

@ -12,11 +12,8 @@ data class MessageAttachment(
@ColumnInfo(name = "real_id")
val realId: Int,
@ColumnInfo(name = "message_id")
val messageId: Int,
@ColumnInfo(name = "one_drive_id")
val oneDriveId: String,
@ColumnInfo(name = "message_global_key")
val messageGlobalKey: String,
@ColumnInfo(name = "url")
val url: String,

View File

@ -7,6 +7,6 @@ data class MessageWithAttachment(
@Embedded
val message: Message,
@Relation(parentColumn = "message_id", entityColumn = "message_id")
@Relation(parentColumn = "message_global_key", entityColumn = "message_global_key")
val attachments: List<MessageAttachment>
)

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@ -8,32 +7,16 @@ import java.io.Serializable
@kotlinx.serialization.Serializable
@Entity(tableName = "Recipients")
data class Recipient(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "real_id")
val realId: String,
val name: String,
@ColumnInfo(name = "real_name")
val realName: String,
@ColumnInfo(name = "login_id")
val loginId: Int,
@ColumnInfo(name = "unit_id")
val unitId: Int,
val role: Int,
val hash: String
val mailboxGlobalKey: String,
val studentMailboxGlobalKey: String,
val fullName: String,
val userName: String,
val schoolShortName: String,
val type: MailboxType,
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
override fun toString() = name
override fun toString() = userName
}

View File

@ -1,32 +0,0 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@Entity(tableName = "ReportingUnits")
data class ReportingUnit(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "real_id")
val unitId: Int,
@ColumnInfo(name = "short")
val shortName: String,
@ColumnInfo(name = "sender_id")
val senderId: Int,
@ColumnInfo(name = "sender_name")
val senderName: String,
val roles: List<Int>
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -0,0 +1,88 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration51 : Migration(50, 51) {
override fun migrate(database: SupportSQLiteDatabase) {
createMailboxTable(database)
recreateMessagesTable(database)
recreateMessageAttachmentsTable(database)
recreateRecipientsTable(database)
deleteReportingUnitTable(database)
}
private fun createMailboxTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Mailboxes")
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Mailboxes` (
`globalKey` TEXT NOT NULL,
`fullName` TEXT NOT NULL,
`userName` TEXT NOT NULL,
`userLoginId` INTEGER NOT NULL,
`studentName` TEXT NOT NULL,
`schoolNameShort` TEXT NOT NULL,
`type` TEXT NOT NULL,
PRIMARY KEY(`globalKey`)
)""".trimIndent()
)
}
private fun recreateMessagesTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Messages")
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Messages` (
`message_global_key` TEXT NOT NULL,
`mailbox_key` TEXT NOT NULL,
`message_id` INTEGER NOT NULL,
`correspondents` TEXT NOT NULL,
`subject` TEXT NOT NULL,
`date` INTEGER NOT NULL,
`folder_id` INTEGER NOT NULL,
`unread` INTEGER NOT NULL,
`has_attachments` INTEGER NOT NULL,
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`is_notified` INTEGER NOT NULL,
`content` TEXT NOT NULL,
`sender` TEXT, `recipients` TEXT
)""".trimIndent()
)
}
private fun recreateMessageAttachmentsTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS MessageAttachments")
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `MessageAttachments` (
`real_id` INTEGER NOT NULL,
`message_global_key` TEXT NOT NULL,
`url` TEXT NOT NULL,
`filename` TEXT NOT NULL,
PRIMARY KEY(`real_id`)
)""".trimIndent()
)
}
private fun recreateRecipientsTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Recipients")
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Recipients` (
`mailboxGlobalKey` TEXT NOT NULL,
`studentMailboxGlobalKey` TEXT NOT NULL,
`fullName` TEXT NOT NULL,
`userName` TEXT NOT NULL,
`schoolShortName` TEXT NOT NULL,
`type` TEXT NOT NULL,
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
)""".trimIndent()
)
}
private fun deleteReportingUnitTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS ReportingUnits")
}
}

View File

@ -0,0 +1,18 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.pojo.Mailbox as SdkMailbox
fun List<SdkMailbox>.mapToEntities(student: Student) = map {
Mailbox(
globalKey = it.globalKey,
fullName = it.fullName,
userName = it.userName,
userLoginId = student.userLoginId,
studentName = it.studentName,
schoolNameShort = it.schoolNameShort,
type = MailboxType.valueOf(it.type.name),
)
}

View File

@ -1,40 +1,31 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student
import java.time.Instant
import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.sdk.pojo.MailboxType
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
fun List<SdkMessage>.mapToEntities(student: Student) = map {
fun List<SdkMessage>.mapToEntities(mailbox: Mailbox) = map {
Message(
studentId = student.id,
realId = it.id ?: 0,
messageId = it.messageId ?: 0,
sender = it.sender?.name.orEmpty(),
senderId = it.sender?.loginId ?: 0,
recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów",
messageGlobalKey = it.globalKey,
mailboxKey = mailbox.globalKey,
messageId = it.id,
correspondents = it.correspondents,
subject = it.subject.trim(),
date = it.dateZoned?.toInstant() ?: Instant.now(),
date = it.dateZoned.toInstant(),
folderId = it.folderId,
unread = it.unread ?: false,
removed = it.removed,
unread = it.unread,
hasAttachments = it.hasAttachments
).apply {
content = it.content.orEmpty()
unreadBy = it.unreadBy ?: 0
readBy = it.readBy ?: 0
}
}
fun List<SdkMessageAttachment>.mapToEntities() = map {
fun List<SdkMessageAttachment>.mapToEntities(messageGlobalKey: String) = map {
MessageAttachment(
realId = it.id,
messageId = it.messageId,
oneDriveId = it.oneDriveId,
messageGlobalKey = messageGlobalKey,
realId = it.url.hashCode(),
url = it.url,
filename = it.filename
)
@ -42,12 +33,11 @@ fun List<SdkMessageAttachment>.mapToEntities() = map {
fun List<Recipient>.mapFromEntities() = map {
SdkRecipient(
id = it.realId,
name = it.realName,
loginId = it.loginId,
reportingUnitId = it.unitId,
role = it.role,
hash = it.hash,
shortName = it.name
fullName = it.fullName,
userName = it.userName,
studentName = it.userName,
mailboxGlobalKey = it.mailboxGlobalKey,
schoolNameShort = it.schoolShortName,
type = MailboxType.valueOf(it.type.name),
)
}

View File

@ -1,17 +1,16 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
fun List<SdkRecipient>.mapToEntities(userLoginId: Int) = map {
fun List<SdkRecipient>.mapToEntities(studentMailboxGlobalKey: String) = map {
Recipient(
studentId = userLoginId,
realId = it.id,
realName = it.name,
name = it.shortName,
hash = it.hash,
loginId = it.loginId,
role = it.role,
unitId = it.reportingUnitId ?: 0
mailboxGlobalKey = it.mailboxGlobalKey,
fullName = it.fullName,
userName = it.userName,
studentMailboxGlobalKey = studentMailboxGlobalKey,
schoolShortName = it.schoolNameShort,
type = MailboxType.valueOf(it.type.name),
)
}

View File

@ -1,16 +0,0 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.pojo.ReportingUnit as SdkReportingUnit
fun List<SdkReportingUnit>.mapToEntities(student: Student) = map {
ReportingUnit(
studentId = student.id.toInt(),
unitId = it.id,
roles = it.roles,
senderId = it.senderId,
senderName = it.senderName,
shortName = it.short
)
}

View File

@ -0,0 +1,48 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.MailboxDao
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class MailboxRepository @Inject constructor(
private val mailboxDao: MailboxDao,
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "mailboxes"
suspend fun refreshMailboxes(student: Student) {
val new = sdk.init(student).getMailboxes().mapToEntities(student)
val old = mailboxDao.loadAll(student.userLoginId)
mailboxDao.deleteAll(old uniqueSubtract new)
mailboxDao.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
suspend fun getMailbox(student: Student): Mailbox {
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
val mailbox = mailboxDao.load(student.userLoginId, student.studentName)
return if (isExpired || mailbox == null) {
refreshMailboxes(student)
val newMailbox = mailboxDao.load(student.userLoginId, student.studentName)
requireNotNull(newMailbox) {
"Mailbox for ${student.userName} - ${student.studentName} not found!"
}
newMailbox
} else mailbox
}
}

View File

@ -10,24 +10,24 @@ import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
import io.github.wulkanowy.data.mappers.mapFromEntities
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.pojos.MessageDraft
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
import io.github.wulkanowy.sdk.pojo.SentMessage
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import timber.log.Timber
import java.time.LocalDateTime.now
import javax.inject.Inject
import javax.inject.Singleton
@ -49,7 +49,7 @@ class MessageRepository @Inject constructor(
@Suppress("UNUSED_PARAMETER")
fun getMessages(
student: Student,
semester: Semester,
mailbox: Mailbox,
folder: MessageFolder,
forceRefresh: Boolean,
notify: Boolean = false,
@ -62,42 +62,20 @@ class MessageRepository @Inject constructor(
)
it.isEmpty() || forceRefresh || isExpired
},
query = { messagesDb.loadAll(student.id.toInt(), folder.id) },
query = { messagesDb.loadAll(mailbox.globalKey, folder.id) },
fetch = {
sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now())
.mapToEntities(student)
sdk.init(student).getMessages(Folder.valueOf(folder.name)).mapToEntities(mailbox)
},
saveFetchResult = { old, new ->
messagesDb.deleteAll(old uniqueSubtract new)
messagesDb.insertAll((new uniqueSubtract old).onEach {
it.isNotified = !notify
})
messagesDb.updateAll(getMessagesWithReadByChange(old, new, !notify))
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder))
}
)
private fun getMessagesWithReadByChange(
old: List<Message>,
new: List<Message>,
setNotified: Boolean
): List<Message> {
val oldMeta = old.map { Triple(it, it.readBy, it.unreadBy) }
val newMeta = new.map { Triple(it, it.readBy, it.unreadBy) }
val updatedItems = newMeta uniqueSubtract oldMeta
return updatedItems.map {
val oldItem = old.find { item -> item.messageId == it.first.messageId }
it.first.apply {
id = oldItem?.id ?: 0
isNotified = oldItem?.isNotified ?: setNotified
content = oldItem?.content.orEmpty()
}
}
}
fun getMessage(
student: Student,
message: Message,
@ -106,34 +84,34 @@ class MessageRepository @Inject constructor(
isResultEmpty = { it?.message?.content.isNullOrBlank() },
shouldFetch = {
checkNotNull(it) { "This message no longer exist!" }
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
it.message.unread || it.message.content.isEmpty()
Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
it.message.unread || it.message.content.isBlank()
},
query = { messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) },
query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
fetch = {
sdk.init(student).getMessageDetails(
messageId = it!!.message.messageId,
folderId = message.folderId,
read = markAsRead,
id = message.realId
).let { details ->
details.content to details.attachments.mapToEntities()
}
sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey)
},
saveFetchResult = { old, (downloadedMessage, attachments) ->
saveFetchResult = { old, new ->
checkNotNull(old) { "Fetched message no longer exist!" }
messagesDb.updateAll(listOf(old.message.apply {
id = old.message.id
unread = !markAsRead
content = content.ifBlank { downloadedMessage }
}))
messageAttachmentDao.insertAttachments(attachments)
messagesDb.updateAll(
listOf(old.message.apply {
id = message.id
unread = !markAsRead
sender = new.sender
recipients = new.recipients.firstOrNull() ?: "Wielu adresoatów"
content = content.ifBlank { new.content }
})
)
messageAttachmentDao.insertAttachments(
items = new.attachments.mapToEntities(message.messageGlobalKey),
)
Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read")
}
)
fun getMessagesFromDatabase(student: Student): Flow<List<Message>> {
return messagesDb.loadAll(student.id.toInt(), RECEIVED.id)
fun getMessagesFromDatabase(mailbox: Mailbox): Flow<List<Message>> {
return messagesDb.loadAll(mailbox.globalKey, RECEIVED.id)
}
suspend fun updateMessages(messages: List<Message>) {
@ -145,32 +123,48 @@ class MessageRepository @Inject constructor(
subject: String,
content: String,
recipients: List<Recipient>,
): SentMessage = sdk.init(student).sendMessage(
subject = subject,
content = content,
recipients = recipients.mapFromEntities()
)
mailboxId: String,
) {
sdk.init(student).sendMessage(
subject = subject,
content = content,
recipients = recipients.mapFromEntities(),
mailboxId = mailboxId,
)
}
suspend fun deleteMessages(student: Student, messages: List<Message>) {
val folderId = messages.first().folderId
val isDeleted = sdk.init(student)
.deleteMessages(messages = messages.map { it.messageId }, folderId = folderId)
suspend fun deleteMessages(student: Student, mailbox: Mailbox, messages: List<Message>) {
val firstMessage = messages.first()
sdk.init(student).deleteMessages(
messages = messages.map { it.messageGlobalKey },
removeForever = firstMessage.folderId == TRASHED.id,
)
if (folderId != MessageFolder.TRASHED.id && isDeleted) {
if (firstMessage.folderId != TRASHED.id) {
val deletedMessages = messages.map {
it.copy(folderId = MessageFolder.TRASHED.id)
it.copy(folderId = TRASHED.id)
.apply {
id = it.id
content = it.content
sender = it.sender
recipients = it.recipients
}
}
messagesDb.updateAll(deletedMessages)
} else messagesDb.deleteAll(messages)
getMessages(
student = student,
mailbox = mailbox,
folder = TRASHED,
forceRefresh = true,
).first()
}
suspend fun deleteMessage(student: Student, message: Message) =
deleteMessages(student, listOf(message))
suspend fun deleteMessage(student: Student, mailbox: Mailbox, message: Message) {
deleteMessages(student, mailbox, listOf(message))
}
var draftMessage: MessageDraft?
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))

View File

@ -1,10 +1,7 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.RecipientDao
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
@ -23,9 +20,10 @@ class RecipientRepository @Inject constructor(
private val cacheKey = "recipient"
suspend fun refreshRecipients(student: Student, unit: ReportingUnit, role: Int) {
val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.studentId)
val old = recipientDb.loadAll(unit.studentId, unit.unitId, role)
suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) {
val new = sdk.init(student).getRecipients(mailbox.globalKey)
.mapToEntities(mailbox.globalKey)
val old = recipientDb.loadAll(type, mailbox.globalKey)
recipientDb.deleteAll(old uniqueSubtract new)
recipientDb.insertAll(new uniqueSubtract old)
@ -33,18 +31,27 @@ class RecipientRepository @Inject constructor(
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List<Recipient> {
val cached = recipientDb.loadAll(unit.studentId, unit.unitId, role)
suspend fun getRecipients(
student: Student,
mailbox: Mailbox,
type: MailboxType
): List<Recipient> {
val cached = recipientDb.loadAll(type, mailbox.globalKey)
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
return if (cached.isEmpty() || isExpired) {
refreshRecipients(student, unit, role)
recipientDb.loadAll(unit.studentId, unit.unitId, role)
refreshRecipients(student, mailbox, type)
recipientDb.loadAll(type, mailbox.globalKey)
} else cached
}
suspend fun getMessageRecipients(student: Student, message: Message): List<Recipient> {
return sdk.init(student).getMessageRecipients(message.messageId, message.senderId)
.mapToEntities(student.studentId)
}
suspend fun getMessageSender(
student: Student,
mailbox: Mailbox,
message: Message
): List<Recipient> = sdk.init(student)
.getMessageReplayDetails(message.messageGlobalKey)
.sender
.let(::listOf)
.mapToEntities(mailbox.globalKey)
}

View File

@ -1,53 +0,0 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.ReportingUnitDao
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ReportingUnitRepository @Inject constructor(
private val reportingUnitDb: ReportingUnitDao,
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "reporting_unit"
suspend fun refreshReportingUnits(student: Student) {
val new = sdk.init(student).getReportingUnits().mapToEntities(student)
val old = reportingUnitDb.load(student.id.toInt())
reportingUnitDb.deleteAll(old.uniqueSubtract(new))
reportingUnitDb.insertAll(new.uniqueSubtract(old))
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
suspend fun getReportingUnits(student: Student): List<ReportingUnit> {
val cached = reportingUnitDb.load(student.id.toInt())
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
return if (cached.isEmpty() || isExpired) {
refreshReportingUnits(student)
reportingUnitDb.load(student.id.toInt())
} else cached
}
suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? {
val cached = reportingUnitDb.loadOne(student.id.toInt(), unitId)
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
return if (cached == null || isExpired) {
refreshReportingUnits(student)
reportingUnitDb.loadOne(student.id.toInt(), unitId)
} else cached
}
}

View File

@ -21,7 +21,7 @@ class NewMessageNotification @Inject constructor(
val notificationDataList = items.map {
NotificationData(
title = context.getPlural(R.plurals.message_new_items, 1),
content = "${it.sender}: ${it.subject}",
content = "${it.correspondents}: ${it.subject}",
destination = Destination.Message,
)
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
@ -11,19 +12,21 @@ import javax.inject.Inject
class MessageWork @Inject constructor(
private val messageRepository: MessageRepository,
private val mailboxRepository: MailboxRepository,
private val newMessageNotification: NewMessageNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
val mailbox = mailboxRepository.getMailbox(student)
messageRepository.getMessages(
student = student,
semester = semester,
mailbox = mailbox,
folder = RECEIVED,
forceRefresh = true,
notify = notify
).waitForResult()
messageRepository.getMessagesFromDatabase(student).first()
messageRepository.getMessagesFromDatabase(mailbox).first()
.filter { !it.isNotified && it.unread }.let {
if (it.isNotEmpty()) newMessageNotification.notify(it, student)
messageRepository.updateMessages(it.onEach { message -> message.isNotified = true })

View File

@ -1,23 +1,22 @@
package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.RecipientRepository
import io.github.wulkanowy.data.repositories.ReportingUnitRepository
import javax.inject.Inject
class RecipientWork @Inject constructor(
private val reportingUnitRepository: ReportingUnitRepository,
private val mailboxRepository: MailboxRepository,
private val recipientRepository: RecipientRepository
) : Work {
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
reportingUnitRepository.refreshReportingUnits(student)
mailboxRepository.refreshMailboxes(student)
reportingUnitRepository.getReportingUnits(student).let { units ->
units.map {
recipientRepository.refreshRecipients(student, it, 2)
}
}
val mailbox = mailboxRepository.getMailbox(student)
recipientRepository.refreshRecipients(student, mailbox, MailboxType.EMPLOYEE)
}
}

View File

@ -25,6 +25,7 @@ class DashboardPresenter @Inject constructor(
private val gradeRepository: GradeRepository,
private val semesterRepository: SemesterRepository,
private val messageRepository: MessageRepository,
private val mailboxRepository: MailboxRepository,
private val attendanceSummaryRepository: AttendanceSummaryRepository,
private val timetableRepository: TimetableRepository,
private val homeworkRepository: HomeworkRepository,
@ -227,6 +228,7 @@ class DashboardPresenter @Inject constructor(
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
flow {
val semester = semesterRepository.getCurrentSemester(student)
val mailbox = mailboxRepository.getMailbox(student)
val selectedTiles = preferencesRepository.selectedDashboardTiles
val flowSuccess = flowOf(Resource.Success(null))
@ -238,7 +240,7 @@ class DashboardPresenter @Inject constructor(
val messageFLow = messageRepository.getMessages(
student = student,
semester = semester,
mailbox = mailbox,
folder = MessageFolder.RECEIVED,
forceRefresh = forceRefresh
).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess

View File

@ -17,16 +17,13 @@ val debugMessageItems = listOf(
)
private fun generateMessage(sender: String, subject: String) = Message(
sender = sender,
subject = subject,
studentId = 0,
realId = 0,
messageId = 0,
senderId = 0,
recipient = "",
messageId = 123,
date = Instant.now(),
folderId = 0,
unread = true,
removed = false,
hasAttachments = false
hasAttachments = false,
messageGlobalKey = "",
correspondents = sender,
mailboxKey = "",
)

View File

@ -4,6 +4,8 @@ import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT
import androidx.core.text.parseAsHtml
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
@ -75,29 +77,25 @@ class MessagePreviewAdapter @Inject constructor() :
@SuppressLint("SetTextI18n")
private fun bindMessage(holder: MessageViewHolder, message: Message) {
val context = holder.binding.root.context
val recipientCount = message.unreadBy + message.readBy
val readText = when {
recipientCount > 1 -> {
context.getString(R.string.message_read_by, message.readBy, recipientCount)
}
message.readBy == 1 -> {
context.getString(R.string.message_read, context.getString(R.string.all_yes))
}
else -> context.getString(R.string.message_read, context.getString(R.string.all_no))
val readTextValue = when {
!message.unread -> R.string.all_yes
else -> R.string.all_no
}
val readText = context.getString(R.string.message_read, context.getString(readTextValue))
with(holder.binding) {
messagePreviewSubject.text =
message.subject.ifBlank { root.context.getString(R.string.message_no_subject) }
messagePreviewDate.text = root.context.getString(
messagePreviewSubject.text = message.subject.ifBlank {
context.getString(R.string.message_no_subject)
}
messagePreviewDate.text = context.getString(
R.string.message_date,
message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
)
messagePreviewRead.text = readText
messagePreviewContent.text = message.content
messagePreviewContent.text = message.content.parseAsHtml(FROM_HTML_MODE_COMPACT)
messagePreviewFromSender.text = message.sender
messagePreviewToRecipient.text = message.recipient
messagePreviewToRecipient.text = message.recipients
}
}

View File

@ -135,8 +135,8 @@ class MessagePreviewFragment :
binding.messagePreviewRecycler.visibility = if (show) VISIBLE else GONE
}
override fun showOptions(show: Boolean) {
menuReplyButton?.isVisible = show
override fun showOptions(show: Boolean, isReplayable: Boolean) {
menuReplyButton?.isVisible = isReplayable
menuForwardButton?.isVisible = show
menuDeleteButton?.isVisible = show
menuShareButton?.isVisible = show

View File

@ -1,10 +1,12 @@
package io.github.wulkanowy.ui.modules.message.preview
import android.annotation.SuppressLint
import androidx.core.text.parseAsHtml
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@ -19,6 +21,7 @@ class MessagePreviewPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
private val mailboxRepository: MailboxRepository,
private val analytics: AnalyticsHelper
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository) {
@ -52,7 +55,7 @@ class MessagePreviewPresenter @Inject constructor(
private fun loadData(messageToLoad: Message) {
flatResourceFlow {
val student = studentRepository.getStudentById(messageToLoad.studentId)
val student = studentRepository.getCurrentStudent()
messageRepository.getMessage(student, messageToLoad, true)
}
.logResourceStatus("message ${messageToLoad.messageId} preview")
@ -104,62 +107,69 @@ class MessagePreviewPresenter @Inject constructor(
}
fun onShare(): Boolean {
message?.let {
var text =
"Temat: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}\n" + when (it.sender.isNotEmpty()) {
true -> "Od: ${it.sender}\n"
false -> "Do: ${it.recipient}\n"
} + "Data: ${it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${it.content}"
val message = message ?: return false
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
attachments?.let { attachments ->
if (attachments.isNotEmpty()) {
text += "\n\nZałączniki:"
val text = buildString {
appendLine("Temat: $subject")
appendLine("Od: ${message.sender}")
appendLine("Do: ${message.recipients}")
appendLine("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}")
attachments.forEach { attachment ->
text += "\n${attachment.filename}: ${attachment.url}"
}
}
appendLine()
appendLine(message.content.parseAsHtml())
if (!attachments.isNullOrEmpty()) {
appendLine()
appendLine("Załączniki:")
append(attachments.orEmpty().joinToString(separator = "\n") { attachment ->
"${attachment.filename}: ${attachment.url}"
})
}
view?.shareText(
text,
"FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}"
)
return true
}
return false
view?.shareText(
subject = "FW: $subject",
text = text,
)
return true
}
@SuppressLint("NewApi")
fun onPrint(): Boolean {
message?.let {
val dateString = it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
val infoContent = "<div><h4>Data wysłania</h4>$dateString</div>" + when {
it.sender.isNotEmpty() -> "<div><h4>Od</h4>${it.sender}</div>"
else -> "<div><h4>Do</h4>${it.recipient}</div>"
}
val message = message ?: return false
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
val messageContent = "<p>${it.content}</p>"
.replace(Regex("[\\n\\r]{2,}"), "</p><p>")
.replace(Regex("[\\n\\r]"), "<br>")
val dateString = message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
val jobName = "Wiadomość " + when {
it.sender.isNotEmpty() -> "od ${it.sender}"
else -> "do ${it.recipient}"
} + " $dateString: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }} | Wulkanowy"
val infoContent = buildString {
append("<div><h4>Data wysłania</h4>$dateString</div>")
view?.apply {
val html = printHTML
.replace(
"%SUBJECT%",
it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() })
.replace("%CONTENT%", messageContent)
.replace("%INFO%", infoContent)
printDocument(html, jobName)
}
return true
append("<div><h4>Od</h4>${message.sender}</div>")
append("<div><h4>DO</h4>${message.recipients}</div>")
}
return false
val messageContent = "<p>${message.content}</p>"
.replace(Regex("[\\n\\r]{2,}"), "</p><p>")
.replace(Regex("[\\n\\r]"), "<br>")
val jobName = buildString {
append("Wiadomość ")
append("od ${message.correspondents}")
append("do ${message.correspondents}")
append(" $dateString: $subject | Wulkanowy")
}
view?.apply {
val html = printHTML
.replace("%SUBJECT%", subject)
.replace("%CONTENT%", messageContent)
.replace("%INFO%", infoContent)
printDocument(html, jobName)
}
return true
}
private fun deleteMessage() {
@ -168,16 +178,17 @@ class MessagePreviewPresenter @Inject constructor(
view?.run {
showContent(false)
showProgress(true)
showOptions(false)
showOptions(show = false, isReplayable = false)
showErrorView(false)
}
Timber.i("Delete message ${message?.id}")
Timber.i("Delete message ${message?.messageGlobalKey}")
presenterScope.launch {
runCatching {
val student = studentRepository.getCurrentStudent()
messageRepository.deleteMessage(student, message!!)
val student = studentRepository.getCurrentStudent(decryptPass = true)
val mailbox = mailboxRepository.getMailbox(student)
messageRepository.deleteMessage(student, mailbox, message!!)
}
.onFailure {
retryCallback = { onMessageDelete() }
@ -211,7 +222,10 @@ class MessagePreviewPresenter @Inject constructor(
private fun initOptions() {
view?.apply {
showOptions(message != null)
showOptions(
show = message != null,
isReplayable = message?.folderId != MessageFolder.SENT.id,
)
message?.let {
when (it.folderId == MessageFolder.TRASHED.id) {
true -> setDeletedOptionsLabels()

View File

@ -28,7 +28,7 @@ interface MessagePreviewView : BaseView {
fun setErrorRetryCallback(callback: () -> Unit)
fun showOptions(show: Boolean)
fun showOptions(show: Boolean, isReplayable: Boolean)
fun setDeletedOptionsLabels()

View File

@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.text.Spanned
import android.view.Menu
import android.view.MenuItem
import android.view.TouchDelegate
@ -13,11 +14,12 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.core.text.parseAsHtml
import androidx.core.text.toHtml
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.databinding.ActivitySendMessageBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.utils.dpToPx
@ -72,17 +74,32 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
override val messageSuccess: String
get() = getString(R.string.message_send_successful)
override val mailboxStudent: String
get() = getString(R.string.message_mailbox_type_student)
override val mailboxParent: String
get() = getString(R.string.message_mailbox_type_parent)
override val mailboxGuardian: String
get() = getString(R.string.message_mailbox_type_guardian)
override val mailboxEmployee: String
get() = getString(R.string.message_mailbox_type_employee)
@Suppress("UNCHECKED_CAST")
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(ActivitySendMessageBinding.inflate(layoutInflater).apply { binding = this }.root)
setContentView(
ActivitySendMessageBinding.inflate(layoutInflater).apply { binding = this }.root
)
setSupportActionBar(binding.sendMessageToolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
messageContainer = binding.sendMessageContainer
formRecipientsData = binding.sendMessageTo.addedChipItems as List<RecipientChipItem>
formSubjectValue = binding.sendMessageSubject.text.toString()
formContentValue = binding.sendMessageMessageContent.text.toString()
formContentValue =
binding.sendMessageMessageContent.text.toString().parseAsHtml().toString()
presenter.onAttachView(
view = this,
@ -110,7 +127,7 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
}
private fun onMessageContentChange(text: CharSequence?) {
formContentValue = text.toString()
formContentValue = (text as Spanned).toHtml()
presenter.onMessageContentChange()
}
@ -132,8 +149,8 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
return presenter.onUpNavigate()
}
override fun setReportingUnit(unit: ReportingUnit) {
binding.sendMessageFrom.text = unit.senderName
override fun setMailbox(mailbox: String) {
binding.sendMessageFrom.text = mailbox
}
override fun setRecipients(recipients: List<RecipientChipItem>) {
@ -165,7 +182,7 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
}
override fun setContent(content: String) {
binding.sendMessageMessageContent.setText(content)
binding.sendMessageMessageContent.setText(content.parseAsHtml())
}
override fun showMessage(text: String) {

View File

@ -1,6 +1,8 @@
package io.github.wulkanowy.ui.modules.message.send
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.logResourceStatus
@ -25,9 +27,8 @@ import javax.inject.Inject
class SendMessagePresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val messageRepository: MessageRepository,
private val reportingUnitRepository: ReportingUnitRepository,
private val mailboxRepository: MailboxRepository,
private val recipientRepository: RecipientRepository,
private val preferencesRepository: PreferencesRepository,
private val analytics: AnalyticsHelper
@ -52,20 +53,21 @@ class SendMessagePresenter @Inject constructor(
message?.let {
setSubject(
when (reply) {
true -> "Re: "
true -> "RE: "
else -> "FW: "
} + message.subject
)
if (preferencesRepository.fillMessageContent || reply != true) {
setContent(
when (reply) {
true -> "\n\n"
else -> ""
} + when (message.sender.isNotEmpty()) {
true -> "Od: ${message.sender}\n"
false -> "Do: ${message.recipient}\n"
} + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}"
)
setContent(buildString {
if (reply == true) {
append("<br><br>")
}
append("Od: ${message.sender}<br>")
append("Do: ${message.recipients}<br>")
append("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}<br><br>")
append(message.content)
})
}
}
}
@ -111,21 +113,24 @@ class SendMessagePresenter @Inject constructor(
private fun loadData(message: Message?, reply: Boolean?) {
resourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
val unit = reportingUnitRepository.getReportingUnit(student, semester.unitId)
val mailbox = mailboxRepository.getMailbox(student)
Timber.i("Loading recipients started")
val recipients = when {
unit != null -> recipientRepository.getRecipients(student, unit, 2)
else -> listOf()
}.let { createChips(it) }
val recipients = createChips(
recipients = recipientRepository.getRecipients(
student = student,
mailbox = mailbox,
type = MailboxType.EMPLOYEE,
)
)
Timber.i("Loading recipients result: Success, fetched %d recipients", recipients.size)
Timber.i("Loading message recipients started")
val messageRecipients = when {
message != null && reply == true -> recipientRepository.getMessageRecipients(
student,
message
message != null && reply == true -> recipientRepository.getMessageSender(
student = student,
message = message,
mailbox = mailbox,
)
else -> emptyList()
}.let { createChips(it) }
@ -134,7 +139,7 @@ class SendMessagePresenter @Inject constructor(
messageRecipients.size
)
Triple(unit, recipients, messageRecipients)
Triple(mailbox, recipients, messageRecipients)
}
.logResourceStatus("load recipients")
.onEach {
@ -143,19 +148,14 @@ class SendMessagePresenter @Inject constructor(
showProgress(true)
showContent(false)
}
is Resource.Success -> it.data.let { (reportingUnit, recipientChips, selectedRecipientChips) ->
is Resource.Success -> it.data.let { (mailbox, recipientChips, selectedRecipientChips) ->
view?.run {
if (reportingUnit != null) {
setReportingUnit(reportingUnit)
setRecipients(recipientChips)
if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(
selectedRecipientChips
)
showContent(true)
} else {
Timber.i("Loading recipients result: Can't find the reporting unit")
view?.showEmpty(true)
}
setMailbox(getMailboxName(mailbox))
setRecipients(recipientChips)
if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(
selectedRecipientChips
)
showContent(true)
}
}
is Resource.Error -> {
@ -171,7 +171,14 @@ class SendMessagePresenter @Inject constructor(
private fun sendMessage(subject: String, content: String, recipients: List<Recipient>) {
resourceFlow {
val student = studentRepository.getCurrentStudent()
messageRepository.sendMessage(student, subject, content, recipients)
val mailbox = mailboxRepository.getMailbox(student)
messageRepository.sendMessage(
student = student,
subject = subject,
content = content,
recipients = recipients,
mailboxId = mailbox.globalKey,
)
}.logResourceStatus("sending message").onEach {
when (it) {
is Resource.Loading -> view?.run {
@ -201,31 +208,44 @@ class SendMessagePresenter @Inject constructor(
}
private fun createChips(recipients: List<Recipient>): List<RecipientChipItem> {
fun generateCorrectSummary(recipientRealName: String): String {
val substring = recipientRealName.substringBeforeLast("-")
return when {
substring == recipientRealName -> recipientRealName
substring.indexOf("(") != -1 -> {
recipientRealName.indexOf("(")
.let { recipientRealName.substring(if (it != -1) it else 0) }
}
substring.indexOf("[") != -1 -> {
recipientRealName.indexOf("[")
.let { recipientRealName.substring(if (it != -1) it else 0) }
}
else -> recipientRealName.substringAfter("-")
}.trim()
}
return recipients.map {
RecipientChipItem(
title = it.name,
summary = generateCorrectSummary(it.realName),
title = it.userName,
summary = buildString {
getMailboxType(it.type)?.let(::append)
if (isNotBlank()) append(" ")
append("(${it.schoolShortName})")
},
recipient = it
)
}
}
private fun getMailboxName(mailbox: Mailbox): String {
return buildString {
append(mailbox.userName)
append(" - ")
append(getMailboxType(mailbox.type))
if (mailbox.type == MailboxType.PARENT) {
append(" - ")
append(mailbox.studentName)
}
append(" - ")
append("(${mailbox.schoolNameShort})")
}
}
private fun getMailboxType(type: MailboxType): String? = when (type) {
MailboxType.STUDENT -> view?.mailboxStudent
MailboxType.PARENT -> view?.mailboxParent
MailboxType.GUARDIAN -> view?.mailboxGuardian
MailboxType.EMPLOYEE -> view?.mailboxEmployee
MailboxType.UNKNOWN -> null
}
fun onMessageContentChange() {
presenterScope.launch {
messageUpdateChannel.send(Unit)
@ -263,7 +283,7 @@ class SendMessagePresenter @Inject constructor(
fun getRecipientsNames(): String {
return messageRepository.draftMessage?.recipients.orEmpty()
.joinToString { it.recipient.name }
.joinToString { it.recipient.userName }
}
fun clearDraft() {

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.message.send
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.ui.base.BaseView
interface SendMessageView : BaseView {
@ -18,9 +18,17 @@ interface SendMessageView : BaseView {
val messageSuccess: String
val mailboxStudent: String
val mailboxParent: String
val mailboxGuardian: String
val mailboxEmployee: String
fun initView()
fun setReportingUnit(unit: ReportingUnit)
fun setMailbox(mailbox: String)
fun setRecipients(recipients: List<RecipientChipItem>)

View File

@ -8,7 +8,6 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.databinding.ItemMessageBinding
import io.github.wulkanowy.databinding.ItemMessageChipsBinding
import io.github.wulkanowy.utils.toFormattedString
@ -88,12 +87,8 @@ class MessageTabAdapter @Inject constructor() :
with(holder.binding) {
val style = if (message.unread) Typeface.BOLD else Typeface.NORMAL
messageItemAuthor.run {
text = if (message.folderId == MessageFolder.SENT.id) {
message.recipient
} else {
message.sender
}
with(messageItemAuthor) {
text = message.correspondents
setTypeface(null, style)
}
messageItemSubject.run {
@ -145,7 +140,7 @@ class MessageTabAdapter @Inject constructor() :
val newItem = new[newItemPosition]
return if (oldItem is MessageTabDataItem.MessageItem && newItem is MessageTabDataItem.MessageItem) {
oldItem.message.id == newItem.message.id
oldItem.message.messageGlobalKey == newItem.message.messageGlobalKey
} else {
oldItem.viewType == newItem.viewType
}

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.ui.modules.message.tab
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@ -26,7 +26,7 @@ class MessageTabPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
private val semesterRepository: SemesterRepository,
private val mailboxRepository: MailboxRepository,
private val analytics: AnalyticsHelper
) : BasePresenter<MessageTabView>(errorHandler, studentRepository) {
@ -122,7 +122,8 @@ class MessageTabPresenter @Inject constructor(
runCatching {
val student = studentRepository.getCurrentStudent(true)
messageRepository.deleteMessages(student, messageList)
val mailbox = mailboxRepository.getMailbox(student)
messageRepository.deleteMessages(student, mailbox, messageList)
}
.onFailure(errorHandler::dispatch)
.onSuccess { view?.showMessagesDeleted() }
@ -159,7 +160,7 @@ class MessageTabPresenter @Inject constructor(
}
fun onMessageItemSelected(messageItem: MessageTabDataItem.MessageItem, position: Int) {
Timber.i("Select message ${messageItem.message.id} item (position: $position)")
Timber.i("Select message ${messageItem.message.messageGlobalKey} item (position: $position)")
if (!isActionMode) {
view?.run {
@ -206,8 +207,8 @@ class MessageTabPresenter @Inject constructor(
flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
messageRepository.getMessages(student, semester, folder, forceRefresh)
val mailbox = mailboxRepository.getMailbox(student)
messageRepository.getMessages(student, mailbox, folder, forceRefresh)
}
.logResourceStatus("load $folder message")
.onResourceData {
@ -333,7 +334,7 @@ class MessageTabPresenter @Inject constructor(
addAll(data.map { message ->
MessageTabDataItem.MessageItem(
message = message,
isSelected = messagesToDelete.any { it.id == message.id },
isSelected = messagesToDelete.any { it.messageGlobalKey == message.messageGlobalKey },
isActionMode = isActionMode
)
})
@ -345,10 +346,9 @@ class MessageTabPresenter @Inject constructor(
private fun calculateMatchRatio(message: Message, query: String): Int {
val subjectRatio = FuzzySearch.tokenSortPartialRatio(query.lowercase(), message.subject)
val senderOrRecipientRatio = FuzzySearch.tokenSortPartialRatio(
val correspondentsRatio = FuzzySearch.tokenSortPartialRatio(
query.lowercase(),
if (message.sender.isNotEmpty()) message.sender.lowercase()
else message.recipient.lowercase()
message.correspondents
)
val dateRatio = listOf(
@ -364,7 +364,7 @@ class MessageTabPresenter @Inject constructor(
return (subjectRatio.toDouble().pow(2)
+ senderOrRecipientRatio.toDouble().pow(2)
+ correspondentsRatio.toDouble().pow(2)
+ dateRatio.toDouble().pow(2) * 2
).toInt()
}

View File

@ -16,8 +16,7 @@
app:layout_constraintBottom_toTopOf="@id/sendMessageScroll"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:targetApi="lollipop" />
app:layout_constraintTop_toTopOf="parent" />
<io.github.wulkanowy.materialchipsinput.ConsumedNestedScrollView
android:id="@+id/sendMessageScroll"

View File

@ -289,6 +289,10 @@
<string name="message_move_to_trash">Move to trash</string>
<string name="message_delete_forever">Delete permanently</string>
<string name="message_delete_success">Message deleted successfully</string>
<string name="message_mailbox_type_student">student</string>
<string name="message_mailbox_type_parent">parent</string>
<string name="message_mailbox_type_guardian">guardian</string>
<string name="message_mailbox_type_employee">employee</string>
<string name="message_share">Share</string>
<string name="message_print">Print</string>
<string name="message_subject">Subject</string>
@ -300,7 +304,6 @@
<string name="message_chip_only_unread">Only unread</string>
<string name="message_chip_only_with_attachments">Only with attachments</string>
<string name="message_read">Read: %s</string>
<string name="message_read_by">Read by: %1$d of %2$d people</string>
<plurals name="message_number_item">
<item quantity="one">%1$d message</item>
<item quantity="other">%1$d messages</item>

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
@ -21,6 +23,16 @@ fun getSemesterEntity(diaryId: Int = 1, semesterId: Int = 1, start: LocalDate =
end = end
)
fun getMailboxEntity() = Mailbox(
globalKey = "v4",
fullName = "",
userName = "",
userLoginId = 0,
studentName = "",
schoolNameShort = "",
type = MailboxType.UNKNOWN,
)
fun getSemesterPojo(diaryId: Int, semesterId: Int, start: LocalDate, end: LocalDate, semesterName: Int = 1) = SdkSemester(
diaryId = diaryId,
kindergartenDiaryId = 0,

View File

@ -10,12 +10,10 @@ import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getMailboxEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
import io.github.wulkanowy.sdk.pojo.MessageDetails
import io.github.wulkanowy.sdk.pojo.Sender
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.Status
import io.github.wulkanowy.utils.status
@ -23,7 +21,6 @@ import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
@ -60,7 +57,7 @@ class MessageRepositoryTest {
private val student = getStudentEntity()
private val semester = getSemesterEntity()
private val mailbox = getMailboxEntity()
private lateinit var repository: MessageRepository
@ -80,59 +77,18 @@ class MessageRepositoryTest {
)
}
@Test
fun `get messages when read by values was changed on already read message`() = runTest {
every { messageDb.loadAll(any(), any()) } returns flow {
val dbMessage = getMessageEntity(3, "", false).apply {
unreadBy = 10
readBy = 5
isNotified = true
}
emit(listOf(dbMessage))
}
coEvery { sdk.getMessages(Folder.RECEIVED, any(), any()) } returns listOf(
getMessageDto(messageId = 3, content = "", unread = false).copy(
unreadBy = 5,
readBy = 10,
)
)
coEvery { messageDb.deleteAll(any()) } just Runs
coEvery { messageDb.insertAll(any()) } returns listOf()
repository.getMessages(
student = student,
semester = semester,
folder = MessageFolder.RECEIVED,
forceRefresh = true,
notify = true, // all new messages will be marked as not notified
).toFirstResult().dataOrNull.orEmpty()
coVerify(exactly = 1) { messageDb.deleteAll(emptyList()) }
coVerify(exactly = 1) { messageDb.insertAll(emptyList()) }
coVerify(exactly = 1) {
messageDb.updateAll(withArg {
assertEquals(1, it.size)
assertEquals(5, it.single().unreadBy)
assertEquals(10, it.single().readBy)
})
}
}
@Test
fun `get messages when fetched completely new message without notify`() = runBlocking {
every { messageDb.loadAll(any(), any()) } returns flowOf(emptyList())
coEvery { sdk.getMessages(Folder.RECEIVED, any(), any()) } returns listOf(
getMessageDto(messageId = 4, content = "Test", unread = true).copy(
unreadBy = 5,
readBy = 10,
)
coEvery { sdk.getMessages(Folder.RECEIVED, any()) } returns listOf(
getMessageDto()
)
coEvery { messageDb.deleteAll(any()) } just Runs
coEvery { messageDb.insertAll(any()) } returns listOf()
repository.getMessages(
student = student,
semester = semester,
mailbox = mailbox,
folder = MessageFolder.RECEIVED,
forceRefresh = true,
notify = false,
@ -151,7 +107,7 @@ class MessageRepositoryTest {
fun `throw error when message is not in the db`() {
val testMessage = getMessageEntity(1, "", false)
coEvery {
messageDb.loadMessageWithAttachment(1, 1)
messageDb.loadMessageWithAttachment("v4")
} throws NoSuchElementException("No message in database")
runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
@ -162,7 +118,7 @@ class MessageRepositoryTest {
val testMessage = getMessageEntity(123, "Test", false)
val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
coEvery { messageDb.loadMessageWithAttachment(1, testMessage.messageId) } returns flowOf(
coEvery { messageDb.loadMessageWithAttachment("v4") } returns flowOf(
messageWithAttachment
)
@ -174,7 +130,7 @@ class MessageRepositoryTest {
}
@Test
fun `get message when content in db is empty`() {
fun `get message when content in db is empty`() = runTest {
val testMessage = getMessageEntity(123, "", true)
val testMessageWithContent = testMessage.copy().apply { content = "Test" }
@ -182,23 +138,19 @@ class MessageRepositoryTest {
val mWaWithContent = MessageWithAttachment(testMessageWithContent, emptyList())
coEvery {
messageDb.loadMessageWithAttachment(
1,
testMessage.messageId
)
messageDb.loadMessageWithAttachment("v4")
} returnsMany listOf(flowOf(mWa), flowOf(mWaWithContent))
coEvery {
sdk.getMessageDetails(
messageId = testMessage.messageId,
folderId = 1,
read = false,
id = testMessage.realId
)
} returns MessageDetails("Test", emptyList())
sdk.getMessageDetails("v4")
} returns mockk {
every { sender } returns ""
every { recipients } returns listOf("")
every { attachments } returns listOf()
}
coEvery { messageDb.updateAll(any()) } just Runs
coEvery { messageAttachmentDao.insertAttachments(any()) } returns listOf(1)
val res = runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
val res = repository.getMessage(student, testMessage).toFirstResult()
assertEquals(null, res.errorOrNull)
assertEquals(Status.SUCCESS, res.status)
@ -211,7 +163,7 @@ class MessageRepositoryTest {
val testMessage = getMessageEntity(123, "", false)
coEvery {
messageDb.loadMessageWithAttachment(1, testMessage.messageId)
messageDb.loadMessageWithAttachment("v4")
} throws UnknownHostException()
runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
@ -222,7 +174,7 @@ class MessageRepositoryTest {
val testMessage = getMessageEntity(123, "", true)
coEvery {
messageDb.loadMessageWithAttachment(1, testMessage.messageId)
messageDb.loadMessageWithAttachment("v4")
} throws UnknownHostException()
runBlocking { repository.getMessage(student, testMessage).toList()[1] }
@ -233,42 +185,30 @@ class MessageRepositoryTest {
content: String,
unread: Boolean
) = Message(
studentId = 1,
realId = 1,
messageGlobalKey = "v4",
mailboxKey = "",
correspondents = "",
messageId = messageId,
sender = "",
senderId = 0,
recipient = "Wielu adresatów",
subject = "",
date = Instant.EPOCH,
folderId = 1,
unread = unread,
removed = false,
hasAttachments = false
).apply {
this.content = content
unreadBy = 1
readBy = 1
}
private fun getMessageDto(
messageId: Int,
content: String,
unread: Boolean,
) = io.github.wulkanowy.sdk.pojo.Message(
id = 1,
messageId = messageId,
sender = Sender("", "", 0, 0, 0, ""),
private fun getMessageDto() = io.github.wulkanowy.sdk.pojo.Message(
globalKey = "v4",
mailbox = "",
correspondents = "",
id = 4,
recipients = listOf(),
subject = "",
content = content,
date = Instant.EPOCH.atZone(ZoneOffset.UTC).toLocalDateTime(),
content = "Test",
dateZoned = Instant.EPOCH.atZone(ZoneOffset.UTC),
folderId = 1,
unread = unread,
unreadBy = 0,
readBy = 0,
removed = false,
unread = true,
hasAttachments = false,
)
}

View File

@ -1,19 +1,15 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.RecipientDao
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.getMailboxEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.MailboxType
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Before
@ -36,9 +32,30 @@ class RecipientLocalTest {
private lateinit var recipientRepository: RecipientRepository
private val remoteList = listOf(
SdkRecipient("2rPracownik", "Kowalski Jan", 3, 4, 2, "hash", "Kowalski Jan [KJ] - Pracownik (Fake123456)"),
SdkRecipient("3rPracownik", "Kowalska Karolina", 4, 4, 2, "hash", "Kowalska Karolina [KK] - Pracownik (Fake123456)"),
SdkRecipient("4rPracownik", "Krupa Stanisław", 5, 4, 1, "hash", "Krupa Stanisław [KS] - Uczeń (Fake123456)")
SdkRecipient(
mailboxGlobalKey = "2rPracownik",
userName = "Kowalski Jan",
fullName = "Kowalski Jan [KJ] - Pracownik (Fake123456)",
studentName = "",
schoolNameShort = "",
type = MailboxType.UNKNOWN,
),
SdkRecipient(
mailboxGlobalKey = "3rPracownik",
userName = "Kowalska Karolina",
fullName = "Kowalska Karolina [KK] - Pracownik (Fake123456)",
studentName = "",
schoolNameShort = "",
type = MailboxType.UNKNOWN,
),
SdkRecipient(
mailboxGlobalKey = "4rPracownik",
userName = "Krupa Stanisław",
fullName = "Krupa Stanisław [KS] - Uczeń (Fake123456)",
studentName = "",
schoolNameShort = "",
type = MailboxType.UNKNOWN,
)
)
@Before
@ -52,39 +69,61 @@ class RecipientLocalTest {
@Test
fun `load recipients when items already in database`() {
// prepare
coEvery { recipientDb.loadAll(4, 123, 7) } returnsMany listOf(
remoteList.mapToEntities(4),
remoteList.mapToEntities(4)
coEvery { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") } returnsMany listOf(
remoteList.mapToEntities("v4"),
remoteList.mapToEntities("v4")
)
coEvery { recipientDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { recipientDb.deleteAll(any()) } just Runs
// execute
val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(4, 123, "", 4, "", listOf()), 7) }
val res = runBlocking {
recipientRepository.getRecipients(
student = student,
mailbox = getMailboxEntity(),
type = io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
)
}
// verify
assertEquals(3, res.size)
coVerify { recipientDb.loadAll(4, 123, 7) }
coVerify {
recipientDb.loadAll(
type = io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
studentMailboxGlobalKey = "v4"
)
}
}
@Test
fun `load recipients when database is empty`() {
// prepare
coEvery { sdk.getRecipients(123, 7) } returns remoteList
coEvery { recipientDb.loadAll(4, 123, 7) } returnsMany listOf(
coEvery { sdk.getRecipients("v4") } returns remoteList
coEvery {
recipientDb.loadAll(
io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
"v4"
)
} returnsMany listOf(
emptyList(),
remoteList.mapToEntities(4)
remoteList.mapToEntities("v4")
)
coEvery { recipientDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { recipientDb.deleteAll(any()) } just Runs
// execute
val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(4, 123, "", 4, "", listOf()), 7) }
val res = runBlocking {
recipientRepository.getRecipients(
student = student,
mailbox = getMailboxEntity(),
type = io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
)
}
// verify
assertEquals(3, res.size)
coVerify { sdk.getRecipients(123, 7) }
coVerify { recipientDb.loadAll(4, 123, 7) }
coVerify { sdk.getRecipients("v4") }
coVerify { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") }
coVerify { recipientDb.insertAll(match { it.isEmpty() }) }
coVerify { recipientDb.deleteAll(match { it.isEmpty() }) }
}