Skip to content

Commit 35b68d4

Browse files
committed
Non originating UI
1 parent 22a9f6f commit 35b68d4

File tree

10 files changed

+230
-60
lines changed

10 files changed

+230
-60
lines changed

app/src/firebaseCommon/kotlin/org/thoughtcrime/securesms/notifications/FirebasePushService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class FirebasePushService : FirebaseMessagingService() {
2424
}
2525

2626
override fun onMessageReceived(message: RemoteMessage) {
27-
Log.d(TAG, "Received a firebase push notification: $message - Priority received: ${message.priority} (Priority expected: ${message.originalPriority}) - Sent time: ${dateUtils.getLocaleFormattedDate(message.sentTime, "HH:mm:ss.SSS")}")
27+
Log.d(TAG, "Received a firebase push notification: $message - Priority received: ${message.priority} (Priority expected: ${message.originalPriority}) - Sent time: ${DateUtils.getLocaleFormattedDate(message.sentTime, "HH:mm:ss.SSS")}")
2828
pushReceiver.onPushDataReceived(message.data)
2929
}
3030
}

app/src/main/java/org/session/libsession/utilities/StringSubKeys.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,5 @@ object StringSubstitutionConstants {
5656
const val MONTHLY_PRICE_KEY: StringSubKey = "monthly_price"
5757
const val PRICE_KEY: StringSubKey = "price"
5858
const val PERCENT_KEY: StringSubKey = "percent"
59+
const val DEVICE_TYPE_KEY: StringSubKey = "device_type"
5960
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ fun BaseCellButtonProSettingsScreen(
105105
buttonText: String,
106106
dangerButton: Boolean,
107107
onButtonClick: () -> Unit,
108-
title: String? = null,
108+
title: CharSequence? = null,
109109
content: @Composable () -> Unit
110110
) {
111111
BaseProSettingsScreen(
@@ -117,7 +117,7 @@ fun BaseCellButtonProSettingsScreen(
117117
if(!title.isNullOrEmpty()) {
118118
Text(
119119
modifier = Modifier.fillMaxWidth(),
120-
text = title,
120+
text = annotatedStringResource(title),
121121
textAlign = TextAlign.Center,
122122
style = LocalType.current.base,
123123
color = LocalColors.current.text,
@@ -190,7 +190,7 @@ fun BaseNonOriginatingProSettingsScreen(
190190
buttonText: String,
191191
dangerButton: Boolean,
192192
onButtonClick: () -> Unit,
193-
headerTitle: String?,
193+
headerTitle: CharSequence?,
194194
contentTitle: String?,
195195
contentDescription: CharSequence?,
196196
linkCellsInfo: String?,
@@ -328,7 +328,6 @@ private fun PreviewBaseNonOrig(
328328
iconRes = R.drawable.ic_phone
329329
)
330330
)
331-
332331
)
333332
}
334333
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package org.thoughtcrime.securesms.preferences.prosettings
2+
3+
import android.icu.util.MeasureUnit
4+
import androidx.compose.animation.ExperimentalSharedTransitionApi
5+
import androidx.compose.material3.ExperimentalMaterial3Api
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.platform.LocalContext
8+
import androidx.compose.ui.res.stringResource
9+
import androidx.compose.ui.tooling.preview.Preview
10+
import androidx.compose.ui.tooling.preview.PreviewParameter
11+
import com.squareup.phrase.Phrase
12+
import network.loki.messenger.R
13+
import org.session.libsession.utilities.NonTranslatableStringConstants
14+
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
15+
import org.session.libsession.utilities.StringSubstitutionConstants.APP_PRO_KEY
16+
import org.session.libsession.utilities.StringSubstitutionConstants.CURRENT_PLAN_KEY
17+
import org.session.libsession.utilities.StringSubstitutionConstants.DATE_KEY
18+
import org.session.libsession.utilities.StringSubstitutionConstants.DEVICE_TYPE_KEY
19+
import org.session.libsession.utilities.StringSubstitutionConstants.PLATFORM_ACCOUNT_KEY
20+
import org.session.libsession.utilities.StringSubstitutionConstants.PLATFORM_STORE_KEY
21+
import org.session.libsession.utilities.recipients.ProStatus
22+
import org.thoughtcrime.securesms.pro.SubscriptionState
23+
import org.thoughtcrime.securesms.pro.subscription.ProSubscriptionDuration
24+
import org.thoughtcrime.securesms.pro.subscription.expiryFromNow
25+
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
26+
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
27+
import org.thoughtcrime.securesms.ui.theme.ThemeColors
28+
import org.thoughtcrime.securesms.util.DateUtils
29+
import java.time.Duration
30+
import java.time.Instant
31+
32+
@OptIn(ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class)
33+
@Composable
34+
fun ChoosePlanNonOriginating(
35+
subscription: SubscriptionState.Active,
36+
sendCommand: (ProSettingsViewModel.Commands) -> Unit,
37+
onBack: () -> Unit,
38+
){
39+
val nonOriginatingData = subscription.nonOriginatingSubscription ?: return
40+
val context = LocalContext.current
41+
42+
val headerTitle = when(subscription) {
43+
is SubscriptionState.Active.Expiring -> Phrase.from(context.getText(R.string.proPlanActivatedNotAuto))
44+
.put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO)
45+
.put(DATE_KEY, subscription.type.expiryFromNow())
46+
.format()
47+
48+
else -> Phrase.from(context.getText(R.string.proPlanActivatedAutoShort))
49+
.put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO)
50+
.put(CURRENT_PLAN_KEY, DateUtils.getLocalisedTimeDuration(
51+
context = context,
52+
amount = subscription.type.duration.months,
53+
unit = MeasureUnit.MONTH
54+
))
55+
.put(DATE_KEY, subscription.type.expiryFromNow())
56+
.format()
57+
}
58+
59+
BaseNonOriginatingProSettingsScreen(
60+
disabled = false,
61+
onBack = onBack,
62+
headerTitle = headerTitle,
63+
buttonText = Phrase.from(context.getText(R.string.openStoreWebsite))
64+
.put(PLATFORM_STORE_KEY, nonOriginatingData.store)
65+
.format().toString(),
66+
dangerButton = false,
67+
onButtonClick = {
68+
//todo PRO implement
69+
},
70+
contentTitle = stringResource(R.string.updatePlan),
71+
contentDescription = Phrase.from(context.getText(R.string.proPlanSignUp))
72+
.put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO)
73+
.put(PLATFORM_STORE_KEY, nonOriginatingData.store)
74+
.put(PLATFORM_ACCOUNT_KEY, nonOriginatingData.platformAccount)
75+
.format(),
76+
linkCellsInfo = stringResource(R.string.updatePlanTwo),
77+
linkCells = listOf(
78+
NonOriginatingLinkCellData(
79+
title = Phrase.from(context.getText(R.string.onDevice))
80+
.put(DEVICE_TYPE_KEY, nonOriginatingData.device)
81+
.format(),
82+
info = Phrase.from(context.getText(R.string.onDeviceDescription))
83+
.put(APP_NAME_KEY, NonTranslatableStringConstants.APP_NAME)
84+
.put(DEVICE_TYPE_KEY, nonOriginatingData.device)
85+
.put(PLATFORM_ACCOUNT_KEY, nonOriginatingData.platformAccount)
86+
.put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO)
87+
.format(),
88+
iconRes = R.drawable.ic_smartphone
89+
),
90+
NonOriginatingLinkCellData(
91+
title = Phrase.from(context.getText(R.string.viaStoreWebsite))
92+
.put(PLATFORM_STORE_KEY, nonOriginatingData.store)
93+
.format(),
94+
info = Phrase.from(context.getText(R.string.viaStoreWebsiteDescription))
95+
.put(PLATFORM_ACCOUNT_KEY, nonOriginatingData.platformAccount)
96+
.put(PLATFORM_STORE_KEY, nonOriginatingData.store)
97+
.format(),
98+
iconRes = R.drawable.ic_globe
99+
)
100+
)
101+
)
102+
}
103+
104+
@Preview
105+
@Composable
106+
private fun PreviewUpdatePlan(
107+
@PreviewParameter(SessionColorsParameterProvider::class) colors: ThemeColors
108+
) {
109+
PreviewTheme(colors) {
110+
val context = LocalContext.current
111+
ChoosePlanNonOriginating (
112+
subscription = SubscriptionState.Active.AutoRenewing(
113+
proStatus = ProStatus.Pro(
114+
visible = true,
115+
validUntil = Instant.now() + Duration.ofDays(14),
116+
),
117+
type = ProSubscriptionDuration.THREE_MONTHS,
118+
nonOriginatingSubscription = SubscriptionState.Active.NonOriginatingSubscription(
119+
device = "iPhone",
120+
store = "Apple App Store",
121+
platform = "Apple",
122+
platformAccount = "Apple Account",
123+
urlSubscription = "https://www.apple.com/account/subscriptions",
124+
)
125+
),
126+
sendCommand = {},
127+
onBack = {},
128+
)
129+
}
130+
}

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

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.thoughtcrime.securesms.preferences.prosettings
22

