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
9 changes: 9 additions & 0 deletions app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ interface NcApiCoroutines {
@DELETE
suspend fun deleteConversationAvatar(@Header("Authorization") authorization: String, @Url url: String): RoomOverall

@FormUrlEncoded
@POST
suspend fun setConversationEmojiAvatar(
@Header("Authorization") authorization: String,
@Url url: String,
@Field("emoji") emoji: String,
@Field("color") color: String?
): RoomOverall

@POST
suspend fun archiveConversation(@Header("Authorization") authorization: String, @Url url: String): GenericOverall

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -72,6 +73,7 @@ import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
import com.nextcloud.talk.models.json.conversations.ConversationEnums
import com.nextcloud.talk.ui.theme.protectEmojiPickerScrollGesture
import com.nextcloud.talk.ui.theme.themeEmojiPickerCategoryTabs
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
Expand Down Expand Up @@ -298,7 +300,7 @@ fun MessageActionsBottomSheet(
) {
val context = LocalContext.current
val recentEmojiProvider = remember(context) { ReactionRecentEmojiProvider(context) }
var showEmojiPicker by remember { mutableStateOf(false) }
var showEmojiPicker by rememberSaveable { mutableStateOf(false) }
val scope = rememberCoroutineScope()

val sheetState = rememberModalBottomSheetState()
Expand Down Expand Up @@ -711,6 +713,7 @@ private fun EmojiPickerSheetContent(
setRecentEmojiProvider(recentEmojiProvider)
setOnEmojiPickedListener(Consumer { item -> onEmojiSelected(item.emoji) })
themeEmojiPickerCategoryTabs(this, selectedTabColor, unselectedTabColor)
protectEmojiPickerScrollGesture(this)
}
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
Expand All @@ -47,6 +47,7 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.chooseaccount.viewmodel.StatusMessageViewModel
import com.nextcloud.talk.models.json.status.Status
import com.nextcloud.talk.models.json.status.predefined.PredefinedStatus
import com.nextcloud.talk.ui.theme.protectEmojiPickerScrollGesture
import com.nextcloud.talk.ui.theme.themeEmojiPickerCategoryTabs

private val emojiPickerHeight = 360.dp
Expand All @@ -56,7 +57,7 @@ private val emojiPickerHeight = 360.dp
fun StatusMessageModalBottomSheet(currentStatus: Status, viewModel: StatusMessageViewModel, onDismiss: () -> Unit) {
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val isDismissed by viewModel.isDismissed.collectAsState()
var showEmojiPicker by remember { mutableStateOf(false) }
var showEmojiPicker by rememberSaveable { mutableStateOf(false) }
val backToStatus: () -> Unit = { showEmojiPicker = false }

LaunchedEffect(currentStatus) {
Expand Down Expand Up @@ -114,6 +115,7 @@ private fun EmojiPickerSheetContent(onEmojiSelected: (String) -> Unit, onBack: (
setBackgroundColor(backgroundColor.toArgb())
setOnEmojiPickedListener(Consumer { item -> onEmojiSelected(item.emoji) })
themeEmojiPickerCategoryTabs(this, selectedTabColor, unselectedTabColor)
protectEmojiPickerScrollGesture(this)
}
}
)
Expand Down
123 changes: 123 additions & 0 deletions app/src/main/java/com/nextcloud/talk/components/AvatarEditPanel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024-2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/

package com.nextcloud.talk.components

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.EmojiEmotions
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.nextcloud.talk.R

data class AvatarEditPanelState(
val selectedEmoji: String? = null,
val selectedEmojiColor: Int? = null,
val showDeleteButton: Boolean = false,
val enabled: Boolean = true
)

data class AvatarEditPanelCallbacks(
val onCameraClick: () -> Unit = {},
val onUploadClick: () -> Unit = {},
val onChooseClick: () -> Unit = {},
val onEmojiAvatarConfirmed: (emoji: String, color: Int?) -> Unit = { _, _ -> },
val onDeleteClick: () -> Unit = {}
)

@Composable
fun AvatarEditPanel(state: AvatarEditPanelState, callbacks: AvatarEditPanelCallbacks, modifier: Modifier = Modifier) {
var showEmojiAvatarPicker by rememberSaveable { mutableStateOf(false) }

AvatarActionButtonsRow(
state = state,
callbacks = callbacks,
onEmojiButtonClick = { showEmojiAvatarPicker = true },
modifier = modifier
)

if (showEmojiAvatarPicker) {
EmojiAvatarPickerBottomSheet(
initialEmoji = state.selectedEmoji,
initialColor = state.selectedEmojiColor,
onConfirm = { emoji, color ->
callbacks.onEmojiAvatarConfirmed(emoji, color)
showEmojiAvatarPicker = false
},
onDismiss = { showEmojiAvatarPicker = false }
)
}
}

@Composable
private fun AvatarActionButtonsRow(
state: AvatarEditPanelState,
callbacks: AvatarEditPanelCallbacks,
onEmojiButtonClick: () -> Unit,
modifier: Modifier = Modifier
) {
Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
AvatarActionButton(onClick = callbacks.onCameraClick, enabled = state.enabled) {
Icon(
painter = painterResource(R.drawable.ic_baseline_photo_camera_24),
contentDescription = stringResource(R.string.set_avatar_from_camera)
)
}
AvatarActionButton(onClick = callbacks.onUploadClick, enabled = state.enabled) {
Icon(
painter = painterResource(R.drawable.upload),
contentDescription = stringResource(R.string.upload_new_avatar_from_device)
)
}
AvatarActionButton(onClick = callbacks.onChooseClick, enabled = state.enabled) {
Icon(
painter = painterResource(R.drawable.ic_folder),
contentDescription = stringResource(R.string.choose_avatar_from_cloud)
)
}
AvatarActionButton(onClick = onEmojiButtonClick, enabled = state.enabled) {
Icon(
imageVector = Icons.Outlined.EmojiEmotions,
contentDescription = stringResource(R.string.nc_set_emoji_as_conversation_picture)
)
}
if (state.showDeleteButton) {
AvatarActionButton(onClick = callbacks.onDeleteClick, enabled = state.enabled) {
Icon(
painter = painterResource(R.drawable.trashbin),
contentDescription = stringResource(R.string.delete_avatar)
)
}
}
}
}

@Composable
private fun AvatarActionButton(onClick: () -> Unit, enabled: Boolean, icon: @Composable () -> Unit) {
FilledTonalIconButton(
onClick = onClick,
enabled = enabled,
modifier = Modifier.size(avatarEditButtonSize),
shape = avatarEditButtonShape,
content = icon
)
}

private val avatarEditButtonSize = 40.dp
private val avatarEditButtonShape = RoundedCornerShape(12.dp)
Loading
Loading