Skip to content

Commit 02683b3

Browse files
feat(conv-list): Migrate federation invitation card to Composable
AI-assistant: Copilot 1.0.6 (Claude Sonnet 4.6) Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
1 parent 5ef1d88 commit 02683b3

4 files changed

Lines changed: 121 additions & 40 deletions

File tree

app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import com.nextcloud.talk.contextchat.ContextChatViewModel
9292
import com.nextcloud.talk.conversationlist.ui.ConversationListFab
9393
import com.nextcloud.talk.conversationlist.ui.ConversationListSkeleton
9494
import com.nextcloud.talk.conversationlist.ui.ConversationsEmptyStateView
95+
import com.nextcloud.talk.conversationlist.ui.FederationInvitationHintCard
9596
import com.nextcloud.talk.conversationlist.ui.NotificationWarningCard
9697
import com.nextcloud.talk.conversationlist.ui.StatusBannerRow
9798
import com.nextcloud.talk.conversationlist.ui.UnreadMentionBubble
@@ -285,6 +286,7 @@ class ConversationsListActivity :
285286
setupUnreadBubble()
286287
setupShimmer()
287288
setupNotificationWarning()
289+
setupFederationHintCard()
288290
initSystemBars()
289291
viewThemeUtils.material.themeSearchCardView(binding.searchToolbarContainer)
290292
viewThemeUtils.material.colorMaterialButtonContent(binding.menuButton, ColorRole.ON_SURFACE_VARIANT)
@@ -347,7 +349,6 @@ class ConversationsListActivity :
347349

348350
loadUserAvatar(binding.switchAccountButton)
349351
viewThemeUtils.material.colorMaterialTextButton(binding.switchAccountButton)
350-
viewThemeUtils.material.themeCardView(binding.conversationListHintInclude.hintLayoutCardview)
351352
searchBehaviorSubject.onNext(false)
352353
fetchRooms()
353354
fetchPendingInvitations()
@@ -394,25 +395,6 @@ class ConversationsListActivity :
394395
}
395396
}
396397

