Skip to content
This repository has been archived by the owner on Jul 6, 2024. It is now read-only.

Commit

Permalink
Use ui-avatars.com to generate user avatars
Browse files Browse the repository at this point in the history
  • Loading branch information
mklkj committed Feb 19, 2024
1 parent 4106182 commit dac6a1b
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 53 deletions.
12 changes: 12 additions & 0 deletions composeApp/src/commonMain/sqldelight/migrations/7.sqm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import kotlinx.uuid.UUID;

DROP TABLE Contacts;
CREATE TABLE Contacts (
id TEXT AS UUID NOT NULL PRIMARY KEY,
userId TEXT AS UUID NOT NULL,
contactUserId TEXT AS UUID NOT NULL,
avatarUrl TEXT NOT NULL,
firstName TEXT NOT NULL,
lastName TEXT NOT NULL,
username TEXT NOT NULL
);
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@ import io.github.mklkj.kommunicator.data.dao.tables.UsersTable
import io.github.mklkj.kommunicator.data.models.ChatParticipantEntity
import io.github.mklkj.kommunicator.data.models.ParticipantReadEntity
import io.github.mklkj.kommunicator.data.models.UserPushTokenEntity
import io.github.mklkj.kommunicator.utils.AvatarHelper
import io.github.mklkj.kommunicator.utils.dbQuery
import io.github.mklkj.kommunicator.utils.md5
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.uuid.UUID
import org.jetbrains.exposed.sql.JoinType
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import org.koin.core.annotation.Singleton