3+
import android.icu.util.MeasureUnit
34
import androidx.compose.animation.ExperimentalSharedTransitionApi
45
import androidx.compose.foundation.Image
56
import androidx.compose.foundation.background
@@ -47,6 +48,7 @@ import org.thoughtcrime.securesms.preferences.prosettings.ProSettingsViewModel.P
4748
import org.thoughtcrime.securesms.preferences.prosettings.ProSettingsViewModel.Commands.*
4849
import org.thoughtcrime.securesms.pro.SubscriptionState
4950
import org.thoughtcrime.securesms.pro.subscription.ProSubscriptionDuration
51+
import org.thoughtcrime.securesms.pro.subscription.expiryFromNow
5052
import org.thoughtcrime.securesms.ui.SpeechBubbleTooltip
5153
import org.thoughtcrime.securesms.ui.components.AccentFillButtonRect
5254
import org.thoughtcrime.securesms.ui.components.RadioButtonIndicator
@@ -62,6 +64,7 @@ import org.thoughtcrime.securesms.ui.theme.PreviewTheme
6264
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
6365
import org.thoughtcrime.securesms.ui.theme.ThemeColors
6466
import org.thoughtcrime.securesms.ui.theme.bold
67+
import org.thoughtcrime.securesms.util.DateUtils
6568

