Skip to content

Commit 0e71212

Browse files
Merge pull request #1472 from session-foundation/fix/SES-4464-reply-mesasge-details
Fix/ses 4464 reply mesasge details
2 parents 675b179 + 309ec17 commit 0e71212

File tree

10 files changed

+251
-28
lines changed

10 files changed

+251
-28
lines changed

app/src/main/java/org/thoughtcrime/securesms/ShareViewModel.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import kotlinx.coroutines.flow.stateIn
2323
import kotlinx.coroutines.flow.update
2424
import kotlinx.coroutines.launch
2525
import network.loki.messenger.R
26+
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_HIDDEN
2627
import org.session.libsession.messaging.groups.LegacyGroupDeprecationManager
2728
import org.session.libsession.utilities.Address
2829
import org.session.libsession.utilities.recipients.RecipientData
@@ -70,7 +71,7 @@ class ShareViewModel @Inject constructor(
7071
).stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
7172

7273
val hasAnyConversations: StateFlow<Boolean> =
73-
contacts
74+
conversationRepository.observeConversationList()
7475
.map { it.isNotEmpty() }
7576
.stateIn(viewModelScope, SharingStarted.Eagerly, false)
7677

@@ -91,14 +92,17 @@ class ShareViewModel @Inject constructor(
9192
.filter { thread ->
9293
val recipient = thread.recipient
9394
when {
95+
// if the recipient is hidden or not approved, ignore it
96+
recipient.priority == PRIORITY_HIDDEN || !recipient.approved -> false
97+
9498
// If the recipient is blocked, ignore it
9599
recipient.blocked -> false
96100

97101
// if the recipient is a legacy group, check if deprecation is enabled
98102
recipient.address is Address.LegacyGroup -> !deprecationManager.isDeprecated
99103

100104
// if the recipient is a community, check if it can write
101-
recipient.data is RecipientData.Community -> recipient.data.openGroup.canWrite
105+
recipient.data is RecipientData.Community && !recipient.data.openGroup.canWrite -> false
102106

103107
else -> {
104108
val name = if (recipient.isSelf) context.getString(R.string.noteToSelf)

app/src/main/java/org/thoughtcrime/securesms/groups/SelectContactsViewModel.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,20 @@ open class SelectContactsViewModel @AssistedInject constructor(
5252
// Output: The search query
5353
val searchQuery: StateFlow<String> get() = mutableSearchQuery
5454

55+
private val contactsFlow = observeContacts()
56+
5557
// Output: the contact items to display and select from
5658
val contacts: StateFlow<List<ContactItem>> = combine(
57-
observeContacts(),
59+
contactsFlow,
5860
mutableSearchQuery.debounce(100L),
5961
mutableSelectedContactAccountIDs,
6062
::filterContacts
6163
).stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
6264

65+
val hasContacts: StateFlow<Boolean> = contactsFlow
66+
.map { it.isNotEmpty() }
67+
.stateIn(viewModelScope, SharingStarted.Eagerly, false)
68+
6369
// Output
6470
val currentSelected: Set<Address>
6571
get() = mutableSelectedContactAccountIDs.value

app/src/main/java/org/thoughtcrime/securesms/preferences/BlockedContactsScreen.kt

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ fun BlockedContactsScreen(
4747
viewModel: BlockedContactsViewModel,
4848
onBack: () -> Unit,
4949
) {
50+
val hasContacts by viewModel.hasContacts.collectAsState()
5051
BlockedContacts(
5152
contacts = viewModel.contacts.collectAsState().value,
53+
hasContacts = hasContacts,
5254
onContactItemClicked = viewModel::onContactItemClicked,
5355
searchQuery = viewModel.searchQuery.collectAsState().value,
5456
onSearchQueryChanged = viewModel::onSearchQueryChanged,
@@ -81,6 +83,7 @@ fun BlockedContactsScreen(
8183
@Composable
8284
fun BlockedContacts(
8385
contacts: List<ContactItem>,
86+
hasContacts: Boolean,
8487
onContactItemClicked: (address: Address) -> Unit,
8588
searchQuery: String,
8689
onSearchQueryChanged: (String) -> Unit,
@@ -119,7 +122,7 @@ fun BlockedContacts(
119122
Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
120123

121124
BottomFadingEdgeBox(modifier = Modifier.weight(1f)) { bottomContentPadding ->
122-
if(contacts.isEmpty() && searchQuery.isEmpty()){
125+
if(!hasContacts){
123126
Text(
124127
text = stringResource(id = R.string.blockBlockedNone),
125128
modifier = Modifier.padding(top = LocalDimensions.current.spacing)
@@ -187,6 +190,7 @@ private fun PreviewSelectContacts() {
187190
PreviewTheme {
188191
BlockedContacts(
189192
contacts = contacts,
193+
hasContacts = true,
190194
onContactItemClicked = {},
191195
searchQuery = "",
192196
onSearchQueryChanged = {},
@@ -205,6 +209,7 @@ private fun PreviewSelectEmptyContacts() {
205209
PreviewTheme {
206210
BlockedContacts(
207211
contacts = contacts,
212+
hasContacts = false,
208213
onContactItemClicked = {},
209214
searchQuery = "",
210215
onSearchQueryChanged = {},
@@ -215,21 +220,4 @@ private fun PreviewSelectEmptyContacts() {
215220
}
216221
}
217222

218-
@Preview
219-
@Composable
220-
private fun PreviewSelectEmptyContactsWithSearch() {
221-
val contacts = emptyList<ContactItem>()
222-
223-
PreviewTheme {
224-
BlockedContacts(
225-
contacts = contacts,
226-
onContactItemClicked = {},
227-
searchQuery = "Test",
228-
onSearchQueryChanged = {},
229-
onSearchQueryClear = {},
230-
onDoneClicked = {},
231-
onBack = {},
232-
)
233-
}
234-
}
235223

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package org.thoughtcrime.securesms.preferences.prosettings
2+
3+
import androidx.compose.animation.ExperimentalSharedTransitionApi
4+
import androidx.compose.foundation.Image
5+
import androidx.compose.foundation.background
6+
import androidx.compose.foundation.border
7+
import androidx.compose.foundation.clickable
8+
import androidx.compose.foundation.layout.*
9+
import androidx.compose.foundation.layout.Arrangement
10+
import androidx.compose.foundation.rememberScrollState
11+
import androidx.compose.foundation.shape.RoundedCornerShape
12+
import androidx.compose.foundation.verticalScroll
13+
import androidx.compose.material3.*
14+
import androidx.compose.runtime.Composable
15+
import androidx.compose.runtime.collectAsState
16+
import androidx.compose.runtime.getValue
17+
import androidx.compose.runtime.mutableStateOf
18+
import androidx.compose.runtime.remember
19+
import androidx.compose.runtime.rememberCoroutineScope
20+
import androidx.compose.runtime.setValue
21+
import androidx.compose.ui.Alignment
22+
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
23+
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.draw.clip
25+
import androidx.compose.ui.draw.clipToBounds
26+
import androidx.compose.ui.graphics.Color
27+
import androidx.compose.ui.graphics.ColorFilter
28+
import androidx.compose.ui.layout.onGloballyPositioned
29+
import androidx.compose.ui.platform.LocalContext
30+
import androidx.compose.ui.platform.LocalDensity
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.text.style.TextOverflow
35+
import androidx.compose.ui.tooling.preview.Preview
36+
import androidx.compose.ui.tooling.preview.PreviewParameter
37+
import androidx.compose.ui.unit.Dp
38+
import androidx.compose.ui.unit.dp
39+
import androidx.compose.ui.unit.max
40+
import com.squareup.phrase.Phrase
41+
import kotlinx.coroutines.launch
42+
import network.loki.messenger.R
43+
import org.session.libsession.utilities.NonTranslatableStringConstants
44+
import org.session.libsession.utilities.StringSubstitutionConstants.APP_PRO_KEY
45+
import org.session.libsession.utilities.StringSubstitutionConstants.CURRENT_PLAN_KEY
46+
import org.session.libsession.utilities.StringSubstitutionConstants.DATE_KEY
47+
import org.session.libsession.utilities.StringSubstitutionConstants.ICON_KEY
48+
import org.session.libsession.utilities.StringSubstitutionConstants.MONTHLY_PRICE_KEY
49+
import org.session.libsession.utilities.StringSubstitutionConstants.PRICE_KEY
50+
import org.session.libsession.utilities.StringSubstitutionConstants.PRO_KEY
51+
import org.session.libsession.utilities.StringSubstitutionConstants.SELECTED_PLAN_KEY
52+
import org.session.libsession.utilities.recipients.ProStatus
53+
import org.thoughtcrime.securesms.preferences.prosettings.ProSettingsViewModel.ProPlan
54+
import org.thoughtcrime.securesms.preferences.prosettings.ProSettingsViewModel.ProPlanBadge
55+
import org.thoughtcrime.securesms.preferences.prosettings.ProSettingsViewModel.Commands.*
56+
import org.thoughtcrime.securesms.pro.SubscriptionState
57+
import org.thoughtcrime.securesms.pro.subscription.ProSubscriptionDuration
58+
import org.thoughtcrime.securesms.ui.SessionProSettingsHeader
59+
import org.thoughtcrime.securesms.ui.SpeechBubbleTooltip
60+
import org.thoughtcrime.securesms.ui.components.AccentFillButtonRect
61+
import org.thoughtcrime.securesms.ui.components.BackAppBar
62+
import org.thoughtcrime.securesms.ui.components.DangerFillButtonRect
63+
import org.thoughtcrime.securesms.ui.components.RadioButtonIndicator
64+
import org.thoughtcrime.securesms.ui.components.annotatedStringResource
65+
import org.thoughtcrime.securesms.ui.components.iconExternalLink
66+
import org.thoughtcrime.securesms.ui.components.inlineContentMap
67+
import org.thoughtcrime.securesms.ui.components.radioButtonColors
68+
import org.thoughtcrime.securesms.ui.qaTag
69+
import org.thoughtcrime.securesms.ui.safeContentWidth
70+
import org.thoughtcrime.securesms.ui.theme.LocalColors
71+
import org.thoughtcrime.securesms.ui.theme.LocalDimensions
72+
import org.thoughtcrime.securesms.ui.theme.LocalType
73+
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
74+
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
75+
import org.thoughtcrime.securesms.ui.theme.ThemeColors
76+
import org.thoughtcrime.securesms.ui.theme.bold
77+
import java.time.Duration
78+
import java.time.Instant
79+
80+
81+
@OptIn(ExperimentalSharedTransitionApi::class)
82+
@Composable
83+
fun PlanConfirmationScreen(
84+
viewModel: ProSettingsViewModel,
85+
onBack: () -> Unit,
86+
) {
87+
val proData by viewModel.proSettingsUIState.collectAsState()
88+
89+
PlanConfirmation(
90+
proData = proData,
91+
sendCommand = viewModel::onCommand,
92+
onBack = onBack,
93+
)
94+
}
95+
96+
@OptIn(ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class)
97+
@Composable
98+
fun PlanConfirmation(
99+
proData: ProSettingsViewModel.ProSettingsUIState,
100+
sendCommand: (ProSettingsViewModel.Commands) -> Unit,
101+
onBack: () -> Unit,
102+
) {
103+
Scaffold(
104+
topBar = {},
105+
contentWindowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal),
106+
) { paddings ->
107+
val context = LocalContext.current
108+
109+
Column(
110+
modifier = Modifier
111+
.fillMaxSize()
112+
.consumeWindowInsets(paddings)
113+
.padding(
114+
horizontal = LocalDimensions.current.spacing,
115+
)
116+
.verticalScroll(rememberScrollState()),
117+
horizontalAlignment = CenterHorizontally
118+
) {
119+
Spacer(Modifier.weight(1f))
120+
121+
SessionProSettingsHeader(
122+
disabled = false,
123+
)
124+
125+
Spacer(Modifier.height(LocalDimensions.current.spacing))
126+
127+
Text(
128+
modifier = Modifier.align(CenterHorizontally),
129+
text = stringResource(R.string.proAllSet),
130+
style = LocalType.current.h6,
131+
color = LocalColors.current.text,
132+
)
133+
134+
Spacer(Modifier.height(LocalDimensions.current.xsSpacing))
135+
136+
Text(
137+
modifier = Modifier.align(CenterHorizontally)
138+
.safeContentWidth(),
139+
//todo PRO the text below can change if the user was renewing vs expiring and/or/auto-renew
140+
text = annotatedStringResource(
141+
Phrase.from(context.getText(R.string.proAllSetDescription))
142+
.put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO)
143+
.put(PRO_KEY, NonTranslatableStringConstants.PRO)
144+
.put(DATE_KEY, proData.subscriptionExpiryDate)
145+
.format()
146+
),
147+
textAlign = TextAlign.Center,
148+
style = LocalType.current.base,
149+
color = LocalColors.current.text,
150+
)
151+
152+
Spacer(Modifier.height(LocalDimensions.current.smallSpacing))
153+
154+
//todo PRO the button text can change if the user was renewing vs expiring and/or/auto-renew
155+
AccentFillButtonRect(
156+
modifier = Modifier.fillMaxWidth()
157+
.widthIn(max = LocalDimensions.current.maxContentWidth),
158+
text = stringResource(R.string.theReturn),
159+
onClick = {}
160+
)
161+
162+
Spacer(Modifier.weight(1f))
163+
}
164+
}
165+
}
166+
167+
168+
@Preview
169+
@Composable
170+
private fun PreviewPlanConfirmation(
171+
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
172+
) {
173+
PreviewTheme(colors) {
174+
PlanConfirmation(
175+
proData = ProSettingsViewModel.ProSettingsUIState(
176+
subscriptionState = SubscriptionState.Active.AutoRenewing(
177+
proStatus = ProStatus.Pro(
178+
visible = true,
179+
validUntil = Instant.now() + Duration.ofDays(14),
180+
),
181+
type = ProSubscriptionDuration.THREE_MONTHS
182+
),
183+
),
184+
sendCommand = {},
185+
onBack = {},
186+
)
187+
}
188+
}
189+
190+

app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsHomeScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ fun ProSettingsHome(
123123
Spacer(Modifier.height(LocalDimensions.current.smallSpacing))
124124
ProSettings(
125125
data = data.subscriptionState,
126-
expiry = data.proExpiryLabel,
126+
expiry = data.subscriptionExpiryLabel,
127127
sendCommand = sendCommand,
128128
)
129129
}

app/src/main/java/org/thoughtcrime/securesms/preferences/prosettings/ProSettingsNavHost.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ sealed interface ProSettingsDestination {
2525

2626
@Serializable
2727
data object UpdatePlan: ProSettingsDestination
28+
29+
@Serializable
30+
data object PlanConfirmation: ProSettingsDestination
2831
}
2932

3033
@SuppressLint("RestrictedApi")
@@ -61,7 +64,7 @@ fun ProSettingsNavHost(
6164
}
6265
}
6366

64-
NavHost(navController = navController, startDestination = Home) {
67+
NavHost(navController = navController, startDestination = PlanConfirmation) {
6568
// Home
6669
horizontalSlideComposable<Home> {
6770
ProSettingsHomeScreen(
@@ -77,6 +80,14 @@ fun ProSettingsNavHost(
7780
onBack = { scope.launch { navigator.navigateUp() }},
7881
)
7982
}
83+
84+
// Subscription plan confirmation
85+
horizontalSlideComposable<PlanConfirmation> {
86+
PlanConfirmationScreen(
87+
viewModel = viewModel,
88+
onBack = { scope.launch { navigator.navigateUp() }},
89+
)
90+
}
8091
}
8192

8293
// Dialogs

0 commit comments

Comments
 (0)