Skip to content

Commit

Permalink
[WIP] add bluesky dm implementaion
Browse files Browse the repository at this point in the history
  • Loading branch information
Tlaster committed Oct 21, 2024
1 parent 00c9583 commit 52453fa
Show file tree
Hide file tree
Showing 15 changed files with 628 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ internal fun DMConversationScreen(
topBar = {
FlareTopAppBar(
title = {
state.user.onSuccess {
HtmlText(
element = it.name.data,
maxLines = 1,
)
state.users.onSuccess {
// HtmlText(
// element = it.name.data,
// maxLines = 1,
// )
}
},
navigationIcon = {
Expand Down Expand Up @@ -157,7 +157,7 @@ private fun DMItem(
Alignment.CenterStart
},
) {
when (val message = item.message) {
when (val message = item.content) {
is UiDMItem.Message.Text ->
HtmlText(
element = message.text.data,
Expand Down Expand Up @@ -193,6 +193,13 @@ private fun DMItem(
MaterialTheme.colorScheme.onSurface
},
)

UiDMItem.Message.Deleted ->
Text(
text = stringResource(id = R.string.dm_deleted),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
Text(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import dev.dimension.flare.ui.component.HtmlText
import dev.dimension.flare.ui.component.RefreshContainer
import dev.dimension.flare.ui.component.ThemeWrapper
import dev.dimension.flare.ui.component.status.ListComponent
import dev.dimension.flare.ui.model.UiDMList
import dev.dimension.flare.ui.model.UiDMRoom
import dev.dimension.flare.ui.model.localizedShortTime
import dev.dimension.flare.ui.presenter.dm.DMListPresenter
import dev.dimension.flare.ui.presenter.dm.DMListState
Expand Down Expand Up @@ -118,7 +118,7 @@ private data class DMPaneNavArgs(
@Composable
private fun DMListScreen(
accountType: AccountType,
onItemClicked: (UiDMList) -> Unit,
onItemClicked: (UiDMRoom) -> Unit,
) {
val state by producePresenter("dm_list_$accountType") { presenter(accountType) }
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@
<string name="dm_list_error">Failed to load direct messages</string>
<string name="send">Send</string>
<string name="dm_send_placeholder">Send a message</string>
<string name="dm_deleted">Deleted</string>


<string name="profile_search_user_using_account">Find %1$s in %2$s using %3$s</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@ import androidx.room.RoomDatabase
import androidx.room.RoomDatabaseConstructor
import androidx.room.TypeConverters

const val CACHE_DATABASE_VERSION = 12

@Database(
entities = [
dev.dimension.flare.data.database.cache.model.DbEmoji::class,
dev.dimension.flare.data.database.cache.model.DbStatusReference::class,
dev.dimension.flare.data.database.cache.model.DbStatus::class,
dev.dimension.flare.data.database.cache.model.DbUser::class,
dev.dimension.flare.data.database.cache.model.DbPagingTimeline::class,
dev.dimension.flare.data.database.cache.model.DbMessageRoom::class,
dev.dimension.flare.data.database.cache.model.DbMessageItem::class,
dev.dimension.flare.data.database.cache.model.DbDirectMessageTimeline::class,
dev.dimension.flare.data.database.cache.model.DbMessageRoomReference::class,
],
views = [
dev.dimension.flare.data.database.cache.model.DbPagingTimelineView::class,
],
version = 11,
version = CACHE_DATABASE_VERSION,
exportSchema = false,
)
@TypeConverters(
Expand All @@ -26,6 +32,7 @@ import androidx.room.TypeConverters
dev.dimension.flare.data.database.cache.model.EmojiContentConverter::class,
dev.dimension.flare.data.database.cache.model.StatusConverter::class,
dev.dimension.flare.data.database.cache.model.UserContentConverters::class,
dev.dimension.flare.data.database.cache.model.MessageContentConverters::class,
)
@ConstructedBy(CacheDatabaseConstructor::class)
abstract class CacheDatabase : RoomDatabase() {
Expand All @@ -38,6 +45,8 @@ abstract class CacheDatabase : RoomDatabase() {
abstract fun userDao(): dev.dimension.flare.data.database.cache.dao.UserDao

abstract fun pagingTimelineDao(): dev.dimension.flare.data.database.cache.dao.PagingTimelineDao

abstract fun messageDao(): dev.dimension.flare.data.database.cache.dao.MessageDao
}

// The Room compiler generates the `actual` implementations.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package dev.dimension.flare.data.database.cache.dao

import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import dev.dimension.flare.data.database.cache.model.DbDirectMessageTimeline
import dev.dimension.flare.data.database.cache.model.DbDirectMessageTimelineWithRoom
import dev.dimension.flare.data.database.cache.model.DbMessageItem
import dev.dimension.flare.data.database.cache.model.DbMessageItemWithUser
import dev.dimension.flare.data.database.cache.model.DbMessageRoom
import dev.dimension.flare.data.database.cache.model.DbMessageRoomReference
import dev.dimension.flare.model.MicroBlogKey
import kotlinx.coroutines.flow.Flow

@Dao
interface MessageDao {
@Transaction
@Query("SELECT * FROM DbDirectMessageTimeline WHERE accountKey = :accountKey ORDER BY sortId DESC")
fun getRoomPagingSource(accountKey: MicroBlogKey): PagingSource<Int, DbDirectMessageTimelineWithRoom>

@Transaction
@Query("SELECT * FROM DbMessageItem WHERE roomKey = :roomKey ORDER BY timestamp DESC")
fun getRoomMessagesPagingSource(roomKey: MicroBlogKey): PagingSource<Int, DbMessageItemWithUser>

@Transaction
@Query("SELECT * FROM DbDirectMessageTimeline WHERE roomKey = :roomKey AND accountKey = :accountKey")
fun getRoomInfo(
roomKey: MicroBlogKey,
accountKey: MicroBlogKey,
): Flow<DbDirectMessageTimelineWithRoom?>

@Insert(onConflict = androidx.room.OnConflictStrategy.REPLACE)
suspend fun insert(items: List<DbMessageRoom>)

@Insert(onConflict = androidx.room.OnConflictStrategy.REPLACE)
suspend fun insertReferences(items: List<DbMessageRoomReference>)

@Insert(onConflict = androidx.room.OnConflictStrategy.REPLACE)
suspend fun insertMessages(items: List<DbMessageItem>)

@Insert(onConflict = androidx.room.OnConflictStrategy.REPLACE)
suspend fun insertTimeline(items: List<DbDirectMessageTimeline>)

@Query("DELETE FROM DbDirectMessageTimeline WHERE roomKey = :roomKey AND accountKey = :accountKey")
suspend fun deleteTimeline(
roomKey: MicroBlogKey,
accountKey: MicroBlogKey,
)

@Query("DELETE FROM DbMessageItem WHERE messageKey = :messageKey")
suspend fun deleteMessage(messageKey: MicroBlogKey)

@Query("SELECT * FROM DbMessageItem WHERE messageKey = :messageKey")
suspend fun getMessage(messageKey: MicroBlogKey): DbMessageItem?

@Query("SELECT * FROM DbMessageItem WHERE roomKey = :roomKey AND isLocal = 0 ORDER BY timestamp DESC")
suspend fun getLatestMessage(roomKey: MicroBlogKey): DbMessageItem?
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@ import app.bsky.feed.PostView
import app.bsky.feed.ReplyRefParentUnion
import app.bsky.notification.ListNotificationsNotification
import app.bsky.notification.ListNotificationsReason
import chat.bsky.convo.ConvoView
import chat.bsky.convo.ConvoViewLastMessageUnion
import chat.bsky.convo.MessageView
import dev.dimension.flare.data.database.cache.CacheDatabase
import dev.dimension.flare.data.database.cache.model.DbDirectMessageTimeline
import dev.dimension.flare.data.database.cache.model.DbMessageItem
import dev.dimension.flare.data.database.cache.model.DbMessageRoom
import dev.dimension.flare.data.database.cache.model.DbMessageRoomReference
import dev.dimension.flare.data.database.cache.model.DbPagingTimelineWithStatus
import dev.dimension.flare.data.database.cache.model.DbStatus
import dev.dimension.flare.data.database.cache.model.DbStatusWithUser
import dev.dimension.flare.data.database.cache.model.DbUser
import dev.dimension.flare.data.database.cache.model.MessageContent
import dev.dimension.flare.data.database.cache.model.StatusContent
import dev.dimension.flare.data.database.cache.model.UserContent
import dev.dimension.flare.data.datasource.bluesky.bskyJson
Expand All @@ -26,6 +34,41 @@ import sh.christian.ozone.api.AtUri
import sh.christian.ozone.api.model.JsonContent

internal object Bluesky {
suspend fun saveDM(
accountKey: MicroBlogKey,
database: CacheDatabase,
data: List<ConvoView>,
) {
val rooms = data.map { it.toDbMessageRoom(accountKey.host) }
val references = data.flatMap { it.toDbMessageRoomReference(accountKey.host) }
val messages =
data.mapNotNull {
it.lastMessage?.toDbMessageItem(it.toDbMessageRoom(accountKey.host).roomKey)
}
val timeline = data.map { it.toDbDirectMessageTimeline(accountKey) }
database.messageDao().insertMessages(messages)
database.messageDao().insertReferences(references)
database.messageDao().insert(rooms)
database.messageDao().insertTimeline(timeline)
}

suspend fun saveMessage(
accountKey: MicroBlogKey,
roomKey: MicroBlogKey,
database: CacheDatabase,
data: List<MessageView>,
) {
val room =
DbMessageRoom(
roomKey = roomKey,
platformType = PlatformType.Bluesky,
messageKey = null,
)
val messages = data.map { it.toDbMessageItem(roomKey) }
database.messageDao().insertMessages(messages)
database.messageDao().insert(room)
}

suspend fun saveFeed(
accountKey: MicroBlogKey,
pagingKey: String,
Expand Down Expand Up @@ -438,3 +481,64 @@ internal fun ProfileViewDetailed.toDbUser(host: String) =
host = host,
content = UserContent.Bluesky(this),
)

private fun ConvoView.toDbDirectMessageTimeline(accountKey: MicroBlogKey): DbDirectMessageTimeline {
val roomKey = toDbMessageRoom(accountKey.host).roomKey
return DbDirectMessageTimeline(
accountKey = accountKey,
roomKey = roomKey,
sortId =
lastMessage?.toDbMessageItem(roomKey)?.timestamp
?: 0L,
)
}

private fun ConvoView.toDbMessageRoom(host: String) =
DbMessageRoom(
roomKey = MicroBlogKey(id = id, host = host),
platformType = PlatformType.Bluesky,
messageKey =
when (val message = lastMessage) {
is ConvoViewLastMessageUnion.MessageView -> MicroBlogKey(id = message.value.id, host = host)
is ConvoViewLastMessageUnion.DeletedMessageView -> MicroBlogKey(id = message.value.id, host = host)
null -> null
},
)

private fun ConvoView.toDbMessageRoomReference(host: String): List<DbMessageRoomReference> {
val roomKey = toDbMessageRoom(host).roomKey
return members.map {
DbMessageRoomReference(
roomKey = roomKey,
userKey = MicroBlogKey(id = it.did.did, host = host),
)
}
}

private fun ConvoViewLastMessageUnion.toDbMessageItem(roomKey: MicroBlogKey) =
when (this) {
is ConvoViewLastMessageUnion.MessageView -> toDbMessageItem(roomKey)
is ConvoViewLastMessageUnion.DeletedMessageView -> toDbMessageItem(roomKey)
}

private fun ConvoViewLastMessageUnion.MessageView.toDbMessageItem(roomKey: MicroBlogKey) =
with(value) {
DbMessageItem(
messageKey = MicroBlogKey(id = id, host = roomKey.host),
roomKey = roomKey,
userKey = MicroBlogKey(id = sender.did.did, host = roomKey.host),
timestamp = sentAt.toEpochMilliseconds(),
content = MessageContent.Bluesky.Message(this),
)
}

private fun ConvoViewLastMessageUnion.DeletedMessageView.toDbMessageItem(roomKey: MicroBlogKey) =
with(value) {
DbMessageItem(
messageKey = MicroBlogKey(id = id, host = roomKey.host),
roomKey = roomKey,
userKey = MicroBlogKey(id = sender.did.did, host = roomKey.host),
timestamp = sentAt.toEpochMilliseconds(),
content = MessageContent.Bluesky.Deleted(this),
)
}
Loading

0 comments on commit 52453fa

Please sign in to comment.