From 4ef5fdc566a037da052efceadc85cf08183d3a7b Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Wed, 4 Nov 2026 09:09:02 +0000 Subject: [PATCH] ui: what's new crowdfunding card opens the crowdfunding page again (#8022) * ui: what's new crowdfunding card opens the crowdfunding page, with ask SimpleX team * ui: what's new references with the crowdfunding card * ui: keep the what's new card texts, only open the crowdfunding page from it --- .../Views/Onboarding/WhatsNewView.swift | 25 +++++++++-- .../common/views/chatlist/GetStakeBanner.kt | 4 +- .../common/views/onboarding/WhatsNewView.kt | 41 +++++++++++++------ .../common/views/usersettings/SettingsView.kt | 2 +- 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift index 178cff4072..bbdc300513 100644 --- a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift +++ b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift @@ -839,6 +839,7 @@ fileprivate struct CreateUpdateAddressShortLink: View { fileprivate struct InvestInSimpleXChat: View { @EnvironmentObject var theme: AppTheme + @State private var showGetStakeSheet = false var body: some View { VStack(alignment: .leading, spacing: 4) { @@ -850,13 +851,16 @@ fileprivate struct InvestInSimpleXChat: View { .cornerRadius(12) .padding(.top, 4) .padding(.bottom, 8) - .onTapGesture { UIApplication.shared.open(wefunderURL) } + .onTapGesture { showGetStakeSheet = true } #endif Text("Learn more on Wefunder.") .foregroundColor(theme.colors.primary) - .onTapGesture { UIApplication.shared.open(wefunderURL) } + .onTapGesture { showGetStakeSheet = true } } .frame(maxWidth: .infinity, alignment: .leading) + .sheet(isPresented: $showGetStakeSheet) { + GetStakeView(fromSettings: false) + } } } @@ -889,7 +893,11 @@ fileprivate let getStakeSlides: [(image: String, heading: String, info: String?, let wefunderURL = URL(string: "https://wefunder.com/simplex.chat?utm_source=app_ios")! +private let simplexCrowdfundingURL = URL(string: "simplex:/a#JxGcOA1_QhlmVFzYYabloMbvMZk5Y9d9iS3ITDnhzYo?h=smp11.simplex.im")! + struct GetStakeView: View { + @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var chatModel: ChatModel var fromSettings: Bool var body: some View { @@ -921,7 +929,18 @@ struct GetStakeView: View { Text(verbatim: "Learn more on Wefunder") } .buttonStyle(OnboardingButtonStyle()) - .padding(.bottom) + + Button { + dismiss() + DispatchQueue.main.async { + ChatModel.shared.appOpenUrl = simplexCrowdfundingURL + } + } label: { + Text(verbatim: "or ask SimpleX team") + .font(.callout) + } + .disabled(chatModel.chatRunning != true) + .frame(maxWidth: .infinity) } .padding() } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/GetStakeBanner.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/GetStakeBanner.kt index 51c8c2da16..ac247ec9d6 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/GetStakeBanner.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/GetStakeBanner.kt @@ -73,8 +73,8 @@ fun GetStakeBanner(showDismiss: Boolean, onTap: () -> Unit, onDismiss: () -> Uni fun openGetStake(modalManager: ModalManager) { appPrefs.getStakeBannerTapped.set(true) - modalManager.showModal(cardScreen = true) { - GetStakeView(inCenterOfWindow = modalManager === ModalManager.center) + modalManager.showModalCloseable(cardScreen = true) { close -> + GetStakeView(inCenterOfWindow = modalManager === ModalManager.center, close = close) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/WhatsNewView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/WhatsNewView.kt index e54e918f77..71e53ba974 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/WhatsNewView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/WhatsNewView.kt @@ -971,7 +971,7 @@ private val versionDescriptions: List = listOf( VersionFeature.FeatureView( icon = null, titleId = MR.strings.v7_1_invest, - view = { InvestInSimpleXChatView() } + view = { modalManager -> InvestInSimpleXChatView(modalManager) } ), VersionFeature.FeatureDescription( icon = MR.images.ic_alternate_email, @@ -998,7 +998,7 @@ private val versionDescriptions: List = listOf( VersionFeature.FeatureView( icon = null, titleId = MR.strings.v7_1_invest, - view = { InvestInSimpleXChatView() } + view = { modalManager -> InvestInSimpleXChatView(modalManager) } ), VersionFeature.FeatureView( icon = null, @@ -1083,6 +1083,8 @@ fun shouldShowWhatsNew(m: ChatModel): Boolean { val WEFUNDER_URL = "https://wefunder.com/simplex.chat?utm_source=$APP_UTM_SOURCE" +private const val CROWDFUNDING_CONTACT_URI = "simplex:/a#JxGcOA1_QhlmVFzYYabloMbvMZk5Y9d9iS3ITDnhzYo?h=smp11.simplex.im" + // the center modal takes the remaining width of the window, so the image is limited to its design width private val MAX_CROWDFUNDING_IMAGE_WIDTH = DEFAULT_MIN_CENTER_MODAL_WIDTH @@ -1095,8 +1097,12 @@ private const val CROWDFUNDING_IMAGE_CORNER_RADIUS_RATIO = 0.03f private class CrowdfundingLayout( val maxImageWidth: Dp, - val imageShape: Shape -) + val imageShape: Shape, + // the modal manager that shows the page in the center of the window, or null when nothing does + private val centerOfWindow: ModalManager? +) { + fun inCenterOfWindow(modalManager: ModalManager) = modalManager === centerOfWindow +} // the images are designed for the width of a phone screen, which Android always gives them. On desktop // they are limited to their own width, and their radius is scaled with them, as they are still shown @@ -1106,9 +1112,9 @@ private val crowdfundingLayout = if (appPlatform.isDesktop) CrowdfundingLayout(CROWDFUNDING_PAGE_IMAGE_WIDTH, object : Shape { override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline = Outline.Rounded(RoundRect(size.toRect(), CornerRadius(size.width * CROWDFUNDING_IMAGE_CORNER_RADIUS_RATIO))) - }) + }, ModalManager.center) else - CrowdfundingLayout(Dp.Unspecified, RoundedCornerShape(CROWDFUNDING_IMAGE_CORNER_RADIUS)) + CrowdfundingLayout(Dp.Unspecified, RoundedCornerShape(CROWDFUNDING_IMAGE_CORNER_RADIUS), null) // Google Play policy restricts promoting investments, so Play builds only show it in the US @Composable @@ -1128,10 +1134,9 @@ fun isInUs(): Boolean = || androidPlayStoreCountry.value == null @Composable -private fun InvestInSimpleXChatView() { +private fun InvestInSimpleXChatView(modalManager: ModalManager) { if (!crowdfundingAvailable()) return - val uriHandler = LocalUriHandler.current - val openWefunder = { uriHandler.openUriCatching(WEFUNDER_URL) } + val showGetStake = { modalManager.showModalCloseable(cardScreen = true) { close -> GetStakeView(inCenterOfWindow = crowdfundingLayout.inCenterOfWindow(modalManager), close = close) } } Column(modifier = Modifier.padding(bottom = 12.dp)) { Text( generalGetString(MR.strings.v7_1_invest), @@ -1153,7 +1158,7 @@ private fun InvestInSimpleXChatView() { .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null, - onClick = openWefunder + onClick = showGetStake ) ) } @@ -1166,7 +1171,7 @@ private fun InvestInSimpleXChatView() { .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null, - onClick = openWefunder + onClick = showGetStake ) ) } @@ -1208,8 +1213,9 @@ private val getStakeSlides: List = listOf( ) @Composable -fun GetStakeView(inCenterOfWindow: Boolean = false) { +fun GetStakeView(inCenterOfWindow: Boolean = false, close: () -> Unit) { val uriHandler = LocalUriHandler.current + val stopped = chatModel.chatRunning.value == false @Composable fun slideImage(slide: CrowdfundingSlide) { @@ -1259,7 +1265,7 @@ fun GetStakeView(inCenterOfWindow: Boolean = false) { } Column( - Modifier.fillMaxWidth().padding(vertical = DEFAULT_PADDING * 2), + Modifier.fillMaxWidth().padding(top = DEFAULT_PADDING * 2), horizontalAlignment = Alignment.CenterHorizontally ) { OnboardingActionButton( @@ -1268,6 +1274,15 @@ fun GetStakeView(inCenterOfWindow: Boolean = false) { onboarding = null, onclick = { uriHandler.openUriCatching(WEFUNDER_URL) } ) + if (!chatModel.desktopNoUserNoRemote) { + TextButtonBelowOnboardingButton( + "or ask SimpleX team", + onClick = if (stopped) null else ({ + close() + uriHandler.openVerifiedSimplexUri(CROWDFUNDING_CONTACT_URI) + }) + ) + } } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt index 5074f97dcd..cdc41b85ad 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt @@ -143,7 +143,7 @@ fun SettingsLayout( SettingsActionItem( painterResource(MR.images.ic_redeem), stringResource(MR.strings.v7_0_crowdfunding), - { ModalManager.start.showModal(cardScreen = true) { GetStakeView() } } + { ModalManager.start.showModalCloseable(cardScreen = true) { close -> GetStakeView(close = close) } } ) } }