From 994ed303e0db9f82dc30c1a1fcf66c47aa236eb2 Mon Sep 17 00:00:00 2001 From: storytellerF <34095089+storytellerF@users.noreply.github.com> Date: Sat, 1 Feb 2025 11:14:25 +0800 Subject: [PATCH] fix user icon recomposition --- .../kotlin/com/storyteller_f/tables/Topics.kt | 15 ++- .../kotlin/com/storyteller_f/tables/Users.kt | 107 +++++++++++------- .../kotlin/com/storyteller_f/cli/AddPreset.kt | 8 +- .../a/app/compontents/ExceptionView.kt | 5 +- .../a/app/compontents/TopicCell.kt | 12 +- .../a/app/compontents/TopicContentField.kt | 2 - .../a/app/compontents/UserIcon.kt | 41 ++++--- .../com/storyteller_f/a/app/model/Build.kt | 15 ++- .../com/storyteller_f/a/app/model/Models.kt | 15 ++- .../a/app/pages/room/RoomPage.kt | 1 - .../a/server/service/Community.kt | 2 +- .../storyteller_f/a/server/service/Topic.kt | 36 +++--- .../storyteller_f/shared/model/TopicInfo.kt | 31 ++--- 13 files changed, 168 insertions(+), 122 deletions(-) diff --git a/backend/src/main/kotlin/com/storyteller_f/tables/Topics.kt b/backend/src/main/kotlin/com/storyteller_f/tables/Topics.kt index d468949..3dec0ed 100644 --- a/backend/src/main/kotlin/com/storyteller_f/tables/Topics.kt +++ b/backend/src/main/kotlin/com/storyteller_f/tables/Topics.kt @@ -97,7 +97,8 @@ fun Topic.toTopicInfo(commentCount: Long = 0, hasComment: Boolean = false, react reactionCount = reactionCount, hasComment = hasComment, isPrivate = false, - lastModifiedTime = now() + lastModifiedTime = now(), + extension = null ) } @@ -167,6 +168,8 @@ suspend fun commonPaginationTopics( } } +class TopicSearchTuple(val topicInfo: Topic, val commentCount: Long, val hasComment: Boolean, val reactionCount: Long) + suspend fun commonTopics( uid: PrimaryKey?, preTopicId: PrimaryKey?, @@ -179,14 +182,14 @@ suspend fun commonTopics( val t2 = Topics.alias("t2") val reactionComment = Reactions.id.countDistinct() val containExpression = topicAuthorContains(uid, t2) - val commentCount = t2[Topics.id].countDistinct() - val baseSelection = Topics.fields + commentCount + reactionComment + val commentCountColumn = t2[Topics.id].countDistinct() + val baseSelection = Topics.fields + commentCountColumn + reactionComment return DatabaseFactory.mapQuery({ - data1.toTopicInfo(data2, data3, data4) + topicInfo.toTopicInfo(commentCount, hasComment, reactionCount) }, { - Tuple4( + TopicSearchTuple( wrapRow(it), - it[commentCount], + it[commentCountColumn], if (containExpression != null) (it[containExpression] == 1L) else false, it[reactionComment] ) diff --git a/backend/src/main/kotlin/com/storyteller_f/tables/Users.kt b/backend/src/main/kotlin/com/storyteller_f/tables/Users.kt index c7a383f..4f465cf 100644 --- a/backend/src/main/kotlin/com/storyteller_f/tables/Users.kt +++ b/backend/src/main/kotlin/com/storyteller_f/tables/Users.kt @@ -2,6 +2,7 @@ package com.storyteller_f.tables import com.storyteller_f.* import com.storyteller_f.shared.model.AMEDIA_BUCKET +import com.storyteller_f.shared.model.MediaInfo import com.storyteller_f.shared.model.UserInfo import com.storyteller_f.shared.type.ObjectType import com.storyteller_f.shared.type.PrimaryKey @@ -52,11 +53,14 @@ class User( } } -fun findUserByAId(aid: String): Query { - return Users.join(Aids, JoinType.LEFT, Users.id, Aids.objectId).selectAll().where { - Aids.value eq aid +fun findUserByAid(aid: String) = Users.join(Aids, JoinType.LEFT, Users.id, Aids.objectId).selectAll().where { + Aids.value eq aid +}.limit(1) + +private fun findUserById(it: PrimaryKey): Query = + Users.join(Aids, JoinType.LEFT, Users.id, Aids.objectId).selectAll().where { + Users.id eq it }.limit(1) -} suspend fun DatabaseFactory.getUserAid(id: PrimaryKey): Result = first({ it[Aids.value] @@ -77,6 +81,20 @@ fun toFinalUserInfo(p: Pair, backend: Backend): Result>, backend: Backend): Result> { + val userInfos = p.map { + it.first + } + val icons = p.map { + it.second + } + return backend.mediaService.get(AMEDIA_BUCKET, icons).map { value -> + value.mapIndexed { index, avatar -> + userInfos[index].copy(avatar = avatar) + } + } +} + suspend fun DatabaseFactory.getUserByAid( aid: String, backend: Backend @@ -94,15 +112,13 @@ suspend fun DatabaseFactory.getUser( suspend fun DatabaseFactory.getRawUserByAid(aid: String) = first({ toUserInfo() to icon }, User::wrapRow) { - findUserByAId(aid) + findUserByAid(aid) } suspend fun DatabaseFactory.getRawUserById(it: PrimaryKey): Result?> = first({ toUserInfo() to icon }, User::wrapRow) { - Users.join(Aids, JoinType.LEFT, Users.id, Aids.objectId).selectAll().where { - Users.id eq it - } + findUserById(it) } suspend fun DatabaseFactory.commonPaginationMemberList( @@ -124,7 +140,7 @@ suspend fun DatabaseFactory.commonPaginationMemberList( size ) }.mapResult { pairs -> - DatabaseFactory.count { + count { buildSearchMembersQuery(objectId, true, word) }.map { value -> pairs to value @@ -214,38 +230,36 @@ suspend fun DatabaseFactory.isUserNotExists(pk: String): Result = isEmp suspend fun DatabaseFactory.updateUser( id: PrimaryKey, newUser: UserInfo -): Result { - return dbQuery { - listOf({ - val avatar = newUser.avatar?.item?.name - if (newUser.nickname.isNotEmpty() || avatar?.isNotEmpty() == true) { - Users.update({ - Users.id eq id - }) { - if (newUser.nickname.isNotEmpty()) { - it[nickname] = newUser.nickname - } - if (avatar?.isNotEmpty() == true) { - it[icon] = avatar - } - } > 0 - } else { - true - } - }, { - val aid = newUser.aid - if (!aid.isNullOrBlank()) { - Aids.upsert(Aids.objectId) { - it[objectId] = id - it[value] = aid - it[objectType] = ObjectType.USER - }.insertedCount > 0 - } else { - true - } - }).all { - it() +) = dbQuery { + listOf({ + val avatar = newUser.avatar?.item?.name + if (newUser.nickname.isNotEmpty() || avatar?.isNotEmpty() == true) { + Users.update({ + Users.id eq id + }) { + if (newUser.nickname.isNotEmpty()) { + it[nickname] = newUser.nickname + } + if (avatar?.isNotEmpty() == true) { + it[icon] = avatar + } + } > 0 + } else { + true } + }, { + val aid = newUser.aid + if (!aid.isNullOrBlank()) { + Aids.upsert(Aids.objectId) { + it[objectId] = id + it[value] = aid + it[objectType] = ObjectType.USER + }.insertedCount > 0 + } else { + true + } + }).all { + it() } } @@ -270,3 +284,16 @@ suspend fun DatabaseFactory.getUserAuthDataBy(predicate: SqlExpressionBuilder.() }) { Users.select(listOf(Users.publicKey, Users.id)).where(predicate) } + +suspend fun DatabaseFactory.getUsersByIds(ids: List, backend: Backend) = mapQuery({ + toUserInfo() to icon +}, User::wrapRow) { + Users + .join(Aids, JoinType.LEFT, Users.id, Aids.objectId) + .select(Users.fields + Aids.value) + .where { + Users.id inList ids + } +}.mapResult { + fillUserMedia(it, backend) +} \ No newline at end of file diff --git a/cli/src/main/kotlin/com/storyteller_f/cli/AddPreset.kt b/cli/src/main/kotlin/com/storyteller_f/cli/AddPreset.kt index 6b9b8e2..1d24a82 100644 --- a/cli/src/main/kotlin/com/storyteller_f/cli/AddPreset.kt +++ b/cli/src/main/kotlin/com/storyteller_f/cli/AddPreset.kt @@ -95,7 +95,7 @@ class AddPreset : Subcommand("add", "add entry") { val userMap = l.flatMap { it.users + it.admin }.distinct().map { - User.wrapRow(findUserByAId(it).first()) + User.wrapRow(findUserByAid(it).first()) }.associateBy { it.aid } val communityMap = l.mapNotNull { it.community @@ -137,7 +137,7 @@ class AddPreset : Subcommand("add", "add entry") { val userList = data.map { it.author }.distinct().map { - User.wrapRow(findUserByAId(it).first()) + User.wrapRow(findUserByAid(it).first()) }.associateBy { it.aid!! } data.groupBy { it.community != null @@ -452,7 +452,7 @@ class AddPreset : Subcommand("add", "add entry") { private suspend fun addCommunity(data: List>) { DatabaseFactory.dbQuery { - val systemId = findUserByAId("System").first()[Users.id] + val systemId = findUserByAid("System").first()[Users.id] Communities.batchInsert(data) { (it, communityIcon, id) -> this[Communities.id] = id @@ -503,7 +503,7 @@ class AddPreset : Subcommand("add", "add entry") { private suspend fun userJoinCommunity(users: List, communityId: PrimaryKey) { users.forEach { - val userId = findUserByAId(it).first()[Users.id] + val userId = findUserByAid(it).first()[Users.id] DatabaseFactory.addCommunityJoin(userId, communityId, now()).getOrThrow() } } diff --git a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/ExceptionView.kt b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/ExceptionView.kt index 99a147b..de7cfef 100644 --- a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/ExceptionView.kt +++ b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/ExceptionView.kt @@ -1,11 +1,8 @@ package com.storyteller_f.a.app.compontents -import androidx.compose.foundation.layout.height import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp import com.mohamedrejeb.richeditor.model.rememberRichTextState import com.mohamedrejeb.richeditor.ui.material3.RichText import com.storyteller_f.a.client_lib.ServerErrorException @@ -24,7 +21,7 @@ fun ExceptionView(throwable: Throwable) { RichText(state = state) } else { - Text("${throwable.status.value} ${throwable.text}") + Text("${throwable.status} ${throwable.text}") } } diff --git a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/TopicCell.kt b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/TopicCell.kt index c0d86d4..a28224d 100644 --- a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/TopicCell.kt +++ b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/TopicCell.kt @@ -26,7 +26,7 @@ fun TopicCell( showAvatar: Boolean = true ) { val author = info.author - val authorViewModel = createUserViewModel(author) + val authorViewModel = createUserViewModel(author, info.extension?.authorInfo) val sheetState = rememberModalBottomSheetState() var showBottomSheet by remember { mutableStateOf(false) } @@ -54,12 +54,10 @@ fun TopicCellInternal( val topicId = topicInfo.id val appNav = LocalAppNav.current Column( - verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier.clip(RoundedCornerShape(8.dp)).clickable { appNav.gotoTopic(topicId) - }.padding(horizontal = 8.dp) + }.padding(8.dp) ) { - val avatarSize = 40.dp if (showAvatar) { UserCell(authorInfo, true) { appNav.gotoUser(it) @@ -67,11 +65,11 @@ fun TopicCellInternal( } Column( if (contentAlignAvatar) { - Modifier.padding(horizontal = 8.dp).padding(bottom = 12.dp) + Modifier.padding(horizontal = 8.dp) } else { - Modifier.fillMaxWidth().padding(start = avatarSize + 8.dp, end = 8.dp) + Modifier.fillMaxWidth().padding(start = 48.dp, end = 8.dp) .background(MaterialTheme.colorScheme.surfaceContainerHigh, RoundedCornerShape(8.dp)) - .padding(12.dp) + .padding(horizontal = 12.dp).padding(top = 8.dp,bottom = 12.dp) }, verticalArrangement = Arrangement.spacedBy(8.dp) ) { diff --git a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/TopicContentField.kt b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/TopicContentField.kt index da4f0e8..64e6fc5 100644 --- a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/TopicContentField.kt +++ b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/TopicContentField.kt @@ -164,7 +164,6 @@ fun CustomMarkdownParagraph( codeSpanStyle, annotator, inlineContentMap, - density ) { buildAnnotatedString { pushStyle(style.toSpanStyle()) @@ -499,7 +498,6 @@ private fun buildInlineContentMap( alpha = imageData.alpha, colorFilter = imageData.colorFilter ) - Text(now().toString(), modifier = Modifier.background(Color.White)) } } } diff --git a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/UserIcon.kt b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/UserIcon.kt index bd3128c..44379c5 100644 --- a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/UserIcon.kt +++ b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/compontents/UserIcon.kt @@ -28,33 +28,44 @@ import com.storyteller_f.shared.model.UserInfo @Composable fun UserIcon(userInfo: UserInfo?, couldShowDialog: Boolean = true) { - val appNav = LocalAppNav.current - val user = LoginViewModel.user.collectAsState() - val isMe = user.value?.id == userInfo?.id + val meInfo = LoginViewModel.user.collectAsState() + val isMe = meInfo.value?.id == userInfo?.id var showMyDialog by remember { mutableStateOf(false) } - val size = 40.dp + val url = userInfo?.avatar?.url + UserIconInternal(isMe, couldShowDialog, url) { -> + showMyDialog = true + } + UserDialog(userInfo, showMyDialog) { + showMyDialog = false + } +} + +@Composable +fun UserIconInternal(isMe: Boolean, couldShowDialog: Boolean, url: String?, showDialog: () -> Unit) { + val appNav = LocalAppNav.current + val showDialog by rememberUpdatedState(showDialog) val onClick = { - if (isMe && userInfo == null) { + if (isMe) { appNav.gotoLogin() } else if (couldShowDialog) { - showMyDialog = true + showDialog() } } - val url = userInfo?.avatar?.url + val size = 40.dp val modifier = if (isMe) Modifier.testTag("me") else Modifier if (url != null) { AsyncImage( globalLoader(url), - contentDescription = "${userInfo.nickname}'s avatar", + contentDescription = "avatar", modifier = modifier.size(size).clip(CircleShape).clickable(couldShowDialog, onClick = onClick), contentScale = ContentScale.Crop ) } else { Image( Icons.Default.AccountCircle, - contentDescription = "default avatar", + contentDescription = "avatar", modifier = modifier.size(size) .clip(CircleShape) .background(MaterialTheme.colorScheme.surfaceVariant, CircleShape) @@ -62,15 +73,15 @@ fun UserIcon(userInfo: UserInfo?, couldShowDialog: Boolean = true) { .padding(size / 5) ) } - UserDialog(userInfo, showMyDialog) { - showMyDialog = false - } } @Composable fun globalLoader(url: String): ImageRequest { val client = LocalClient.current - return ImageRequest.Builder(LocalPlatformContext.current).data(url).crossfade(true).fetcherFactory( - KtorNetworkFetcherFactory(client) - ).build() + val platformContext = LocalPlatformContext.current + return remember(url) { + ImageRequest.Builder(platformContext).data(url).crossfade(true).fetcherFactory( + KtorNetworkFetcherFactory(client) + ).build() + } } diff --git a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/model/Build.kt b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/model/Build.kt index 449112a..26a99df 100644 --- a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/model/Build.kt +++ b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/model/Build.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import com.storyteller_f.a.app.common.viewModel import com.storyteller_f.a.app.pages.search.SearchScope import com.storyteller_f.shared.model.RoomInfo +import com.storyteller_f.shared.model.UserInfo import com.storyteller_f.shared.obj.JoinStatusSearch import com.storyteller_f.shared.type.DEFAULT_PRIMARY_KEY import com.storyteller_f.shared.type.ObjectType @@ -212,10 +213,9 @@ fun createUserViewModel(userAid: String) = viewModel(keys = listOf("user", userA } @Composable -fun createUserViewModel(userId: PrimaryKey) = - viewModel(keys = listOf("user", userId)) { - UserViewModel(userId, it) - } +fun createUserViewModel(userId: PrimaryKey, init: UserInfo? = null) = viewModel(keys = listOf("user", userId)) { + UserViewModel(userId, it, init) +} @Composable fun createWorldViewModel() = viewModel(keys = listOf("world")) { @@ -223,7 +223,6 @@ fun createWorldViewModel() = viewModel(keys = listOf("world")) { } @Composable -fun createReactionsViewModel(objectId: PrimaryKey) = - viewModel(keys = listOf("reactions", objectId)) { - ReactionsViewModel(objectId, it) - } +fun createReactionsViewModel(objectId: PrimaryKey) = viewModel(keys = listOf("reactions", objectId)) { + ReactionsViewModel(objectId, it) +} diff --git a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/model/Models.kt b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/model/Models.kt index e314fb5..221aa07 100644 --- a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/model/Models.kt +++ b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/model/Models.kt @@ -302,19 +302,18 @@ class MediaListViewModel(private val objectId: PrimaryKey, private val objectTyp override suspend fun loadInternal() = client.getMediaList(objectId, objectType) } -class UserViewModel(private val requestInfo: suspend HttpClient.() -> Result, client: HttpClient) : - SimpleViewModel( - client - ) { - constructor(userId: PrimaryKey, client: HttpClient) : this({ +class UserViewModel(private val requestInfo: suspend HttpClient.() -> Result, client: HttpClient, val init: UserInfo? = null) : + SimpleViewModel(client) { + constructor(userId: PrimaryKey, client: HttpClient, init: UserInfo? = null) : this({ getUserInfo(userId) - }, client) + }, client, init) - constructor(userAid: String, client: HttpClient) : this({ + constructor(userAid: String, client: HttpClient, init: UserInfo? = null) : this({ getUserInfoByAid(userAid) - }, client) + }, client, init) init { + handler.data.value = init load() viewModelScope.launch { bus.collect { diff --git a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/pages/room/RoomPage.kt b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/pages/room/RoomPage.kt index f9b270e..1a5d9ba 100644 --- a/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/pages/room/RoomPage.kt +++ b/composeApp/src/commonMain/kotlin/com/storyteller_f/a/app/pages/room/RoomPage.kt @@ -129,7 +129,6 @@ private fun RoomPageInternal( false, next?.author != info.author ) - Spacer(modifier = Modifier.height(10.dp)) } } } diff --git a/server/src/main/kotlin/com/storyteller_f/a/server/service/Community.kt b/server/src/main/kotlin/com/storyteller_f/a/server/service/Community.kt index d2de651..ad8ba7d 100644 --- a/server/src/main/kotlin/com/storyteller_f/a/server/service/Community.kt +++ b/server/src/main/kotlin/com/storyteller_f/a/server/service/Community.kt @@ -181,7 +181,7 @@ suspend fun getCommunityTopicList( it.id } backend.topicSearchService.getDocuments(ids).mapResult { list -> - processMediaLink(backend, data, list).map { + processMediaAndAuthor(backend, data, list).map { PaginationResult(it, count) } } diff --git a/server/src/main/kotlin/com/storyteller_f/a/server/service/Topic.kt b/server/src/main/kotlin/com/storyteller_f/a/server/service/Topic.kt index 946b84c..3c55c9d 100644 --- a/server/src/main/kotlin/com/storyteller_f/a/server/service/Topic.kt +++ b/server/src/main/kotlin/com/storyteller_f/a/server/service/Topic.kt @@ -67,7 +67,7 @@ suspend fun addTopicAtCommunity(uid: PrimaryKey, backend: Backend, newTopic: New DatabaseFactory.saveTopic(topic, backend, content, rootId, rootType, newTopic, uid).mapResult { backend.topicSearchService.getDocuments(listOf(newId)).mapResult { documents -> val topicInfo = topic.toTopicInfo() - processMediaLink(backend, listOf(topicInfo), documents).map { + processMediaAndAuthor(backend, listOf(topicInfo), documents).map { it.firstOrNull() } } @@ -223,7 +223,7 @@ suspend fun getTopic( getCommonTopic(topicId, uid).mapResultNotNull { info -> when { !isPrivate -> backend.topicSearchService.getDocuments(listOf(topicId)).mapResult { value -> - processMediaLink(backend, listOf(info), value).map { + processMediaAndAuthor(backend, listOf(info), value).map { it.first() } } @@ -273,7 +273,7 @@ suspend fun getTopics( backend.topicSearchService.getDocuments(data.map { it.id }).mapResult { documents -> - processMediaLink(backend, data, documents) + processMediaAndAuthor(backend, data, documents) }.map { topicContents -> PaginationResult(topicContents, count) } @@ -413,14 +413,14 @@ suspend fun searchPublicTopics( commonTopics(uid, null, nextTopicId, size, search.parent.fillHasCommented) { Topics.id inList ids }.mapResult { infos -> - processMediaLink(backend, infos, list).map { + processMediaAndAuthor(backend, infos, list).map { PaginationResult(it, total) } } } } -fun processMediaLink( +suspend fun processMediaAndAuthor( backend: Backend, infos: List, documentList: List @@ -440,22 +440,32 @@ fun processMediaLink( it.first } return backend.mediaService.get(AMEDIA_BUCKET, mediaNameList).map { mediaUrls -> - val mediaInfoMap = mediaUrls.mapIndexedNotNull { index, url -> - url?.let { - mediaNameList[index] to it - } - }.associateBy { - it.first + val mediaInfoMap = mediaUrls.filterNotNull().mapIndexed { index, url -> + mediaNameList[index] to url + }.associate { + it.first to it.second } infos.map { info -> documentMap[info.id]?.let { document -> val m = mediaMap[document.id]?.mapNotNull { val mediaName = it.second - mediaInfoMap[mediaName]?.second + mediaInfoMap[mediaName] }.orEmpty() info.copy(content = TopicContent.Plain(document.content, m)) } ?: info } + }.mapResult { infos -> + val ids = infos.map { + it.author + } + DatabaseFactory.getUsersByIds(ids, backend).map { users -> + val userMap = users.associate { + it.id to it + } + infos.map { + it.copy(extension = TopicInfo.Extension(userMap[it.author])) + } + } } } @@ -491,7 +501,7 @@ suspend fun recommendTopics( commonTopics(uid, null, nextTopicId, size, fillHasCommented) { Topics.id inList ids }.mapResult { infos -> - processMediaLink(backend, infos, list).map { + processMediaAndAuthor(backend, infos, list).map { PaginationResult(it, total) } } diff --git a/shared/src/commonMain/kotlin/com/storyteller_f/shared/model/TopicInfo.kt b/shared/src/commonMain/kotlin/com/storyteller_f/shared/model/TopicInfo.kt index 3015776..56d3e2c 100644 --- a/shared/src/commonMain/kotlin/com/storyteller_f/shared/model/TopicInfo.kt +++ b/shared/src/commonMain/kotlin/com/storyteller_f/shared/model/TopicInfo.kt @@ -24,25 +24,30 @@ data class TopicInfo( val hasComment: Boolean, val isPrivate: Boolean, val lastModifiedTime: LocalDateTime?, + val extension: Extension? ) : Identifiable { companion object { val EMPTY = TopicInfo( - DEFAULT_PRIMARY_KEY, - TopicContent.Nil, - DEFAULT_PRIMARY_KEY, - DEFAULT_PRIMARY_KEY, - ObjectType.TOPIC, - DEFAULT_PRIMARY_KEY, - ObjectType.TOPIC, - false, - now(), - 0, - 0, - false, + id = DEFAULT_PRIMARY_KEY, + content = TopicContent.Nil, + author = DEFAULT_PRIMARY_KEY, + rootId = DEFAULT_PRIMARY_KEY, + rootType = ObjectType.TOPIC, + parentId = DEFAULT_PRIMARY_KEY, + parentType = ObjectType.TOPIC, + hasJoined = false, + createdTime = now(), + commentCount = 0, + reactionCount = 0, + hasComment = false, isPrivate = false, - lastModifiedTime = now() + lastModifiedTime = now(), + extension = Extension(UserInfo.EMPTY) ) } + + @Serializable + data class Extension(val authorInfo: UserInfo?) } @Serializable