397-
conversationsListViewModel.getFederationInvitationsViewState.observe(this) { state ->
398-
when (state) {
399-
is ConversationsListViewModel.GetFederationInvitationsStartState -> {
400-
binding.conversationListHintInclude.conversationListHintLayout.visibility = View.GONE
401-
}
402-
403-
is ConversationsListViewModel.GetFederationInvitationsSuccessState -> {
404-
binding.conversationListHintInclude.conversationListHintLayout.visibility =
405-
if (state.showInvitationsHint) View.VISIBLE else View.GONE
406-
}
407-
408-
is ConversationsListViewModel.GetFederationInvitationsErrorState -> {
409-
// do nothing
410-
}
411-
412-
else -> {}
413-
}
414-
}
415-
416398
conversationsListViewModel.showBadgeViewState.observe(this) { state ->
417399
when (state) {
418400
is ConversationsListViewModel.ShowBadgeStartState -> {
@@ -1053,10 +1035,6 @@ class ConversationsListActivity :
10531035

10541036
private fun fetchPendingInvitations() {
10551037
if (hasSpreedFeatureCapability(currentUser?.capabilities?.spreedCapability, SpreedFeatures.FEDERATION_V1)) {
1056-
binding.conversationListHintInclude.conversationListHintLayout.setOnClickListener {
1057-
val intent = Intent(this, InvitationsActivity::class.java)
1058-
startActivity(intent)
1059-
}
10601038
conversationsListViewModel.getFederationInvitations()
10611039
}
10621040
}
@@ -1845,6 +1823,25 @@ class ConversationsListActivity :
18451823
}
18461824
}
18471825

1826+
private fun setupFederationHintCard() {
1827+
binding.federationHintComposeView.apply {
1828+
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
1829+
setContent {
1830+
val colorScheme = remember { viewThemeUtils.getColorScheme(context) }
1831+
val visible by conversationsListViewModel.federationInvitationHintVisible.collectAsStateWithLifecycle()
1832+
MaterialTheme(colorScheme = colorScheme) {
1833+
FederationInvitationHintCard(
1834+
visible = visible,
1835+
onClick = {
1836+
val intent = Intent(context, InvitationsActivity::class.java)
1837+
startActivity(intent)
1838+
}
1839+
)
1840+
}
1841+
}
1842+
}
1843+
}
1844+
18481845
private fun shouldShowNotificationWarning(): Boolean {
18491846
fun shouldShowWarningIfDateTooOld(date1: Long): Boolean {
18501847
val currentTimeMillis = System.currentTimeMillis()
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Nextcloud Talk - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: GPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.talk.conversationlist.ui
9+
10+
import android.content.res.Configuration
11+
import androidx.compose.animation.AnimatedVisibility
12+
import androidx.compose.animation.expandVertically
13+
import androidx.compose.animation.shrinkVertically
14+
import androidx.compose.foundation.isSystemInDarkTheme
15+
import androidx.compose.foundation.layout.Row
16+
import androidx.compose.foundation.layout.Spacer
17+
import androidx.compose.foundation.layout.fillMaxWidth
18+
import androidx.compose.foundation.layout.padding
19+
import androidx.compose.foundation.layout.size
20+
import androidx.compose.foundation.layout.width
21+
import androidx.compose.material3.OutlinedCard
22+
import androidx.compose.material3.Icon
23+
import androidx.compose.material3.MaterialTheme
24+
import androidx.compose.material3.Text
25+
import androidx.compose.material3.darkColorScheme
26+
import androidx.compose.material3.lightColorScheme
27+
import androidx.compose.runtime.Composable
28+
import androidx.compose.ui.Alignment
29+
import androidx.compose.ui.Modifier
30+
import androidx.compose.ui.res.dimensionResource
31+
import androidx.compose.ui.res.painterResource
32+
import androidx.compose.ui.res.stringResource
33+
import androidx.compose.ui.text.style.TextAlign
34+
import androidx.compose.ui.tooling.preview.Preview
35+
import com.nextcloud.talk.R
36+
37+
/**
38+
* Composable card that shows a hint about pending federation invitations.
39+
*
40+
* @param visible Whether the card should be visible.
41+
* @param onClick Called when the user taps the card (typically navigates to InvitationsActivity).
42+
*/
43+
@Composable
44+
fun FederationInvitationHintCard(visible: Boolean, onClick: () -> Unit) {
45+
AnimatedVisibility(
46+
visible = visible,
47+
enter = expandVertically(),
48+
exit = shrinkVertically()
49+
) {
50+
OutlinedCard(
51+
onClick = onClick,
52+
modifier = Modifier
53+
.fillMaxWidth()
54+
.padding(
55+
horizontal = dimensionResource(R.dimen.standard_margin),
56+
vertical = dimensionResource(R.dimen.standard_half_margin)
57+
)
58+
) {
59+
Row(
60+
modifier = Modifier
61+
.fillMaxWidth()
62+
.padding(dimensionResource(R.dimen.standard_padding)),
63+
verticalAlignment = Alignment.CenterVertically
64+
) {
65+
Icon(
66+
painter = painterResource(R.drawable.ic_info_24px),
67+
contentDescription = null,
68+
modifier = Modifier.size(dimensionResource(R.dimen.iconized_single_line_item_icon_size))
69+
)
70+
Spacer(modifier = Modifier.width(dimensionResource(R.dimen.standard_half_margin)))
71+
Text(
72+
text = stringResource(R.string.nc_federation_pending_invitation_hint),
73+
style = MaterialTheme.typography.bodyMedium,
74+
textAlign = TextAlign.Start,
75+
modifier = Modifier.weight(1f)
76+
)
77+
}
78+
}
79+
}
80+
}
81+
82+
@Preview(showBackground = true, name = "Light")
83+
@Preview(showBackground = true, name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES)
84+
@Preview(showBackground = true, name = "RTL Arabic", locale = "ar")
85+
@Composable
86+
private fun FederationInvitationHintCardDarkPreview() {
87+
val colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()
88+
MaterialTheme(colorScheme = colorScheme) {
89+
FederationInvitationHintCard(visible = true, onClick = {})
90+
}
91+
}

app/src/main/java/com/nextcloud/talk/conversationlist/viewmodels/ConversationsListViewModel.kt

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,8 @@ class ConversationsListViewModel @Inject constructor(
121121
.roomListFlow
122122
.stateIn(viewModelScope, SharingStarted.Eagerly, listOf())
123123

124-
object GetFederationInvitationsStartState : ViewState
125-
object GetFederationInvitationsErrorState : ViewState
126-
127-
open class GetFederationInvitationsSuccessState(val showInvitationsHint: Boolean) : ViewState
128-
129-
private val _getFederationInvitationsViewState: MutableLiveData<ViewState> =
130-
MutableLiveData(GetFederationInvitationsStartState)
131-
val getFederationInvitationsViewState: LiveData<ViewState>
132-
get() = _getFederationInvitationsViewState
124+
private val _federationInvitationHintVisible = MutableStateFlow(false)
125+
val federationInvitationHintVisible: StateFlow<Boolean> = _federationInvitationHintVisible.asStateFlow()
133126

134127
object ShowBadgeStartState : ViewState
135128
object ShowBadgeErrorState : ViewState
@@ -140,7 +133,7 @@ class ConversationsListViewModel @Inject constructor(
140133
get() = _showBadgeViewState
141134

142135
fun getFederationInvitations() {
143-
_getFederationInvitationsViewState.value = GetFederationInvitationsStartState
136+
_federationInvitationHintVisible.value = false
144137
_showBadgeViewState.value = ShowBadgeStartState
145138

146139
userManager.users.blockingGet()?.forEach {
@@ -393,9 +386,9 @@ class ConversationsListViewModel @Inject constructor(
393386
invitationsModel.user.baseUrl?.equals(currentUser.baseUrl) == true
394387
) {
395388
if (invitationsModel.invitations.isNotEmpty()) {
396-
_getFederationInvitationsViewState.value = GetFederationInvitationsSuccessState(true)
389+
_federationInvitationHintVisible.value = true
397390
} else {
398-
_getFederationInvitationsViewState.value = GetFederationInvitationsSuccessState(false)
391+
_federationInvitationHintVisible.value = false
399392
}
400393
} else {
401394
if (invitationsModel.invitations.isNotEmpty()) {
@@ -405,7 +398,6 @@ class ConversationsListViewModel @Inject constructor(
405398
}
406399

407400
override fun onError(e: Throwable) {
408-
_getFederationInvitationsViewState.value = GetFederationInvitationsErrorState
409401
Log.e(TAG, "Failed to fetch pending invitations", e)
410402
}
411403

app/src/main/res/layout/activity_conversations.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,10 @@
194194
android:layout_height="match_parent"
195195
android:orientation="vertical">
196196

197-
<include
198-
android:id="@+id/conversation_list_hint_include"
199-
layout="@layout/federated_invitation_hint" />
197+
<androidx.compose.ui.platform.ComposeView
198+
android:id="@+id/federation_hint_compose_view"
199+
android:layout_width="match_parent"
200+
android:layout_height="wrap_content" />
200201

201202
<androidx.compose.ui.platform.ComposeView
202203
android:id="@+id/notification_warning_compose_view"

0 commit comments

Comments
 (0)