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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import org.session.libsession.utilities.StringSubstitutionConstants.PLATFORM_KEY
import org.session.libsession.utilities.StringSubstitutionConstants.PLATFORM_STORE_KEY
import org.thoughtcrime.securesms.preferences.prosettings.ProSettingsViewModel.Commands.ShowOpenUrlDialog
import org.thoughtcrime.securesms.pro.ProStatus
import org.thoughtcrime.securesms.pro.ProUrls
import org.thoughtcrime.securesms.pro.getPlatformDisplayName
import org.thoughtcrime.securesms.pro.previewAutoRenewingApple
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
Expand All @@ -24,6 +26,10 @@ import org.thoughtcrime.securesms.ui.theme.ThemeColors
@Composable
fun RefundPlanNonOriginating(
subscription: ProStatus.Active.WithPlan,
/// Whether the store's own quick-refund window is still open. Decides all three of the button, the
/// body copy and the link, exactly as it does on the originating screen — while the store will take
/// the request it is sent there, and once it will not, only Session can action it.
isQuickRefund: Boolean,
sendCommand: (ProSettingsViewModel.Commands) -> Unit,
onBack: () -> Unit,
){
Expand All @@ -34,18 +40,42 @@ fun RefundPlanNonOriginating(
disabled = true,
onBack = onBack,
headerTitle = stringResource(R.string.proRefundDescription),
buttonText = Phrase.from(context.getText(R.string.openPlatformWebsite))
.put(PLATFORM_KEY, subscription.providerData.platform)
.format().toString(),
buttonText = if (isQuickRefund)
// See RefundPlanScreen: "Google Play Store", not "Google".
Phrase.from(context.getText(R.string.openPlatformWebsite))
.put(PLATFORM_KEY, subscription.providerData.getPlatformDisplayName())
.format().toString()
else stringResource(R.string.requestRefund),
dangerButton = true,
onButtonClick = {
sendCommand(ShowOpenUrlDialog(subscription.providerData.refundSupportUrl))
// The store route's url is libsession's, read through `providerUrls` — it owns the
// per-provider table and says so. Note the slot: for Google Play its `refund_support_url`
// IS the Session short link that redirects into the Play store, so the value we want for
// the window-OPEN route sits under libsession's "support" name. The window-closed route
// uses `ProUrls.SUPPORT`, which reads libsession's `url_pro_support`.
//
// No provider check on top of the window: `providerData` is already the ORIGINATING
// provider's table, so an Apple plan yields Apple's own refund page here rather than the
// Play-store link. Gating on the provider as well would replace that correct page with
// Session's form.
sendCommand(
ShowOpenUrlDialog(
if (isQuickRefund) subscription.providerData.refundSupportUrl
else ProUrls.SUPPORT
)
)
},
contentTitle = Phrase.from(context.getText(R.string.proRefunding))
.format().toString(),
contentDescription = Phrase.from(context.getText(R.string.proPlanPlatformRefund))
// Past the window the request is Session's to handle, and the copy has to say so — this is the
// same sentence the originating screen shows, with the non-originating premise kept.
contentDescription = if (isQuickRefund)
Phrase.from(context.getText(R.string.proPlanPlatformRefund))
.put(PLATFORM_STORE_KEY, subscription.providerData.store)
.put(PLATFORM_ACCOUNT_KEY, subscription.providerData.platformAccount)
.format()
else Phrase.from(context.getText(R.string.proPlanPlatformRefundLong))
.put(PLATFORM_STORE_KEY, subscription.providerData.store)
.put(PLATFORM_ACCOUNT_KEY, subscription.providerData.platformAccount)
.format(),
linkCellsInfo = stringResource(R.string.refundRequestOptions),
linkCells = listOf(
Expand Down Expand Up @@ -88,6 +118,7 @@ private fun PreviewUpdatePlan(
val context = LocalContext.current
RefundPlanNonOriginating (
subscription = previewAutoRenewingApple,
isQuickRefund = true,
sendCommand = {},
onBack = {},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import network.loki.messenger.R
import org.session.libsession.utilities.StringSubstitutionConstants.PLATFORM_KEY
import org.thoughtcrime.securesms.preferences.prosettings.ProSettingsViewModel.Commands.ShowOpenUrlDialog
import org.thoughtcrime.securesms.pro.ProStatus
import org.thoughtcrime.securesms.pro.ProUrls
import org.thoughtcrime.securesms.pro.getPlatformDisplayName
import org.thoughtcrime.securesms.pro.isFromAnotherPlatform
import org.thoughtcrime.securesms.pro.previewAutoRenewingApple
import org.thoughtcrime.securesms.ui.qaTag
Expand Down Expand Up @@ -66,6 +68,10 @@ fun RefundPlanScreen(
|| !refundData.hasValidSubscription ->
RefundPlanNonOriginating(
subscription = activePlan,
// The window governs this screen too, and it was resolved right here — this branch
// simply never passed it on, so one screen served both the <48h and >48h states with
// the former's button and the latter's link.
isQuickRefund = refundData.isQuickRefund,
sendCommand = viewModel::onCommand,
onBack = onBack,
)
Expand Down Expand Up @@ -98,16 +104,33 @@ fun RefundPlan(
disabled = true,
onBack = onBack,
buttonText = if(isQuickRefund) Phrase.from(context.getText(R.string.openPlatformWebsite))
.put(PLATFORM_KEY, data.providerData.platform)
// `getPlatformDisplayName`, not `platform`: the designs spell the rule out - Apple reads
// "Apple" but our own store reads "Google Play Store", never "Google". iOS and Desktop
// both branch the same way; these two refund screens were the only sites still passing
// the raw platform name.
.put(PLATFORM_KEY, data.providerData.getPlatformDisplayName())
.format().toString()
else stringResource(R.string.requestRefund),
dangerButton = true,
onButtonClick = {
if(isQuickRefund && !quickRefundUrl.isNullOrEmpty()){
sendCommand(ShowOpenUrlDialog(quickRefundUrl))
} else {
sendCommand(ShowOpenUrlDialog(data.providerData.refundSupportUrl))
}
// Two Session-owned links, chosen on the window alone - not the provider's own
// `refund_platform_url`/`refund_support_url`. Being ours, the destinations can be
// repointed without a client release, and all three clients agree on them.
//
// The window is what decides who can act: while it is open the store takes the request,
// and once it closes only Session can, which is what this screen's copy promises.
// The store route's url is libsession's, read through `providerUrls` — it owns the
// per-provider table and says so. Note the slot: for Google Play its `refund_support_url`
// IS the Session short link that redirects into the Play store, so the value we want for
// the window-OPEN route sits under libsession's "support" name. The window-closed route
// uses `ProUrls.SUPPORT`, which reads libsession's `url_pro_support`.
//
// No provider gate here: this screen only ever shows a plan bought on this store.
sendCommand(
ShowOpenUrlDialog(
if (isQuickRefund) data.providerData.refundSupportUrl else ProUrls.SUPPORT
)
)
},
title = stringResource(R.string.proRefundDescription),
){
Expand Down
15 changes: 13 additions & 2 deletions app/src/main/java/org/thoughtcrime/securesms/pro/ProProofs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,22 @@ import org.session.protos.SessionProtos

/**
* Copies values from a libsession ProProof into a protobuf-based ProProof.
*
* There is deliberately no version field. A proof's format is bound into its signature by the
* 16-byte domain prefix the signer selects (`ProProof_v0_____`), so the version was never part of
* the signed payload — carrying it alongside as a plain value proved nothing and could disagree
* with the format the signature actually attests to. Verification derives the format from the
* prefix instead, and libsession-util has dropped `ProProof::version` accordingly.
*
* Protobuf field tag 1 on `ProProof` is retired and must never be reused: peers built before the
* removal still put a `uint32` there, so a new field on tag 1 would be fed their stale version
* numbers. A peer that sends one is handled by libsession, which reports the proof as
* `ProStatus::UnsupportedVersion` and lets the message through without Pro content rather than
* dropping it.
*/
fun SessionProtos.ProProof.Builder.copyFromLibSession(
proProof: ProProof
): SessionProtos.ProProof.Builder = setVersion(proProof.version)
.setExpiryUnixTs(proProof.expirySeconds)
): SessionProtos.ProProof.Builder = setExpiryUnixTs(proProof.expirySeconds)
.setRevocationTag(ByteString.copyFrom(proProof.revocationTagHex.hexToByteArray()))
.setRotatingPublicKey(ByteString.copyFrom(proProof.rotatingPubKeyHex.hexToByteArray()))
.setSig(ByteString.copyFrom(proProof.signatureHex.hexToByteArray()))
23 changes: 7 additions & 16 deletions app/src/main/java/org/thoughtcrime/securesms/pro/ProUrls.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
package org.thoughtcrime.securesms.pro

/**
* The Pro destinations, mirroring libsession's URL registry
* (`session_protocol.cpp`, the `url_pro_*` fields).
*
* Copies rather than reads: the registry is a C struct of `const char*` with no accessor exposed to
* Kotlin, so consuming it directly would mean adding JNI surface for five constants.
*
* Being copies, they can drift from it, and they have — every value here was once wrong at the use site
* that needed it. They are defined together so that comparing this file against the registry is the whole
* check; correcting a single link where it happens to be used is what let them diverge one at a time.
*/
import network.loki.messenger.libsession_util.pro.BackendRequests

object ProUrls {
const val FAQ = "https://getsession.org/pro#faq"
const val PRIVACY_POLICY = "https://getsession.org/pro-privacy"
const val ROADMAP = "https://getsession.org/pro#roadmap"
const val SUPPORT = "https://getsession.org/pro-support"
const val TERMS_OF_SERVICE = "https://getsession.org/pro-terms"
val FAQ: String get() = BackendRequests.proUrls().faq
val PRIVACY_POLICY: String get() = BackendRequests.proUrls().privacyPolicy
val ROADMAP: String get() = BackendRequests.proUrls().roadmap
val SUPPORT: String get() = BackendRequests.proUrls().support
val TERMS_OF_SERVICE: String get() = BackendRequests.proUrls().termsOfService
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ kotlinxImmutableVersion = "0.5.1"
kryoVersion = "5.6.2"
kspVersion = "2.3.11"
legacySupportV13Version = "1.0.0"
libsessionUtilAndroidVersion = "1.1.0-49-g5dbfffc"
libsessionUtilAndroidVersion = "1.1.0-53-gc8906a1"
media3ExoplayerVersion = "1.10.0"
mockitoCoreVersion = "5.23.0"
navVersion = "2.9.8"
Expand Down
Loading