6669

6770
@OptIn(ExperimentalSharedTransitionApi::class)
@@ -70,13 +73,27 @@ fun ChoosePlanScreen(
7073
viewModel: ProSettingsViewModel,
7174
onBack: () -> Unit,
7275
) {
73-
val planData by viewModel.choose.collectAsState()
76+
val planData by viewModel.choosePlanState.collectAsState()
77+
78+
// there are different UI depending on the state
79+
when {
80+
// there is an active subscription but from a different platform
81+
(planData.subscriptionState as? SubscriptionState.Active)?.nonOriginatingSubscription != null ->
82+
ChoosePlanNonOriginating(
83+
subscription = planData.subscriptionState as SubscriptionState.Active,
84+
sendCommand = viewModel::onCommand,
85+
onBack = onBack,
86+
)
7487

75-
ChoosePlan(
76-
planData = planData,
77-
sendCommand = viewModel::onCommand,
78-
onBack = onBack,
79-
)
88+
//todo PRO handle the case here when there are no SubscriptionManager available (for example fdroid builds)
89+
90+
// default plan chooser
91+
else -> ChoosePlan(
92+
planData = planData,
93+
sendCommand = viewModel::onCommand,
94+
onBack = onBack,
95+
)
96+
}
8097
}
8198

8299
@OptIn(ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class)
@@ -107,15 +124,21 @@ fun ChoosePlan(
107124

108125
is SubscriptionState.Active.Expiring -> Phrase.from(context.getText(R.string.proPlanActivatedNotAuto))
109126
.put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO)
110-
.put(DATE_KEY, "May 21st, 2025") //todo PRO implement properly
127+
.put(DATE_KEY, planData.subscriptionState.type.expiryFromNow())
111128
.format()
112129

