Skip to content

Commit 93ae440

Browse files
committed
WIP - Allowing Shared Pinned Items + reworking the pinned ui and logic
Signed-off-by: rapterjet2004 <juliuslinus1@gmail.com>
1 parent 4e0363d commit 93ae440

File tree

11 files changed

+168
-51
lines changed

11 files changed

+168
-51
lines changed

app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt

Lines changed: 99 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,22 @@ import androidx.appcompat.app.AlertDialog
6161
import androidx.appcompat.view.ContextThemeWrapper
6262
import androidx.cardview.widget.CardView
6363
import androidx.compose.foundation.background
64-
import androidx.compose.foundation.clickable
6564
import androidx.compose.foundation.layout.Arrangement
6665
import androidx.compose.foundation.layout.Box
6766
import androidx.compose.foundation.layout.Column
68-
import androidx.compose.foundation.layout.Row
69-
import androidx.compose.foundation.layout.Spacer
67+
import androidx.compose.foundation.layout.offset
7068
import androidx.compose.foundation.layout.padding
7169
import androidx.compose.foundation.layout.size
7270
import androidx.compose.foundation.rememberScrollState
7371
import androidx.compose.foundation.shape.RoundedCornerShape
7472
import androidx.compose.foundation.verticalScroll
73+
import androidx.compose.material.icons.Icons
74+
import androidx.compose.material.icons.filled.Menu
75+
import androidx.compose.material3.Divider
76+
import androidx.compose.material3.DropdownMenu
77+
import androidx.compose.material3.DropdownMenuItem
7578
import androidx.compose.material3.Icon
79+
import androidx.compose.material3.IconButton
7680
import androidx.compose.material3.MaterialTheme
7781
import androidx.compose.material3.Text
7882
import androidx.compose.runtime.Composable
@@ -1375,8 +1379,13 @@ class ChatActivity :
13751379
@Composable
13761380
private fun PinnedMessageView(message: ChatMessage) {
13771381
message.incoming = true
1382+
13781383
val pinnedBy = stringResource(R.string.pinned_by)
1379-
message.actorDisplayName = "${message.actorDisplayName}\n$pinnedBy ${message.pinnedActorDisplayName}"
1384+
1385+
// FIXME this causes some problems with duplicate some times
1386+
message.actorDisplayName = remember(message.pinnedActorDisplayName) {
1387+
"${message.actorDisplayName}\n$pinnedBy ${message.pinnedActorDisplayName}"
1388+
}
13801389
val scrollState = rememberScrollState()
13811390

13821391
val outgoingBubbleColor = remember {
@@ -1393,7 +1402,7 @@ class ChatActivity :
13931402
Color(colorInt)
13941403
}
13951404

1396-
val isAllowed = remember {
1405+
remember {
13971406
ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!)
13981407
}
13991408

@@ -1411,51 +1420,98 @@ class ChatActivity :
14111420
ComposeChatAdapter().GetComposableForMessage(message)
14121421
}
14131422

