Repo created
This commit is contained in:
parent
75dc487a7a
commit
39c29d175b
6317 changed files with 388324 additions and 2 deletions
33
feature/navigation/drawer/dropdown/build.gradle.kts
Normal file
33
feature/navigation/drawer/dropdown/build.gradle.kts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
plugins {
|
||||
id(ThunderbirdPlugins.Library.androidCompose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "net.thunderbird.feature.navigation.drawer.dropdown"
|
||||
resourcePrefix = "navigation_drawer_dropdown_"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.feature.navigation.drawer.api)
|
||||
|
||||
implementation(projects.core.android.account)
|
||||
implementation(projects.core.ui.theme.api)
|
||||
implementation(projects.core.ui.compose.designsystem)
|
||||
|
||||
implementation(projects.feature.account.avatar.impl)
|
||||
implementation(projects.feature.mail.account.api)
|
||||
implementation(projects.feature.mail.folder.api)
|
||||
|
||||
implementation(projects.feature.search.implLegacy)
|
||||
implementation(projects.legacy.mailstore)
|
||||
implementation(projects.legacy.message)
|
||||
implementation(projects.legacy.ui.folder)
|
||||
implementation(projects.core.featureflag)
|
||||
|
||||
testImplementation(projects.core.ui.compose.testing)
|
||||
testImplementation(projects.core.logging.testing)
|
||||
|
||||
// Fakes
|
||||
debugImplementation(projects.feature.account.fake)
|
||||
testImplementation(projects.feature.account.fake)
|
||||
}
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui
|
||||
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import net.thunderbird.feature.navigation.drawer.api.NavigationDrawerExternalContract.DrawerConfig
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.DISPLAY_FOLDER
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.MAIL_DISPLAY_ACCOUNT
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.UNIFIED_FOLDER
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.createAccountList
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.createDisplayFolderList
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentPreview() {
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = persistentListOf(),
|
||||
selectedAccountId = null,
|
||||
folders = persistentListOf(),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentWithAccountPreview() {
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = persistentListOf(MAIL_DISPLAY_ACCOUNT),
|
||||
selectedAccountId = MAIL_DISPLAY_ACCOUNT.id,
|
||||
folders = persistentListOf(),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentWithFoldersPreview() {
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = persistentListOf(
|
||||
MAIL_DISPLAY_ACCOUNT,
|
||||
),
|
||||
selectedAccountId = null,
|
||||
folders = persistentListOf(
|
||||
UNIFIED_FOLDER,
|
||||
DISPLAY_FOLDER,
|
||||
),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentWithSelectedFolderPreview() {
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = persistentListOf(
|
||||
MAIL_DISPLAY_ACCOUNT,
|
||||
),
|
||||
selectedAccountId = MAIL_DISPLAY_ACCOUNT.id,
|
||||
folders = persistentListOf(
|
||||
UNIFIED_FOLDER,
|
||||
DISPLAY_FOLDER,
|
||||
),
|
||||
selectedFolderId = DISPLAY_FOLDER.id,
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentWithSelectedUnifiedFolderPreview() {
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = persistentListOf(
|
||||
MAIL_DISPLAY_ACCOUNT,
|
||||
),
|
||||
selectedAccountId = MAIL_DISPLAY_ACCOUNT.id,
|
||||
folders = persistentListOf(
|
||||
UNIFIED_FOLDER,
|
||||
DISPLAY_FOLDER,
|
||||
),
|
||||
selectedFolderId = UNIFIED_FOLDER.id,
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentSingleAccountPreview() {
|
||||
val displayFolders = createDisplayFolderList(hasUnifiedFolder = false)
|
||||
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = persistentListOf(
|
||||
MAIL_DISPLAY_ACCOUNT,
|
||||
),
|
||||
selectedAccountId = MAIL_DISPLAY_ACCOUNT.id,
|
||||
folders = displayFolders,
|
||||
selectedFolderId = displayFolders[0].id,
|
||||
config = DrawerConfig(
|
||||
showUnifiedFolders = false,
|
||||
showStarredCount = false,
|
||||
showAccountSelector = false,
|
||||
),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentSingleAccountWithAccountSelectionPreview() {
|
||||
val displayFolders = createDisplayFolderList(hasUnifiedFolder = false)
|
||||
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = persistentListOf(
|
||||
MAIL_DISPLAY_ACCOUNT,
|
||||
),
|
||||
selectedAccountId = MAIL_DISPLAY_ACCOUNT.id,
|
||||
folders = displayFolders,
|
||||
selectedFolderId = displayFolders[0].id,
|
||||
config = DrawerConfig(
|
||||
showUnifiedFolders = false,
|
||||
showStarredCount = false,
|
||||
showAccountSelector = true,
|
||||
),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentMultipleAccountsAccountPreview() {
|
||||
val accountList = createAccountList()
|
||||
val displayFolders = createDisplayFolderList(hasUnifiedFolder = true)
|
||||
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = accountList,
|
||||
selectedAccountId = accountList[0].id,
|
||||
folders = displayFolders,
|
||||
selectedFolderId = UNIFIED_FOLDER.id,
|
||||
config = DrawerConfig(
|
||||
showUnifiedFolders = false,
|
||||
showStarredCount = false,
|
||||
showAccountSelector = false,
|
||||
),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentMultipleAccountsWithAccountSelectionPreview() {
|
||||
val accountList = createAccountList()
|
||||
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = accountList,
|
||||
selectedAccountId = accountList[1].id,
|
||||
folders = createDisplayFolderList(hasUnifiedFolder = true),
|
||||
selectedFolderId = UNIFIED_FOLDER.id,
|
||||
config = DrawerConfig(
|
||||
showUnifiedFolders = false,
|
||||
showStarredCount = false,
|
||||
showAccountSelector = true,
|
||||
),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentMultipleAccountsWithDifferentAccountSelectionPreview() {
|
||||
val accountList = createAccountList()
|
||||
|
||||
PreviewWithTheme {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = accountList,
|
||||
selectedAccountId = accountList[2].id,
|
||||
folders = createDisplayFolderList(hasUnifiedFolder = true),
|
||||
selectedFolderId = UNIFIED_FOLDER.id,
|
||||
config = DrawerConfig(
|
||||
showUnifiedFolders = false,
|
||||
showStarredCount = false,
|
||||
showAccountSelector = true,
|
||||
),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentSmallScreenPreview() {
|
||||
val accountList = createAccountList()
|
||||
|
||||
PreviewWithTheme {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.width(320.dp)
|
||||
.height(480.dp),
|
||||
) {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = accountList,
|
||||
selectedAccountId = accountList[2].id,
|
||||
folders = createDisplayFolderList(hasUnifiedFolder = true),
|
||||
selectedFolderId = UNIFIED_FOLDER.id,
|
||||
config = DrawerConfig(
|
||||
showUnifiedFolders = false,
|
||||
showStarredCount = false,
|
||||
showAccountSelector = true,
|
||||
),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentVerySmallScreenPreview() {
|
||||
val accountList = createAccountList()
|
||||
|
||||
PreviewWithTheme {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.width(240.dp)
|
||||
.height(320.dp),
|
||||
) {
|
||||
DrawerContent(
|
||||
state = DrawerContract.State(
|
||||
accounts = accountList,
|
||||
selectedAccountId = accountList[2].id,
|
||||
folders = createDisplayFolderList(hasUnifiedFolder = true),
|
||||
selectedFolderId = UNIFIED_FOLDER.id,
|
||||
config = DrawerConfig(
|
||||
showUnifiedFolders = false,
|
||||
showStarredCount = false,
|
||||
showAccountSelector = true,
|
||||
),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import net.thunderbird.account.fake.FakeAccountData.ACCOUNT_ID_RAW
|
||||
import net.thunderbird.core.android.account.Identity
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.feature.mail.folder.api.Folder
|
||||
import net.thunderbird.feature.mail.folder.api.FolderType
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayTreeFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolderType
|
||||
|
||||
internal object FakeData {
|
||||
|
||||
const val DISPLAY_NAME = "Account Name"
|
||||
const val EMAIL_ADDRESS = "test@example.com"
|
||||
|
||||
val ACCOUNT = LegacyAccount(
|
||||
uuid = ACCOUNT_ID_RAW,
|
||||
).apply {
|
||||
identities = ArrayList()
|
||||
|
||||
val identity = Identity(
|
||||
signatureUse = false,
|
||||
signature = "",
|
||||
description = "",
|
||||
)
|
||||
identities.add(identity)
|
||||
|
||||
name = DISPLAY_NAME
|
||||
email = EMAIL_ADDRESS
|
||||
}
|
||||
|
||||
val UNIFIED_DISPLAY_ACCOUNT = UnifiedDisplayAccount(
|
||||
unreadMessageCount = 224,
|
||||
starredMessageCount = 42,
|
||||
)
|
||||
|
||||
val MAIL_DISPLAY_ACCOUNT = MailDisplayAccount(
|
||||
id = ACCOUNT_ID_RAW,
|
||||
name = DISPLAY_NAME,
|
||||
email = EMAIL_ADDRESS,
|
||||
color = Color.Red.toArgb(),
|
||||
unreadMessageCount = 0,
|
||||
starredMessageCount = 0,
|
||||
)
|
||||
|
||||
val FOLDER = Folder(
|
||||
id = 1,
|
||||
name = "Folder Name",
|
||||
type = FolderType.REGULAR,
|
||||
isLocalOnly = false,
|
||||
)
|
||||
|
||||
val DISPLAY_FOLDER = MailDisplayFolder(
|
||||
accountId = ACCOUNT_ID_RAW,
|
||||
folder = FOLDER,
|
||||
isInTopGroup = false,
|
||||
unreadMessageCount = 14,
|
||||
starredMessageCount = 5,
|
||||
pathDelimiter = "/",
|
||||
)
|
||||
|
||||
val DISPLAY_TREE_FOLDER = DisplayTreeFolder(
|
||||
displayFolder = null,
|
||||
displayName = null,
|
||||
totalUnreadCount = 14,
|
||||
totalStarredCount = 5,
|
||||
children = persistentListOf(
|
||||
DisplayTreeFolder(
|
||||
displayFolder = DISPLAY_FOLDER,
|
||||
displayName = DISPLAY_FOLDER.folder.name,
|
||||
totalUnreadCount = 14,
|
||||
totalStarredCount = 5,
|
||||
children = persistentListOf(),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val EMPTY_DISPLAY_TREE_FOLDER = DisplayTreeFolder(
|
||||
displayFolder = null,
|
||||
displayName = null,
|
||||
totalUnreadCount = 0,
|
||||
totalStarredCount = 0,
|
||||
children = persistentListOf(),
|
||||
)
|
||||
|
||||
val UNIFIED_FOLDER = UnifiedDisplayFolder(
|
||||
id = "unified_inbox",
|
||||
unifiedType = UnifiedDisplayFolderType.INBOX,
|
||||
unreadMessageCount = 123,
|
||||
starredMessageCount = 567,
|
||||
)
|
||||
|
||||
val DISPLAY_TREE_FOLDER_WITH_UNIFIED_FOLDER = DisplayTreeFolder(
|
||||
displayFolder = null,
|
||||
displayName = null,
|
||||
totalUnreadCount = 14,
|
||||
totalStarredCount = 5,
|
||||
children = persistentListOf(
|
||||
DisplayTreeFolder(
|
||||
displayFolder = UNIFIED_FOLDER,
|
||||
displayName = null,
|
||||
totalUnreadCount = 7,
|
||||
totalStarredCount = 2,
|
||||
children = persistentListOf(),
|
||||
),
|
||||
DisplayTreeFolder(
|
||||
displayFolder = DISPLAY_FOLDER,
|
||||
displayName = DISPLAY_FOLDER.folder.name,
|
||||
totalUnreadCount = 7,
|
||||
totalStarredCount = 3,
|
||||
children = persistentListOf(),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val DISPLAY_TREE_FOLDER_WITH_NESTED_FOLDERS = DisplayTreeFolder(
|
||||
displayFolder = null,
|
||||
displayName = null,
|
||||
totalUnreadCount = 14,
|
||||
totalStarredCount = 5,
|
||||
children = persistentListOf(
|
||||
DisplayTreeFolder(
|
||||
displayFolder = DISPLAY_FOLDER,
|
||||
displayName = DISPLAY_FOLDER.folder.name,
|
||||
totalUnreadCount = 7,
|
||||
totalStarredCount = 3,
|
||||
children = persistentListOf(
|
||||
DisplayTreeFolder(
|
||||
displayFolder = null,
|
||||
displayName = null,
|
||||
totalUnreadCount = 7,
|
||||
totalStarredCount = 3,
|
||||
children = persistentListOf(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
fun createAccountList(): PersistentList<MailDisplayAccount> {
|
||||
return persistentListOf(
|
||||
MailDisplayAccount(
|
||||
id = "account1",
|
||||
name = "job@example.com",
|
||||
email = "job@example.com",
|
||||
color = Color.Green.toArgb(),
|
||||
unreadMessageCount = 2,
|
||||
starredMessageCount = 0,
|
||||
),
|
||||
MailDisplayAccount(
|
||||
id = "account2",
|
||||
name = "Jodie Doe",
|
||||
email = "jodie@example.com",
|
||||
color = Color.Red.toArgb(),
|
||||
unreadMessageCount = 12,
|
||||
starredMessageCount = 0,
|
||||
),
|
||||
MailDisplayAccount(
|
||||
id = "account3",
|
||||
name = "John Doe",
|
||||
email = "john@example.com",
|
||||
color = Color.Cyan.toArgb(),
|
||||
unreadMessageCount = 0,
|
||||
starredMessageCount = 0,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun createDisplayFolderList(hasUnifiedFolder: Boolean): PersistentList<DisplayFolder> {
|
||||
val folders = mutableListOf<DisplayFolder>()
|
||||
|
||||
if (hasUnifiedFolder) {
|
||||
folders.add(UNIFIED_FOLDER)
|
||||
}
|
||||
|
||||
folders.addAll(
|
||||
listOf(
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 2, name = "Inbox", type = FolderType.INBOX),
|
||||
unreadMessageCount = 12,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 3, name = "Outbox", type = FolderType.OUTBOX),
|
||||
unreadMessageCount = 0,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 4, name = "Drafts", type = FolderType.DRAFTS),
|
||||
unreadMessageCount = 0,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 5, name = "Sent", type = FolderType.SENT),
|
||||
unreadMessageCount = 0,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 6, name = "Spam", type = FolderType.SPAM),
|
||||
unreadMessageCount = 5,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 7, name = "Trash", type = FolderType.TRASH),
|
||||
unreadMessageCount = 0,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 8, name = "Archive", type = FolderType.ARCHIVE),
|
||||
unreadMessageCount = 0,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 9, name = "Work", type = FolderType.REGULAR),
|
||||
unreadMessageCount = 3,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 10, name = "Personal", type = FolderType.REGULAR),
|
||||
unreadMessageCount = 4,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 11, name = "Important", type = FolderType.REGULAR),
|
||||
unreadMessageCount = 0,
|
||||
),
|
||||
DISPLAY_FOLDER.copy(
|
||||
folder = FOLDER.copy(id = 12, name = "Later", type = FolderType.REGULAR),
|
||||
unreadMessageCount = 0,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
return folders.toPersistentList()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.MAIL_DISPLAY_ACCOUNT
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountAvatarPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountAvatar(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = {},
|
||||
selected = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountAvatarWithUnreadCountPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountAvatar(
|
||||
account = MAIL_DISPLAY_ACCOUNT.copy(
|
||||
unreadMessageCount = 12,
|
||||
),
|
||||
onClick = {},
|
||||
selected = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountAvatarWithUnreadCountMaxedPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountAvatar(
|
||||
account = MAIL_DISPLAY_ACCOUNT.copy(
|
||||
unreadMessageCount = 100,
|
||||
),
|
||||
onClick = {},
|
||||
selected = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountAvatarSelectedPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountAvatar(
|
||||
account = MAIL_DISPLAY_ACCOUNT.copy(
|
||||
color = 0xFFFF0000.toInt(),
|
||||
),
|
||||
onClick = {},
|
||||
selected = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountListItemBadgePreview() {
|
||||
PreviewWithThemes {
|
||||
AccountListItemBadge(
|
||||
unreadCount = 5,
|
||||
starredCount = 3,
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountListItemBadgeWithMaxUnreadPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountListItemBadge(
|
||||
unreadCount = 999,
|
||||
starredCount = 0,
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountListItemBadgeWithStarsPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountListItemBadge(
|
||||
unreadCount = 5,
|
||||
starredCount = 3,
|
||||
showStarredCount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountListItemBadgeWithStarsAndMaxCountPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountListItemBadge(
|
||||
unreadCount = 5,
|
||||
starredCount = 999,
|
||||
showStarredCount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.MAIL_DISPLAY_ACCOUNT
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountListItemPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountListItem(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = { },
|
||||
selected = false,
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountListItemSelectedPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountListItem(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = { },
|
||||
selected = true,
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.MAIL_DISPLAY_ACCOUNT
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountListPreview() {
|
||||
PreviewWithTheme {
|
||||
AccountList(
|
||||
accounts = persistentListOf(
|
||||
MAIL_DISPLAY_ACCOUNT,
|
||||
),
|
||||
selectedAccount = null,
|
||||
onAccountClick = { },
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountListWithSelectedPreview() {
|
||||
PreviewWithTheme {
|
||||
AccountList(
|
||||
accounts = persistentListOf(
|
||||
MAIL_DISPLAY_ACCOUNT,
|
||||
),
|
||||
selectedAccount = MAIL_DISPLAY_ACCOUNT,
|
||||
onAccountClick = { },
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.MAIL_DISPLAY_ACCOUNT
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountViewPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountView(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = {},
|
||||
showAccountSelection = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AccountViewWithoutAccountPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountView(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = {},
|
||||
showAccountSelection = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.folder
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemBadgePreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItemBadge(
|
||||
unreadCount = 99,
|
||||
starredCount = 0,
|
||||
showStarredCount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemBadgeWithStarredCountPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItemBadge(
|
||||
unreadCount = 99,
|
||||
starredCount = 1,
|
||||
showStarredCount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemBadgeWithZeroUnreadCountPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItemBadge(
|
||||
unreadCount = 0,
|
||||
starredCount = 1,
|
||||
showStarredCount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemBadgeWithZeroStarredCountPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItemBadge(
|
||||
unreadCount = 99,
|
||||
starredCount = 0,
|
||||
showStarredCount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemBadgeWithZeroCountsPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItemBadge(
|
||||
unreadCount = 0,
|
||||
starredCount = 0,
|
||||
showStarredCount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemBadgeWithoutStarredCountPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItemBadge(
|
||||
unreadCount = 99,
|
||||
starredCount = 1,
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemBadgeWith100CountsPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItemBadge(
|
||||
unreadCount = 100,
|
||||
starredCount = 100,
|
||||
showStarredCount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemBadgeWith1000CountsPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItemBadge(
|
||||
unreadCount = 1000,
|
||||
starredCount = 1000,
|
||||
showStarredCount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.folder
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import app.k9mail.legacy.ui.folder.FolderNameFormatter
|
||||
import net.thunderbird.feature.mail.folder.api.FolderType
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.DISPLAY_FOLDER
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.DISPLAY_TREE_FOLDER_WITH_UNIFIED_FOLDER
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.UNIFIED_FOLDER
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItem(
|
||||
displayFolder = DISPLAY_FOLDER,
|
||||
selectedFolderId = "unknown",
|
||||
showStarredCount = false,
|
||||
onClick = {},
|
||||
folderNameFormatter = FolderNameFormatter(LocalContext.current.resources),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemSelectedPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItem(
|
||||
displayFolder = DISPLAY_FOLDER,
|
||||
selectedFolderId = DISPLAY_FOLDER.id,
|
||||
showStarredCount = false,
|
||||
onClick = {},
|
||||
folderNameFormatter = FolderNameFormatter(LocalContext.current.resources),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemWithStarredPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItem(
|
||||
displayFolder = DISPLAY_FOLDER,
|
||||
selectedFolderId = "unknown",
|
||||
showStarredCount = true,
|
||||
onClick = {},
|
||||
folderNameFormatter = FolderNameFormatter(LocalContext.current.resources),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemWithStarredSelectedPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItem(
|
||||
displayFolder = DISPLAY_FOLDER,
|
||||
selectedFolderId = DISPLAY_FOLDER.id,
|
||||
showStarredCount = true,
|
||||
onClick = {},
|
||||
folderNameFormatter = FolderNameFormatter(LocalContext.current.resources),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemWithInboxFolderPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItem(
|
||||
displayFolder = DISPLAY_FOLDER.copy(
|
||||
folder = DISPLAY_FOLDER.folder.copy(
|
||||
type = FolderType.INBOX,
|
||||
),
|
||||
),
|
||||
selectedFolderId = "unknown",
|
||||
showStarredCount = true,
|
||||
onClick = {},
|
||||
folderNameFormatter = FolderNameFormatter(LocalContext.current.resources),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemWithUnifiedFolderPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItem(
|
||||
displayFolder = UNIFIED_FOLDER,
|
||||
selectedFolderId = "unknown",
|
||||
showStarredCount = false,
|
||||
onClick = {},
|
||||
folderNameFormatter = FolderNameFormatter(LocalContext.current.resources),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemWithUnifiedFolderSelectedPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItem(
|
||||
displayFolder = UNIFIED_FOLDER,
|
||||
treeFolder = DISPLAY_TREE_FOLDER_WITH_UNIFIED_FOLDER,
|
||||
selectedFolderId = UNIFIED_FOLDER.id,
|
||||
showStarredCount = false,
|
||||
onClick = {},
|
||||
folderNameFormatter = FolderNameFormatter(LocalContext.current.resources),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListItemStarredCountPreview() {
|
||||
PreviewWithThemes {
|
||||
FolderListItem(
|
||||
displayFolder = UNIFIED_FOLDER,
|
||||
treeFolder = DISPLAY_TREE_FOLDER_WITH_UNIFIED_FOLDER,
|
||||
selectedFolderId = null,
|
||||
showStarredCount = true,
|
||||
onClick = {},
|
||||
folderNameFormatter = FolderNameFormatter(LocalContext.current.resources),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.folder
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.DISPLAY_FOLDER
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.DISPLAY_TREE_FOLDER
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.DISPLAY_TREE_FOLDER_WITH_NESTED_FOLDERS
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.DISPLAY_TREE_FOLDER_WITH_UNIFIED_FOLDER
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.EMPTY_DISPLAY_TREE_FOLDER
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListPreview() {
|
||||
PreviewWithTheme {
|
||||
FolderList(
|
||||
rootFolder = EMPTY_DISPLAY_TREE_FOLDER,
|
||||
selectedFolder = null,
|
||||
onFolderClick = {},
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListPreviewSelected() {
|
||||
PreviewWithTheme {
|
||||
FolderList(
|
||||
rootFolder = DISPLAY_TREE_FOLDER,
|
||||
selectedFolder = DISPLAY_FOLDER,
|
||||
onFolderClick = {},
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListWithUnifiedFolderPreview() {
|
||||
PreviewWithTheme {
|
||||
FolderList(
|
||||
rootFolder = DISPLAY_TREE_FOLDER_WITH_UNIFIED_FOLDER,
|
||||
selectedFolder = DISPLAY_FOLDER,
|
||||
onFolderClick = {},
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun FolderListWithUnifiedFolderPreviewSelected() {
|
||||
PreviewWithTheme {
|
||||
FolderList(
|
||||
rootFolder = DISPLAY_TREE_FOLDER_WITH_NESTED_FOLDERS,
|
||||
selectedFolder = null,
|
||||
onFolderClick = {},
|
||||
showStarredCount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.setting
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SettingListItemPreview() {
|
||||
PreviewWithThemes {
|
||||
SettingListItem(
|
||||
label = "Settings",
|
||||
onClick = {},
|
||||
icon = Icons.Outlined.Settings,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.setting
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SettingListPreview() {
|
||||
PreviewWithTheme {
|
||||
FolderSettingList(
|
||||
onManageFoldersClick = {},
|
||||
onSettingsClick = {},
|
||||
isUnifiedAccount = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SettingListWithUnifiedAccountPreview() {
|
||||
PreviewWithTheme {
|
||||
FolderSettingList(
|
||||
onManageFoldersClick = {},
|
||||
onSettingsClick = {},
|
||||
isUnifiedAccount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.account
|
||||
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountIndicatorPreview() {
|
||||
PreviewWithThemes {
|
||||
SideRailAccountIndicator(
|
||||
accountColor = Color.Unspecified,
|
||||
modifier = Modifier.height(MainTheme.spacings.double),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountIndicatorPreviewWithYellowAccountColor() {
|
||||
PreviewWithThemes {
|
||||
SideRailAccountIndicator(
|
||||
accountColor = Color.Yellow,
|
||||
modifier = Modifier.height(MainTheme.spacings.double),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountIndicatorPreviewWithGrayAccountColor() {
|
||||
PreviewWithThemes {
|
||||
SideRailAccountIndicator(
|
||||
accountColor = Color.Gray,
|
||||
modifier = Modifier.height(MainTheme.spacings.double),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.MAIL_DISPLAY_ACCOUNT
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountListItemPreview() {
|
||||
PreviewWithThemes {
|
||||
SideRailAccountListItem(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = { },
|
||||
selected = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountListItemSelectedPreview() {
|
||||
PreviewWithThemes {
|
||||
SideRailAccountListItem(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = { },
|
||||
selected = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.MAIL_DISPLAY_ACCOUNT
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountListPreview() {
|
||||
PreviewWithTheme {
|
||||
SideRailAccountList(
|
||||
accounts = persistentListOf(
|
||||
MAIL_DISPLAY_ACCOUNT,
|
||||
),
|
||||
selectedAccount = null,
|
||||
onAccountClick = { },
|
||||
onSettingsClick = { },
|
||||
onSyncAllAccountsClick = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountListWithSelectedPreview() {
|
||||
PreviewWithTheme {
|
||||
SideRailAccountList(
|
||||
accounts = persistentListOf(
|
||||
MAIL_DISPLAY_ACCOUNT,
|
||||
),
|
||||
selectedAccount = MAIL_DISPLAY_ACCOUNT,
|
||||
onAccountClick = { },
|
||||
onSettingsClick = { },
|
||||
onSyncAllAccountsClick = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.FakeData.MAIL_DISPLAY_ACCOUNT
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountViewPreview() {
|
||||
PreviewWithThemes {
|
||||
SideRailAccountView(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = {},
|
||||
showAvatar = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountViewWithColorPreview() {
|
||||
PreviewWithThemes {
|
||||
SideRailAccountView(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = {},
|
||||
showAvatar = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountViewWithLongDisplayName() {
|
||||
PreviewWithThemes {
|
||||
SideRailAccountView(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = {},
|
||||
showAvatar = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailAccountViewWithLongEmailPreview() {
|
||||
PreviewWithThemes {
|
||||
SideRailAccountView(
|
||||
account = MAIL_DISPLAY_ACCOUNT,
|
||||
onClick = {},
|
||||
showAvatar = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.setting
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailSettingItemPreview() {
|
||||
PreviewWithThemes {
|
||||
SideRailSettingItem(
|
||||
icon = Icons.Outlined.Settings,
|
||||
label = "Setting",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.setting
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailSettingListPreview() {
|
||||
PreviewWithTheme {
|
||||
SideRailSettingList(
|
||||
onAccountSelectorClick = {},
|
||||
onManageFoldersClick = {},
|
||||
showAccountSelector = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun SideRailSettingListShowAccountSelectorPreview() {
|
||||
PreviewWithTheme {
|
||||
SideRailSettingList(
|
||||
onAccountSelectorClick = {},
|
||||
onManageFoldersClick = {},
|
||||
showAccountSelector = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import net.thunderbird.core.featureflag.FeatureFlagProvider
|
||||
import net.thunderbird.core.ui.theme.api.FeatureThemeProvider
|
||||
import net.thunderbird.feature.navigation.drawer.api.NavigationDrawer
|
||||
import net.thunderbird.feature.navigation.drawer.api.R
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolderType
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.createMailDisplayAccountFolderId
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerView
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
internal data class FolderDrawerState(
|
||||
val selectedAccountUuid: String? = null,
|
||||
val selectedFolderId: String? = null,
|
||||
)
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
class DropDownDrawer(
|
||||
override val parent: AppCompatActivity,
|
||||
private val openAccount: (accountId: String) -> Unit,
|
||||
private val openFolder: (accountId: String, folderId: Long) -> Unit,
|
||||
private val openUnifiedFolder: () -> Unit,
|
||||
private val openManageFolders: () -> Unit,
|
||||
private val openSettings: () -> Unit,
|
||||
private val openAddAccount: () -> Unit,
|
||||
createDrawerListener: () -> DrawerLayout.DrawerListener,
|
||||
) : NavigationDrawer, KoinComponent {
|
||||
|
||||
private val themeProvider: FeatureThemeProvider by inject()
|
||||
private val featureFlagProvider: FeatureFlagProvider by inject()
|
||||
|
||||
private val drawer: DrawerLayout = parent.findViewById(R.id.navigation_drawer_layout)
|
||||
private val drawerContent: ComposeView = parent.findViewById(R.id.navigation_drawer_content)
|
||||
|
||||
private val drawerState = MutableStateFlow(FolderDrawerState())
|
||||
|
||||
init {
|
||||
drawer.addDrawerListener(createDrawerListener())
|
||||
|
||||
drawerContent.setContent {
|
||||
themeProvider.WithTheme {
|
||||
val state = drawerState.collectAsStateWithLifecycle()
|
||||
|
||||
DrawerView(
|
||||
drawerState = state.value,
|
||||
openAccount = openAccount,
|
||||
openFolder = openFolder,
|
||||
openUnifiedFolder = openUnifiedFolder,
|
||||
openManageFolders = openManageFolders,
|
||||
openSettings = openSettings,
|
||||
openAddAccount = openAddAccount,
|
||||
featureFlagProvider = featureFlagProvider,
|
||||
closeDrawer = { close() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val isOpen: Boolean
|
||||
get() = drawer.isOpen
|
||||
|
||||
override fun selectAccount(accountUuid: String) {
|
||||
drawerState.update {
|
||||
it.copy(selectedAccountUuid = accountUuid)
|
||||
}
|
||||
}
|
||||
|
||||
override fun selectFolder(accountUuid: String, folderId: Long) {
|
||||
drawerState.update {
|
||||
it.copy(
|
||||
selectedAccountUuid = accountUuid,
|
||||
selectedFolderId = createMailDisplayAccountFolderId(accountUuid, folderId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun selectUnifiedInbox() {
|
||||
drawerState.update {
|
||||
it.copy(
|
||||
selectedAccountUuid = UnifiedDisplayAccount.UNIFIED_ACCOUNT_ID,
|
||||
selectedFolderId = UnifiedDisplayFolderType.INBOX.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun deselect() {
|
||||
drawerState.update {
|
||||
it.copy(
|
||||
selectedFolderId = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun open() {
|
||||
drawer.openDrawer(GravityCompat.START)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
drawer.closeDrawer(GravityCompat.START)
|
||||
}
|
||||
|
||||
override fun lock() {
|
||||
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
}
|
||||
|
||||
override fun unlock() {
|
||||
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown
|
||||
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.data.UnifiedFolderRepository
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase.GetDisplayAccounts
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase.GetDisplayFoldersForAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase.GetDisplayTreeFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase.GetDrawerConfig
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase.SaveDrawerConfig
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase.SyncAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase.SyncAllAccounts
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerViewModel
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.core.module.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val navigationDropDownDrawerModule: Module = module {
|
||||
|
||||
single<DomainContract.UnifiedFolderRepository> {
|
||||
UnifiedFolderRepository(
|
||||
messageCountsProvider = get(),
|
||||
)
|
||||
}
|
||||
|
||||
single<UseCase.GetDrawerConfig> {
|
||||
GetDrawerConfig(
|
||||
configLoader = get(),
|
||||
)
|
||||
}
|
||||
single<UseCase.SaveDrawerConfig> {
|
||||
SaveDrawerConfig(
|
||||
drawerConfigWriter = get(),
|
||||
)
|
||||
}
|
||||
|
||||
single<UseCase.GetDisplayAccounts> {
|
||||
GetDisplayAccounts(
|
||||
accountManager = get(),
|
||||
messageCountsProvider = get(),
|
||||
messageListRepository = get(),
|
||||
)
|
||||
}
|
||||
|
||||
single<UseCase.GetDisplayFoldersForAccount> {
|
||||
GetDisplayFoldersForAccount(
|
||||
displayFolderRepository = get(),
|
||||
unifiedFolderRepository = get(),
|
||||
)
|
||||
}
|
||||
|
||||
single<UseCase.GetDisplayTreeFolder> {
|
||||
GetDisplayTreeFolder(
|
||||
logger = get(),
|
||||
)
|
||||
}
|
||||
|
||||
single<UseCase.SyncAccount> {
|
||||
SyncAccount(
|
||||
accountManager = get(),
|
||||
messagingController = get(),
|
||||
)
|
||||
}
|
||||
|
||||
single<UseCase.SyncAllAccounts> {
|
||||
SyncAllAccounts(
|
||||
messagingController = get(),
|
||||
)
|
||||
}
|
||||
|
||||
viewModel {
|
||||
DrawerViewModel(
|
||||
getDrawerConfig = get(),
|
||||
saveDrawerConfig = get(),
|
||||
getDisplayAccounts = get(),
|
||||
getDisplayFoldersForAccount = get(),
|
||||
getDisplayTreeFolder = get(),
|
||||
syncAccount = get(),
|
||||
syncAllAccounts = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.data
|
||||
|
||||
import app.k9mail.legacy.message.controller.MessageCountsProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolderType
|
||||
import net.thunderbird.feature.search.legacy.LocalMessageSearch
|
||||
import net.thunderbird.feature.search.legacy.api.MessageSearchField
|
||||
import net.thunderbird.feature.search.legacy.api.SearchAttribute
|
||||
|
||||
internal class UnifiedFolderRepository(
|
||||
private val messageCountsProvider: MessageCountsProvider,
|
||||
) : DomainContract.UnifiedFolderRepository {
|
||||
|
||||
override fun getUnifiedDisplayFolderFlow(unifiedFolderType: UnifiedDisplayFolderType): Flow<UnifiedDisplayFolder> {
|
||||
return messageCountsProvider.getMessageCountsFlow(createUnifiedFolderSearch(unifiedFolderType)).map {
|
||||
UnifiedDisplayFolder(
|
||||
id = UNIFIED_INBOX_ID,
|
||||
unifiedType = UnifiedDisplayFolderType.INBOX,
|
||||
unreadMessageCount = it.unread,
|
||||
starredMessageCount = it.starred,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createUnifiedFolderSearch(unifiedFolderType: UnifiedDisplayFolderType): LocalMessageSearch {
|
||||
return when (unifiedFolderType) {
|
||||
UnifiedDisplayFolderType.INBOX -> return createUnifiedInboxSearch()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createUnifiedInboxSearch(): LocalMessageSearch {
|
||||
return LocalMessageSearch().apply {
|
||||
id = UNIFIED_INBOX_ID
|
||||
and(MessageSearchField.INTEGRATE, "1", SearchAttribute.EQUALS)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val UNIFIED_INBOX_ID = "unified_inbox"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import net.thunderbird.feature.navigation.drawer.api.NavigationDrawerExternalContract.DrawerConfig
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayTreeFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolderType
|
||||
|
||||
internal interface DomainContract {
|
||||
|
||||
interface UseCase {
|
||||
fun interface GetDrawerConfig {
|
||||
operator fun invoke(): Flow<DrawerConfig>
|
||||
}
|
||||
|
||||
fun interface SaveDrawerConfig {
|
||||
operator fun invoke(drawerConfig: DrawerConfig): Flow<Unit>
|
||||
}
|
||||
|
||||
fun interface GetDisplayAccounts {
|
||||
operator fun invoke(showUnifiedAccount: Boolean): Flow<List<DisplayAccount>>
|
||||
}
|
||||
|
||||
fun interface GetDisplayFoldersForAccount {
|
||||
operator fun invoke(accountId: String): Flow<List<DisplayFolder>>
|
||||
}
|
||||
|
||||
fun interface GetDisplayTreeFolder {
|
||||
operator fun invoke(folders: List<DisplayFolder>, maxDepth: Int): DisplayTreeFolder
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize the given account uuid.
|
||||
*/
|
||||
fun interface SyncAccount {
|
||||
operator fun invoke(accountUuid: String): Flow<Result<Unit>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize all accounts.
|
||||
*/
|
||||
fun interface SyncAllAccounts {
|
||||
operator fun invoke(): Flow<Result<Unit>>
|
||||
}
|
||||
}
|
||||
|
||||
interface UnifiedFolderRepository {
|
||||
fun getUnifiedDisplayFolderFlow(unifiedFolderType: UnifiedDisplayFolderType): Flow<UnifiedDisplayFolder>
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.entity
|
||||
|
||||
sealed interface DisplayAccount {
|
||||
val id: String
|
||||
val unreadMessageCount: Int
|
||||
val starredMessageCount: Int
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.entity
|
||||
|
||||
import net.thunderbird.feature.mail.folder.api.FolderPathDelimiter
|
||||
|
||||
internal interface DisplayFolder {
|
||||
val id: String
|
||||
val unreadMessageCount: Int
|
||||
val starredMessageCount: Int
|
||||
val pathDelimiter: FolderPathDelimiter
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.entity
|
||||
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class DisplayTreeFolder(
|
||||
val displayFolder: DisplayFolder?,
|
||||
val displayName: String?,
|
||||
val totalUnreadCount: Int,
|
||||
val totalStarredCount: Int,
|
||||
val children: ImmutableList<DisplayTreeFolder>,
|
||||
)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.entity
|
||||
|
||||
internal data class MailDisplayAccount(
|
||||
override val id: String,
|
||||
val name: String,
|
||||
val email: String,
|
||||
val color: Int,
|
||||
override val unreadMessageCount: Int,
|
||||
override val starredMessageCount: Int,
|
||||
) : DisplayAccount
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.entity
|
||||
|
||||
import net.thunderbird.feature.mail.folder.api.Folder
|
||||
import net.thunderbird.feature.mail.folder.api.FolderPathDelimiter
|
||||
|
||||
internal data class MailDisplayFolder(
|
||||
val accountId: String?,
|
||||
val folder: Folder,
|
||||
val isInTopGroup: Boolean,
|
||||
override val unreadMessageCount: Int,
|
||||
override val starredMessageCount: Int,
|
||||
override val pathDelimiter: FolderPathDelimiter,
|
||||
) : DisplayFolder {
|
||||
override val id: String = createMailDisplayAccountFolderId(accountId.orEmpty(), folder.id)
|
||||
}
|
||||
|
||||
fun createMailDisplayAccountFolderId(accountId: String, folderId: Long): String {
|
||||
return "${accountId}_$folderId"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.entity
|
||||
|
||||
data class UnifiedDisplayAccount(
|
||||
override val unreadMessageCount: Int,
|
||||
override val starredMessageCount: Int,
|
||||
) : DisplayAccount {
|
||||
override val id: String = UNIFIED_ACCOUNT_ID
|
||||
|
||||
companion object {
|
||||
const val UNIFIED_ACCOUNT_ID = "unified_account"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.entity
|
||||
|
||||
import net.thunderbird.feature.mail.folder.api.FolderPathDelimiter
|
||||
|
||||
internal data class UnifiedDisplayFolder(
|
||||
override val id: String,
|
||||
val unifiedType: UnifiedDisplayFolderType,
|
||||
override val unreadMessageCount: Int,
|
||||
override val starredMessageCount: Int,
|
||||
) : DisplayFolder {
|
||||
override val pathDelimiter: FolderPathDelimiter = "/"
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.entity
|
||||
|
||||
/**
|
||||
* Represents a unified folder in the drawer.
|
||||
*
|
||||
* The id is unique for each unified folder type.
|
||||
*/
|
||||
internal enum class UnifiedDisplayFolderType(
|
||||
val id: String,
|
||||
) {
|
||||
INBOX("unified_inbox"),
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase
|
||||
|
||||
import app.k9mail.legacy.mailstore.MessageListChangedListener
|
||||
import app.k9mail.legacy.mailstore.MessageListRepository
|
||||
import app.k9mail.legacy.message.controller.MessageCounts
|
||||
import app.k9mail.legacy.message.controller.MessageCountsProvider
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.launch
|
||||
import net.thunderbird.core.android.account.AccountManager
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayAccount
|
||||
|
||||
internal class GetDisplayAccounts(
|
||||
private val accountManager: AccountManager,
|
||||
private val messageCountsProvider: MessageCountsProvider,
|
||||
private val messageListRepository: MessageListRepository,
|
||||
private val coroutineContext: CoroutineContext = Dispatchers.IO,
|
||||
) : UseCase.GetDisplayAccounts {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun invoke(showUnifiedAccount: Boolean): Flow<List<DisplayAccount>> {
|
||||
return accountManager.getAccountsFlow()
|
||||
.flatMapLatest { accounts ->
|
||||
val messageCountsFlows: List<Flow<MessageCounts>> = accounts.map { account ->
|
||||
getMessageCountsFlow(account)
|
||||
}
|
||||
|
||||
combine(messageCountsFlows) { messageCountsList ->
|
||||
val displayAccounts = messageCountsList.mapIndexed { index, messageCounts ->
|
||||
MailDisplayAccount(
|
||||
id = accounts[index].uuid,
|
||||
name = accounts[index].displayName,
|
||||
email = accounts[index].email,
|
||||
color = accounts[index].chipColor,
|
||||
unreadMessageCount = messageCounts.unread,
|
||||
starredMessageCount = messageCounts.starred,
|
||||
)
|
||||
}
|
||||
|
||||
if (showUnifiedAccount) {
|
||||
withUnifiedAccount(displayAccounts)
|
||||
} else {
|
||||
displayAccounts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun withUnifiedAccount(accounts: List<DisplayAccount>): List<DisplayAccount> {
|
||||
val unified = UnifiedDisplayAccount(
|
||||
unreadMessageCount = accounts.sumOf { it.unreadMessageCount },
|
||||
starredMessageCount = accounts.sumOf { it.starredMessageCount },
|
||||
)
|
||||
|
||||
return listOf(unified) + accounts
|
||||
}
|
||||
|
||||
private fun getMessageCountsFlow(account: LegacyAccount): Flow<MessageCounts> {
|
||||
return callbackFlow {
|
||||
send(messageCountsProvider.getMessageCounts(account))
|
||||
|
||||
val listener = MessageListChangedListener {
|
||||
launch {
|
||||
send(messageCountsProvider.getMessageCounts(account))
|
||||
}
|
||||
}
|
||||
messageListRepository.addListener(account.uuid, listener)
|
||||
|
||||
awaitClose {
|
||||
messageListRepository.removeListener(listener)
|
||||
}
|
||||
}.flowOn(coroutineContext)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase
|
||||
|
||||
import app.k9mail.legacy.ui.folder.DisplayFolderRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UnifiedFolderRepository
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolderType
|
||||
|
||||
internal class GetDisplayFoldersForAccount(
|
||||
private val displayFolderRepository: DisplayFolderRepository,
|
||||
private val unifiedFolderRepository: UnifiedFolderRepository,
|
||||
) : UseCase.GetDisplayFoldersForAccount {
|
||||
override fun invoke(accountId: String): Flow<List<DisplayFolder>> {
|
||||
if (accountId == UnifiedDisplayAccount.UNIFIED_ACCOUNT_ID) {
|
||||
return unifiedFolderRepository.getUnifiedDisplayFolderFlow(UnifiedDisplayFolderType.INBOX)
|
||||
.map { displayUnifiedFolder ->
|
||||
listOf(displayUnifiedFolder)
|
||||
}
|
||||
} else {
|
||||
return displayFolderRepository.getDisplayFoldersFlow(accountId).map { displayFolders ->
|
||||
displayFolders.map { displayFolder ->
|
||||
MailDisplayFolder(
|
||||
accountId = accountId,
|
||||
folder = displayFolder.folder,
|
||||
isInTopGroup = displayFolder.isInTopGroup,
|
||||
unreadMessageCount = displayFolder.unreadMessageCount,
|
||||
starredMessageCount = displayFolder.starredMessageCount,
|
||||
pathDelimiter = displayFolder.pathDelimiter,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase
|
||||
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import net.thunderbird.core.logging.Logger
|
||||
import net.thunderbird.feature.mail.folder.api.FOLDER_DEFAULT_PATH_DELIMITER
|
||||
import net.thunderbird.feature.mail.folder.api.Folder
|
||||
import net.thunderbird.feature.mail.folder.api.FolderPathDelimiter
|
||||
import net.thunderbird.feature.mail.folder.api.FolderType
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayTreeFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolder
|
||||
|
||||
internal class GetDisplayTreeFolder(
|
||||
private val logger: Logger,
|
||||
) : UseCase.GetDisplayTreeFolder {
|
||||
private var placeholderCounter = 0L
|
||||
|
||||
override fun invoke(folders: List<DisplayFolder>, maxDepth: Int): DisplayTreeFolder {
|
||||
val unifiedFolderTreeList = folders.filterIsInstance<UnifiedDisplayFolder>().map {
|
||||
DisplayTreeFolder(
|
||||
displayFolder = it,
|
||||
displayName = it.unifiedType.id,
|
||||
totalUnreadCount = it.unreadMessageCount,
|
||||
totalStarredCount = it.starredMessageCount,
|
||||
children = persistentListOf(),
|
||||
)
|
||||
}
|
||||
|
||||
val pathDelimiter = folders.firstOrNull()?.pathDelimiter ?: FOLDER_DEFAULT_PATH_DELIMITER
|
||||
val accountFolders = folders.filterIsInstance<MailDisplayFolder>().map {
|
||||
val path = flattenPath(it.folder.name, pathDelimiter, maxDepth)
|
||||
logger.debug { "Flattened path for ${it.folder.name} → $path" }
|
||||
path to it
|
||||
}
|
||||
val accountFolderTreeList = buildAccountFolderTree(accountFolders, pathDelimiter)
|
||||
|
||||
return DisplayTreeFolder(
|
||||
displayFolder = null,
|
||||
displayName = null,
|
||||
totalUnreadCount = accountFolderTreeList.sumOf { it.totalUnreadCount },
|
||||
totalStarredCount = accountFolderTreeList.sumOf { it.totalStarredCount },
|
||||
children = (unifiedFolderTreeList + accountFolderTreeList).toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun flattenPath(folderName: String, folderPathDelimiter: FolderPathDelimiter, maxDepth: Int): List<String> {
|
||||
val parts = folderName.split(folderPathDelimiter).map { it.takeIf { it.isNotBlank() } ?: "(Unnamed)" }
|
||||
|
||||
return if (parts.size <= maxDepth) {
|
||||
parts
|
||||
} else {
|
||||
parts.take(maxDepth) + listOf(parts.drop(maxDepth).joinToString(folderPathDelimiter))
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildAccountFolderTree(
|
||||
paths: List<Pair<List<String>, MailDisplayFolder>>,
|
||||
pathDelimiter: FolderPathDelimiter,
|
||||
parentPath: String = "",
|
||||
): List<DisplayTreeFolder> {
|
||||
return paths.groupBy { it.first.getOrNull(0) ?: "(Unnamed)" }
|
||||
.map { (segment, entries) ->
|
||||
val childPaths = entries.mapNotNull { (segments, folders) ->
|
||||
if (segments.size > 1) {
|
||||
Pair(segments.drop(1), folders)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val currentFolders = entries.mapNotNull { (segments, folder) ->
|
||||
if (segments.size == 1) folder else null
|
||||
}
|
||||
|
||||
val fullPath = if (parentPath.isBlank()) segment else "${parentPath}${pathDelimiter}$segment"
|
||||
|
||||
val currentFolder = currentFolders.firstOrNull() ?: createPlaceholderFolder(fullPath, pathDelimiter)
|
||||
|
||||
val children = buildAccountFolderTree(
|
||||
paths = childPaths,
|
||||
pathDelimiter = pathDelimiter,
|
||||
parentPath = fullPath,
|
||||
)
|
||||
|
||||
val totalUnread = children.sumOf { it.totalUnreadCount } + currentFolder.unreadMessageCount
|
||||
val totalStarred = children.sumOf { it.totalStarredCount } + currentFolder.starredMessageCount
|
||||
|
||||
DisplayTreeFolder(
|
||||
displayFolder = currentFolder,
|
||||
displayName = segment,
|
||||
totalUnreadCount = totalUnread,
|
||||
totalStarredCount = totalStarred,
|
||||
children = children.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPlaceholderFolder(name: String, pathDelimiter: FolderPathDelimiter): MailDisplayFolder {
|
||||
placeholderCounter += 1
|
||||
return MailDisplayFolder(
|
||||
accountId = null,
|
||||
folder = Folder(
|
||||
id = placeholderCounter,
|
||||
name = name,
|
||||
type = FolderType.REGULAR,
|
||||
isLocalOnly = false,
|
||||
),
|
||||
isInTopGroup = true,
|
||||
unreadMessageCount = 0,
|
||||
starredMessageCount = 0,
|
||||
pathDelimiter = pathDelimiter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import net.thunderbird.feature.navigation.drawer.api.NavigationDrawerExternalContract.DrawerConfig
|
||||
import net.thunderbird.feature.navigation.drawer.api.NavigationDrawerExternalContract.DrawerConfigLoader
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase
|
||||
|
||||
internal class GetDrawerConfig(
|
||||
private val configLoader: DrawerConfigLoader,
|
||||
) : UseCase.GetDrawerConfig {
|
||||
override operator fun invoke(): Flow<DrawerConfig> {
|
||||
return configLoader.loadDrawerConfigFlow()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import net.thunderbird.feature.navigation.drawer.api.NavigationDrawerExternalContract
|
||||
import net.thunderbird.feature.navigation.drawer.api.NavigationDrawerExternalContract.DrawerConfigWriter
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase
|
||||
|
||||
internal class SaveDrawerConfig(
|
||||
private val drawerConfigWriter: DrawerConfigWriter,
|
||||
) : UseCase.SaveDrawerConfig {
|
||||
override fun invoke(drawerConfig: NavigationDrawerExternalContract.DrawerConfig): Flow<Unit> {
|
||||
return flow {
|
||||
emit(drawerConfigWriter.writeDrawerConfig(drawerConfig))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase
|
||||
|
||||
import android.content.Context
|
||||
import app.k9mail.legacy.message.controller.MessagingControllerMailChecker
|
||||
import app.k9mail.legacy.message.controller.SimpleMessagingListener
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import net.thunderbird.core.android.account.AccountManager
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase
|
||||
|
||||
internal class SyncAccount(
|
||||
private val accountManager: AccountManager,
|
||||
private val messagingController: MessagingControllerMailChecker,
|
||||
private val coroutineContext: CoroutineContext = Dispatchers.IO,
|
||||
) : UseCase.SyncAccount {
|
||||
override fun invoke(accountUuid: String): Flow<Result<Unit>> = callbackFlow {
|
||||
val listener = object : SimpleMessagingListener() {
|
||||
override fun checkMailFinished(context: Context?, account: LegacyAccount?) {
|
||||
trySend(Result.success(Unit))
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
val account = accountManager.getAccount(accountUuid)
|
||||
|
||||
messagingController.checkMail(
|
||||
account = account,
|
||||
ignoreLastCheckedTime = true,
|
||||
useManualWakeLock = true,
|
||||
notify = true,
|
||||
listener = listener,
|
||||
)
|
||||
|
||||
awaitClose()
|
||||
}.flowOn(coroutineContext)
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.domain.usecase
|
||||
|
||||
import android.content.Context
|
||||
import app.k9mail.legacy.message.controller.MessagingControllerMailChecker
|
||||
import app.k9mail.legacy.message.controller.SimpleMessagingListener
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import net.thunderbird.core.android.account.LegacyAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase
|
||||
|
||||
class SyncAllAccounts(
|
||||
private val messagingController: MessagingControllerMailChecker,
|
||||
private val coroutineContext: CoroutineContext = Dispatchers.IO,
|
||||
) : UseCase.SyncAllAccounts {
|
||||
override fun invoke(): Flow<Result<Unit>> = callbackFlow {
|
||||
val listener = object : SimpleMessagingListener() {
|
||||
override fun checkMailFinished(context: Context?, account: LegacyAccount?) {
|
||||
trySend(Result.success(Unit))
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
messagingController.checkMail(
|
||||
account = null,
|
||||
ignoreLastCheckedTime = true,
|
||||
useManualWakeLock = true,
|
||||
notify = true,
|
||||
listener = listener,
|
||||
)
|
||||
|
||||
awaitClose()
|
||||
}.flowOn(coroutineContext)
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.DividerHorizontal
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.core.ui.compose.common.modifier.testTagAsResourceId
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.Event
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.State
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.account.AccountList
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.account.AccountView
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.account.getDisplayCutOutHorizontalInsetPadding
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.DRAWER_WIDTH
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getAdditionalWidth
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.folder.FolderList
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.setting.AccountSettingList
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.setting.FolderSettingList
|
||||
|
||||
@Composable
|
||||
internal fun DrawerContent(
|
||||
state: State,
|
||||
onEvent: (Event) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val additionalWidth = getAdditionalWidth()
|
||||
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.width(DRAWER_WIDTH + additionalWidth)
|
||||
.fillMaxHeight()
|
||||
.testTagAsResourceId("DrawerContent"),
|
||||
color = MainTheme.colors.surfaceContainerLow,
|
||||
) {
|
||||
val selectedAccount = state.accounts.firstOrNull { it.id == state.selectedAccountId }
|
||||
val horizontalInsetPadding = getDisplayCutOutHorizontalInsetPadding()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.safeDrawing)
|
||||
.windowInsetsPadding(horizontalInsetPadding),
|
||||
) {
|
||||
selectedAccount?.let {
|
||||
AccountView(
|
||||
account = selectedAccount,
|
||||
onClick = { onEvent(Event.OnAccountSelectorClick) },
|
||||
showAccountSelection = state.showAccountSelection,
|
||||
)
|
||||
|
||||
DividerHorizontal()
|
||||
}
|
||||
AnimatedContent(
|
||||
targetState = state.showAccountSelection,
|
||||
label = "AccountSelectorVisibility",
|
||||
transitionSpec = {
|
||||
if (targetState) {
|
||||
slideInVertically { -it } togetherWith slideOutVertically { it }
|
||||
} else {
|
||||
slideInVertically { it } togetherWith slideOutVertically { -it }
|
||||
}
|
||||
},
|
||||
) { targetState ->
|
||||
if (targetState) {
|
||||
AccountContent(
|
||||
state = state,
|
||||
onEvent = onEvent,
|
||||
selectedAccount = selectedAccount,
|
||||
)
|
||||
} else {
|
||||
FolderContent(
|
||||
state = state,
|
||||
onEvent = onEvent,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccountContent(
|
||||
state: State,
|
||||
onEvent: (Event) -> Unit,
|
||||
selectedAccount: DisplayAccount?,
|
||||
) {
|
||||
Surface(
|
||||
color = MainTheme.colors.surfaceContainerLow,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
AccountList(
|
||||
accounts = state.accounts,
|
||||
selectedAccount = selectedAccount,
|
||||
onAccountClick = { onEvent(Event.OnAccountClick(it)) },
|
||||
showStarredCount = state.config.showStarredCount,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
DividerHorizontal()
|
||||
AccountSettingList(
|
||||
onAddAccountClick = { onEvent(Event.OnAddAccountClick) },
|
||||
onSyncAllAccountsClick = { onEvent(Event.OnSyncAllAccounts) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FolderContent(
|
||||
state: State,
|
||||
onEvent: (Event) -> Unit,
|
||||
) {
|
||||
val isUnifiedAccount = remember(state.selectedAccountId) {
|
||||
state.accounts.any { it.id == state.selectedAccountId && it is UnifiedDisplayAccount }
|
||||
}
|
||||
|
||||
Surface(
|
||||
color = MainTheme.colors.surfaceContainerLow,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
FolderList(
|
||||
rootFolder = state.rootFolder,
|
||||
selectedFolder = state.selectedFolder,
|
||||
onFolderClick = { folder ->
|
||||
onEvent(Event.OnFolderClick(folder))
|
||||
},
|
||||
showStarredCount = state.config.showStarredCount,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
DividerHorizontal()
|
||||
FolderSettingList(
|
||||
onManageFoldersClick = { onEvent(Event.OnManageFoldersClick) },
|
||||
onSettingsClick = { onEvent(Event.OnSettingsClick) },
|
||||
isUnifiedAccount = isUnifiedAccount,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import net.thunderbird.feature.navigation.drawer.api.NavigationDrawerExternalContract.DrawerConfig
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayTreeFolder
|
||||
|
||||
internal interface DrawerContract {
|
||||
|
||||
interface ViewModel : UnidirectionalViewModel<State, Event, Effect>
|
||||
|
||||
@Stable
|
||||
data class State(
|
||||
val config: DrawerConfig = DrawerConfig(
|
||||
showUnifiedFolders = false,
|
||||
showStarredCount = false,
|
||||
showAccountSelector = true,
|
||||
),
|
||||
val accounts: ImmutableList<DisplayAccount> = persistentListOf(),
|
||||
val selectedAccountId: String? = null,
|
||||
val rootFolder: DisplayTreeFolder = DisplayTreeFolder(
|
||||
displayFolder = null,
|
||||
displayName = null,
|
||||
totalUnreadCount = 0,
|
||||
totalStarredCount = 0,
|
||||
children = persistentListOf(),
|
||||
),
|
||||
val folders: ImmutableList<DisplayFolder> = persistentListOf(),
|
||||
val selectedFolderId: String? = null,
|
||||
val selectedFolder: DisplayFolder? = null,
|
||||
val showAccountSelection: Boolean = false,
|
||||
val isLoading: Boolean = false,
|
||||
)
|
||||
|
||||
sealed interface Event {
|
||||
data class SelectAccount(val accountId: String?) : Event
|
||||
data class SelectFolder(val folderId: String?) : Event
|
||||
data class OnAccountClick(val account: DisplayAccount) : Event
|
||||
data class OnAccountViewClick(val account: DisplayAccount) : Event
|
||||
data class OnFolderClick(val folder: DisplayFolder) : Event
|
||||
data object OnAccountSelectorClick : Event
|
||||
data object OnManageFoldersClick : Event
|
||||
data object OnSettingsClick : Event
|
||||
data object OnSyncAccount : Event
|
||||
data object OnSyncAllAccounts : Event
|
||||
data object OnAddAccountClick : Event
|
||||
}
|
||||
|
||||
sealed interface Effect {
|
||||
data class OpenAccount(val accountId: String) : Effect
|
||||
data class OpenFolder(val accountId: String, val folderId: Long) : Effect
|
||||
data object OpenUnifiedFolder : Effect
|
||||
data object OpenManageFolders : Effect
|
||||
data object OpenSettings : Effect
|
||||
data object OpenAddAccount : Effect
|
||||
data object CloseDrawer : Effect
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import app.k9mail.core.ui.compose.common.mvi.observe
|
||||
import app.k9mail.core.ui.compose.designsystem.molecule.PullToRefreshBox
|
||||
import net.thunderbird.core.featureflag.FeatureFlagKey
|
||||
import net.thunderbird.core.featureflag.FeatureFlagProvider
|
||||
import net.thunderbird.core.featureflag.FeatureFlagResult
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.FolderDrawerState
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.Effect
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.Event
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.ViewModel
|
||||
import net.thunderbird.feature.navigation.drawer.siderail.ui.SideRailDrawerContent
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun DrawerView(
|
||||
drawerState: FolderDrawerState,
|
||||
openAccount: (accountId: String) -> Unit,
|
||||
openFolder: (accountId: String, folderId: Long) -> Unit,
|
||||
openUnifiedFolder: () -> Unit,
|
||||
openManageFolders: () -> Unit,
|
||||
openSettings: () -> Unit,
|
||||
openAddAccount: () -> Unit,
|
||||
closeDrawer: () -> Unit,
|
||||
featureFlagProvider: FeatureFlagProvider,
|
||||
viewModel: ViewModel = koinViewModel<DrawerViewModel>(),
|
||||
) {
|
||||
val (state, dispatch) = viewModel.observe { effect ->
|
||||
when (effect) {
|
||||
is Effect.OpenAccount -> openAccount(effect.accountId)
|
||||
is Effect.OpenFolder -> openFolder(
|
||||
effect.accountId,
|
||||
effect.folderId,
|
||||
)
|
||||
|
||||
Effect.OpenUnifiedFolder -> openUnifiedFolder()
|
||||
is Effect.OpenManageFolders -> openManageFolders()
|
||||
is Effect.OpenSettings -> openSettings()
|
||||
Effect.OpenAddAccount -> openAddAccount()
|
||||
Effect.CloseDrawer -> closeDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
val isDropdownDrawerEnabled = remember {
|
||||
featureFlagProvider.provide(FeatureFlagKey("enable_dropdown_drawer_ui")) == FeatureFlagResult.Enabled
|
||||
}
|
||||
|
||||
LaunchedEffect(drawerState.selectedAccountUuid) {
|
||||
dispatch(Event.SelectAccount(drawerState.selectedAccountUuid))
|
||||
}
|
||||
|
||||
LaunchedEffect(drawerState.selectedFolderId) {
|
||||
dispatch(Event.SelectFolder(drawerState.selectedFolderId))
|
||||
}
|
||||
|
||||
PullToRefreshBox(
|
||||
isRefreshing = state.value.isLoading,
|
||||
onRefresh = { dispatch(Event.OnSyncAccount) },
|
||||
) {
|
||||
if (isDropdownDrawerEnabled) {
|
||||
DrawerContent(
|
||||
state = state.value,
|
||||
onEvent = { dispatch(it) },
|
||||
)
|
||||
} else {
|
||||
SideRailDrawerContent(
|
||||
state = state.value,
|
||||
onEvent = { dispatch(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.DomainContract.UseCase
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayTreeFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.Effect
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.Event
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.State
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.ViewModel
|
||||
|
||||
/**
|
||||
* Delay before closing the drawer to avoid the drawer being closed immediately and give time
|
||||
* for the ripple effect to finish.
|
||||
*/
|
||||
private const val DRAWER_CLOSE_DELAY = 250L
|
||||
private const val ACCOUNT_CLOSE_DELAY = 150L
|
||||
|
||||
@Suppress("MagicNumber", "TooManyFunctions")
|
||||
internal class DrawerViewModel(
|
||||
private val getDrawerConfig: UseCase.GetDrawerConfig,
|
||||
private val saveDrawerConfig: UseCase.SaveDrawerConfig,
|
||||
private val getDisplayAccounts: UseCase.GetDisplayAccounts,
|
||||
private val getDisplayFoldersForAccount: UseCase.GetDisplayFoldersForAccount,
|
||||
private val getDisplayTreeFolder: UseCase.GetDisplayTreeFolder,
|
||||
private val syncAccount: UseCase.SyncAccount,
|
||||
private val syncAllAccounts: UseCase.SyncAllAccounts,
|
||||
private val maxNestingLevel: Int = 2,
|
||||
initialState: State = State(),
|
||||
) : BaseViewModel<State, Event, Effect>(
|
||||
initialState = initialState,
|
||||
),
|
||||
ViewModel {
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
getDrawerConfig().collectLatest { config ->
|
||||
updateState {
|
||||
it.copy(config = config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
loadAccounts()
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
loadFolders()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private suspend fun loadAccounts() {
|
||||
state.map { it.config.showUnifiedFolders }
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { showUnifiedFolders ->
|
||||
getDisplayAccounts(showUnifiedFolders)
|
||||
}.collectLatest { accounts ->
|
||||
updateAccounts(accounts)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAccounts(accounts: List<DisplayAccount>) {
|
||||
val selectedAccount = accounts.find { it.id == state.value.selectedAccountId }
|
||||
?: accounts.firstOrNull()
|
||||
|
||||
updateState {
|
||||
it.copy(
|
||||
accounts = accounts.toImmutableList(),
|
||||
selectedAccountId = selectedAccount?.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private suspend fun loadFolders() {
|
||||
state.map {
|
||||
it.selectedAccountId
|
||||
}.filterNotNull()
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { accountId ->
|
||||
getDisplayFoldersForAccount(accountId)
|
||||
}.collect { folders ->
|
||||
updateFolders(folders, getDisplayTreeFolder(folders, maxNestingLevel))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFolders(displayFolders: List<DisplayFolder>, rootFolder: DisplayTreeFolder) {
|
||||
// First try to find the folder in the flat list
|
||||
var selectedFolder = displayFolders.find {
|
||||
it.id == state.value.selectedFolderId
|
||||
}
|
||||
|
||||
// If not found, try to find it in the tree hierarchy
|
||||
if (selectedFolder == null) {
|
||||
selectedFolder = findFolderById(rootFolder, state.value.selectedFolderId)
|
||||
}
|
||||
|
||||
// If still not found, default to the first folder
|
||||
if (selectedFolder == null) {
|
||||
selectedFolder = displayFolders.firstOrNull() ?: rootFolder.children.firstOrNull()?.displayFolder
|
||||
}
|
||||
|
||||
updateState {
|
||||
it.copy(
|
||||
rootFolder = rootFolder,
|
||||
folders = displayFolders.toImmutableList(),
|
||||
selectedFolderId = selectedFolder?.id,
|
||||
selectedFolder = selectedFolder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches for a folder with the given ID in the DisplayTreeFolder hierarchy.
|
||||
*/
|
||||
private fun findFolderById(treeFolder: DisplayTreeFolder, folderId: String?): DisplayFolder? {
|
||||
if (folderId == null) return null
|
||||
|
||||
return if (treeFolder.displayFolder?.id == folderId) {
|
||||
treeFolder.displayFolder
|
||||
} else {
|
||||
// Recursively search in children
|
||||
var folder: DisplayFolder? = null
|
||||
for (child in treeFolder.children) {
|
||||
val found = findFolderById(child, folderId)
|
||||
if (found != null) {
|
||||
folder = found
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
folder
|
||||
}
|
||||
}
|
||||
|
||||
override fun event(event: Event) {
|
||||
when (event) {
|
||||
is Event.SelectAccount -> selectAccount(event.accountId)
|
||||
is Event.SelectFolder -> selectFolder(event.folderId)
|
||||
|
||||
is Event.OnAccountClick -> openAccount(event.account)
|
||||
is Event.OnFolderClick -> openFolder(event.folder)
|
||||
is Event.OnAccountViewClick -> {
|
||||
openAccount(
|
||||
state.value.accounts.nextOrFirst(event.account),
|
||||
)
|
||||
}
|
||||
|
||||
Event.OnAccountSelectorClick -> {
|
||||
viewModelScope.launch {
|
||||
saveDrawerConfig(
|
||||
state.value.config.copy(showAccountSelector = state.value.config.showAccountSelector.not()),
|
||||
).launchIn(viewModelScope)
|
||||
delay(ACCOUNT_CLOSE_DELAY)
|
||||
updateState {
|
||||
it.copy(showAccountSelection = it.showAccountSelection.not())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Event.OnManageFoldersClick -> emitEffect(Effect.OpenManageFolders)
|
||||
Event.OnSettingsClick -> emitEffect(Effect.OpenSettings)
|
||||
Event.OnSyncAccount -> onSyncAccount()
|
||||
Event.OnSyncAllAccounts -> onSyncAllAccounts()
|
||||
Event.OnAddAccountClick -> emitEffect(Effect.OpenAddAccount)
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectAccount(accountId: String?) {
|
||||
if (accountId != state.value.selectedAccountId) {
|
||||
viewModelScope.launch {
|
||||
updateState {
|
||||
it.copy(
|
||||
selectedAccountId = accountId,
|
||||
)
|
||||
}
|
||||
delay(ACCOUNT_CLOSE_DELAY)
|
||||
updateState {
|
||||
it.copy(
|
||||
showAccountSelection = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectFolder(folderId: String?) {
|
||||
// Find the folder with the given ID
|
||||
val folder = folderId?.let {
|
||||
state.value.folders.find { it.id == folderId }
|
||||
// If not found, try to find it in the tree hierarchy
|
||||
?: findFolderById(state.value.rootFolder, folderId)
|
||||
}
|
||||
|
||||
updateState {
|
||||
it.copy(
|
||||
selectedFolderId = folderId,
|
||||
selectedFolder = folder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openAccount(account: DisplayAccount?) {
|
||||
if (account != null) {
|
||||
emitEffect(Effect.OpenAccount(account.id))
|
||||
}
|
||||
}
|
||||
|
||||
private fun ImmutableList<DisplayAccount>.nextOrFirst(account: DisplayAccount): DisplayAccount? {
|
||||
val index = indexOf(account)
|
||||
return if (index == -1) {
|
||||
null
|
||||
} else if (index == size - 1) {
|
||||
get(0)
|
||||
} else {
|
||||
get(index + 1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openFolder(folder: DisplayFolder) {
|
||||
// Update the selected folder ID in the state
|
||||
selectFolder(folder.id)
|
||||
|
||||
if (folder is MailDisplayFolder) {
|
||||
if (folder.accountId != null) {
|
||||
emitEffect(
|
||||
Effect.OpenFolder(
|
||||
accountId = folder.accountId,
|
||||
folderId = folder.folder.id,
|
||||
),
|
||||
)
|
||||
}
|
||||
} else if (folder is UnifiedDisplayFolder) {
|
||||
emitEffect(Effect.OpenUnifiedFolder)
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
delay(DRAWER_CLOSE_DELAY)
|
||||
emitEffect(Effect.CloseDrawer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSyncAccount() {
|
||||
if (state.value.isLoading || state.value.selectedAccountId == null) return
|
||||
|
||||
viewModelScope.launch {
|
||||
updateState {
|
||||
it.copy(isLoading = true)
|
||||
}
|
||||
|
||||
state.value.selectedAccountId?.let { syncAccount(it).collect() }
|
||||
|
||||
updateState {
|
||||
it.copy(isLoading = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSyncAllAccounts() {
|
||||
if (state.value.isLoading) return
|
||||
|
||||
viewModelScope.launch {
|
||||
updateState {
|
||||
it.copy(isLoading = true)
|
||||
}
|
||||
|
||||
syncAllAccounts().collect()
|
||||
|
||||
updateState {
|
||||
it.copy(isLoading = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelSmall
|
||||
import app.k9mail.core.ui.compose.theme2.ColorRoles
|
||||
import net.thunderbird.feature.account.avatar.ui.Avatar
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getDisplayAccountColor
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getDisplayAccountName
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.labelForCount
|
||||
|
||||
@Composable
|
||||
internal fun AccountAvatar(
|
||||
account: DisplayAccount,
|
||||
selected: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: ((DisplayAccount) -> Unit)? = null,
|
||||
) {
|
||||
val name = getDisplayAccountName(account)
|
||||
val color = getDisplayAccountColor(account)
|
||||
val accountColor = rememberCalculatedAccountColor(color)
|
||||
val accountColorRoles = rememberCalculatedAccountColorRoles(accountColor)
|
||||
|
||||
Box(
|
||||
modifier = modifier,
|
||||
contentAlignment = Alignment.BottomEnd,
|
||||
) {
|
||||
Avatar(
|
||||
color = accountColor,
|
||||
name = name,
|
||||
onClick = onClick?.let { { onClick(account) } },
|
||||
selected = selected,
|
||||
)
|
||||
UnreadBadge(
|
||||
unreadCount = account.unreadMessageCount,
|
||||
accountColorRoles = accountColorRoles,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UnreadBadge(
|
||||
unreadCount: Int,
|
||||
accountColorRoles: ColorRoles,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (unreadCount > 0) {
|
||||
val resources = LocalContext.current.resources
|
||||
|
||||
Surface(
|
||||
color = accountColorRoles.accent,
|
||||
shape = CircleShape,
|
||||
modifier = modifier,
|
||||
) {
|
||||
TextLabelSmall(
|
||||
text = labelForCount(
|
||||
count = unreadCount,
|
||||
resources = resources,
|
||||
),
|
||||
color = accountColorRoles.onAccent,
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 3.dp,
|
||||
vertical = 2.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
|
||||
@Composable
|
||||
internal fun AccountList(
|
||||
accounts: ImmutableList<DisplayAccount>,
|
||||
selectedAccount: DisplayAccount?,
|
||||
onAccountClick: (DisplayAccount) -> Unit,
|
||||
showStarredCount: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(vertical = MainTheme.spacings.default),
|
||||
) {
|
||||
items(
|
||||
items = accounts,
|
||||
key = { account -> account.id },
|
||||
) { account ->
|
||||
AccountListItem(
|
||||
account = account,
|
||||
onClick = { onAccountClick(account) },
|
||||
selected = selectedAccount == account,
|
||||
showStarredCount = showStarredCount,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
|
||||
import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItem
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.feature.account.avatar.ui.AvatarOutlined
|
||||
import net.thunderbird.feature.account.avatar.ui.AvatarSize
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getDisplayAccountColor
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getDisplayAccountName
|
||||
|
||||
@Composable
|
||||
internal fun AccountListItem(
|
||||
account: DisplayAccount,
|
||||
onClick: (DisplayAccount) -> Unit,
|
||||
selected: Boolean,
|
||||
showStarredCount: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val color = getDisplayAccountColor(account)
|
||||
val name = getDisplayAccountName(account)
|
||||
|
||||
NavigationDrawerItem(
|
||||
label = { AccountLabel(account = account) },
|
||||
selected = selected,
|
||||
onClick = { onClick(account) },
|
||||
modifier = modifier.fillMaxWidth()
|
||||
.height(MainTheme.sizes.large),
|
||||
icon = {
|
||||
AvatarOutlined(
|
||||
color = color,
|
||||
name = name,
|
||||
size = AvatarSize.MEDIUM,
|
||||
)
|
||||
},
|
||||
badge = {
|
||||
AccountListItemBadge(
|
||||
unreadCount = account.unreadMessageCount,
|
||||
starredCount = account.starredMessageCount,
|
||||
showStarredCount = showStarredCount,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccountLabel(
|
||||
account: DisplayAccount,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val name = getDisplayAccountName(account)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.half),
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
TextBodyLarge(
|
||||
text = buildAnnotatedString {
|
||||
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(name)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (account is MailDisplayAccount && account.name != account.email) {
|
||||
TextBodyMedium(
|
||||
text = account.email,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItemBadge
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.core.ui.compose.designsystem.atom.icon.filled.Star
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.labelForCount
|
||||
|
||||
@Composable
|
||||
internal fun AccountListItemBadge(
|
||||
unreadCount: Int,
|
||||
starredCount: Int,
|
||||
showStarredCount: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
AccountCountAndStarredBadge(
|
||||
unreadCount = unreadCount,
|
||||
starredCount = starredCount,
|
||||
showStarredCount = showStarredCount,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccountCountAndStarredBadge(
|
||||
unreadCount: Int,
|
||||
starredCount: Int,
|
||||
showStarredCount: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
val resources = LocalContext.current.resources
|
||||
|
||||
if (unreadCount > 0) {
|
||||
NavigationDrawerItemBadge(
|
||||
label = labelForCount(
|
||||
count = unreadCount,
|
||||
resources = resources,
|
||||
),
|
||||
imageVector = if (showStarredCount) Icons.Filled.Dot else null,
|
||||
)
|
||||
}
|
||||
|
||||
if (showStarredCount && starredCount > 0) {
|
||||
Spacer(modifier = Modifier.Companion.width(MainTheme.spacings.half))
|
||||
NavigationDrawerItemBadge(
|
||||
label = labelForCount(
|
||||
count = starredCount,
|
||||
resources = resources,
|
||||
),
|
||||
imageVector = Icons.Filled.Star,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.displayCutout
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.feature.account.avatar.ui.AvatarOutlined
|
||||
import net.thunderbird.feature.account.avatar.ui.AvatarSize
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.R
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.AnimatedExpandIcon
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getDisplayAccountColor
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getDisplayAccountName
|
||||
|
||||
@Composable
|
||||
internal fun AccountView(
|
||||
account: DisplayAccount,
|
||||
onClick: () -> Unit,
|
||||
showAccountSelection: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
AccountLayout(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
) {
|
||||
if (showAccountSelection) {
|
||||
AccountSelectionView()
|
||||
} else {
|
||||
AccountSelectedView(
|
||||
account = account,
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedExpandIcon(
|
||||
isExpanded = showAccountSelection,
|
||||
modifier = Modifier.padding(end = MainTheme.spacings.double),
|
||||
tint = MainTheme.colors.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.AccountSelectedView(
|
||||
account: DisplayAccount,
|
||||
) {
|
||||
val color = getDisplayAccountColor(account)
|
||||
val name = getDisplayAccountName(account)
|
||||
|
||||
AvatarOutlined(
|
||||
color = color,
|
||||
name = name,
|
||||
size = AvatarSize.MEDIUM,
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.half),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
) {
|
||||
TextBodyLarge(
|
||||
text = buildAnnotatedString {
|
||||
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(name)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (account is MailDisplayAccount && account.name != account.email) {
|
||||
TextBodyMedium(
|
||||
text = account.email,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.AccountSelectionView() {
|
||||
TextBodyLarge(
|
||||
text = buildAnnotatedString {
|
||||
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(stringResource(R.string.navigation_drawer_dropdown_avount_view_selection_title))
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccountLayout(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable RowScope.() -> Unit,
|
||||
) {
|
||||
val horizontalInsetPadding = getDisplayCutOutHorizontalInsetPadding()
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.windowInsetsPadding(horizontalInsetPadding)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(
|
||||
top = MainTheme.spacings.default,
|
||||
start = MainTheme.spacings.triple,
|
||||
end = MainTheme.spacings.double,
|
||||
bottom = MainTheme.spacings.default,
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(MainTheme.sizes.large),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getDisplayCutOutHorizontalInsetPadding(): WindowInsets {
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
return WindowInsets.displayCutout.only(if (isRtl) WindowInsetsSides.Right else WindowInsetsSides.Left)
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import app.k9mail.core.ui.compose.theme2.toHarmonizedColor
|
||||
|
||||
/**
|
||||
* Calculates the account color based on the provided account color and surface color.
|
||||
*
|
||||
* If the account color is unspecified, it returns the fallback color.
|
||||
* Otherwise, it harmonizes the account color with the surface color.
|
||||
*
|
||||
* @param accountColor The color of the account.
|
||||
* @param fallbackColor The fallback color to use if the account color is unspecified.
|
||||
*/
|
||||
@Composable
|
||||
internal fun rememberCalculatedAccountColor(
|
||||
accountColor: Color,
|
||||
fallbackColor: Color = MainTheme.colors.primary,
|
||||
): Color {
|
||||
val surfaceColor = MainTheme.colors.surface
|
||||
return remember(accountColor, surfaceColor, fallbackColor) {
|
||||
if (accountColor == Color.Unspecified) {
|
||||
fallbackColor
|
||||
} else {
|
||||
accountColor.toHarmonizedColor(surfaceColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.account
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import app.k9mail.core.ui.compose.theme2.ColorRoles
|
||||
import app.k9mail.core.ui.compose.theme2.toColorRoles
|
||||
|
||||
/**
|
||||
* Calculates the color roles for the given account color.
|
||||
*
|
||||
* This function is used to derive the color roles for an account based on its color and
|
||||
* use remember to avoid unnecessary recomputations.
|
||||
*
|
||||
* @param accountColor The color of the account.
|
||||
*/
|
||||
@Composable
|
||||
internal fun rememberCalculatedAccountColorRoles(
|
||||
accountColor: Color,
|
||||
): ColorRoles {
|
||||
val context = LocalContext.current
|
||||
|
||||
return remember(accountColor) {
|
||||
accountColor.toColorRoles(context)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.common
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
|
||||
@Composable
|
||||
internal fun AnimatedExpandIcon(
|
||||
isExpanded: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color? = null,
|
||||
) {
|
||||
val rotationAngle by animateFloatAsState(
|
||||
targetValue = if (isExpanded) 180f else 0f,
|
||||
label = "rotationAngle",
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
tint = tint,
|
||||
modifier = modifier
|
||||
.rotate(rotationAngle),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.common
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.R
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayAccount
|
||||
|
||||
@Composable
|
||||
internal fun getDisplayAccountColor(account: DisplayAccount): Color {
|
||||
return when (account) {
|
||||
is UnifiedDisplayAccount -> {
|
||||
MainTheme.colors.onSurfaceVariant
|
||||
}
|
||||
is MailDisplayAccount -> {
|
||||
Color(account.color)
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown account type: ${account::class.java.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun getDisplayAccountName(account: DisplayAccount): String {
|
||||
return when (account) {
|
||||
is UnifiedDisplayAccount -> {
|
||||
stringResource(R.string.navigation_drawer_dropdown_unified_account_title)
|
||||
}
|
||||
is MailDisplayAccount -> {
|
||||
account.name
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException("Unknown account type: ${account::class.java.simpleName}")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.common
|
||||
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.displayCutout
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
// As long as we use DrawerLayout, we don't have to worry about screens narrower than DRAWER_WIDTH. DrawerLayout will
|
||||
// automatically limit the width of the content view so there's still room for a scrim with minimum tap width.
|
||||
internal val DRAWER_WIDTH = 360.dp
|
||||
|
||||
@Composable
|
||||
internal fun getAdditionalWidth(): Dp {
|
||||
val density = LocalDensity.current
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
|
||||
return if (isRtl) {
|
||||
WindowInsets.displayCutout.getRight(density = density, layoutDirection = layoutDirection)
|
||||
} else {
|
||||
WindowInsets.displayCutout.getLeft(density = density, layoutDirection = layoutDirection)
|
||||
}.pxToDp()
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Int.pxToDp() = with(LocalDensity.current) { this@pxToDp.toDp() }
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.common
|
||||
|
||||
import android.content.res.Resources
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.R
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
internal fun labelForCount(
|
||||
count: Int,
|
||||
resources: Resources,
|
||||
) = when {
|
||||
count in 1..99 -> "$count"
|
||||
|
||||
count in 100..1000 -> resources.getString(
|
||||
R.string.navigation_drawer_dropdown_folder_item_badge_count_greater_than_99,
|
||||
)
|
||||
|
||||
count > 1000 -> resources.getString(
|
||||
R.string.navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000,
|
||||
)
|
||||
|
||||
else -> ""
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.folder
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import app.k9mail.legacy.ui.folder.FolderNameFormatter
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayTreeFolder
|
||||
|
||||
@Composable
|
||||
internal fun FolderList(
|
||||
rootFolder: DisplayTreeFolder,
|
||||
selectedFolder: DisplayFolder?,
|
||||
onFolderClick: (DisplayFolder) -> Unit,
|
||||
showStarredCount: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val resources = LocalContext.current.resources
|
||||
val folderNameFormatter = remember { FolderNameFormatter(resources) }
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(vertical = MainTheme.spacings.default),
|
||||
) {
|
||||
items(
|
||||
items = rootFolder.children,
|
||||
key = { it.displayFolder?.id ?: '0' },
|
||||
) { folder ->
|
||||
val currentDisplayFolder = folder.displayFolder
|
||||
FolderListItem(
|
||||
displayFolder = requireNotNull(currentDisplayFolder) {
|
||||
"Null DisplayFolder for folder ${folder.displayName}"
|
||||
},
|
||||
treeFolder = folder,
|
||||
showStarredCount = showStarredCount,
|
||||
onClick = onFolderClick,
|
||||
folderNameFormatter = folderNameFormatter,
|
||||
selectedFolderId = selectedFolder?.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.folder
|
||||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelLarge
|
||||
import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItem
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import app.k9mail.legacy.ui.folder.FolderNameFormatter
|
||||
import net.thunderbird.feature.mail.folder.api.FolderType
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.R
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayTreeFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolder
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.UnifiedDisplayFolderType
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.AnimatedExpandIcon
|
||||
|
||||
@Composable
|
||||
internal fun FolderListItem(
|
||||
displayFolder: DisplayFolder,
|
||||
onClick: (DisplayFolder) -> Unit,
|
||||
showStarredCount: Boolean,
|
||||
folderNameFormatter: FolderNameFormatter,
|
||||
selectedFolderId: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
treeFolder: DisplayTreeFolder? = null,
|
||||
parentPrefix: String? = "",
|
||||
indentationLevel: Int = 1,
|
||||
) {
|
||||
val isExpanded = rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
var unreadCount = displayFolder.unreadMessageCount
|
||||
var starredCount = displayFolder.starredMessageCount
|
||||
|
||||
if (treeFolder !== null && !isExpanded.value) {
|
||||
unreadCount = treeFolder.totalUnreadCount
|
||||
starredCount = treeFolder.totalStarredCount
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.animateContentSize(),
|
||||
) {
|
||||
NavigationDrawerItem(
|
||||
label = {
|
||||
NavigationDrawerLabel(
|
||||
label = mapFolderName(displayFolder, folderNameFormatter, parentPrefix),
|
||||
expandableState = if (treeFolder !== null && treeFolder.children.isNotEmpty()) isExpanded else null,
|
||||
badge = {
|
||||
FolderListItemBadge(
|
||||
unreadCount = unreadCount,
|
||||
starredCount = starredCount,
|
||||
showStarredCount = showStarredCount,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
selected = selectedFolderId == displayFolder.id,
|
||||
onClick = {
|
||||
when (displayFolder) {
|
||||
is MailDisplayFolder if displayFolder.accountId == null -> isExpanded.value = !isExpanded.value
|
||||
else -> onClick(displayFolder)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = { Icon(imageVector = mapFolderIcon(displayFolder)) },
|
||||
)
|
||||
|
||||
// Managing children
|
||||
if (!isExpanded.value) return
|
||||
if (treeFolder === null) return
|
||||
for (child in treeFolder.children) {
|
||||
val displayParent = treeFolder.displayFolder
|
||||
val displayChild = child.displayFolder
|
||||
if (displayChild == null) continue
|
||||
FolderListItem(
|
||||
displayFolder = displayChild,
|
||||
selectedFolderId = selectedFolderId,
|
||||
showStarredCount = showStarredCount,
|
||||
onClick = onClick,
|
||||
folderNameFormatter = folderNameFormatter,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = MainTheme.spacings.double * indentationLevel),
|
||||
treeFolder = child,
|
||||
parentPrefix = if (displayParent is MailDisplayFolder) displayParent.folder.name else null,
|
||||
indentationLevel = indentationLevel + 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NavigationDrawerLabel(
|
||||
label: String,
|
||||
badge: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
expandableState: MutableState<Boolean>? = null,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TextLabelLarge(
|
||||
text = label,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 2,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
if (expandableState?.value != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(MainTheme.sizes.iconAvatar)
|
||||
.padding(
|
||||
start = MainTheme.spacings.quarter,
|
||||
end = MainTheme.spacings.quarter,
|
||||
)
|
||||
.clip(CircleShape)
|
||||
.clickable(onClick = { expandableState.value = !expandableState.value }),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
AnimatedExpandIcon(
|
||||
isExpanded = expandableState.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
badge()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun mapFolderName(
|
||||
displayFolder: DisplayFolder,
|
||||
folderNameFormatter: FolderNameFormatter,
|
||||
parentPrefix: String? = "",
|
||||
): String {
|
||||
return when (displayFolder) {
|
||||
is MailDisplayFolder ->
|
||||
folderNameFormatter
|
||||
.displayName(displayFolder.folder)
|
||||
.removePrefix("$parentPrefix${displayFolder.pathDelimiter}")
|
||||
|
||||
is UnifiedDisplayFolder -> mapUnifiedFolderName(displayFolder)
|
||||
else -> throw IllegalArgumentException("Unknown display folder: $displayFolder")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun mapUnifiedFolderName(folder: UnifiedDisplayFolder): String {
|
||||
return when (folder.unifiedType) {
|
||||
UnifiedDisplayFolderType.INBOX -> stringResource(R.string.navigation_drawer_dropdown_unified_inbox_title)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapFolderIcon(folder: DisplayFolder): ImageVector {
|
||||
return when (folder) {
|
||||
is MailDisplayFolder -> mapDisplayAccountFolderIcon(folder)
|
||||
is UnifiedDisplayFolder -> mapDisplayUnifiedFolderIcon(folder)
|
||||
else -> throw IllegalArgumentException("Unknown display folder type: $folder")
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapDisplayAccountFolderIcon(folder: MailDisplayFolder): ImageVector {
|
||||
return when (folder.folder.type) {
|
||||
FolderType.INBOX -> Icons.Outlined.Inbox
|
||||
FolderType.OUTBOX -> Icons.Outlined.Outbox
|
||||
FolderType.SENT -> Icons.Outlined.Send
|
||||
FolderType.TRASH -> Icons.Outlined.Delete
|
||||
FolderType.DRAFTS -> Icons.Outlined.Drafts
|
||||
FolderType.ARCHIVE -> Icons.Outlined.Archive
|
||||
FolderType.SPAM -> Icons.Outlined.Report
|
||||
FolderType.REGULAR -> Icons.Outlined.Folder
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapDisplayUnifiedFolderIcon(folder: UnifiedDisplayFolder): ImageVector {
|
||||
when (folder.unifiedType) {
|
||||
UnifiedDisplayFolderType.INBOX -> return Icons.Outlined.AllInbox
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.folder
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItemBadge
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.core.ui.compose.designsystem.atom.icon.filled.Star
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.labelForCount
|
||||
|
||||
@Composable
|
||||
internal fun FolderListItemBadge(
|
||||
unreadCount: Int,
|
||||
starredCount: Int,
|
||||
showStarredCount: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
FolderCountAndStarredBadge(
|
||||
unreadCount = unreadCount,
|
||||
starredCount = starredCount,
|
||||
showStarredCount = showStarredCount,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FolderCountAndStarredBadge(
|
||||
unreadCount: Int,
|
||||
starredCount: Int,
|
||||
showStarredCount: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
val resources = LocalContext.current.resources
|
||||
|
||||
if (unreadCount > 0) {
|
||||
NavigationDrawerItemBadge(
|
||||
label = labelForCount(
|
||||
count = unreadCount,
|
||||
resources = resources,
|
||||
),
|
||||
imageVector = if (showStarredCount) Icons.Filled.Dot else null,
|
||||
)
|
||||
}
|
||||
|
||||
if (showStarredCount && starredCount > 0) {
|
||||
Spacer(modifier = Modifier.width(MainTheme.spacings.half))
|
||||
NavigationDrawerItemBadge(
|
||||
label = labelForCount(
|
||||
count = starredCount,
|
||||
resources = resources,
|
||||
),
|
||||
imageVector = Icons.Filled.Star,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.setting
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.R
|
||||
|
||||
@Composable
|
||||
internal fun AccountSettingList(
|
||||
onAddAccountClick: () -> Unit,
|
||||
onSyncAllAccountsClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SettingList(
|
||||
modifier = modifier
|
||||
.padding(vertical = MainTheme.spacings.default)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
item {
|
||||
SettingListItem(
|
||||
label = stringResource(id = R.string.navigation_drawer_dropdown_action_sync_all_accounts),
|
||||
onClick = onSyncAllAccountsClick,
|
||||
icon = Icons.Outlined.Sync,
|
||||
)
|
||||
}
|
||||
item {
|
||||
SettingListItem(
|
||||
label = stringResource(id = R.string.navigation_drawer_dropdown_action_add_account),
|
||||
onClick = onAddAccountClick,
|
||||
icon = Icons.Outlined.Add,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.setting
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.R
|
||||
|
||||
@Composable
|
||||
internal fun FolderSettingList(
|
||||
onManageFoldersClick: () -> Unit,
|
||||
onSettingsClick: () -> Unit,
|
||||
isUnifiedAccount: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SettingList(
|
||||
modifier = modifier
|
||||
.padding(vertical = MainTheme.spacings.default)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
if (isUnifiedAccount.not()) {
|
||||
item {
|
||||
SettingListItem(
|
||||
label = stringResource(R.string.navigation_drawer_dropdown_action_manage_folders),
|
||||
onClick = onManageFoldersClick,
|
||||
icon = Icons.Outlined.FolderManaged,
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
SettingListItem(
|
||||
label = stringResource(id = R.string.navigation_drawer_dropdown_action_settings),
|
||||
onClick = onSettingsClick,
|
||||
icon = Icons.Outlined.Settings,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.setting
|
||||
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyGridScope
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.common.window.WindowSizeClass
|
||||
import app.k9mail.core.ui.compose.common.window.getWindowSizeInfo
|
||||
|
||||
@Composable
|
||||
internal fun SettingList(
|
||||
modifier: Modifier = Modifier,
|
||||
content: LazyGridScope.() -> Unit,
|
||||
) {
|
||||
val windowSizeInfo = getWindowSizeInfo()
|
||||
val isLandscape = windowSizeInfo.screenWidth > windowSizeInfo.screenHeight
|
||||
val useMultipleRows = isLandscape && windowSizeInfo.screenWidthSizeClass != WindowSizeClass.Compact
|
||||
|
||||
val rows = if (useMultipleRows) 2 else 1
|
||||
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(rows),
|
||||
modifier = modifier,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package net.thunderbird.feature.navigation.drawer.dropdown.ui.setting
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon
|
||||
import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItem
|
||||
|
||||
@Composable
|
||||
internal fun SettingListItem(
|
||||
label: String,
|
||||
onClick: () -> Unit,
|
||||
icon: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
NavigationDrawerItem(
|
||||
label = label,
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
selected = false,
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.DividerHorizontal
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import net.thunderbird.core.ui.compose.common.modifier.testTagAsResourceId
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.Event
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.DrawerContract.State
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.DRAWER_WIDTH
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getAdditionalWidth
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.folder.FolderList
|
||||
import net.thunderbird.feature.navigation.drawer.siderail.ui.account.SideRailAccountList
|
||||
import net.thunderbird.feature.navigation.drawer.siderail.ui.account.SideRailAccountView
|
||||
import net.thunderbird.feature.navigation.drawer.siderail.ui.setting.SideRailSettingList
|
||||
|
||||
@Composable
|
||||
internal fun SideRailDrawerContent(
|
||||
state: State,
|
||||
onEvent: (Event) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val additionalWidth = getAdditionalWidth()
|
||||
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.windowInsetsPadding(WindowInsets.statusBars)
|
||||
.width(DRAWER_WIDTH + additionalWidth)
|
||||
.fillMaxHeight()
|
||||
.testTagAsResourceId("DrawerContent"),
|
||||
) {
|
||||
val selectedAccount = state.accounts.firstOrNull { it.id == state.selectedAccountId }
|
||||
Column {
|
||||
selectedAccount?.let {
|
||||
SideRailAccountView(
|
||||
account = selectedAccount,
|
||||
onClick = { onEvent(Event.OnAccountViewClick(selectedAccount)) },
|
||||
showAvatar = state.config.showAccountSelector,
|
||||
)
|
||||
|
||||
DividerHorizontal()
|
||||
}
|
||||
Row {
|
||||
AnimatedVisibility(
|
||||
visible = state.config.showAccountSelector,
|
||||
) {
|
||||
SideRailAccountList(
|
||||
accounts = state.accounts,
|
||||
selectedAccount = selectedAccount,
|
||||
onAccountClick = { onEvent(Event.OnAccountClick(it)) },
|
||||
onSyncAllAccountsClick = { onEvent(Event.OnSyncAllAccounts) },
|
||||
onSettingsClick = { onEvent(Event.OnSettingsClick) },
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
FolderList(
|
||||
rootFolder = state.rootFolder,
|
||||
selectedFolder = state.folders.firstOrNull { it.id == state.selectedFolderId },
|
||||
onFolderClick = { folder ->
|
||||
onEvent(Event.OnFolderClick(folder))
|
||||
},
|
||||
showStarredCount = state.config.showStarredCount,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
DividerHorizontal()
|
||||
SideRailSettingList(
|
||||
onAccountSelectorClick = { onEvent(Event.OnAccountSelectorClick) },
|
||||
onManageFoldersClick = { onEvent(Event.OnManageFoldersClick) },
|
||||
showAccountSelector = state.config.showAccountSelector,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.account
|
||||
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.account.rememberCalculatedAccountColor
|
||||
|
||||
@Composable
|
||||
internal fun SideRailAccountIndicator(
|
||||
accountColor: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.width(MainTheme.spacings.half)
|
||||
.defaultMinSize(
|
||||
minHeight = MainTheme.spacings.default,
|
||||
),
|
||||
color = rememberCalculatedAccountColor(accountColor),
|
||||
shape = MainTheme.shapes.medium,
|
||||
) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.account
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.R
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.account.getDisplayCutOutHorizontalInsetPadding
|
||||
import net.thunderbird.feature.navigation.drawer.siderail.ui.setting.SideRailSettingItem
|
||||
|
||||
@Composable
|
||||
internal fun SideRailAccountList(
|
||||
accounts: ImmutableList<DisplayAccount>,
|
||||
selectedAccount: DisplayAccount?,
|
||||
onAccountClick: (DisplayAccount) -> Unit,
|
||||
onSyncAllAccountsClick: () -> Unit,
|
||||
onSettingsClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier,
|
||||
color = MainTheme.colors.surfaceContainer,
|
||||
) {
|
||||
val horizontalInsetPadding = getDisplayCutOutHorizontalInsetPadding()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.windowInsetsPadding(horizontalInsetPadding)
|
||||
.width(MainTheme.sizes.large),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.weight(1f),
|
||||
contentPadding = PaddingValues(vertical = MainTheme.spacings.default),
|
||||
) {
|
||||
items(
|
||||
items = accounts,
|
||||
key = { account -> account.id },
|
||||
) { account ->
|
||||
SideRailAccountListItem(
|
||||
account = account,
|
||||
onClick = { onAccountClick(account) },
|
||||
selected = selectedAccount == account,
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = MainTheme.spacings.oneHalf),
|
||||
) {
|
||||
SideRailSettingItem(
|
||||
icon = Icons.Outlined.Sync,
|
||||
label = stringResource(id = R.string.navigation_drawer_dropdown_action_sync_all_accounts),
|
||||
onClick = onSyncAllAccountsClick,
|
||||
)
|
||||
// Hack to compensate the column placement at an uneven coordinate, caused by the 1.dp divider.
|
||||
Spacer(modifier = Modifier.height(7.dp))
|
||||
SideRailSettingItem(
|
||||
icon = Icons.Outlined.Settings,
|
||||
label = stringResource(id = R.string.navigation_drawer_dropdown_action_settings),
|
||||
onClick = onSettingsClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.account
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.account.AccountAvatar
|
||||
|
||||
@Composable
|
||||
internal fun SideRailAccountListItem(
|
||||
account: DisplayAccount,
|
||||
onClick: (DisplayAccount) -> Unit,
|
||||
selected: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.width(MainTheme.sizes.large)
|
||||
.padding(vertical = MainTheme.spacings.half),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
AccountAvatar(
|
||||
account = account,
|
||||
onClick = onClick,
|
||||
selected = selected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.account
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.displayCutout
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.DisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.domain.entity.MailDisplayAccount
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.account.AccountAvatar
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getDisplayAccountColor
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.common.getDisplayAccountName
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
internal fun SideRailAccountView(
|
||||
account: DisplayAccount,
|
||||
onClick: () -> Unit,
|
||||
showAvatar: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.height(intrinsicSize = IntrinsicSize.Max),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
AnimatedVisibility(visible = showAvatar) {
|
||||
Surface(
|
||||
color = MainTheme.colors.surfaceContainer,
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
) {
|
||||
val horizontalInsetPadding = getDisplayCutOutHorizontalInsetPadding()
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(horizontalInsetPadding)
|
||||
.width(MainTheme.sizes.large),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
AccountAvatar(
|
||||
account = account,
|
||||
onClick = null,
|
||||
selected = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = modifier
|
||||
.clickable(onClick = onClick)
|
||||
.height(intrinsicSize = IntrinsicSize.Max)
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = MainTheme.sizes.large)
|
||||
.padding(
|
||||
top = MainTheme.spacings.double,
|
||||
start = MainTheme.spacings.double,
|
||||
end = MainTheme.spacings.triple,
|
||||
bottom = MainTheme.spacings.double,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val color = getDisplayAccountColor(account)
|
||||
val name = getDisplayAccountName(account)
|
||||
|
||||
SideRailAccountIndicator(
|
||||
accountColor = color,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.padding(end = MainTheme.spacings.oneHalf),
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.half),
|
||||
) {
|
||||
TextBodyLarge(
|
||||
text = name,
|
||||
color = MainTheme.colors.onSurface,
|
||||
)
|
||||
if (account is MailDisplayAccount && account.name != account.email) {
|
||||
TextBodyMedium(
|
||||
text = account.email,
|
||||
color = MainTheme.colors.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getDisplayCutOutHorizontalInsetPadding(): WindowInsets {
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
return WindowInsets.displayCutout.only(if (isRtl) WindowInsetsSides.Right else WindowInsetsSides.Left)
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.setting
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
|
||||
@Composable
|
||||
internal fun SideRailSettingItem(
|
||||
icon: ImageVector,
|
||||
label: String,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.width(MainTheme.sizes.large),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Surface(
|
||||
color = MainTheme.colors.surfaceContainer,
|
||||
shape = CircleShape,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = label,
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onClick)
|
||||
.padding(MainTheme.spacings.oneHalf),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package net.thunderbird.feature.navigation.drawer.siderail.ui.setting
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.R
|
||||
import net.thunderbird.feature.navigation.drawer.dropdown.ui.setting.SettingListItem
|
||||
|
||||
@Composable
|
||||
internal fun SideRailSettingList(
|
||||
onAccountSelectorClick: () -> Unit,
|
||||
onManageFoldersClick: () -> Unit,
|
||||
showAccountSelector: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(vertical = MainTheme.spacings.default)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
SettingListItem(
|
||||
label = stringResource(R.string.navigation_drawer_dropdown_action_manage_folders),
|
||||
onClick = onManageFoldersClick,
|
||||
icon = Icons.Outlined.FolderManaged,
|
||||
)
|
||||
SettingListItem(
|
||||
label = if (showAccountSelector) {
|
||||
stringResource(R.string.navigation_drawer_dropdown_action_hide_accounts)
|
||||
} else {
|
||||
stringResource(R.string.navigation_drawer_dropdown_action_show_accounts)
|
||||
},
|
||||
onClick = onAccountSelectorClick,
|
||||
icon = if (showAccountSelector) {
|
||||
Icons.Outlined.ChevronLeft
|
||||
} else {
|
||||
Icons.Outlined.ChevronRight
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">ማህደሮችን ማስተዳደር</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">ሁሉንም አካውንቶች አሳይ</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">አካውንቶችን አሳይ</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">አካውንቶችን ደብቅ</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">ጠቅላላ መልእክት ማስቀመጫ</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+ ዘጠና ዘጠኝ እና ከዛ በላይ</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">الإعدادات</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">إدارة المجلدات</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">البريد الوارد</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">إخفاء الحسابات</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">إظهار الحسابات</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1k+</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">مزامنة جميع الحسابات</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">قائمة الحسابات</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">حساب موحد</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">إضافة حساب</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Налады</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Кіраванне каталогамі</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Усе атрыманыя</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Сінхранізаваць усе акаўнты</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Паказаць акаўнты</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Схаваць акаўнты</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1000+</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Настройки</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Управление на папки</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">входящи</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Списък с профили</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Обединен профил</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Синхронизация на всички профили</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Добавяне на профил</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Показване на профилите</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Скриване на профилите</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1000+</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Arventennoù</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Merañ an teuliadoù</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Boest degemer</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Roll ar c\'hontoù</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Kont unanet</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Sinkronaat an holl gontoù</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Ouzhpennañ ur gont</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Diskwel ar c\'hontoù</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Kuzhat ar c\'hontoù</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">>99</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">>1 k</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Postavke</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Upravljajte direktorijumima</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Configuració</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Gestioneu les carpetes</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Bústia d\'entrada unificada</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Sincronitza tots els comptes</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Mostra els comptes</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Amaga els comptes</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1000+</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Llistat de comptes</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Compte unificat</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Afegir compte</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Parametri</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Ghjestione di i cartulari</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Currieru ricevutu</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Sincrunizà tutti i conti</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Affissà i conti</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">> 99</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Piattà i conti</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">> 1000</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Lista di i conti</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Contu di cuncolta</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Aghjunghje un contu</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Nastavení</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Správa složek</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Doručené</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1 tis.+</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Zobrazit účty</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Synchronizovat všechny účty</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Skrýt účty</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Seznam účtů</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Jednotný účet</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Přidat účet</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Gosodiadau</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Rheoli ffolderi</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Blwch Derbyn</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Rhestr cyfrifon</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Cyfrif Cyfun</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Cydweddu pob cyfrif</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Ychwanegu cyfrif</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Dangos cyfrifon</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Cuddio cyfrifon</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1k+</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Indstillinger</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Håndtér mapper</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Fælles indbakke</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Einstellungen</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Ordner verwalten</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Posteingang</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1k+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Konten ausblenden</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Alle Konten synchronisieren</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Konten anzeigen</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Kontenliste</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Konto hinzufügen</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Gemeinsames Konto</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Ρυθμίσεις</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Διαχείριση φακέλων</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Ενιαία Εισερχόμενα</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1χ+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Συγχρονισμός όλων των λογαριασμών</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Εμφάνιση λογαριασμών</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Απόκρυψη λογαριασμών</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Settings</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Manage folders</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Unified Inbox</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Account list</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Agordoj</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Administri mesaĝujojn</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Unuigita ricevujo</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Montri kontojn</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Samtempigi ĉiujn kontojn</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Kaŝi kontojn</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Ajustes</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Administrar carpetas</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Recibidos</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">>99</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">>mil</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Sincronizar todas las cuentas</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Mostrar cuentas</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Ocultar cuentas</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Cuentas disponibles</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Cuenta unificada</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Añadir cuenta</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Seadistused</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Halda kaustu</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Sisendkaust</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Sünkroniseeri kõik kasutajakontod</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Näita kasutajakontosid</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Peida kasutajakontod</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1k+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Kasutajakontode loend</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Koondkasutajakonto</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Lisa kasutajakonto</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Ezarpenak</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Kudeatu karpetak</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Sarrerako ontzi bateratua</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Sinkronizatu kontu guztiak</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Erakutsi kontuak</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Ezkutatu kontuak</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1.000+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Kontuen zerrenda</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Bateratutako kontua</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Gehitu kontua</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">تنظیمات</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">مدیریت پوشهها</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">صندوق ورودی</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">نهفتن حسابها</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">همگام سازی همهٔ آشنایان</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">نمایش حسابها</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">۹۹+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">ه+</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">لیست حساب</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">حساب یکپارچه</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">افزودن حساب</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Asetukset</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Hallitse kansioita</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Postilaatikko</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Synkronoi kaikki tilit</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Näytä tilit</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Piilota tilit</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1k+</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Tililuettelo</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Yhdistetty tili</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Lisää tili</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Paramètres</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Gérer les dossiers</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Boîte de réception</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Cacher les comptes</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">>99</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">>1 k</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Afficher les comptes</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Synchroniser tous les comptes</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Liste des comptes</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Compte unifié</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Ajouter un compte</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Ynstellingen</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Mappen beheare</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Postfek YN</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1k+</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Alle accounts syngronisearje</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Accounts toane</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Accounts ferstopje</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Accountlist</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Kombinearre account</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Account tafoegje</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="navigation_drawer_dropdown_action_settings">Socruithe</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Bainistigh fillteáin</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Sioncronaigh gach cuntas</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1k+</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Taispeáin cuntais</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Folaigh cuntais</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Bosca isteach</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Liosta cuntas</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Cuntas Aontaithe</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Cuir cuntas leis</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Roghainnean</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Stiùirich na pasganan</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">An t-oll-bhogsa</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">Còrr is 99</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">Còrr is 1k</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Sioncronaich a h-uile cunntas</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Seall na cunntasan</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Falaich na cunntasan</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Configuración</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Xestionar cartafoles</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Entrada unificada</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">सेटिंग</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">फोल्डर मैनेज करें</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Podešenja</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Upravljanje mapama</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Objedinjena dolazna pošta</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Sinkroniziraj sve račune</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Prikaži račune</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Sakrij račune</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1k+</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="navigation_drawer_dropdown_action_settings">Beállítások</string>
|
||||
<string name="navigation_drawer_dropdown_action_manage_folders">Mappák kezelése</string>
|
||||
<string name="navigation_drawer_dropdown_unified_inbox_title">Bérkezett üzenetek</string>
|
||||
<string name="navigation_drawer_dropdown_action_sync_all_accounts">Összes fiók szinkronizálása</string>
|
||||
<string name="navigation_drawer_dropdown_action_show_accounts">Fiókok megjelenítése</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_99">99+</string>
|
||||
<string name="navigation_drawer_dropdown_action_hide_accounts">Fiókok elrejtése</string>
|
||||
<string name="navigation_drawer_dropdown_folder_item_badge_count_greater_than_1_000">1e+</string>
|
||||
<string name="navigation_drawer_dropdown_avount_view_selection_title">Fióklista</string>
|
||||
<string name="navigation_drawer_dropdown_unified_account_title">Egyesített fiók</string>
|
||||
<string name="navigation_drawer_dropdown_action_add_account">Fiók hozzáadása</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue