Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -11,6 +11,8 @@ import net.thunderbird.feature.notification.api.NotificationGroup
import net.thunderbird.feature.notification.api.NotificationSeverity
import net.thunderbird.feature.notification.api.ui.action.NotificationAction
import net.thunderbird.feature.notification.api.ui.icon.NotificationIcon
import net.thunderbird.feature.notification.api.ui.style.InAppNotificationStyle
import net.thunderbird.feature.notification.api.ui.style.SystemNotificationStyle

/**
* Represents a notification that can be displayed to the user.
Expand Down Expand Up @@ -62,12 +64,17 @@ sealed class AppNotification : Notification {
* @property subText Additional text displayed below the content text, can be null.
* @property channel The notification channel to which this notification belongs.
* @property group The notification group to which this notification belongs, can be null.
* @property systemNotificationStyle The style of the system notification.
* Defaults to [SystemNotificationStyle.Undefined].
* @see LockscreenNotificationAppearance
* @see SystemNotificationStyle
* @see net.thunderbird.feature.notification.api.ui.style.systemNotificationStyle
*/
sealed interface SystemNotification : Notification {
val subText: String? get() = null
val channel: NotificationChannel
val group: NotificationGroup? get() = null
val systemNotificationStyle: SystemNotificationStyle get() = SystemNotificationStyle.Undefined

/**
* Converts this notification to a [LockscreenNotification].
Expand Down Expand Up @@ -95,10 +102,16 @@ sealed interface SystemNotification : Notification {
}

/**
*
* Represents a notification displayed within the application.
*
* In-app notifications are typically less intrusive than system notifications and **do not require**
* system notification permissions to be displayed.
*
* @property inAppNotificationStyle The style of the in-app notification.
* Defaults to [InAppNotificationStyle.Undefined].
* @see InAppNotificationStyle
* @see net.thunderbird.feature.notification.api.ui.style.inAppNotificationStyle
*/
sealed interface InAppNotification : Notification
sealed interface InAppNotification : Notification {
val inAppNotificationStyle: InAppNotificationStyle get() = InAppNotificationStyle.Undefined
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import net.thunderbird.feature.notification.api.ui.icon.NewMailSingleMail
import net.thunderbird.feature.notification.api.ui.icon.NewMailSummaryMail
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.SystemNotificationStyle
import net.thunderbird.feature.notification.api.ui.style.systemNotificationStyle
import net.thunderbird.feature.notification.resources.api.Res
import net.thunderbird.feature.notification.resources.api.notification_additional_messages
import net.thunderbird.feature.notification.resources.api.notification_bg_send_ticker
Expand Down Expand Up @@ -126,6 +128,7 @@ sealed class MailNotification : AppNotification(), SystemNotification {
SystemNotification.LockscreenNotification(
notification = copy(contentText = null),
)

override val actions: Set<NotificationAction> = setOf(NotificationAction.Retry)

companion object {
Expand Down Expand Up @@ -188,6 +191,9 @@ sealed class MailNotification : AppNotification(), SystemNotification {
NotificationAction.Archive,
NotificationAction.MarkAsSpam,
)
override val systemNotificationStyle: SystemNotificationStyle = systemNotificationStyle {
bigText(preview)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package net.thunderbird.feature.notification.api.ui.style

import net.thunderbird.feature.notification.api.ui.style.builder.InAppNotificationStyleBuilder

/**
* Represents the style of an in-app notification.
*
* In-app notifications are displayed within the application itself to provide immediate
* feedback or information.
*
* TODO(#9312): The subtypes of [InAppNotificationStyle] Style might change after designer's feedback.
*/
enum class InAppNotificationStyle {
/**
* Represents an undefined in-app notification style.
* This can be used as a default or placeholder when no specific style is applicable.
*/
Undefined,

/**
* Represents a fatal error notification that cannot be dismissed by the user.
*
* This type of notification typically indicates a fatal issue that requires user attention
* and prevents normal operation of the application.
*/
Fatal,

/**
* Represents a critical in-app notification style.
*
* This style is used for important messages that require user attention but do not
* necessarily halt the application's functionality like a [Fatal] error.
*/
Critical,

/**
* Represents a temporary in-app notification style.
*
* This style is typically used for notifications that are displayed briefly and then dismissed
* automatically or by user interaction.
*/
Temporary,

/**
* Represents a general warning notification.
*/
Warning,

/**
* Represents an in-app notification that displays general information.
*
* This style is typically used for notifications that convey important updates or messages
* that don't fit into more specific categories like errors or successes.
*/
Information,
}

/**
* Configures the in-app notification style.
*
* @param builder A lambda function with [InAppNotificationStyleBuilder] as its receiver,
* used to configure the system notification style.
*
* Example:
* ```
* inAppNotificationStyle {
* severity(NotificationSeverity.Fatal)
* }
* ```
*
* TODO(#9312): The subtypes of [InAppNotificationStyle] Style might change after designer's feedback.
*/
@NotificationStyleMarker
fun inAppNotificationStyle(
builder: @NotificationStyleMarker InAppNotificationStyleBuilder.() -> Unit,
): InAppNotificationStyle {
return InAppNotificationStyleBuilder().apply(builder).build()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.thunderbird.feature.notification.api.ui.style

/**
* A DSL marker for building notification styles.
*
* This annotation is used to restrict the scope of lambda receivers, ensuring that
* methods belonging to an outer scope cannot be called from an inner scope.
* This helps in creating a more structured and type-safe DSL for constructing
* different notification styles.
*
* Example:
* ```
* // OK:
* val systemStyle = systemNotificationStyle {
* bigText("This is a big text notification.")
* }
*
* // Compile error:
* val systemStyle = systemNotificationStyle {
* inbox {
* // bigText must be called within systemNotificationStyle and not within inbox configuration.
* bigText("This is a big text notification.")
* }
* }
* ```
*/
@DslMarker
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
internal annotation class NotificationStyleMarker
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package net.thunderbird.feature.notification.api.ui.style

import net.thunderbird.feature.notification.api.ui.style.builder.SystemNotificationStyleBuilder
import org.jetbrains.annotations.VisibleForTesting

/**
* Represents the style of a system notification.
*/
sealed interface SystemNotificationStyle {
/**
* Represents an undefined notification style.
* This can be used as a default or placeholder when no specific style is applicable.
*/
data object Undefined : SystemNotificationStyle

/**
* Style for large-format notifications that include a lot of text.
*
* @property text The main text content of the notification.
*/
data class BigTextStyle @VisibleForTesting constructor(
val text: String,
) : SystemNotificationStyle

/**
* Style for large-format notifications that include a list of (up to 5) strings.
*
* @property bigContentTitle Overrides the title of the notification.
* @property summary Overrides the summary of the notification.
* @property lines List of strings to display in the notification.
*/
data class InboxStyle @VisibleForTesting constructor(
val bigContentTitle: String,
val summary: String,
val lines: List<CharSequence>,
) : SystemNotificationStyle
}

/**
* Configures the system notification style.
*
* @param builder A lambda function with [SystemNotificationStyleBuilder] as its receiver,
* used to configure the system notification style.
*
* Example:
* ```
* systemNotificationStyle {
* bigText("This is a big text notification.")
* // or
* inbox {
* // Add more inbox style configurations here
* }
* }
* ```
*/
@NotificationStyleMarker
fun systemNotificationStyle(
builder: @NotificationStyleMarker SystemNotificationStyleBuilder.() -> Unit,
): SystemNotificationStyle {
return SystemNotificationStyleBuilder().apply(builder).build()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package net.thunderbird.feature.notification.api.ui.style.builder

import net.thunderbird.feature.notification.api.NotificationSeverity
import net.thunderbird.feature.notification.api.ui.style.InAppNotificationStyle

/**
* Builder for creating [InAppNotificationStyle] instances.
* This interface defines the methods available for configuring the style of an in-app notification.
*/
class InAppNotificationStyleBuilder internal constructor() {
private var style = InAppNotificationStyle.Undefined

/**
* Sets the severity of the in-app notification.
*
* @param severity The severity level for the notification.
*/
fun severity(severity: NotificationSeverity) {
require(style == InAppNotificationStyle.Undefined) {
"In-App Notifications must have only one severity."
}
style = when (severity) {
NotificationSeverity.Fatal -> InAppNotificationStyle.Fatal
NotificationSeverity.Critical -> InAppNotificationStyle.Critical
NotificationSeverity.Temporary -> InAppNotificationStyle.Temporary
NotificationSeverity.Warning -> InAppNotificationStyle.Warning
NotificationSeverity.Information -> InAppNotificationStyle.Information
}
}

/**
* Builds the [InAppNotificationStyle] based on the provided parameters.
*
* @return The constructed [InAppNotificationStyle].
*/
internal fun build(): InAppNotificationStyle {
check(style != InAppNotificationStyle.Undefined) {
"You must add severity of the in-app notification."
}
return style
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package net.thunderbird.feature.notification.api.ui.style.builder

import net.thunderbird.feature.notification.api.ui.style.SystemNotificationStyle
import org.jetbrains.annotations.VisibleForTesting

@VisibleForTesting
internal const val MAX_LINES = 5
private const val MAX_LINES_ERROR_MESSAGE = "The maximum number of lines for a inbox notification is $MAX_LINES"

/**
* Builder for [SystemNotificationStyle.InboxStyle].
*
* This style is used to display a list of items in the notification's content.
* It is commonly used for email or messaging apps.
*/
class InboxSystemNotificationStyleBuilder internal constructor(
private var bigContentTitle: String? = null,
private var summary: String? = null,
private val lines: MutableList<CharSequence> = mutableListOf(),
) {
/**
* Sets the title for the notification's big content view.
*
* This method is used to specify the main title text that will be displayed
* when the notification is expanded to show its detailed content.
*
* @param title The string to be used as the big content title.
*/
fun title(title: String) {
bigContentTitle = title
}

/**
* Sets the summary of the item.
*
* @param summary The summary of the item.
*/
fun summary(summary: String) {
this.summary = summary
}

/**
* Append a line to the digest section of the Inbox notification.
*
* @param line The line to add.
*/
fun line(line: CharSequence) {
require(lines.size < MAX_LINES) { MAX_LINES_ERROR_MESSAGE }
lines += line
}

/**
* Adds one or more lines to the digest section of the Inbox notification.
*
* @param lines A variable number of CharSequence objects representing the lines to be added.
*/
fun lines(vararg lines: CharSequence) {
require(lines.size < MAX_LINES) { MAX_LINES_ERROR_MESSAGE }
this.lines += lines
}

/**
* Builds and returns a [SystemNotificationStyle.InboxStyle] object.
*
* This method performs checks to ensure that mandatory fields like the big content title
* and summary are provided before creating the notification style object.
*
* @return A [SystemNotificationStyle.InboxStyle] object configured with the specified
* title, summary, and lines.
* @throws IllegalStateException if the big content title or summary is not set.
*/
@Suppress("VisibleForTests")
internal fun build(): SystemNotificationStyle.InboxStyle = SystemNotificationStyle.InboxStyle(
bigContentTitle = checkNotNull(bigContentTitle) {
"The inbox notification's title is required"
},
summary = checkNotNull(summary) {
"The inbox notification's summary is required"
},
lines = lines.toList(),
)
}
Loading