Skip to content

Commit 0a8821a

Browse files
Merge pull request #9880 from rafaeltonholo/feat/9862/create-error-notifications-full-screen-dialog
feat(in-app-notifications): create error notifications full screen dialog
2 parents 4c4025f + 7f5b3ed commit 0a8821a

18 files changed

Lines changed: 344 additions & 101 deletions

File tree

feature/notification/api/src/androidMain/kotlin/net/thunderbird/feature/notification/api/ui/BannerInlineNotificationListHost.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.thunderbird.feature.notification.api.ui
22

33
import androidx.compose.animation.AnimatedContent
4+
import androidx.compose.animation.animateContentSize
45
import androidx.compose.foundation.layout.Arrangement
56
import androidx.compose.foundation.layout.Column
67
import androidx.compose.foundation.layout.fillMaxWidth
@@ -69,6 +70,7 @@ fun BannerInlineNotificationListHost(
6970
targetState = bannerInlineSet,
7071
modifier = modifier.testTagAsResourceId(TEST_TAG_HOST_PARENT),
7172
transitionSpec = { bannerSlideInSlideOutAnimationSpec() },
73+
contentKey = { it.isEmpty() },
7274
) { bannerInlineSet ->
7375
if (bannerInlineSet.isNotEmpty()) {
7476
BannerInlineNotificationListHostLayout(
@@ -121,6 +123,7 @@ private fun BannerInlineNotificationListHostLayout(
121123
}
122124
},
123125
behaviour = BannerInlineNotificationCardBehaviour.Clipped,
126+
modifier = Modifier.animateContentSize(),
124127
)
125128
}
126129

@@ -139,7 +142,9 @@ private fun BannerInlineNotificationListHostLayout(
139142
modifier = Modifier.testTagAsResourceId(TEST_TAG_CHECK_ERROR_NOTIFICATIONS_ACTION),
140143
)
141144
},
142-
modifier = Modifier.testTagAsResourceId(TEST_TAG_CHECK_ERROR_NOTIFICATIONS),
145+
modifier = Modifier
146+
.testTagAsResourceId(TEST_TAG_CHECK_ERROR_NOTIFICATIONS)
147+
.animateContentSize(),
143148
)
144149
}
145150
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
package net.thunderbird.feature.notification.api.ui.action
22

33
import androidx.compose.runtime.Composable
4-
import androidx.compose.runtime.LaunchedEffect
5-
import androidx.compose.runtime.getValue
6-
import androidx.compose.runtime.mutableStateOf
7-
import androidx.compose.runtime.remember
8-
import androidx.compose.runtime.setValue
94
import androidx.compose.ui.Modifier
105
import app.k9mail.core.ui.compose.designsystem.molecule.notification.NotificationActionButton
11-
import kotlin.let
6+
import org.jetbrains.compose.resources.stringResource
127