@Singleton
class ChatParticipantsDao {
class ChatParticipantsDao(
private val avatarHelper: AvatarHelper,
) {

private fun resultRowToParticipant(row: ResultRow): ChatParticipantEntity {
return ChatParticipantEntity(
Expand All @@ -31,7 +33,11 @@ class ChatParticipantsDao {
username = row[UsersTable.username],
userFirstName = row[UsersTable.firstName],
userLastName = row[UsersTable.lastName],
userAvatarUrl = "https://gravatar.com/avatar/${md5(row[UsersTable.email])}",
userAvatarUrl = avatarHelper.getUserAvatar(
firstName = row[UsersTable.firstName],
lastName = row[UsersTable.lastName],
customName = row[ChatParticipantsTable.customName],
),
readAt = row[ChatParticipantsTable.readAt],
)
}
Expand All @@ -51,9 +57,8 @@ class ChatParticipantsDao {

suspend fun getChatParticipantId(chatId: UUID, userId: UUID): UUID = dbQuery {
ChatParticipantsTable
.select {
(ChatParticipantsTable.chatId eq chatId) and (ChatParticipantsTable.userId eq userId)
}
.selectAll()
.where { (ChatParticipantsTable.chatId eq chatId) and (ChatParticipantsTable.userId eq userId) }
.map { it[ChatParticipantsTable.id] }
.first()
}
Expand All @@ -66,7 +71,8 @@ class ChatParticipantsDao {
otherColumn = UsersTable.id,
joinType = JoinType.LEFT,
)
.select { ChatParticipantsTable.chatId eq chatId }
.selectAll()
.where { ChatParticipantsTable.chatId eq chatId }
.limit(15)
// todo: pagination
.map(::resultRowToParticipant)
Expand All @@ -82,7 +88,8 @@ class ChatParticipantsDao {
otherColumn = UserPushTokensTable.userId,
joinType = JoinType.LEFT
)
.select { (ChatParticipantsTable.chatId eq chatId) and (UserPushTokensTable.token.isNotNull()) }
.selectAll()
.where { (ChatParticipantsTable.chatId eq chatId) and (UserPushTokensTable.token.isNotNull()) }
.map {
UserPushTokenEntity(
userId = it[ChatParticipantsTable.userId],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@ package io.github.mklkj.kommunicator.data.repository
import io.github.mklkj.kommunicator.data.dao.ContactsDao
import io.github.mklkj.kommunicator.data.models.Contact
import io.github.mklkj.kommunicator.data.models.ContactEntity
import io.github.mklkj.kommunicator.utils.md5
import io.github.mklkj.kommunicator.utils.AvatarHelper
import kotlinx.uuid.UUID
import org.koin.core.annotation.Singleton

@Singleton
class ContactRepository(
private val contactsDao: ContactsDao,
private val avatarHelper: AvatarHelper,
) {

suspend fun getContacts(userId: UUID): List<Contact> {
return contactsDao.getContacts(userId).map {
Contact(
id = it.id,
contactUserId = it.contactUserId,
avatarUrl = "https://gravatar.com/avatar/${md5(it.email)}",
avatarUrl = avatarHelper.getUserAvatar(it.firstName, it.lastName),
firstName = it.firstName,
lastName = it.lastName,
username = it.username,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import io.github.mklkj.kommunicator.data.models.ChatParticipant
import io.github.mklkj.kommunicator.data.models.ChatParticipantEntity
import io.github.mklkj.kommunicator.data.models.Message
import io.github.mklkj.kommunicator.data.repository.ChatRepository
import io.github.mklkj.kommunicator.utils.md5
import io.github.mklkj.kommunicator.utils.AvatarHelper
import kotlinx.uuid.UUID
import org.koin.core.annotation.Singleton

@Singleton
class ChatService(
private val chatRepository: ChatRepository,
private val avatarHelper: AvatarHelper,
) {

suspend fun addChat(request: ChatCreateRequest): UUID {
Expand All @@ -33,28 +34,35 @@ class ChatService(
val participants = chatRepository.getParticipants(chatId)
val notCurrentUserParticipants = participants.filterNot { it.userId == userId }

val chatName = chat.customName.takeIf { !it.isNullOrBlank() } ?: buildString {
when (notCurrentUserParticipants.size) {
1 -> notCurrentUserParticipants.single().let {
append(it.userFirstName)
append(" ")
append(it.userLastName)
}

else -> {
val names = notCurrentUserParticipants.joinToString(", ") {
"${it.userFirstName} ${it.userLastName}"
}
append(names)
}
}
}

return Chat(
id = chat.id,
avatarUrl = when (notCurrentUserParticipants.size) {
1 -> "https://gravatar.com/avatar/${md5(notCurrentUserParticipants.first().email)}"
else -> "https://i.pravatar.cc/256?u=${chat.id}"
},
customName = chat.customName.takeIf { !it.isNullOrBlank() } ?: buildString {
when (notCurrentUserParticipants.size) {
1 -> notCurrentUserParticipants.single().let {
append(it.userFirstName)
append(" ")
append(it.userLastName)
}
1 -> avatarHelper.getUserAvatar(
firstName = notCurrentUserParticipants.first().userFirstName,
lastName = notCurrentUserParticipants.first().userLastName,
customName = notCurrentUserParticipants.first().customName,
)

else -> {
val names = notCurrentUserParticipants.joinToString(", ") {
"${it.userFirstName} ${it.userLastName}"
}
append(names)
}
}
else -> avatarHelper.getGroupAvatar(chatName)
},
customName = chatName,
lastMessage = null,
participants = participants.map {
ChatParticipant(
Expand All @@ -73,42 +81,53 @@ class ChatService(
suspend fun getChats(userId: UUID): List<Chat> {
return chatRepository.getChats(userId).map { chat ->
val notCurrentUserParticipants = chat.participants.filterNot { it.userId == userId }

val chatName = chat.customName.takeIf { !it.isNullOrBlank() } ?: buildString {
when (notCurrentUserParticipants.size) {
1 -> {
append(notCurrentUserParticipants.single().firstName)
append(" ")
append(notCurrentUserParticipants.single().lastName)
}

else -> {
val names = notCurrentUserParticipants.joinToString(", ") {
"${it.firstName} ${it.lastName}"
}
append(names)
}
}
}
Chat(
id = chat.id,
avatarUrl = when (notCurrentUserParticipants.size) {
1 -> "https://gravatar.com/avatar/${md5(notCurrentUserParticipants.first().email)}"
else -> "https://i.pravatar.cc/256?u=${chat.id}"
1 -> avatarHelper.getUserAvatar(
firstName = notCurrentUserParticipants.first().firstName,
lastName = notCurrentUserParticipants.first().lastName,
customName = notCurrentUserParticipants.first().customName,
)

else -> avatarHelper.getGroupAvatar(chatName)
},
lastMessage = Message(
id = chat.lastMessage.messageId,
participantId = chat.lastMessage.authorId,
createdAt = chat.lastMessage.createdAt,
content = chat.lastMessage.content,
),
customName = chat.customName.takeIf { !it.isNullOrBlank() } ?: buildString {
when (notCurrentUserParticipants.size) {
1 -> {
append(notCurrentUserParticipants.single().firstName)
append(" ")
append(notCurrentUserParticipants.single().lastName)
}

else -> {
val names = notCurrentUserParticipants.joinToString(", ") {
"${it.firstName} ${it.lastName}"
}
append(names)
}
}
},
customName = chatName,
participants = chat.participants.map {
ChatParticipant(
id = it.id,
userId = it.userId,
customName = it.customName,
firstName = it.firstName,
lastName = it.lastName,
avatarUrl = "https://gravatar.com/avatar/${md5(it.email)}",
avatarUrl = avatarHelper.getUserAvatar(
firstName = it.firstName,
lastName = it.lastName,
customName = it.customName,
),
readAt = it.readAt,
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.github.mklkj.kommunicator.data.models.User
import io.github.mklkj.kommunicator.data.models.UserRequest
import io.github.mklkj.kommunicator.data.models.UserResponse
import io.github.mklkj.kommunicator.data.service.UserService
import io.github.mklkj.kommunicator.utils.md5
import io.github.mklkj.kommunicator.utils.AvatarHelper
import io.github.mklkj.kommunicator.utils.principalId
import io.github.mklkj.kommunicator.utils.principalUsername
import io.ktor.http.HttpStatusCode
Expand All @@ -23,6 +23,7 @@ import org.koin.ktor.ext.inject

fun Route.userRoutes() {
val userService by inject<UserService>()
val avatarHelper by inject<AvatarHelper>()

post {
val userRequest = call.receive<UserRequest>()
Expand All @@ -45,7 +46,7 @@ fun Route.userRoutes() {
get {
val users = userService.findAll()
call.respond(
message = users.map(User::toResponse)
message = users.map { it.toResponse(avatarHelper) }
)
}

Expand All @@ -65,18 +66,18 @@ fun Route.userRoutes() {
if (foundUser.username != call.principalUsername)
return@get call.respond(HttpStatusCode.Conflict)

call.respond(message = foundUser.toResponse())
call.respond(message = foundUser.toResponse(avatarHelper))
}
}
}

fun User.toResponse(): UserResponse = UserResponse(
fun User.toResponse(avatarHelper: AvatarHelper): UserResponse = UserResponse(
id = id,
username = username,
email = email,
firstName = firstName,
lastName = lastName,
dateOfBirth = dateOfBirth,
gender = gender,
avatarUrl = "https://gravatar.com/avatar/${md5(email)}"
avatarUrl = avatarHelper.getUserAvatar(firstName, lastName),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.github.mklkj.kommunicator.utils

import org.koin.core.annotation.Singleton
import kotlin.math.abs

@Singleton
class AvatarHelper {

private val backgroundColors = listOf(
"000033",
"003333",
"0099cc",
"00cc33",
"3300ff",
"666600",
"669933",
"990099",
"996666",
"cc0000",
"cc6633",
)

private val color = "fff"

fun getUserAvatar(firstName: String, lastName: String, customName: String? = null): String {
return getAvatar(customName ?: "$firstName $lastName")
}

fun getGroupAvatar(name: String): String = getAvatar(name)

private fun getAvatar(name: String): String {
return "https://ui-avatars.com/api/?background=${getColor(name)}&color=$color&name=$name"
}

// fun getGravatarAvatar(email: String): String {
// return "https://gravatar.com/avatar/${md5(email)}"
// }

private fun getColor(name: String): String {
return backgroundColors[abs(name.hashCode() % backgroundColors.size)]
}
}

0 comments on commit dac6a1b

Please sign in to comment.