Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import coil.compose.AsyncImage
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.ParticipantUiState
import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.ui.ActorAvatarImage
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CharacterAvatarUtils
import com.nextcloud.talk.utils.DisplayUtils.isDarkModeOn

@Composable
Expand All @@ -35,21 +37,31 @@ fun AvatarWithFallback(participant: ParticipantUiState, displayName: String, mod
.clip(CircleShape),
contentAlignment = Alignment.Center
) {
val avatarUrl = getUrlForAvatar(
participant = participant,
displayName = displayName
)
if (avatarUrl.isNotEmpty()) {
AsyncImage(
model = avatarUrl,
contentDescription = stringResource(R.string.avatar),
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.clip(CircleShape)
)
// Guests and email participants have no avatar on the server, so theirs is drawn here
val isGuest = Participant.ActorType.GUESTS == participant.actorType ||
Participant.ActorType.EMAILS == participant.actorType
val guestAvatar = if (isGuest) {
CharacterAvatarUtils.guestAvatar(displayName, stringResource(R.string.nc_guest))
} else {
null
}

if (guestAvatar != null) {
ActorAvatarImage(avatar = guestAvatar, modifier = Modifier.fillMaxSize())
} else {
FallbackAvatar(participant = participant)
val avatarUrl = getUrlForAvatar(participant = participant)
if (avatarUrl.isNotEmpty()) {
AsyncImage(
model = avatarUrl,
contentDescription = stringResource(R.string.avatar),
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.clip(CircleShape)
)
} else {
FallbackAvatar(participant = participant)
}
}
}
}
Expand All @@ -76,31 +88,20 @@ private fun FallbackAvatar(participant: ParticipantUiState) {
}

