Skip to content

Commit

Permalink
fix user icon recomposition
Browse files Browse the repository at this point in the history
  • Loading branch information
storytellerF committed Feb 1, 2025
1 parent 4ffdb97 commit 994ed30
Show file tree
Hide file tree
Showing 13 changed files with 168 additions and 122 deletions.
15 changes: 9 additions & 6 deletions backend/src/main/kotlin/com/storyteller_f/tables/Topics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}

Expand Down Expand Up @@ -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?,
Expand All @@ -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]
)
Expand Down
107 changes: 67 additions & 40 deletions backend/src/main/kotlin/com/storyteller_f/tables/Users.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<String?> = first({
it[Aids.value]
Expand All @@ -77,6 +81,20 @@ fun toFinalUserInfo(p: Pair<UserInfo, String?>, backend: Backend): Result<UserIn
}
}

fun DatabaseFactory.fillUserMedia(p: List<Pair<UserInfo, String?>>, backend: Backend): Result<List<UserInfo>> {
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
Expand All @@ -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<Pair<UserInfo, String?>?> = 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(
Expand All @@ -124,7 +140,7 @@ suspend fun DatabaseFactory.commonPaginationMemberList(
size
)
}.mapResult { pairs ->
DatabaseFactory.count {
count {
buildSearchMembersQuery(objectId, true, word)
}.map { value ->
pairs to value
Expand Down Expand Up @@ -214,38 +230,36 @@ suspend fun DatabaseFactory.isUserNotExists(pk: String): Result<Boolean> = isEmp
suspend fun DatabaseFactory.updateUser(
id: PrimaryKey,
newUser: UserInfo
): Result<Boolean> {
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()
}
}

Expand All @@ -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<PrimaryKey>, 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)
}
8 changes: 4 additions & 4 deletions cli/src/main/kotlin/com/storyteller_f/cli/AddPreset.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -452,7 +452,7 @@ class AddPreset : Subcommand("add", "add entry") {

private suspend fun addCommunity(data: List<Triple<PresetCommunity, String?, PrimaryKey>>) {
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
Expand Down Expand Up @@ -503,7 +503,7 @@ class AddPreset : Subcommand("add", "add entry") {

private suspend fun userJoinCommunity(users: List<String>, communityId: PrimaryKey) {
users.forEach {
val userId = findUserByAId(it).first()[Users.id]
val userId = findUserByAid(it).first()[Users.id]
DatabaseFactory.addCommunityJoin(userId, communityId, now()).getOrThrow()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -24,7 +21,7 @@ fun ExceptionView(throwable: Throwable) {

RichText(state = state)
} else {
Text("${throwable.status.value} ${throwable.text}")
Text("${throwable.status} ${throwable.text}")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
Expand Down Expand Up @@ -54,24 +54,22 @@ 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)
}
}
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)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ fun CustomMarkdownParagraph(
codeSpanStyle,
annotator,
inlineContentMap,
density
) {
buildAnnotatedString {
pushStyle(style.toSpanStyle())
Expand Down Expand Up @@ -499,7 +498,6 @@ private fun buildInlineContentMap(
alpha = imageData.alpha,
colorFilter = imageData.colorFilter
)
Text(now().toString(), modifier = Modifier.background(Color.White))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,49 +28,60 @@ 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)
.clickable(couldShowDialog, onClick = onClick)
.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()
}
}
Loading

0 comments on commit 994ed30

Please sign in to comment.