Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
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 @@ -201,20 +201,26 @@ internal class DebugNotificationSectionViewModel(
state: State,
): Notification? = when (notificationType) {
AuthenticationErrorNotification::class -> AuthenticationErrorNotification(
isIncomingServerError = true,
accountUuid = selectedAccount.uuid,
accountDisplayName = accountDisplay,
accountNumber = 0,
)

CertificateErrorNotification::class -> CertificateErrorNotification(
isIncomingServerError = true,
accountUuid = selectedAccount.uuid,
accountDisplayName = accountDisplay,
accountNumber = 0,
)

FailedToCreateNotification::class -> FailedToCreateNotification(
accountUuid = selectedAccount.uuid,
failedNotification = AuthenticationErrorNotification(
isIncomingServerError = true,
accountUuid = selectedAccount.uuid,
accountDisplayName = accountDisplay,
accountNumber = 0,
),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ fun InAppNotificationHost(

val state by hostStateHolder.currentInAppNotificationHostState.collectAsState()

LaunchedEffect(inAppNotificationEvents) {
LaunchedEffect(inAppNotificationEvents, eventFilter) {
val event = inAppNotificationEvents
if (event != null && eventFilter(event)) {
when (event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.SubcomposeLayout
import androidx.compose.ui.layout.SubcomposeMeasureScope
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMap
import androidx.compose.ui.util.fastMaxBy
Expand Down Expand Up @@ -73,10 +74,17 @@ internal fun InAppNotificationHostLayout(
// In case the maxHeight is not defined (for example when no content is passed to the content lambda),
// we manually calculate the layout height to avoid a crash caused by the pre-condition check
// of the layout function.
val layoutHeight = if (constraints.maxHeight == Constraints.Infinity) {
bannerGlobalHeight + bannerInlineListHeight.roundToInt() + mainContentHeight
} else {
constraints.maxHeight
val layoutHeight = when {
constraints.minHeight == 0 ||
constraints.maxHeight == Constraints.Infinity -> {
constraints.constrainHeight(
bannerGlobalHeight + bannerInlineListHeight.roundToInt() + mainContentHeight,
)
}

else -> {
constraints.maxHeight
}
}

layout(layoutWidth, layoutHeight) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@
<string name="notification_notify_error_text">An error has occurred while trying to create a system notification for a new message. The reason is most likely a missing notification sound.\n\nTap to open notification settings.</string>

<string name="notification_authentication_error_title">Authentication failed</string>
<string name="notification_authentication_error_text">Authentication failed for %s. Update your server settings.</string>
<string name="notification_authentication_error_text">Authentication failed for %1$s. Update your server settings.</string>

<string name="notification_certificate_error_public">Certificate error</string>
<string name="notification_certificate_error_title">Certificate error for %s</string>
<string name="notification_certificate_error_title">Certificate error for %1$s</string>
<string name="notification_certificate_error_text">Check your server settings</string>

<string name="notification_bg_sync_ticker">Checking mail: %1$s: %2$s</string>
<string name="notification_bg_sync_title">Checking mail</string>
<string name="notification_bg_sync_text">%1$s: %2$s</string>
<string name="notification_bg_send_ticker">Sending mail: %s</string>
<string name="notification_bg_send_ticker">Sending mail: %1$s</string>
<string name="notification_bg_send_title">Sending mail</string>

<string name="send_failure_subject">Failed to send some messages</string>

<plurals name="notification_new_messages_title">
<item quantity="one">%d new message</item>
<item quantity="other">%d new messages</item>
<item quantity="one">%1$d new message</item>
<item quantity="other">%1$d new messages</item>
</plurals>
<string name="notification_additional_messages">+ %1$d more on %2$s</string>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import net.thunderbird.feature.notification.api.ui.style.SystemNotificationStyle
* @see AppNotification
*/
sealed interface Notification {
val accountUuid: String?
val title: String
val accessibilityText: String
val contentText: String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import net.thunderbird.feature.notification.api.ui.action.NotificationAction
import net.thunderbird.feature.notification.api.ui.icon.AuthenticationError
import net.thunderbird.feature.notification.api.ui.icon.NotificationIcon
import net.thunderbird.feature.notification.api.ui.icon.NotificationIcons
import net.thunderbird.feature.notification.api.ui.style.inAppNotificationStyle
import net.thunderbird.feature.notification.resources.api.Res
import net.thunderbird.feature.notification.resources.api.notification_authentication_error_text
import net.thunderbird.feature.notification.resources.api.notification_authentication_error_title
Expand All @@ -18,16 +19,25 @@ import org.jetbrains.compose.resources.getString
*/
@ConsistentCopyVisibility
data class AuthenticationErrorNotification private constructor(
val isIncomingServerError: Boolean,
override val accountUuid: String,
val accountNumber: Int,
override val title: String,
override val contentText: String?,
override val channel: NotificationChannel,
override val icon: NotificationIcon = NotificationIcons.AuthenticationError,
) : AppNotification(), SystemNotification, InAppNotification {
override val severity: NotificationSeverity = NotificationSeverity.Fatal
override val actions: Set<NotificationAction> = setOf(
NotificationAction.Retry,
NotificationAction.UpdateServerSettings,
)
override val actions: Set<NotificationAction> = buildSet {
val action = if (isIncomingServerError) {
NotificationAction.UpdateIncomingServerSettings(accountUuid, accountNumber)
} else {
NotificationAction.UpdateOutgoingServerSettings(accountUuid, accountNumber)
}
add(action)
add(NotificationAction.Tap(override = action))
}
override val inAppNotificationStyle = inAppNotificationStyle { bannerInline() }

override fun asLockscreenNotification(): SystemNotification.LockscreenNotification =
SystemNotification.LockscreenNotification(
Expand All @@ -45,12 +55,17 @@ data class AuthenticationErrorNotification private constructor(
suspend operator fun invoke(
accountUuid: String,
accountDisplayName: String,
accountNumber: Int,
isIncomingServerError: Boolean,
): AuthenticationErrorNotification = AuthenticationErrorNotification(
title = getString(
resource = Res.string.notification_authentication_error_title,
isIncomingServerError = isIncomingServerError,
accountUuid = accountUuid,
accountNumber = accountNumber,
title = getString(resource = Res.string.notification_authentication_error_title),
contentText = getString(
resource = Res.string.notification_authentication_error_text,
accountDisplayName,
),
contentText = getString(resource = Res.string.notification_authentication_error_text),
channel = NotificationChannel.Miscellaneous(accountUuid = accountUuid),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,23 @@ import org.jetbrains.compose.resources.getString
*/
@ConsistentCopyVisibility
data class CertificateErrorNotification private constructor(
val isIncomingServerError: Boolean,
override val accountUuid: String,
val accountNumber: Int,
override val title: String,
override val contentText: String,
val lockScreenTitle: String,
override val channel: NotificationChannel,
override val icon: NotificationIcon = NotificationIcons.CertificateError,
) : AppNotification(), SystemNotification, InAppNotification {
override val severity: NotificationSeverity = NotificationSeverity.Fatal
override val actions: Set<NotificationAction> = setOf(NotificationAction.UpdateServerSettings)
override val actions: Set<NotificationAction> = setOf(
if (isIncomingServerError) {
NotificationAction.UpdateIncomingServerSettings(accountUuid, accountNumber)
} else {
NotificationAction.UpdateOutgoingServerSettings(accountUuid, accountNumber)
},
)

override fun asLockscreenNotification(): SystemNotification.LockscreenNotification =
SystemNotification.LockscreenNotification(
Expand All @@ -45,7 +54,12 @@ data class CertificateErrorNotification private constructor(
suspend operator fun invoke(
accountUuid: String,
accountDisplayName: String,
accountNumber: Int,
isIncomingServerError: Boolean,
): CertificateErrorNotification = CertificateErrorNotification(
isIncomingServerError = isIncomingServerError,
accountUuid = accountUuid,
accountNumber = accountNumber,
title = getString(resource = Res.string.notification_certificate_error_title, accountDisplayName),
lockScreenTitle = getString(resource = Res.string.notification_certificate_error_public),
contentText = getString(resource = Res.string.notification_certificate_error_text),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import org.jetbrains.compose.resources.getString
*/
@ConsistentCopyVisibility
data class FailedToCreateNotification private constructor(
override val accountUuid: String,
override val title: String,
override val contentText: String?,
override val channel: NotificationChannel,
Expand All @@ -38,6 +39,7 @@ data class FailedToCreateNotification private constructor(
accountUuid: String,
failedNotification: AppNotification,
): FailedToCreateNotification = FailedToCreateNotification(
accountUuid = accountUuid,
title = getString(resource = Res.string.notification_notify_error_title),
contentText = getString(resource = Res.string.notification_notify_error_text),
channel = NotificationChannel.Miscellaneous(accountUuid = accountUuid),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ sealed class MailNotification : AppNotification(), SystemNotification {
override val severity: NotificationSeverity = NotificationSeverity.Information

data class Fetching(
override val accountUuid: String,
override val title: String,
override val accessibilityText: String,
override val contentText: String?,
Expand Down Expand Up @@ -61,6 +62,7 @@ sealed class MailNotification : AppNotification(), SystemNotification {
): Fetching {
val title = getString(resource = Res.string.notification_bg_sync_title)
return Fetching(
accountUuid = accountUuid,
title = title,
accessibilityText = folderName?.let { folderName ->
getString(
Expand All @@ -83,6 +85,7 @@ sealed class MailNotification : AppNotification(), SystemNotification {
}

data class Sending(
override val accountUuid: String,
override val title: String,
override val accessibilityText: String,
override val contentText: String?,
Expand All @@ -106,6 +109,7 @@ sealed class MailNotification : AppNotification(), SystemNotification {
accountUuid: String,
accountDisplayName: String,
): Sending = Sending(
accountUuid = accountUuid,
title = getString(resource = Res.string.notification_bg_send_title),
accessibilityText = getString(
resource = Res.string.notification_bg_send_ticker,
Expand All @@ -118,6 +122,7 @@ sealed class MailNotification : AppNotification(), SystemNotification {
}

data class SendFailed(
override val accountUuid: String,
override val title: String,
override val contentText: String?,
override val channel: NotificationChannel,
Expand All @@ -143,6 +148,7 @@ sealed class MailNotification : AppNotification(), SystemNotification {
accountUuid: String,
exception: Exception,
): SendFailed = SendFailed(
accountUuid = accountUuid,
title = getString(resource = Res.string.send_failure_subject),
contentText = exception.rootCauseMassage,
channel = NotificationChannel.Miscellaneous(accountUuid = accountUuid),
Expand All @@ -163,7 +169,7 @@ sealed class MailNotification : AppNotification(), SystemNotification {
* @property group The notification group this notification belongs to, if any.
*/
data class NewMailSingleMail(
val accountUuid: String,
override val accountUuid: String,
val accountName: String,
val messagesNotificationChannelSuffix: String,
val summary: String,
Expand Down Expand Up @@ -208,7 +214,7 @@ sealed class MailNotification : AppNotification(), SystemNotification {
*/
@ConsistentCopyVisibility
data class NewMailSummaryMail private constructor(
val accountUuid: String,
override val accountUuid: String,
val accountName: String,
val messagesNotificationChannelSuffix: String,
override val title: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package net.thunderbird.feature.notification.api.content

import androidx.annotation.Discouraged
import kotlinx.coroutines.runBlocking

object NotificationFactoryCoroutineCompat {
@JvmStatic
@Discouraged("Should not be used outside a Java class.")
fun <T> create(builder: suspend () -> T): T = runBlocking { builder() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ sealed class PushServiceNotification : AppNotification(), SystemNotification {
override val actions: Set<NotificationAction>,
override val icon: NotificationIcon = NotificationIcons.PushServiceInitializing,
) : PushServiceNotification() {
override val accountUuid: String? = null

companion object {
/**
* Creates an [Initializing] notification.
Expand All @@ -67,6 +69,8 @@ sealed class PushServiceNotification : AppNotification(), SystemNotification {
override val actions: Set<NotificationAction>,
override val icon: NotificationIcon = NotificationIcons.PushServiceListening,
) : PushServiceNotification() {
override val accountUuid: String? = null

companion object {
/**
* Creates a new [Listening] push service notification.
Expand All @@ -92,6 +96,8 @@ sealed class PushServiceNotification : AppNotification(), SystemNotification {
override val actions: Set<NotificationAction>,
override val icon: NotificationIcon = NotificationIcons.PushServiceWaitBackgroundSync,
) : PushServiceNotification() {
override val accountUuid: String? = null

companion object {
/**
* Creates a [WaitBackgroundSync] notification.
Expand All @@ -117,6 +123,8 @@ sealed class PushServiceNotification : AppNotification(), SystemNotification {
override val actions: Set<NotificationAction>,
override val icon: NotificationIcon = NotificationIcons.PushServiceWaitNetwork,
) : PushServiceNotification() {
override val accountUuid: String? = null

companion object {
/**
* Creates a [WaitNetwork] notification.
Expand Down Expand Up @@ -145,6 +153,7 @@ sealed class PushServiceNotification : AppNotification(), SystemNotification {
override val contentText: String?,
override val icon: NotificationIcon = NotificationIcons.AlarmPermissionMissing,
) : PushServiceNotification(), InAppNotification {
override val accountUuid: String? = null
override val severity: NotificationSeverity = NotificationSeverity.Critical

companion object {
Expand All @@ -170,7 +179,7 @@ sealed class PushServiceNotification : AppNotification(), SystemNotification {
* @return A set of [NotificationAction] instances.
*/
private suspend fun buildNotificationActions(): Set<NotificationAction> = setOf(
NotificationAction.Tap,
NotificationAction.Tap(),
NotificationAction.CustomAction(
title = getString(resource = Res.string.push_info_disable_push_action),
icon = NotificationActionIcons.DisablePushAction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ interface InAppNotificationReceiver {
}

sealed interface InAppNotificationEvent {
data class Show(val notification: InAppNotification) : InAppNotificationEvent
data class Dismiss(val notification: InAppNotification) : InAppNotificationEvent
val notification: InAppNotification

data class Show(override val notification: InAppNotification) : InAppNotificationEvent
data class Dismiss(override val notification: InAppNotification) : InAppNotificationEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import net.thunderbird.feature.notification.api.content.Notification
/**
* Responsible for sending notifications by creating and executing the appropriate commands.
*/
interface NotificationSender {
fun interface NotificationSender {
/**
* Sends a notification by creating and executing the appropriate commands.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ sealed class NotificationAction {
*
* All [SystemNotification] will have this action implicitly, even if not specified in the
* [SystemNotification.actions] set.
*
* @property override The action that will override the tap action for this notification.
*/
data object Tap : NotificationAction() {
override val icon: NotificationIcon? = null
override val titleResource: StringResource? = null
data class Tap(val override: NotificationAction? = null) : NotificationAction() {
override val icon: NotificationIcon? = override?.icon
override val titleResource: StringResource? = override?.titleResource
}

/**
Expand Down Expand Up @@ -93,9 +95,16 @@ sealed class NotificationAction {
/**
* Action to prompt the user to update server settings, typically when authentication fails.
*/
data object UpdateServerSettings : NotificationAction() {
data class UpdateIncomingServerSettings(val accountUuid: String, val accountNumber: Int) : NotificationAction() {
override val icon: NotificationIcon = NotificationActionIcons.UpdateServerSettings
override val titleResource: StringResource = Res.string.notification_action_update_server_settings
}

/**
* Action to prompt the user to update server settings, typically when authentication fails.
*/
data class UpdateOutgoingServerSettings(val accountUuid: String, val accountNumber: Int) : NotificationAction() {
override val icon: NotificationIcon = NotificationActionIcons.UpdateServerSettings
override val titleResource: StringResource = Res.string.notification_action_update_server_settings
}

Expand Down
Loading
Loading