@Composable
fun getUrlForAvatar(participant: ParticipantUiState, displayName: String): String {
var url = ApiUtils.getUrlForAvatar(
participant.baseUrl,
participant.actorId,
true,
darkMode = isDarkModeOn(LocalContext.current)
)
if (Participant.ActorType.GUESTS == participant.actorType ||
Participant.ActorType.EMAILS == participant.actorType
) {
url = ApiUtils.getUrlForGuestAvatar(
participant.baseUrl,
displayName,
true
)
}
fun getUrlForAvatar(participant: ParticipantUiState): String =
if (participant.actorType == Participant.ActorType.FEDERATED) {
val darkTheme = if (isDarkModeOn(LocalContext.current)) 1 else 0
url = ApiUtils.getUrlForFederatedAvatar(
ApiUtils.getUrlForFederatedAvatar(
participant.baseUrl,
participant.roomToken,
participant.actorId!!,
darkTheme,
if (isDarkModeOn(LocalContext.current)) 1 else 0,
true
)
} else {
ApiUtils.getUrlForAvatar(
participant.baseUrl,
participant.actorId,
true,
darkMode = isDarkModeOn(LocalContext.current)
)
}
return url
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import com.nextcloud.talk.adapters.items.MentionAutocompleteItem.Companion.SOURC
import com.nextcloud.talk.adapters.items.MentionAutocompleteItem.Companion.SOURCE_TEAMS
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.RvItemConversationInfoParticipantBinding
import com.nextcloud.talk.extensions.loadDefaultAvatar
import com.nextcloud.talk.extensions.loadFederatedUserAvatar
import com.nextcloud.talk.extensions.loadGuestAvatar
import com.nextcloud.talk.extensions.loadUserAvatar
Expand Down Expand Up @@ -138,11 +137,7 @@ class MentionAutocompleteAdapter(
}

SOURCE_GUESTS, SOURCE_EMAILS -> {
if (item.displayName.equals(context.resources.getString(R.string.nc_guest))) {
avatarView.loadDefaultAvatar(viewThemeUtils)
} else {
avatarView.loadGuestAvatar(currentUser, item.displayName!!, false)
}
avatarView.loadGuestAvatar(item.displayName, viewThemeUtils)
}

SOURCE_TEAMS ->
Expand Down
60 changes: 36 additions & 24 deletions app/src/main/java/com/nextcloud/talk/chat/ui/ShowReactionsSheet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ import com.nextcloud.talk.api.NcApiCoroutines
import com.nextcloud.talk.chat.data.model.ChatMessage
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.reactions.ReactionVoter
import com.nextcloud.talk.ui.ActorAvatarImage
import com.nextcloud.talk.utils.ActorAvatar
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CharacterAvatarUtils
import java.util.Collections

private const val TAG = "ShowReactionsSheet"
Expand Down Expand Up @@ -222,30 +225,24 @@ private fun ReactionVoterRow(
val canDelete = hasReactPermission && reactionItem.reactionVoter.actorId == user.userId
val guestLabel = stringResource(R.string.nc_guest)

val avatarUrl = remember(reactionItem, isDark) {
when (reactionItem.reactionVoter.actorType) {
ReactionVoter.ReactionActorType.GUESTS -> {
val displayName = reactionItem.reactionVoter.actorDisplayName
?.takeIf { it.isNotEmpty() }
?: guestLabel
ApiUtils.getUrlForGuestAvatar(user.baseUrl, displayName, false)
}

ReactionVoter.ReactionActorType.USERS -> {
ApiUtils.getUrlForAvatar(user.baseUrl, reactionItem.reactionVoter.actorId, false, isDark)
}

else -> null
// Guests have no avatar on the server, so theirs is drawn from their name here
val guestAvatar = remember(reactionItem, guestLabel) {
if (reactionItem.reactionVoter.actorType == ReactionVoter.ReactionActorType.GUESTS) {
CharacterAvatarUtils.guestAvatar(reactionItem.reactionVoter.actorDisplayName, guestLabel)
} else {
null
}
}

val avatarRequest = remember(avatarUrl, credentials) {
avatarUrl?.let {
val avatarRequest = remember(reactionItem, isDark, credentials) {
if (reactionItem.reactionVoter.actorType == ReactionVoter.ReactionActorType.USERS) {
ImageRequest.Builder(context)
.data(it)
.data(ApiUtils.getUrlForAvatar(user.baseUrl, reactionItem.reactionVoter.actorId, false, isDark))
.transformations(CircleCropTransformation())
.addHeader("Authorization", credentials ?: "")
.build()
} else {
null
}
}

Expand All @@ -258,13 +255,7 @@ private fun ReactionVoterRow(
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.Companion.CenterVertically
) {
AsyncImage(
model = avatarRequest ?: R.drawable.account_circle_96dp,
contentDescription = null,
placeholder = painterResource(R.drawable.account_circle_96dp),
error = painterResource(R.drawable.account_circle_96dp),
modifier = Modifier.Companion.size(40.dp)
)
ReactionVoterAvatar(guestAvatar = guestAvatar, avatarRequest = avatarRequest)
Spacer(modifier = Modifier.Companion.width(16.dp))
Text(
text = reactionItem.reactionVoter.actorDisplayName ?: "",
Expand All @@ -279,6 +270,27 @@ private fun ReactionVoterRow(
}
}

/**
* The voter's avatar: the one drawn for a guest, otherwise the avatar loaded from the server.
*/
@Composable
private fun ReactionVoterAvatar(guestAvatar: ActorAvatar?, avatarRequest: ImageRequest?) {
if (guestAvatar != null) {
ActorAvatarImage(
avatar = guestAvatar,
modifier = Modifier.Companion.size(40.dp)
)
} else {
AsyncImage(
model = avatarRequest ?: R.drawable.account_circle_96dp,
contentDescription = null,
placeholder = painterResource(R.drawable.account_circle_96dp),
error = painterResource(R.drawable.account_circle_96dp),
modifier = Modifier.Companion.size(40.dp)
)
}
}

private class ReactionComparator(private val activeUser: String?) : Comparator<ReactionItem> {
@Suppress("ReturnCount")
override fun compare(item1: ReactionItem?, item2: ReactionItem?): Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ data class ChatMessageUi(
val plainMessage: String = "",
val renderMarkdown: Boolean,
val actorDisplayName: String,
val actorType: String? = null,
val actorId: String? = null,
val isThread: Boolean,
val threadTitle: String,
val threadTitleIconRes: Int = R.drawable.outline_forum_24,
Expand Down Expand Up @@ -129,6 +131,8 @@ fun ChatMessage.toUiModel(
plainMessage = message.orEmpty(),
renderMarkdown = renderMarkdown == true,
actorDisplayName = actorDisplayName.orEmpty(),
actorType = actorType,
actorId = actorId,
threadTitle = threadTitle.orEmpty(),
isThread = isThread,
threadReplies = threadReplies ?: 0,
Expand Down Expand Up @@ -180,6 +184,8 @@ fun ChatMessage.toScheduledMessageUiModel(
plainMessage = message.orEmpty(),
renderMarkdown = renderMarkdown != false,
actorDisplayName = actorDisplayName.orEmpty(),
actorType = actorType,
actorId = actorId,
isThread = isThread,
threadTitle = threadTitle.orEmpty(),
threadReplies = threadReplies ?: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import com.nextcloud.talk.threadsoverview.data.ThreadsRepository
import com.nextcloud.talk.ui.PlaybackSpeed
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
import com.nextcloud.talk.utils.CharacterAvatarUtils
import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.Mimetype
import com.nextcloud.talk.utils.MimetypeUtils
Expand Down Expand Up @@ -1691,8 +1692,12 @@ class ChatViewModel @AssistedInject constructor(

// val timeString = DateUtils.getLocalTimeStringFromTimestamp(message.timestamp)

/**
* Avatar to request from the server for a message, empty for the actors the server has none for
* - those get their avatar drawn on the client instead, see [CharacterAvatarUtils].
*/
fun getAvatarUrl(message: ChatMessage): String =
if (this::currentUser.isInitialized) {
if (this::currentUser.isInitialized && !message.hasClientSideAvatar()) {
ApiUtils.getUrlForAvatar(
currentUser.baseUrl,
message.actorId,
Expand All @@ -1702,6 +1707,9 @@ class ChatViewModel @AssistedInject constructor(
""
}

private fun ChatMessage.hasClientSideAvatar(): Boolean =
CharacterAvatarUtils.avatarFor(actorType, actorId, actorDisplayName, guestLabel = null) != null

fun initData(user: User, credentials: String, urlForChatting: String, threadId: Long?) {
currentUser = user

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA
import com.nextcloud.talk.conversationinfo.model.ParticipantModel
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.RvItemConversationInfoParticipantBinding
import com.nextcloud.talk.extensions.loadDefaultAvatar
import com.nextcloud.talk.extensions.loadDefaultGroupCallAvatar
import com.nextcloud.talk.extensions.loadFederatedUserAvatar
import com.nextcloud.talk.extensions.loadFirstLetterAvatar
import com.nextcloud.talk.extensions.loadGuestAvatar
import com.nextcloud.talk.extensions.loadPhoneAvatar
import com.nextcloud.talk.extensions.loadTeamAvatar
import com.nextcloud.talk.extensions.loadUserAvatar
Expand Down Expand Up @@ -210,12 +209,7 @@ class ParticipantItemAdapter(
}

Participant.ActorType.GUESTS, Participant.ActorType.EMAILS -> {
val actorName = model.displayName
if (!actorName.isNullOrBlank()) {
binding.avatarView.loadFirstLetterAvatar(actorName)
} else {
binding.avatarView.loadDefaultAvatar(viewThemeUtils)
}
binding.avatarView.loadGuestAvatar(model.displayName, viewThemeUtils)
}

Participant.ActorType.FEDERATED -> {
Expand Down
Loading
Loading