1414-
Row(
1415-
modifier = Modifier
1416-
.padding(start = 16.dp)
1417-
.background(outgoingBubbleColor, RoundedCornerShape(16.dp))
1418-
.padding(16.dp)
1419-
) {
1420-
val hiddenEye = painterResource(R.drawable.ic_eye_off)
1421-
Icon(
1422-
hiddenEye,
1423-
"Hide pin",
1424-
modifier = Modifier
1425-
.size(16.dp)
1426-
.clickable {
1427-
hidePinnedMessage(message)
1428-
}
1429-
)
1423+
var expanded by remember { mutableStateOf(false) }
14301424

1431-
if (isAllowed) {
1432-
Spacer(modifier = Modifier.size(16.dp))
1433-
val read = painterResource(R.drawable.keep_off_24px)
1434-
Icon(
1435-
read,
1436-
"Unpin",
1437-
modifier = Modifier
1438-
.size(16.dp)
1439-
.clickable {
1440-
unPinMessage(message)
1441-
}
1442-
)
1443-
}
1425+
val pinnedText = remember(message.pinnedUntil) {
1426+
val pinnedUntilStr = context.getString(R.string.pinned_until)
1427+
val pinnedIndefinitely = context.getString(R.string.pinned_indefinitely)
14441428

1445-
val pinnedUntilStr = stringResource(R.string.pinned_until)
1446-
val pinnedIndefinitely = stringResource(R.string.pinned_indefinitely)
1447-
val pinnedText = message.pinnedUntil?.let {
1429+
message.pinnedUntil?.let {
14481430
val format = if (DateFormat.is24HourFormat(context)) "EEE, HH:mm" else "EEE, hh:mm a"
14491431
val localDateTime = Instant.ofEpochMilli(it)
14501432
.atZone(ZoneId.systemDefault())
14511433
.toLocalDateTime()
1452-
14531434
val timeString = localDateTime.format(DateTimeFormatter.ofPattern(format))
1454-
14551435
"$pinnedUntilStr $timeString"
14561436
} ?: pinnedIndefinitely
1437+
}
1438+
1439+
Box(
1440+
modifier = Modifier
1441+
.offset(16.dp, 0.dp)
1442+
.background(outgoingBubbleColor, RoundedCornerShape(16.dp))
1443+
) {
1444+
IconButton(onClick = { expanded = true }) {
1445+
Icon(
1446+
imageVector = Icons.Default.Menu, // Or use a Pin icon here
1447+
contentDescription = "Pinned Message Options"
1448+
)
1449+
}
14571450

1458-
Text(pinnedText, modifier = Modifier.padding(start = 16.dp))
1451+
DropdownMenu(
1452+
expanded = expanded,
1453+
onDismissRequest = { expanded = false },
1454+
modifier = Modifier.background(outgoingBubbleColor)
1455+
) {
1456+
DropdownMenuItem(
1457+
text = {
1458+
Text(
1459+
text = pinnedText,
1460+
style = MaterialTheme.typography.bodySmall,
1461+
color = MaterialTheme.colorScheme.onSurfaceVariant
1462+
)
1463+
},
1464+
onClick = { /* No-op or toggle expansion */ },
1465+
enabled = false // Visually distinct as information, not action
1466+
)
1467+
1468+
Divider()
1469+
1470+
DropdownMenuItem(
1471+
text = { Text("Go to message") },
1472+
leadingIcon = {
1473+
Icon(
1474+
painter = painterResource(R.drawable.baseline_chat_bubble_outline_24),
1475+
contentDescription = null,
1476+
modifier = Modifier.size(24.dp)
1477+
)
1478+
},
1479+
onClick = {
1480+
expanded = false
1481+
scrollToMessageWithId(message.id)
1482+
}
1483+
)
1484+
1485+
DropdownMenuItem(
1486+
text = { Text("Dismiss") },
1487+
leadingIcon = {
1488+
Icon(
1489+
painter = painterResource(R.drawable.ic_eye_off),
1490+
contentDescription = null,
1491+
modifier = Modifier.size(24.dp)
1492+
)
1493+
},
1494+
onClick = {
1495+
expanded = false
1496+
hidePinnedMessage(message)
1497+
}
1498+
)
1499+
1500+
DropdownMenuItem(
1501+
text = { Text("Unpin") },
1502+
leadingIcon = {
1503+
Icon(
1504+
painter = painterResource(R.drawable.keep_off_24px),
1505+
contentDescription = null,
1506+
modifier = Modifier.size(24.dp)
1507+
)
1508+
},
1509+
onClick = {
1510+
expanded = false
1511+
unPinMessage(message)
1512+
}
1513+
)
1514+
}
14591515
}
14601516
}
14611517
}
@@ -1559,10 +1615,7 @@ class ChatActivity :
15591615

15601616
cancelNotificationsForCurrentConversation()
15611617

1562-
chatViewModel.getRoom(
1563-
conversationUser,
1564-
roomToken
1565-
)
1618+
chatViewModel.getRoom(roomToken)
15661619

15671620
actionBar?.show()
15681621