138
/**
149
* Displays a [NotificationActionButton] with the title resolved from the [NotificationAction].
@@ -27,19 +22,19 @@ internal fun ResolvedNotificationActionButton(
2722
onActionClick: (NotificationAction) -> Unit,
2823
modifier: Modifier = Modifier,
2924
) {
30-
var text by remember(action) { mutableStateOf<String?>(value = null) }
25+
NotificationActionButton(
26+
text = when (val labelResource = action.labelResource) {
27+
null if action.label.isNotEmpty() -> action.label
28+
null -> error(
29+
"You must specify at least one of labelResource or label in ${
30+
action::class.simpleName
31+
}",
32+
)
3133

32-
LaunchedEffect(action) {
33-
text = action.resolveTitle()
34-
}
35-
36-
text?.let { text ->
37-
NotificationActionButton(
38-
text = text,
39-
onClick = {
40-
onActionClick(action)
41-
},
42-
modifier = modifier,
43-
)
44-
}
34+
else -> stringResource(labelResource)
35+
},
36+
onClick = { onActionClick(action) },
37+
isExternalLink = action is NotificationAction.ViewSupportArticle,
38+
modifier = modifier,
39+
)
4540
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package net.thunderbird.feature.notification.api.ui.dialog
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.PaddingValues
6+
import androidx.compose.foundation.lazy.LazyColumn
7+
import androidx.compose.runtime.Composable
8+
import androidx.compose.runtime.remember
9+
import androidx.compose.ui.Modifier
10+
import androidx.compose.ui.window.Dialog
11+
import androidx.compose.ui.window.DialogProperties
12+
import app.k9mail.core.ui.compose.designsystem.atom.Surface
13+
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonIcon
14+
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
15+
import app.k9mail.core.ui.compose.designsystem.organism.TopAppBar
16+
import app.k9mail.core.ui.compose.designsystem.organism.banner.inline.BannerInlineNotificationCardBehaviour
17+
import app.k9mail.core.ui.compose.designsystem.organism.banner.inline.ErrorBannerInlineNotificationCard
18+
import app.k9mail.core.ui.compose.theme2.MainTheme
19+
import kotlinx.collections.immutable.ImmutableSet
20+
import net.thunderbird.core.ui.compose.common.modifier.testTagAsResourceId
21+
import net.thunderbird.feature.notification.api.ui.action.NotificationAction
22+
import net.thunderbird.feature.notification.api.ui.action.ResolvedNotificationActionButton
23+
import net.thunderbird.feature.notification.api.ui.host.visual.BannerInlineVisual
24+
25+
@Composable
26+
fun ErrorNotificationsDialog(
27+
visuals: ImmutableSet<BannerInlineVisual>,
28+
onDismiss: () -> Unit,
29+
onNotificationActionClick: (NotificationAction) -> Unit,
30+
modifier: Modifier = Modifier,
31+
properties: DialogProperties = ErrorNotificationsDialogDefaults.defaultProperties,
32+
) {
33+
val showDialog = remember(visuals) { visuals.isNotEmpty() }
34+
if (showDialog) {
35+
Dialog(
36+
onDismissRequest = onDismiss,
37+
properties = properties,
38+
) {
39+
ErrorNotificationsDialogContent(
40+
bannerInlineVisuals = visuals,
41+
onDismiss = onDismiss,
42+
onNotificationActionClick = onNotificationActionClick,
43+
modifier = modifier,
44+
)
45+
}
46+
}
47+
}
48+
49+
@Composable
50+
private fun ErrorNotificationsDialogContent(
51+
bannerInlineVisuals: ImmutableSet<BannerInlineVisual>,
52+
onDismiss: () -> Unit,
53+
onNotificationActionClick: (NotificationAction) -> Unit,
54+
modifier: Modifier = Modifier,
55+
) {
56+
Surface(modifier = modifier) {
57+
Column {
58+
TopAppBar(
59+
title = "Error Notifications",
60+
navigationIcon = {
61+
ButtonIcon(onClick = onDismiss, imageVector = Icons.Outlined.Close)
62+
},
63+
)
64+
LazyColumn(
65+
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
66+
contentPadding = PaddingValues(
67+
top = MainTheme.spacings.default,
68+
start = MainTheme.spacings.double,
69+
end = MainTheme.spacings.double,
70+
bottom = MainTheme.spacings.double,
71+
),
72+
) {
73+
items(
74+
count = bannerInlineVisuals.size,
75+
) { index ->
76+
val item = bannerInlineVisuals.elementAt(index)
77+
ErrorBannerInlineNotificationCard(
78+
title = item.title,
79+
supportingText = item.supportingText,
80+
actions = {
81+
item.actions.forEachIndexed { actionIndex, action ->
82+
ResolvedNotificationActionButton(
83+
action = action,
84+
onActionClick = onNotificationActionClick,
85+
modifier = Modifier.testTagAsResourceId(
86+
tag = ErrorNotificationsDialogDefaults.testTagBannerInlineListItemAction(
87+
index = index,
88+
actionIndex = actionIndex,
89+
),
90+
),
91+
)
92+
}
93+
},
94+
behaviour = BannerInlineNotificationCardBehaviour.Expanded,
95+
)
96+
}
97+
}
98+
}
99+
}
100+
}
101+
102+
object ErrorNotificationsDialogDefaults {
103+
const val DEFAULT_TAG = "error_notifications_dialog"
104+
105+
val defaultProperties = DialogProperties(
106+
dismissOnBackPress = true,
107+
dismissOnClickOutside = false,
108+
usePlatformDefaultWidth = false,
109+
)
110+
111+
internal fun testTagBannerInlineListItemAction(index: Int, actionIndex: Int) =
112+
"${DEFAULT_TAG}_banner_inline_notification_list_item_action_${index}_$actionIndex"
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package net.thunderbird.feature.notification.api.ui.dialog
2+
3+
import androidx.fragment.app.FragmentManager
4+
import net.thunderbird.feature.notification.api.ui.action.NotificationAction
5+
6+
fun interface ErrorNotificationsDialogFragmentFactory {
7+
fun show(fragmentManager: FragmentManager)
8+
}
9+
10+
fun interface ErrorNotificationsDialogFragmentActionListener {
11+
fun onNotificationActionClick(action: NotificationAction)
12+
}

feature/notification/api/src/androidUnitTest/kotlin/net/thunderbird/feature/notification/api/ui/BannerGlobalNotificationHostTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ class BannerGlobalNotificationHostTest : ComposeTest() {
238238
title = title,
239239
contentText = contentText,
240240
severity = NotificationSeverity.Fatal,
241-
action = createFakeNotificationAction(title = actionText),
241+
action = createFakeNotificationAction(label = actionText),
242242
)
243243
mainClock.autoAdvance = false
244244

@@ -319,7 +319,7 @@ class BannerGlobalNotificationHostTest : ComposeTest() {
319319
title = title,
320320
contentText = contentText,
321321
severity = NotificationSeverity.Fatal,
322-
action = createFakeNotificationAction(title = actionText),
322+
action = createFakeNotificationAction(label = actionText),
323323
)
324324
mainClock.autoAdvance = false
325325

feature/notification/api/src/androidUnitTest/kotlin/net/thunderbird/feature/notification/api/ui/BannerInlineNotificationListHostTest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class BannerInlineNotificationListHostTest : ComposeTest() {
4848
// Arrange
4949
val title = "Notification in test"
5050
val supportingText = "The supporting text"
51-
val action = createFakeNotificationAction(title = "Action 1")
51+
val action = createFakeNotificationAction(label = "Action 1")
5252
val notification = createNotification(title = title, supportingText = supportingText, actions = setOf(action))
5353
mainClock.autoAdvance = false
5454
setContentWithTheme {
@@ -94,7 +94,7 @@ class BannerInlineNotificationListHostTest : ComposeTest() {
9494

9595
actionButton
9696
.onChildren()
97-
.filterToOne(hasTextExactly(action.title))
97+
.filterToOne(hasTextExactly(action.label))
9898
.assertIsDisplayed()
9999
},
100100
)
@@ -200,7 +200,7 @@ class BannerInlineNotificationListHostTest : ComposeTest() {
200200
// Arrange
201201
val title = "Notification in test"
202202
val supportingText = "The supporting text"
203-
val action = createFakeNotificationAction(title = "Action 1")
203+
val action = createFakeNotificationAction(label = "Action 1")
204204
val notification = createNotification(title = title, supportingText = supportingText, actions = setOf(action))
205205
mainClock.autoAdvance = false
206206
val actionClicked = mutableStateOf<NotificationAction?>(value = null)
@@ -215,7 +215,7 @@ class BannerInlineNotificationListHostTest : ComposeTest() {
215215
onNodeWithTag(BUTTON_NOTIFICATION_TEST_TAG).performClick()
216216
// Advance Animation
217217
mainClock.advanceTimeBy(1000L)
218-
onNodeWithText(action.title).performClick()
218+
onNodeWithText(action.label).performClick()
219219

220220
// Assert
221221
printSemanticTree()
@@ -320,8 +320,8 @@ class BannerInlineNotificationListHostTest : ComposeTest() {
320320
title: String,
321321
supportingText: String,
322322
actions: Set<NotificationAction> = setOf(
323-
createFakeNotificationAction(title = "Action 1"),
324-
createFakeNotificationAction(title = "Action 2"),
323+
createFakeNotificationAction(label = "Action 1"),
324+
createFakeNotificationAction(label = "Action 2"),
325325
),
326326
): FakeInAppOnlyNotification {
327327
return FakeInAppOnlyNotification(

feature/notification/api/src/commonMain/composeResources/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,5 @@
6060
<string name="banner_inline_notification_check_error_notifications">Check Error Notifications</string>
6161
<string name="banner_inline_notification_some_messages_need_attention">Some messages need your attention.</string>
6262
<string name="banner_inline_notification_open_notifications">Open notifications</string>
63+
<string name="banner_inline_notification_view_support_article">View support article</string>
6364
</resources>

feature/notification/api/src/commonMain/kotlin/net/thunderbird/feature/notification/api/content/PushServiceNotification.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ sealed class PushServiceNotification : AppNotification(), SystemNotification {
181181
private suspend fun buildNotificationActions(): Set<NotificationAction> = setOf(
182182
NotificationAction.Tap(),
183183
NotificationAction.CustomAction(
184-
title = getString(resource = Res.string.push_info_disable_push_action),
184+
label = getString(resource = Res.string.push_info_disable_push_action),
185185
icon = NotificationActionIcons.DisablePushAction,
186186
),
187187
)

0 commit comments

Comments
 (0)