113-
else -> Phrase.from(context.getText(R.string.proPlanActivatedAuto))
130+
is SubscriptionState.Active.AutoRenewing -> Phrase.from(context.getText(R.string.proPlanActivatedAuto))
114131
.put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO)
115-
.put(CURRENT_PLAN_KEY, "3 months") //todo PRO implement properly
116-
.put(DATE_KEY, "May 21st, 2025") //todo PRO implement properly
132+
.put(CURRENT_PLAN_KEY, DateUtils.getLocalisedTimeDuration(
133+
context = context,
134+
amount = planData.subscriptionState.type.duration.months,
135+
unit = MeasureUnit.MONTH
136+
))
137+
.put(DATE_KEY, planData.subscriptionState.type.expiryFromNow())
117138
.put(PRO_KEY, NonTranslatableStringConstants.PRO)
118139
.format()
140+
141+
else -> ""
119142
}
120143

121144
Text(

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

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ import org.session.libsession.utilities.StringSubstitutionConstants.MONTHLY_PRIC
2222
import org.session.libsession.utilities.StringSubstitutionConstants.PERCENT_KEY
2323
import org.session.libsession.utilities.StringSubstitutionConstants.PRICE_KEY
2424
import org.session.libsession.utilities.StringSubstitutionConstants.PRO_KEY
25-
import org.session.libsession.utilities.StringSubstitutionConstants.RELATIVE_TIME_KEY
2625
import org.session.libsession.utilities.StringSubstitutionConstants.SELECTED_PLAN_KEY
2726
import org.session.libsession.utilities.StringSubstitutionConstants.TIME_KEY
2827
import org.thoughtcrime.securesms.pro.SubscriptionState
2928
import org.thoughtcrime.securesms.pro.ProStatusManager
3029
import org.thoughtcrime.securesms.pro.subscription.ProSubscriptionDuration
3130
import org.thoughtcrime.securesms.pro.subscription.SubscriptionCoordinator
31+
import org.thoughtcrime.securesms.pro.subscription.expiryFromNow
3232
import org.thoughtcrime.securesms.ui.SimpleDialogData
3333
import org.thoughtcrime.securesms.ui.UINavigator
3434
import org.thoughtcrime.securesms.util.DateUtils
@@ -53,9 +53,7 @@ class ProSettingsViewModel @Inject constructor(
5353
val dialogState: StateFlow<DialogsState> = _dialogState
5454

5555
private val _choosePlanState: MutableStateFlow<ChoosePlanState> = MutableStateFlow(ChoosePlanState())
56-
val choose: StateFlow<ChoosePlanState> = _choosePlanState
57-
58-
private val proSettingsDateFormat = "MMMM d, yyyy"
56+
val choosePlanState: StateFlow<ChoosePlanState> = _choosePlanState
5957

6058
init {
6159
generateState()
@@ -86,15 +84,7 @@ class ProSettingsViewModel @Inject constructor(
8684
else -> ""
8785
},
8886
subscriptionExpiryDate = when(subscriptionState){
89-
is SubscriptionState.Active -> {
90-
val newSubscriptionExpiryDate = ZonedDateTime.now()
91-
.plus(subscriptionState.type.duration)
92-
.toInstant()
93-
.toEpochMilli()
94-
95-
dateUtils.getLocaleFormattedDate(newSubscriptionExpiryDate, proSettingsDateFormat)
96-
}
97-
87+
is SubscriptionState.Active -> subscriptionState.type.expiryFromNow()
9888
else -> ""
9989
}
10090
)
@@ -112,17 +102,23 @@ class ProSettingsViewModel @Inject constructor(
112102

113103
is SubscriptionState.Active.Expiring -> Phrase.from(context.getText(R.string.proPlanActivatedNotAuto))
114104
.put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO)
115-
.put(DATE_KEY, "May 21st, 2025") //todo PRO implement properly
105+
.put(DATE_KEY, subscriptionState.type.expiryFromNow())
116106
.format() to
117107
context.getString(R.string.updatePlan)
118108

119-
else -> Phrase.from(context.getText(R.string.proPlanActivatedAuto))
109+
is SubscriptionState.Active.AutoRenewing -> Phrase.from(context.getText(R.string.proPlanActivatedAuto))
120110
.put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO)
121-
.put(CURRENT_PLAN_KEY, "3 months") //todo PRO implement properly
122-
.put(DATE_KEY, "May 21st, 2025") //todo PRO implement properly
111+
.put(CURRENT_PLAN_KEY, DateUtils.getLocalisedTimeDuration(
112+
context = context,
113+
amount = subscriptionState.type.duration.months,
114+
unit = MeasureUnit.MONTH
115+
))
116+
.put(DATE_KEY, subscriptionState.type.expiryFromNow())
123117
.put(PRO_KEY, NonTranslatableStringConstants.PRO)
124118
.format() to
125119
context.getString(R.string.updatePlan)
120+
121+
else -> "" to ""
126122
}
127123
val isActive = subscriptionState is SubscriptionState.Active
128124
val currentPlan12Months = isActive && subscriptionState.type == ProSubscriptionDuration.TWELVE_MONTHS
@@ -253,20 +249,16 @@ class ProSettingsViewModel @Inject constructor(
253249
Commands.GetProPlan -> {
254250
// if we already have a current plan, ask for confirmation first
255251
if(_proSettingsUIState.value.subscriptionState is SubscriptionState.Active){
256-
val newSubscriptionExpiryDate = ZonedDateTime.now()
257-
.plus(getSelectedPlan().durationType.duration)
258-
.toInstant()
259-
.toEpochMilli()
260-
val newSubscriptionExpiryString = dateUtils.getLocaleFormattedDate(
261-
newSubscriptionExpiryDate, proSettingsDateFormat
262-
)
252+
val newSubscriptionExpiryString = getSelectedPlan().durationType.expiryFromNow()
263253

264-
val currentSubscriptionDuration = dateUtils.getLocalisedTimeDuration(
254+
val currentSubscriptionDuration = DateUtils.getLocalisedTimeDuration(
255+
context = context,
265256
amount = (_proSettingsUIState.value.subscriptionState as SubscriptionState.Active).type.duration.months,
266257
unit = MeasureUnit.MONTH
267258
)
268259

269-
val selectedSubscriptionDuration = dateUtils.getLocalisedTimeDuration(
260+
val selectedSubscriptionDuration = DateUtils.getLocalisedTimeDuration(
261+
context = context,
270262
amount = getSelectedPlan().durationType.duration.months,
271263
unit = MeasureUnit.MONTH
272264
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
package org.thoughtcrime.securesms.pro.subscription
22

3+
import org.thoughtcrime.securesms.util.DateUtils
34
import java.time.Duration
45
import java.time.Period
6+
import java.time.ZonedDateTime
57

68
enum class ProSubscriptionDuration(val duration: Period) {
79
ONE_MONTH(Period.ofMonths(1)),
810
THREE_MONTHS(Period.ofMonths(3)),
911
TWELVE_MONTHS(Period.ofMonths(12))
12+
}
13+
14+
private val proSettingsDateFormat = "MMMM d, yyyy"
15+
16+
fun ProSubscriptionDuration.expiryFromNow(): String {
17+
val newSubscriptionExpiryDate = ZonedDateTime.now()
18+
.plus(duration)
19+
.toInstant()
20+
.toEpochMilli()
21+
return DateUtils.getLocaleFormattedDate(
22+
newSubscriptionExpiryDate, proSettingsDateFormat
23+
)
1024
}

0 commit comments

Comments
 (0)