@@ -2041,10 +2094,7 @@ class ChatActivity :
20412094
}
20422095
getRoomInfoTimerHandler?.postDelayed(
20432096
{
2044-
chatViewModel.getRoom(
2045-
conversationUser,
2046-
roomToken
2047-
)
2097+
chatViewModel.getRoom(roomToken)
20482098
},
20492099
if (delay > 0) delay else delayForRecursiveCall
20502100
)
@@ -4072,7 +4122,7 @@ class ChatActivity :
40724122
}
40734123

40744124
fun hidePinnedMessage(message: ChatMessage) {
4075-
val url = ApiUtils.getUrlForChatMessagePinning(chatApiVersion, conversationUser?.baseUrl, roomToken, message.id)
4125+
val url = ApiUtils.getUrlForChatMessageHiding(chatApiVersion, conversationUser?.baseUrl, roomToken, message.id)
40764126
chatViewModel.hidePinnedMessage(credentials!!, url)
40774127
}
40784128

app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,9 @@ class ChatViewModel @Inject constructor(
304304
chatRepository.updateConversation(currentConversation)
305305
}
306306

307-
fun getRoom(user: User, token: String) {
307+
fun getRoom(token: String) {
308308
_getRoomViewState.value = GetRoomStartState
309-
conversationRepository.getRoom(user, token)
309+
conversationRepository.getRoom(currentUser, token)
310310
}
311311

312312
fun getCapabilities(user: User, token: String, conversationModel: ConversationModel) {

app/src/main/java/com/nextcloud/talk/shareditems/activities/SharedItemsActivity.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ class SharedItemsActivity : BaseActivity() {
188188
binding.sharedItemsTabs.addTab(tabVoice)
189189
}
190190

191+
if (sharedItemTypes.contains(SharedItemType.PINNED)) {
192+
val tabPinned: TabLayout.Tab = binding.sharedItemsTabs.newTab()
193+
tabPinned.tag = SharedItemType.PINNED
194+
tabPinned.setText(R.string.pinned)
195+
binding.sharedItemsTabs.addTab(tabPinned)
196+
}
197+
191198
if (sharedItemTypes.contains(SharedItemType.LOCATION)) {
192199
val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab()
193200
tabLocation.tag = SharedItemType.LOCATION

app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsAdapter.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.nextcloud.talk.shareditems.model.SharedFileItem
2121
import com.nextcloud.talk.shareditems.model.SharedItem
2222
import com.nextcloud.talk.shareditems.model.SharedLocationItem
2323
import com.nextcloud.talk.shareditems.model.SharedOtherItem
24+
import com.nextcloud.talk.shareditems.model.SharedPinnedItem
2425
import com.nextcloud.talk.shareditems.model.SharedPollItem
2526
import com.nextcloud.talk.ui.theme.ViewThemeUtils
2627

@@ -64,6 +65,7 @@ class SharedItemsAdapter(
6465
is SharedLocationItem -> holder.onBind(item)
6566
is SharedOtherItem -> holder.onBind(item)
6667
is SharedDeckCardItem -> holder.onBind(item)
68+
is SharedPinnedItem -> holder.onBind(item)
6769
}
6870
}
6971

app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsListViewHolder.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import com.nextcloud.talk.shareditems.model.SharedFileItem
2323
import com.nextcloud.talk.shareditems.model.SharedItem
2424
import com.nextcloud.talk.shareditems.model.SharedLocationItem
2525
import com.nextcloud.talk.shareditems.model.SharedOtherItem
26+
import com.nextcloud.talk.shareditems.model.SharedPinnedItem
2627
import com.nextcloud.talk.shareditems.model.SharedPollItem
2728
import com.nextcloud.talk.ui.theme.ViewThemeUtils
2829

@@ -127,4 +128,24 @@ class SharedItemsListViewHolder(
127128
it.context.startActivity(browserIntent)
128129
}
129130
}
131+
132+
override fun onBind(item: SharedPinnedItem) {
133+
super.onBind(item)
134+
binding.fileName.text = item.name // actually the message of the chat item
135+
binding.fileSize.visibility = View.GONE
136+
binding.separator1.visibility = View.GONE
137+
binding.fileDate.text = item.dateTime
138+
binding.actor.text = item.actorName
139+
140+
image.load(R.drawable.keep_off_24px)
141+
image.setColorFilter(
142+
ContextCompat.getColor(image.context, R.color.high_emphasis_menu_icon),
143+
android.graphics.PorterDuff.Mode.SRC_IN
144+
)
145+
146+
clickTarget.setOnClickListener {
147+
// TODO implement unpin functionality in viewHolder
148+
}
149+
150+
}
130151
}

