diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/BadgeImageView.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/BadgeImageView.kt index 3750f02674..5d12d596eb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/BadgeImageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/BadgeImageView.kt @@ -8,6 +8,7 @@ import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.badges.glide.BadgeSpriteTransformation import org.thoughtcrime.securesms.badges.models.Badge +import org.thoughtcrime.securesms.components.settings.app.subscription.BadgeImageSize import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge import org.thoughtcrime.securesms.glide.GiftBadgeModel import org.thoughtcrime.securesms.mms.GlideApp @@ -31,6 +32,10 @@ class BadgeImageView @JvmOverloads constructor( isClickable = false } + constructor(context: Context, badgeImageSize: BadgeImageSize) : this(context) { + badgeSize = badgeImageSize.sizeCode + } + override fun setOnClickListener(l: OnClickListener?) { val wasClickable = isClickable super.setOnClickListener(l) diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/models/Badge.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/models/Badge.kt index 2b616b49f6..c1cabbe0b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/models/Badge.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/models/Badge.kt @@ -6,6 +6,7 @@ import android.os.Parcelable import android.view.View import android.widget.ImageView import android.widget.TextView +import androidx.compose.runtime.Stable import com.bumptech.glide.load.Key import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy @@ -25,6 +26,7 @@ typealias OnBadgeClicked = (Badge, Boolean, Boolean) -> Unit /** * A Badge that can be collected and displayed by a user. */ +@Stable @Parcelize data class Badge( val id: String, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/BadgeImage.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/BadgeImage.kt new file mode 100644 index 0000000000..6b4b09d462 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/BadgeImage.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.subscription + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.viewinterop.AndroidView +import org.thoughtcrime.securesms.badges.BadgeImageView +import org.thoughtcrime.securesms.badges.models.Badge + +enum class BadgeImageSize(val sizeCode: Int) { + SMALL(0), + MEDIUM(1), + LARGE(2), + X_LARGE(3), + BADGE_64(4), + BADGE_112(5) +} + +@Composable +fun BadgeImage112( + badge: Badge?, + modifier: Modifier = Modifier +) { + if (LocalInspectionMode.current) { + Box(modifier = modifier.background(color = Color.Red)) + } else { + AndroidView( + factory = { + BadgeImageView(it, BadgeImageSize.BADGE_112) + }, + update = { + it.setBadge(badge) + } + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPendingBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPendingBottomSheet.kt new file mode 100644 index 0000000000..8909583a6f --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPendingBottomSheet.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components.settings.app.subscription + +import android.content.DialogInterface +import android.net.Uri +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import org.signal.core.ui.BottomSheets +import org.signal.core.ui.Buttons +import org.signal.core.ui.Texts +import org.signal.core.ui.theme.SignalTheme +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.badges.models.Badge +import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity +import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonateToSignalType +import org.thoughtcrime.securesms.compose.ComposeBottomSheetDialogFragment +import org.thoughtcrime.securesms.util.SpanUtil + +/** + * Displayed after the user completes the donation flow for a bank transfer. + */ +class DonationPendingBottomSheet : ComposeBottomSheetDialogFragment() { + + private val args: DonationPendingBottomSheetArgs by navArgs() + + @Composable + override fun SheetContent() { + DonationPendingBottomSheetContent( + badge = args.request.badge, + onDoneClick = this::onDoneClick + ) + } + + private fun onDoneClick() { + dismissAllowingStateLoss() + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + + if (args.request.donateToSignalType == DonateToSignalType.ONE_TIME) { + findNavController().popBackStack() + } else { + requireActivity().finish() + requireActivity().startActivity(AppSettingsActivity.manageSubscriptions(requireContext())) + } + } +} + +@Preview +@Composable +fun DonationPendingBottomSheetContentPreview() { + SignalTheme { + Surface { + DonationPendingBottomSheetContent( + badge = Badge( + id = "", + category = Badge.Category.Donor, + name = "Signal Star", + description = "", + imageUrl = Uri.EMPTY, + imageDensity = "", + expirationTimestamp = 0L, + visible = true, + duration = 0L + ), + onDoneClick = {} + ) + } + } +} + +@Composable +private fun DonationPendingBottomSheetContent( + badge: Badge, + onDoneClick: () -> Unit +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(horizontal = 44.dp) + ) { + BottomSheets.Handle() + + BadgeImage112( + badge = badge, + modifier = Modifier + .padding(top = 21.dp, bottom = 16.dp) + .size(80.dp) + ) + + Text( + text = stringResource(id = R.string.DonationPendingBottomSheet__donation_pending), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(bottom = 8.dp) + ) + + // TODO [sepa] -- Need proper copy here for one-time donations. + Text( + text = stringResource(id = R.string.DonationPendingBottomSheet__your_monthly_donation_is_pending, badge.name), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(bottom = 20.dp) + ) + + val learnMore = stringResource(id = R.string.DonationPendingBottomSheet__learn_more) + val fullString = stringResource(id = R.string.DonationPendingBottomSheet__bank_transfers_usually_take, learnMore) + val spanned = SpanUtil.urlSubsequence(fullString, learnMore, "") // TODO [sepa] URL + Texts.LinkifiedText( + textWithUrlSpans = spanned, + onUrlClick = {}, // TODO [sepa] URL + style = LocalTextStyle.current.copy(textAlign = TextAlign.Center, color = MaterialTheme.colorScheme.onSurfaceVariant), + modifier = Modifier.padding(bottom = 48.dp) + ) + + Buttons.LargeTonal( + onClick = onDoneClick, + modifier = Modifier + .defaultMinSize(minWidth = 220.dp) + .padding(bottom = 56.dp) + ) { + Text(text = stringResource(id = R.string.DonationPendingBottomSheet__done)) + } + } +} diff --git a/app/src/main/res/navigation/donate_to_signal.xml b/app/src/main/res/navigation/donate_to_signal.xml index b7a162a528..213509859c 100644 --- a/app/src/main/res/navigation/donate_to_signal.xml +++ b/app/src/main/res/navigation/donate_to_signal.xml @@ -45,6 +45,9 @@ + @@ -249,4 +252,16 @@ app:destination="@id/yourInformationIsPrivateBottomSheet" /> + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 59badd3117..b0e32ec92e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5865,6 +5865,17 @@ Look for your IBAN number at the top of your bank statement. IBAN numbers contain up to 32 characters. The name you enter should match your full name on your bank account. Contact your bank for more information. + + Donation pending + + Your monthly donation is pending. You’ll be able to display the %1$s badge on your profile when your donation is received. + + Bank transfers usually take 1 business day to process. %1$s + + Learn more + + Done + Cancelling…