ui: show close button on badges banner only after it was opened (#7560)

This commit is contained in:
spaced4ndy
2026-09-22 13:40:56 +00:00
committed by GitHub
parent 150897d637
commit fae017d5b3
8 changed files with 34 additions and 4 deletions
@@ -15,6 +15,7 @@ struct SupportSimpleXBanner: View {
var title: LocalizedStringKey = "Support SimpleX"
var subtitle: LocalizedStringKey = "Get badge + better files"
var warning: Bool = false
var showDismiss: Bool = true
let onTap: () -> Void
let onDismiss: () -> Void
@@ -56,7 +57,9 @@ struct SupportSimpleXBanner: View {
.allowsHitTesting(false)
}
BannerDismissButton(onDismiss: onDismiss)
if showDismiss {
BannerDismissButton(onDismiss: onDismiss)
}
}
}
@@ -177,6 +177,7 @@ struct ChatListView: View {
@AppStorage(DEFAULT_ONE_HAND_UI_CARD_SHOWN) private var oneHandUICardShown = false
@AppStorage(DEFAULT_ADDRESS_CREATION_CARD_SHOWN) private var addressCreationCardShown = false
@AppStorage(DEFAULT_SUPPORTER_BANNER_SHOWN) private var supporterBannerShown = false
@AppStorage(DEFAULT_SUPPORTER_BANNER_TAPPED) private var supporterBannerTapped = false
@AppStorage(DEFAULT_GET_STAKE_BANNER_TAPPED) private var getStakeBannerTapped = false
@AppStorage(DEFAULT_GET_STAKE_BANNER_DISMISSED) private var getStakeBannerDismissed = false
@AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
@@ -516,7 +517,11 @@ struct ChatListView: View {
.onAppear { chatModel.chatListBanner = .badgeIssueFailed }
} else if chatModel.bannerSlotFree(for: .badgePitch) && !supporterBannerShown && noShownBadge && chatModel.chats.count > 3 {
SupportSimpleXBanner(
onTap: { showBadgesSheet = true },
showDismiss: supporterBannerTapped,
onTap: {
supporterBannerTapped = true
showBadgesSheet = true
},
onDismiss: showSupportSimpleXDismissAlert
)
.padding(.vertical, 3)
@@ -57,6 +57,7 @@ let DEFAULT_CHAT_ITEM_TAIL = "chatItemTail"
let DEFAULT_ONE_HAND_UI_CARD_SHOWN = "oneHandUICardShown"
let DEFAULT_ADDRESS_CREATION_CARD_SHOWN = "addressCreationCardShown"
let DEFAULT_SUPPORTER_BANNER_SHOWN = "supporterBannerShown"
let DEFAULT_SUPPORTER_BANNER_TAPPED = "supporterBannerTapped"
let DEFAULT_GET_STAKE_BANNER_TAPPED = "getStakeBannerTapped"
let DEFAULT_GET_STAKE_BANNER_DISMISSED = "getStakeBannerDismissed"
let DEFAULT_TOOLBAR_MATERIAL = "toolbarMaterial"
@@ -121,6 +122,7 @@ let appDefaults: [String: Any] = [
DEFAULT_ONE_HAND_UI_CARD_SHOWN: false,
DEFAULT_ADDRESS_CREATION_CARD_SHOWN: false,
DEFAULT_SUPPORTER_BANNER_SHOWN: false,
DEFAULT_SUPPORTER_BANNER_TAPPED: false,
DEFAULT_GET_STAKE_BANNER_TAPPED: false,
DEFAULT_GET_STAKE_BANNER_DISMISSED: false,
DEFAULT_TOOLBAR_MATERIAL: ToolbarMaterial.defaultMaterial,
@@ -155,6 +157,7 @@ let hintDefaults = [
DEFAULT_ONE_HAND_UI_CARD_SHOWN,
DEFAULT_ADDRESS_CREATION_CARD_SHOWN,
DEFAULT_SUPPORTER_BANNER_SHOWN,
DEFAULT_SUPPORTER_BANNER_TAPPED,
DEFAULT_GET_STAKE_BANNER_TAPPED,
DEFAULT_GET_STAKE_BANNER_DISMISSED,
DEFAULT_LIVE_MESSAGE_ALERT_SHOWN,
+4
View File
@@ -303,6 +303,10 @@ In the onboarding branch the `.scaleEffect` and `ThemedBackground` are applied t
|---------|--------|--------|
| `DEFAULT_GET_STAKE_BANNER_TAPPED` | [`openGetStake()`](../../Shared/Views/ChatList/ChatListView.swift#L408) | the dismiss X appears from then on, while there are chats |
| `DEFAULT_GET_STAKE_BANNER_DISMISSED` | the dismiss X | hides the banner in both placements |
| `DEFAULT_SUPPORTER_BANNER_TAPPED` | tapping the supporter pitch | the pitch's dismiss X appears from then on |
| `DEFAULT_SUPPORTER_BANNER_SHOWN` | the pitch's dismiss X, through its "You can support SimpleX later in Settings." alert, and a successful code redemption | hides the pitch |
The two badge alert banners always offer the X; only the pitch waits to be tapped once, so a user who has not looked at it cannot dismiss it unseen.
Both are in `hintDefaults`, so "Reset all hints" in the developer settings restores the banner. The X is never offered in the onboarding branch, so the banner cannot be dismissed before the user has a chat.
@@ -193,6 +193,7 @@ class AppPreferences {
val oneHandUICardShown = mkBoolPreference(SHARED_PREFS_ONE_HAND_UI_CARD_SHOWN, false)
val addressCreationCardShown = mkBoolPreference(SHARED_PREFS_ADDRESS_CREATION_CARD_SHOWN, false)
val supporterBannerShown = mkBoolPreference(SHARED_PREFS_SUPPORTER_BANNER_SHOWN, false)
val supporterBannerTapped = mkBoolPreference(SHARED_PREFS_SUPPORTER_BANNER_TAPPED, false)
val getStakeBannerTapped = mkBoolPreference(SHARED_PREFS_GET_STAKE_BANNER_TAPPED, false)
val getStakeBannerDismissed = mkBoolPreference(SHARED_PREFS_GET_STAKE_BANNER_DISMISSED, false)
val showMuteProfileAlert = mkBoolPreference(SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT, true)
@@ -277,6 +278,7 @@ class AppPreferences {
hintPref(oneHandUICardShown, false),
hintPref(addressCreationCardShown, false),
hintPref(supporterBannerShown, false),
hintPref(supporterBannerTapped, false),
hintPref(getStakeBannerTapped, false),
hintPref(getStakeBannerDismissed, false),
hintPref(liveMessageAlertShown, false),
@@ -471,6 +473,7 @@ class AppPreferences {
private const val SHARED_PREFS_ONE_HAND_UI_CARD_SHOWN = "OneHandUICardShown"
private const val SHARED_PREFS_ADDRESS_CREATION_CARD_SHOWN = "AddressCreationCardShown"
private const val SHARED_PREFS_SUPPORTER_BANNER_SHOWN = "SupporterBannerShown"
private const val SHARED_PREFS_SUPPORTER_BANNER_TAPPED = "SupporterBannerTapped"
private const val SHARED_PREFS_GET_STAKE_BANNER_TAPPED = "GetStakeBannerTapped"
private const val SHARED_PREFS_GET_STAKE_BANNER_DISMISSED = "GetStakeBannerDismissed"
private const val SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT = "ShowMuteProfileAlert"
@@ -27,6 +27,7 @@ fun SupportSimpleXBanner(
title: String = generalGetString(MR.strings.badges_banner_title),
subtitle: String = generalGetString(MR.strings.badges_banner_subtitle),
warning: Boolean = false,
showDismiss: Boolean = true,
onTap: () -> Unit,
onDismiss: () -> Unit
) {
@@ -75,7 +76,9 @@ fun SupportSimpleXBanner(
}
}
BannerDismissButton(Modifier.align(Alignment.TopEnd), onDismiss)
if (showDismiss) {
BannerDismissButton(Modifier.align(Alignment.TopEnd), onDismiss)
}
}
HeroThumbnail(
@@ -950,6 +950,7 @@ private fun BoxScope.ChatList(searchText: MutableState<TextFieldValue>, listStat
val oneHandUICardShown = remember { appPrefs.oneHandUICardShown.state }
val addressCreationCardShown = remember { appPrefs.addressCreationCardShown.state }
val supporterBannerShown = remember { appPrefs.supporterBannerShown.state }
val supporterBannerTapped = remember { appPrefs.supporterBannerTapped.state }
val getStakeBannerTapped = remember { appPrefs.getStakeBannerTapped.state }
val getStakeBannerDismissed = remember { appPrefs.getStakeBannerDismissed.state }
// read here rather than in the LazyColumn: it launches an effect, so it needs a composable scope
@@ -1074,7 +1075,11 @@ private fun BoxScope.ChatList(searchText: MutableState<TextFieldValue>, listStat
SideEffect { chatModel.chatListBanner = ChatListBanner.BadgePitch }
Box(Modifier.zIndex(1f).padding(16.dp)) {
SupportSimpleXBanner(
onTap = { ModalManager.start.showCustomModal { close -> BadgesView(close) } },
showDismiss = supporterBannerTapped.value,
onTap = {
appPrefs.supporterBannerTapped.set(true)
ModalManager.start.showCustomModal { close -> BadgesView(close) }
},
onDismiss = ::showSupportSimpleXDismissAlert
)
}
@@ -346,6 +346,10 @@ The list has a single banner slot, filled by an `if`/`else if` chain in priority
|------------|--------|--------|
| `getStakeBannerTapped` | `openGetStake()` | the dismiss X appears from then on, while there are chats |
| `getStakeBannerDismissed` | the dismiss X | hides the banner in both placements |
| `supporterBannerTapped` | tapping the supporter pitch | the pitch's dismiss X appears from then on |
| `supporterBannerShown` | the pitch's dismiss X, through its "You can support SimpleX later in Settings." alert, and a successful code redemption | hides the pitch |
The two badge alert banners always offer the X; only the pitch waits to be tapped once, so a user who has not looked at it cannot dismiss it unseen.
Both are in `AppPreferences.hintPreferences`, so "Reset all hints" restores the banner. The X is never offered below the onboarding cards, so the banner cannot be dismissed before the user has a chat.