app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsViewHolder.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.nextcloud.talk.shareditems.model.SharedFileItem
2020
import com.nextcloud.talk.shareditems.model.SharedItem
2121
import com.nextcloud.talk.shareditems.model.SharedLocationItem
2222
import com.nextcloud.talk.shareditems.model.SharedOtherItem
23+
import com.nextcloud.talk.shareditems.model.SharedPinnedItem
2324
import com.nextcloud.talk.shareditems.model.SharedPollItem
2425
import com.nextcloud.talk.ui.theme.ViewThemeUtils
2526
import com.nextcloud.talk.utils.FileViewerUtils
@@ -89,4 +90,6 @@ abstract class SharedItemsViewHolder(
8990
open fun onBind(item: SharedOtherItem) {}
9091

9192
open fun onBind(item: SharedDeckCardItem) {}
93+
94+
open fun onBind(item: SharedPinnedItem) {}
9295
}

app/src/main/java/com/nextcloud/talk/shareditems/model/SharedItemType.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enum class SharedItemType {
2020
LOCATION,
2121
DECKCARD,
2222
OTHER,
23+
PINNED,
2324
POLL;
2425

2526
companion object {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Nextcloud Talk - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2025 Julius Linus <juliuslinus1@gmail.com>
5+
* SPDX-License-Identifier: GPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.talk.shareditems.model
9+
10+
data class SharedPinnedItem(
11+
override val id: String,
12+
override val name: String,
13+
override val actorId: String,
14+
override val actorName: String,
15+
override val dateTime: String,
16+
) : SharedItem

app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.nextcloud.talk.shareditems.model.SharedItemType
2020
import com.nextcloud.talk.shareditems.model.SharedItems
2121
import com.nextcloud.talk.shareditems.model.SharedLocationItem
2222
import com.nextcloud.talk.shareditems.model.SharedOtherItem
23+
import com.nextcloud.talk.shareditems.model.SharedPinnedItem
2324
import com.nextcloud.talk.shareditems.model.SharedPollItem
2425
import com.nextcloud.talk.utils.ApiUtils
2526
import com.nextcloud.talk.utils.DateConstants
@@ -71,6 +72,18 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr
7172
it.value.timestamp * DateConstants.SECOND_DIVIDER
7273
)
7374

75+
if (it.value.messageParameters?.containsKey("pinned") == true) {
76+
val pinnedParameters = it.value.messageParameters!!["pinned"]!!
77+
val metaDataParameters = it.value.messageParameters!!["metaData"]!!
78+
items[it.value.id.toString()] = SharedPinnedItem(
79+
pinnedParameters["id"]!!,
80+
pinnedParameters["message"]!!,
81+
pinnedParameters["actorId"]!!,
82+
metaDataParameters["pinnedActorDisplayName"]!!,
83+
dateTime
84+
)
85+
}
86+
7487
if (it.value.messageParameters?.containsKey("file") == true) {
7588
val fileParameters = it.value.messageParameters!!["file"]!!
7689

app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ object ApiUtils {
248248
fun getUrlForChatMessagePinning(version: Int, baseUrl: String?, token: String, messageId: String): String =
249249
"${getUrlForChatMessage(version, baseUrl, token, messageId)}/pin"
250250

251+
fun getUrlForChatMessageHiding(version: Int, baseUrl: String?, token: String, messageId: String): String =
252+
"${getUrlForChatMessage(version, baseUrl, token, messageId)}/pin/self"
253+
251254
fun getUrlForSignaling(version: Int, baseUrl: String?): String = getUrlForApi(version, baseUrl) + "/signaling"
252255

253256
fun getUrlForTestPushNotifications(baseUrl: String): String =

0 commit comments

Comments
 (0)