mirror of
https://github.com/LiveFastEatTrashRaccoon/RaccoonForLemmy.git
synced 2025-02-02 00:36:55 +01:00
feat: configurable bottom navigation • part 3 (#1117)
This commit is contained in:
parent
29ecfe8e1c
commit
3f99e68764
@ -0,0 +1,84 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
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.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomSheetHeader
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInt
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toReadableName
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||||
|
|
||||||
|
class SelectTabNavigationSectionBottomSheet(
|
||||||
|
private val values: List<TabNavigationSection>,
|
||||||
|
) : Screen {
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||||
|
val notificationCenter = remember { getNotificationCenter() }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||||
|
.padding(
|
||||||
|
top = Spacing.s,
|
||||||
|
start = Spacing.s,
|
||||||
|
end = Spacing.s,
|
||||||
|
bottom = Spacing.m,
|
||||||
|
),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||||
|
) {
|
||||||
|
BottomSheetHeader(LocalStrings.current.selectTabNavigationTitle)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(Spacing.xxs),
|
||||||
|
) {
|
||||||
|
for (value in values) {
|
||||||
|
Row(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(
|
||||||
|
horizontal = Spacing.s,
|
||||||
|
vertical = Spacing.s,
|
||||||
|
).fillMaxWidth()
|
||||||
|
.onClick(
|
||||||
|
onClick = {
|
||||||
|
notificationCenter.send(
|
||||||
|
NotificationCenterEvent.TabNavigationSectionSelected(value.toInt()),
|
||||||
|
)
|
||||||
|
navigationCoordinator.hideBottomSheet()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = value.toReadableName(),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -429,4 +429,6 @@ internal open class DefaultStrings : Strings {
|
|||||||
override val settingsSubtitleOpenPostWebPageOnImageClick =
|
override val settingsSubtitleOpenPostWebPageOnImageClick =
|
||||||
"If a post has an URL, open web page on image click"
|
"If a post has an URL, open web page on image click"
|
||||||
override val settingsItemAlternateMarkdownRendering = "Enable alternate Markdown rendering"
|
override val settingsItemAlternateMarkdownRendering = "Enable alternate Markdown rendering"
|
||||||
|
override val settingsItemConfigureBottomNavigationBar = "Configure navigation bar"
|
||||||
|
override val selectTabNavigationTitle = "Select a section"
|
||||||
}
|
}
|
||||||
|
@ -427,6 +427,8 @@ interface Strings {
|
|||||||
val settingsItemOpenPostWebPageOnImageClick: String
|
val settingsItemOpenPostWebPageOnImageClick: String
|
||||||
val settingsSubtitleOpenPostWebPageOnImageClick: String
|
val settingsSubtitleOpenPostWebPageOnImageClick: String
|
||||||
val settingsItemAlternateMarkdownRendering: String
|
val settingsItemAlternateMarkdownRendering: String
|
||||||
|
val settingsItemConfigureBottomNavigationBar: String
|
||||||
|
val selectTabNavigationTitle: String
|
||||||
}
|
}
|
||||||
|
|
||||||
object Locales {
|
object Locales {
|
||||||
|
@ -42,6 +42,7 @@ kotlin {
|
|||||||
implementation(libs.voyager.screenmodel)
|
implementation(libs.voyager.screenmodel)
|
||||||
implementation(libs.voyager.koin)
|
implementation(libs.voyager.koin)
|
||||||
|
|
||||||
|
implementation(projects.core.l10n)
|
||||||
implementation(projects.core.persistence)
|
implementation(projects.core.persistence)
|
||||||
implementation(projects.core.preferences)
|
implementation(projects.core.preferences)
|
||||||
implementation(projects.domain.lemmy.data)
|
implementation(projects.domain.lemmy.data)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.core.navigation.di
|
package com.github.diegoberaldin.raccoonforlemmy.core.navigation.di
|
||||||
|
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.DrawerCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.DrawerCoordinator
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.NavigationCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.NavigationCoordinator
|
||||||
import org.koin.java.KoinJavaComponent.inject
|
import org.koin.java.KoinJavaComponent.inject
|
||||||
@ -13,3 +14,8 @@ actual fun getDrawerCoordinator(): DrawerCoordinator {
|
|||||||
val res: DrawerCoordinator by inject(DrawerCoordinator::class.java)
|
val res: DrawerCoordinator by inject(DrawerCoordinator::class.java)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actual fun getBottomNavItemsRepository(): BottomNavItemsRepository {
|
||||||
|
val res: BottomNavItemsRepository by inject(BottomNavItemsRepository::class.java)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
@ -79,8 +79,18 @@ class DefaultBottomNavItemsRepositoryTest {
|
|||||||
runTest {
|
runTest {
|
||||||
val otherAccountId = 1L
|
val otherAccountId = 1L
|
||||||
val accountId = 2L
|
val accountId = 2L
|
||||||
every { keyStore.get("BottomNavItemsRepository.$otherAccountId.items", any<List<String>>()) } returns ITEMS_IDS
|
every {
|
||||||
every { keyStore.get("BottomNavItemsRepository.$accountId.items", any<List<String>>()) } returns emptyList()
|
keyStore.get(
|
||||||
|
"BottomNavItemsRepository.$otherAccountId.items",
|
||||||
|
any<List<String>>(),
|
||||||
|
)
|
||||||
|
} returns ITEMS_IDS
|
||||||
|
every {
|
||||||
|
keyStore.get(
|
||||||
|
"BottomNavItemsRepository.$accountId.items",
|
||||||
|
any<List<String>>(),
|
||||||
|
)
|
||||||
|
} returns emptyList()
|
||||||
|
|
||||||
val res = sut.get(accountId)
|
val res = sut.get(accountId)
|
||||||
|
|
||||||
@ -94,8 +104,18 @@ class DefaultBottomNavItemsRepositoryTest {
|
|||||||
fun givenDataForOtherUser_whenGetForAnonymousAccount_thenResultAndInteractionsIsAsExpected() =
|
fun givenDataForOtherUser_whenGetForAnonymousAccount_thenResultAndInteractionsIsAsExpected() =
|
||||||
runTest {
|
runTest {
|
||||||
val otherAccountId = 1
|
val otherAccountId = 1
|
||||||
every { keyStore.get("BottomNavItemsRepository.$otherAccountId.items", any<List<String>>()) } returns ITEMS_IDS
|
every {
|
||||||
every { keyStore.get("BottomNavItemsRepository.items", any<List<String>>()) } returns emptyList()
|
keyStore.get(
|
||||||
|
"BottomNavItemsRepository.$otherAccountId.items",
|
||||||
|
any<List<String>>(),
|
||||||
|
)
|
||||||
|
} returns ITEMS_IDS
|
||||||
|
every {
|
||||||
|
keyStore.get(
|
||||||
|
"BottomNavItemsRepository.items",
|
||||||
|
any<List<String>>(),
|
||||||
|
)
|
||||||
|
} returns emptyList()
|
||||||
|
|
||||||
val res = sut.get(null)
|
val res = sut.get(null)
|
||||||
|
|
||||||
@ -105,6 +125,28 @@ class DefaultBottomNavItemsRepositoryTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenUpdateAnonymousUser_thenInteractionsAreAsExpected() =
|
||||||
|
runTest {
|
||||||
|
sut.update(accountId = null, items = ITEMS)
|
||||||
|
|
||||||
|
coVerify {
|
||||||
|
keyStore.save("BottomNavItemsRepository.items", ITEMS_IDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenUpdateLoggedUser_thenInteractionsAreAsExpected() =
|
||||||
|
runTest {
|
||||||
|
val accountId = 1L
|
||||||
|
|
||||||
|
sut.update(accountId = accountId, items = ITEMS)
|
||||||
|
|
||||||
|
coVerify {
|
||||||
|
keyStore.save("BottomNavItemsRepository.1.items", ITEMS_IDS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val ITEMS_IDS = listOf("0", "1", "3", "2", "4")
|
private val ITEMS_IDS = listOf("0", "1", "3", "2", "4")
|
||||||
private val ITEMS =
|
private val ITEMS =
|
||||||
|
@ -11,7 +11,7 @@ internal class DefaultBottomNavItemsRepository(
|
|||||||
override suspend fun get(accountId: Long?): List<TabNavigationSection> =
|
override suspend fun get(accountId: Long?): List<TabNavigationSection> =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val key = getKey(accountId)
|
val key = getKey(accountId)
|
||||||
val itemIds = keyStore.get(key, emptyList())
|
val itemIds = keyStore.get(key, emptyList()).mapNotNull { it.toIntOrNull() }
|
||||||
val res = itemIds.mapNotNull { it.toTabNavigationSection() }.takeUnless { it.isEmpty() }
|
val res = itemIds.mapNotNull { it.toTabNavigationSection() }.takeUnless { it.isEmpty() }
|
||||||
res ?: BottomNavItemsRepository.DEFAULT_ITEMS
|
res ?: BottomNavItemsRepository.DEFAULT_ITEMS
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@ internal class DefaultBottomNavItemsRepository(
|
|||||||
items: List<TabNavigationSection>,
|
items: List<TabNavigationSection>,
|
||||||
) = withContext(Dispatchers.IO) {
|
) = withContext(Dispatchers.IO) {
|
||||||
val key = getKey(accountId)
|
val key = getKey(accountId)
|
||||||
val itemIds = items.map { it.toTabNavigationId() }
|
val itemIds = items.map { it.toInt().toString() }
|
||||||
keyStore.save(key, itemIds)
|
keyStore.save(key, itemIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,24 +35,3 @@ internal class DefaultBottomNavItemsRepository(
|
|||||||
append(".items")
|
append(".items")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.toTabNavigationSection(): TabNavigationSection? =
|
|
||||||
when (this) {
|
|
||||||
"0" -> TabNavigationSection.Home
|
|
||||||
"1" -> TabNavigationSection.Explore
|
|
||||||
"2" -> TabNavigationSection.Inbox
|
|
||||||
"3" -> TabNavigationSection.Profile
|
|
||||||
"4" -> TabNavigationSection.Settings
|
|
||||||
"5" -> TabNavigationSection.Bookmarks
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun TabNavigationSection.toTabNavigationId(): String =
|
|
||||||
when (this) {
|
|
||||||
TabNavigationSection.Home -> "0"
|
|
||||||
TabNavigationSection.Explore -> "1"
|
|
||||||
TabNavigationSection.Inbox -> "2"
|
|
||||||
TabNavigationSection.Profile -> "3"
|
|
||||||
TabNavigationSection.Settings -> "4"
|
|
||||||
TabNavigationSection.Bookmarks -> "5"
|
|
||||||
}
|
|
||||||
|
@ -11,20 +11,6 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
sealed interface TabNavigationSection {
|
|
||||||
data object Home : TabNavigationSection
|
|
||||||
|
|
||||||
data object Explore : TabNavigationSection
|
|
||||||
|
|
||||||
data object Profile : TabNavigationSection
|
|
||||||
|
|
||||||
data object Inbox : TabNavigationSection
|
|
||||||
|
|
||||||
data object Settings : TabNavigationSection
|
|
||||||
|
|
||||||
data object Bookmarks : TabNavigationSection
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface ComposeEvent {
|
sealed interface ComposeEvent {
|
||||||
data class WithUrl(
|
data class WithUrl(
|
||||||
val url: String,
|
val url: String,
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.core.navigation
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
|
||||||
|
sealed interface TabNavigationSection {
|
||||||
|
data object Home : TabNavigationSection
|
||||||
|
|
||||||
|
data object Explore : TabNavigationSection
|
||||||
|
|
||||||
|
data object Profile : TabNavigationSection
|
||||||
|
|
||||||
|
data object Inbox : TabNavigationSection
|
||||||
|
|
||||||
|
data object Settings : TabNavigationSection
|
||||||
|
|
||||||
|
data object Bookmarks : TabNavigationSection
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TabNavigationSection.toReadableName(): String =
|
||||||
|
when (this) {
|
||||||
|
TabNavigationSection.Bookmarks -> LocalStrings.current.navigationDrawerTitleBookmarks
|
||||||
|
TabNavigationSection.Explore -> LocalStrings.current.navigationSearch
|
||||||
|
TabNavigationSection.Home -> LocalStrings.current.navigationHome
|
||||||
|
TabNavigationSection.Inbox -> LocalStrings.current.navigationInbox
|
||||||
|
TabNavigationSection.Profile -> LocalStrings.current.navigationProfile
|
||||||
|
TabNavigationSection.Settings -> LocalStrings.current.navigationSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Int.toTabNavigationSection(): TabNavigationSection? =
|
||||||
|
when (this) {
|
||||||
|
0 -> TabNavigationSection.Home
|
||||||
|
1 -> TabNavigationSection.Explore
|
||||||
|
2 -> TabNavigationSection.Inbox
|
||||||
|
3 -> TabNavigationSection.Profile
|
||||||
|
4 -> TabNavigationSection.Settings
|
||||||
|
5 -> TabNavigationSection.Bookmarks
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TabNavigationSection.toInt(): Int =
|
||||||
|
when (this) {
|
||||||
|
TabNavigationSection.Home -> 0
|
||||||
|
TabNavigationSection.Explore -> 1
|
||||||
|
TabNavigationSection.Inbox -> 2
|
||||||
|
TabNavigationSection.Profile -> 3
|
||||||
|
TabNavigationSection.Settings -> 4
|
||||||
|
TabNavigationSection.Bookmarks -> 5
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<Int>.toTabNavigationSections(): List<TabNavigationSection> = mapNotNull { it.toTabNavigationSection() }
|
||||||
|
|
||||||
|
fun List<TabNavigationSection>.toInts(): List<Int> = map { it.toInt() }
|
@ -1,8 +1,11 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.core.navigation.di
|
package com.github.diegoberaldin.raccoonforlemmy.core.navigation.di
|
||||||
|
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.DrawerCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.DrawerCoordinator
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.NavigationCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.NavigationCoordinator
|
||||||
|
|
||||||
expect fun getNavigationCoordinator(): NavigationCoordinator
|
expect fun getNavigationCoordinator(): NavigationCoordinator
|
||||||
|
|
||||||
expect fun getDrawerCoordinator(): DrawerCoordinator
|
expect fun getDrawerCoordinator(): DrawerCoordinator
|
||||||
|
|
||||||
|
expect fun getBottomNavItemsRepository(): BottomNavItemsRepository
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.core.navigation.di
|
package com.github.diegoberaldin.raccoonforlemmy.core.navigation.di
|
||||||
|
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.DrawerCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.DrawerCoordinator
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.NavigationCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.NavigationCoordinator
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
@ -9,7 +10,10 @@ actual fun getNavigationCoordinator() = CoreNavigationHelper.navigationCoordinat
|
|||||||
|
|
||||||
actual fun getDrawerCoordinator() = CoreNavigationHelper.drawerCoordinator
|
actual fun getDrawerCoordinator() = CoreNavigationHelper.drawerCoordinator
|
||||||
|
|
||||||
|
actual fun getBottomNavItemsRepository() = CoreNavigationHelper.bottomNavItemsRepository
|
||||||
|
|
||||||
object CoreNavigationHelper : KoinComponent {
|
object CoreNavigationHelper : KoinComponent {
|
||||||
val navigationCoordinator: NavigationCoordinator by inject()
|
val navigationCoordinator: NavigationCoordinator by inject()
|
||||||
val drawerCoordinator: DrawerCoordinator by inject()
|
val drawerCoordinator: DrawerCoordinator by inject()
|
||||||
|
val bottomNavItemsRepository: BottomNavItemsRepository by inject()
|
||||||
}
|
}
|
||||||
|
@ -245,4 +245,8 @@ sealed interface NotificationCenterEvent {
|
|||||||
data object FavoritesUpdated : NotificationCenterEvent
|
data object FavoritesUpdated : NotificationCenterEvent
|
||||||
|
|
||||||
data object OpenSearchInExplore : NotificationCenterEvent
|
data object OpenSearchInExplore : NotificationCenterEvent
|
||||||
|
|
||||||
|
data class TabNavigationSectionSelected(
|
||||||
|
val sectionId: Int,
|
||||||
|
) : NotificationCenterEvent
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,15 @@ class DefaultSettingsRepositoryTest {
|
|||||||
assertEquals(model, value)
|
assertEquals(model, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenChangeCurrentBottomBarSections_thenValueIsUpdated() =
|
||||||
|
runTest {
|
||||||
|
val sectionIds = listOf(0, 1, 2)
|
||||||
|
sut.changeCurrentBottomBarSections(sectionIds)
|
||||||
|
val value = sut.currentBottomBarSections.value
|
||||||
|
assertEquals(sectionIds, value)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun whenCreateSettings_thenResultIsAsExpected() =
|
fun whenCreateSettings_thenResultIsAsExpected() =
|
||||||
runTest {
|
runTest {
|
||||||
|
@ -13,6 +13,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.preferences.TemporaryKeySto
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.IO
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.Duration.Companion.minutes
|
import kotlin.time.Duration.Companion.minutes
|
||||||
@ -77,6 +78,7 @@ internal class DefaultSettingsRepository(
|
|||||||
private val db = provider.getDatabase()
|
private val db = provider.getDatabase()
|
||||||
|
|
||||||
override val currentSettings = MutableStateFlow(SettingsModel())
|
override val currentSettings = MutableStateFlow(SettingsModel())
|
||||||
|
override val currentBottomBarSections = MutableStateFlow<List<Int>>(emptyList())
|
||||||
|
|
||||||
override suspend fun createSettings(
|
override suspend fun createSettings(
|
||||||
settings: SettingsModel,
|
settings: SettingsModel,
|
||||||
@ -375,7 +377,10 @@ internal class DefaultSettingsRepository(
|
|||||||
settings.defaultExploreResultType,
|
settings.defaultExploreResultType,
|
||||||
)
|
)
|
||||||
keyStore.save(KeyStoreKeys.RANDOM_THEME_COLOR, settings.randomThemeColor)
|
keyStore.save(KeyStoreKeys.RANDOM_THEME_COLOR, settings.randomThemeColor)
|
||||||
keyStore.save(KeyStoreKeys.OPEN_POST_WEB_PAGE_ON_IMAGE_CLICK, settings.openPostWebPageOnImageClick)
|
keyStore.save(
|
||||||
|
KeyStoreKeys.OPEN_POST_WEB_PAGE_ON_IMAGE_CLICK,
|
||||||
|
settings.openPostWebPageOnImageClick,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
db.settingsQueries.update(
|
db.settingsQueries.update(
|
||||||
theme = settings.theme?.toLong(),
|
theme = settings.theme?.toLong(),
|
||||||
@ -466,7 +471,11 @@ internal class DefaultSettingsRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun changeCurrentSettings(settings: SettingsModel) {
|
override fun changeCurrentSettings(settings: SettingsModel) {
|
||||||
currentSettings.value = settings
|
currentSettings.update { settings }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun changeCurrentBottomBarSections(sectionIds: List<Int>) {
|
||||||
|
currentBottomBarSections.update { sectionIds }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
@Stable
|
@Stable
|
||||||
interface SettingsRepository {
|
interface SettingsRepository {
|
||||||
val currentSettings: StateFlow<SettingsModel>
|
val currentSettings: StateFlow<SettingsModel>
|
||||||
|
val currentBottomBarSections: StateFlow<List<Int>>
|
||||||
|
|
||||||
suspend fun createSettings(
|
suspend fun createSettings(
|
||||||
settings: SettingsModel,
|
settings: SettingsModel,
|
||||||
@ -21,4 +22,6 @@ interface SettingsRepository {
|
|||||||
)
|
)
|
||||||
|
|
||||||
fun changeCurrentSettings(settings: SettingsModel)
|
fun changeCurrentSettings(settings: SettingsModel)
|
||||||
|
|
||||||
|
fun changeCurrentBottomBarSections(sectionIds: List<Int>)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"alternateMarkdownRenderingSettingsItemEnabled": true
|
"alternateMarkdownRenderingSettingsItemEnabled": false
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ kotlin {
|
|||||||
implementation(projects.core.utils)
|
implementation(projects.core.utils)
|
||||||
implementation(projects.core.appearance)
|
implementation(projects.core.appearance)
|
||||||
implementation(projects.core.persistence)
|
implementation(projects.core.persistence)
|
||||||
|
implementation(projects.core.navigation)
|
||||||
implementation(projects.core.notifications)
|
implementation(projects.core.notifications)
|
||||||
implementation(projects.domain.lemmy.repository)
|
implementation(projects.domain.lemmy.repository)
|
||||||
implementation(projects.domain.lemmy.data)
|
implementation(projects.domain.lemmy.data)
|
||||||
@ -58,8 +59,14 @@ kotlin {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.github.diegoberaldin.raccoonforlemmy.domain.identity"
|
namespace = "com.github.diegoberaldin.raccoonforlemmy.domain.identity"
|
||||||
compileSdk = libs.versions.android.targetSdk.get().toInt()
|
compileSdk =
|
||||||
|
libs.versions.android.targetSdk
|
||||||
|
.get()
|
||||||
|
.toInt()
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
minSdk =
|
||||||
|
libs.versions.android.minSdk
|
||||||
|
.get()
|
||||||
|
.toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
||||||
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.LoginResponse
|
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.LoginResponse
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.SettingsModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.SettingsModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
|
||||||
@ -40,6 +41,10 @@ class DefaultLoginUseCaseTest {
|
|||||||
private val communitySortRepository = mockk<CommunitySortRepository>(relaxUnitFun = true)
|
private val communitySortRepository = mockk<CommunitySortRepository>(relaxUnitFun = true)
|
||||||
private val communityPreferredLanguageRepository =
|
private val communityPreferredLanguageRepository =
|
||||||
mockk<CommunityPreferredLanguageRepository>(relaxUnitFun = true)
|
mockk<CommunityPreferredLanguageRepository>(relaxUnitFun = true)
|
||||||
|
private val bottomNavItemsRepository =
|
||||||
|
mockk<BottomNavItemsRepository>(relaxUnitFun = true) {
|
||||||
|
coEvery { get(accountId = any()) } returns BottomNavItemsRepository.DEFAULT_ITEMS
|
||||||
|
}
|
||||||
private val lemmyValueCache = mockk<LemmyValueCache>(relaxUnitFun = true)
|
private val lemmyValueCache = mockk<LemmyValueCache>(relaxUnitFun = true)
|
||||||
private val sut =
|
private val sut =
|
||||||
DefaultLoginUseCase(
|
DefaultLoginUseCase(
|
||||||
@ -51,6 +56,7 @@ class DefaultLoginUseCaseTest {
|
|||||||
siteRepository = siteRepository,
|
siteRepository = siteRepository,
|
||||||
communitySortRepository = communitySortRepository,
|
communitySortRepository = communitySortRepository,
|
||||||
communityPreferredLanguageRepository = communityPreferredLanguageRepository,
|
communityPreferredLanguageRepository = communityPreferredLanguageRepository,
|
||||||
|
bottomNavItemsRepository = bottomNavItemsRepository,
|
||||||
lemmyValueCache = lemmyValueCache,
|
lemmyValueCache = lemmyValueCache,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
||||||
|
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
||||||
@ -27,6 +28,10 @@ class DefaultLogoutUseCaseTest {
|
|||||||
private val notificationCenter = mockk<NotificationCenter>(relaxUnitFun = true)
|
private val notificationCenter = mockk<NotificationCenter>(relaxUnitFun = true)
|
||||||
private val communitySortRepository = mockk<CommunitySortRepository>(relaxUnitFun = true)
|
private val communitySortRepository = mockk<CommunitySortRepository>(relaxUnitFun = true)
|
||||||
private val lemmyValueCache = mockk<LemmyValueCache>(relaxUnitFun = true)
|
private val lemmyValueCache = mockk<LemmyValueCache>(relaxUnitFun = true)
|
||||||
|
private val bottomNavItemsRepository =
|
||||||
|
mockk<BottomNavItemsRepository>(relaxUnitFun = true) {
|
||||||
|
coEvery { get(accountId = any()) } returns BottomNavItemsRepository.DEFAULT_ITEMS
|
||||||
|
}
|
||||||
private val sut =
|
private val sut =
|
||||||
DefaultLogoutUseCase(
|
DefaultLogoutUseCase(
|
||||||
identityRepository = identityRepository,
|
identityRepository = identityRepository,
|
||||||
@ -34,6 +39,7 @@ class DefaultLogoutUseCaseTest {
|
|||||||
settingsRepository = settingsRepository,
|
settingsRepository = settingsRepository,
|
||||||
notificationCenter = notificationCenter,
|
notificationCenter = notificationCenter,
|
||||||
communitySortRepository = communitySortRepository,
|
communitySortRepository = communitySortRepository,
|
||||||
|
bottomNavItemsRepository = bottomNavItemsRepository,
|
||||||
lemmyValueCache = lemmyValueCache,
|
lemmyValueCache = lemmyValueCache,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
||||||
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider
|
import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
||||||
@ -32,6 +33,10 @@ class DefaultSwitchAccountUseCaseTest {
|
|||||||
private val communityPreferredLanguageRepository =
|
private val communityPreferredLanguageRepository =
|
||||||
mockk<CommunityPreferredLanguageRepository>(relaxUnitFun = true)
|
mockk<CommunityPreferredLanguageRepository>(relaxUnitFun = true)
|
||||||
private val lemmyValueCache = mockk<LemmyValueCache>(relaxUnitFun = true)
|
private val lemmyValueCache = mockk<LemmyValueCache>(relaxUnitFun = true)
|
||||||
|
private val bottomNavItemsRepository =
|
||||||
|
mockk<BottomNavItemsRepository>(relaxUnitFun = true) {
|
||||||
|
coEvery { get(accountId = any()) } returns BottomNavItemsRepository.DEFAULT_ITEMS
|
||||||
|
}
|
||||||
private val sut =
|
private val sut =
|
||||||
DefaultSwitchAccountUseCase(
|
DefaultSwitchAccountUseCase(
|
||||||
identityRepository = identityRepository,
|
identityRepository = identityRepository,
|
||||||
@ -41,6 +46,7 @@ class DefaultSwitchAccountUseCaseTest {
|
|||||||
notificationCenter = notificationCenter,
|
notificationCenter = notificationCenter,
|
||||||
communitySortRepository = communitySortRepository,
|
communitySortRepository = communitySortRepository,
|
||||||
communityPreferredLanguageRepository = communityPreferredLanguageRepository,
|
communityPreferredLanguageRepository = communityPreferredLanguageRepository,
|
||||||
|
bottomNavItemsRepository = bottomNavItemsRepository,
|
||||||
lemmyValueCache = lemmyValueCache,
|
lemmyValueCache = lemmyValueCache,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ val coreIdentityModule =
|
|||||||
siteRepository = get(),
|
siteRepository = get(),
|
||||||
communitySortRepository = get(),
|
communitySortRepository = get(),
|
||||||
communityPreferredLanguageRepository = get(),
|
communityPreferredLanguageRepository = get(),
|
||||||
|
bottomNavItemsRepository = get(),
|
||||||
lemmyValueCache = get(),
|
lemmyValueCache = get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -57,6 +58,7 @@ val coreIdentityModule =
|
|||||||
notificationCenter = get(),
|
notificationCenter = get(),
|
||||||
settingsRepository = get(),
|
settingsRepository = get(),
|
||||||
communitySortRepository = get(),
|
communitySortRepository = get(),
|
||||||
|
bottomNavItemsRepository = get(),
|
||||||
lemmyValueCache = get(),
|
lemmyValueCache = get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -69,6 +71,7 @@ val coreIdentityModule =
|
|||||||
notificationCenter = get(),
|
notificationCenter = get(),
|
||||||
communitySortRepository = get(),
|
communitySortRepository = get(),
|
||||||
communityPreferredLanguageRepository = get(),
|
communityPreferredLanguageRepository = get(),
|
||||||
|
bottomNavItemsRepository = get(),
|
||||||
lemmyValueCache = get(),
|
lemmyValueCache = get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
||||||
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.VoteFormat
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.VoteFormat
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInts
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.CommunityPreferredLanguageRepository
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.CommunityPreferredLanguageRepository
|
||||||
@ -21,6 +23,7 @@ internal class DefaultLoginUseCase(
|
|||||||
private val siteRepository: SiteRepository,
|
private val siteRepository: SiteRepository,
|
||||||
private val communitySortRepository: CommunitySortRepository,
|
private val communitySortRepository: CommunitySortRepository,
|
||||||
private val communityPreferredLanguageRepository: CommunityPreferredLanguageRepository,
|
private val communityPreferredLanguageRepository: CommunityPreferredLanguageRepository,
|
||||||
|
private val bottomNavItemsRepository: BottomNavItemsRepository,
|
||||||
private val lemmyValueCache: LemmyValueCache,
|
private val lemmyValueCache: LemmyValueCache,
|
||||||
) : LoginUseCase {
|
) : LoginUseCase {
|
||||||
override suspend operator fun invoke(
|
override suspend operator fun invoke(
|
||||||
@ -106,6 +109,9 @@ internal class DefaultLoginUseCase(
|
|||||||
|
|
||||||
val newSettings = settingsRepository.getSettings(accountId)
|
val newSettings = settingsRepository.getSettings(accountId)
|
||||||
settingsRepository.changeCurrentSettings(newSettings)
|
settingsRepository.changeCurrentSettings(newSettings)
|
||||||
|
|
||||||
|
val bottomBarSections = bottomNavItemsRepository.get(accountId)
|
||||||
|
settingsRepository.changeCurrentBottomBarSections(bottomBarSections.toInts())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
||||||
|
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInts
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
|
||||||
@ -14,6 +16,7 @@ internal class DefaultLogoutUseCase(
|
|||||||
private val notificationCenter: NotificationCenter,
|
private val notificationCenter: NotificationCenter,
|
||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val communitySortRepository: CommunitySortRepository,
|
private val communitySortRepository: CommunitySortRepository,
|
||||||
|
private val bottomNavItemsRepository: BottomNavItemsRepository,
|
||||||
private val lemmyValueCache: LemmyValueCache,
|
private val lemmyValueCache: LemmyValueCache,
|
||||||
) : LogoutUseCase {
|
) : LogoutUseCase {
|
||||||
override suspend operator fun invoke() {
|
override suspend operator fun invoke() {
|
||||||
@ -31,5 +34,8 @@ internal class DefaultLogoutUseCase(
|
|||||||
}
|
}
|
||||||
val anonSettings = settingsRepository.getSettings(null)
|
val anonSettings = settingsRepository.getSettings(null)
|
||||||
settingsRepository.changeCurrentSettings(anonSettings)
|
settingsRepository.changeCurrentSettings(anonSettings)
|
||||||
|
|
||||||
|
val bottomBarSections = bottomNavItemsRepository.get(null)
|
||||||
|
settingsRepository.changeCurrentBottomBarSections(bottomBarSections.toInts())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase
|
||||||
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider
|
import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInts
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel
|
||||||
@ -19,6 +21,7 @@ internal class DefaultSwitchAccountUseCase(
|
|||||||
private val settingsRepository: SettingsRepository,
|
private val settingsRepository: SettingsRepository,
|
||||||
private val communitySortRepository: CommunitySortRepository,
|
private val communitySortRepository: CommunitySortRepository,
|
||||||
private val communityPreferredLanguageRepository: CommunityPreferredLanguageRepository,
|
private val communityPreferredLanguageRepository: CommunityPreferredLanguageRepository,
|
||||||
|
private val bottomNavItemsRepository: BottomNavItemsRepository,
|
||||||
private val lemmyValueCache: LemmyValueCache,
|
private val lemmyValueCache: LemmyValueCache,
|
||||||
) : SwitchAccountUseCase {
|
) : SwitchAccountUseCase {
|
||||||
override suspend fun invoke(account: AccountModel) {
|
override suspend fun invoke(account: AccountModel) {
|
||||||
@ -44,5 +47,8 @@ internal class DefaultSwitchAccountUseCase(
|
|||||||
|
|
||||||
val newSettings = settingsRepository.getSettings(accountId)
|
val newSettings = settingsRepository.getSettings(accountId)
|
||||||
settingsRepository.changeCurrentSettings(newSettings)
|
settingsRepository.changeCurrentSettings(newSettings)
|
||||||
|
|
||||||
|
val bottomBarSections = bottomNavItemsRepository.get(accountId)
|
||||||
|
settingsRepository.changeCurrentBottomBarSections(bottomBarSections.toInts())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import cafe.adriel.voyager.navigator.Navigator
|
|||||||
import cafe.adriel.voyager.navigator.tab.Tab
|
import cafe.adriel.voyager.navigator.tab.Tab
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInt
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.postlist.PostListScreen
|
import com.github.diegoberaldin.raccoonforlemmy.unit.postlist.PostListScreen
|
||||||
|
|
||||||
object HomeTab : Tab {
|
object HomeTab : Tab {
|
||||||
@ -18,7 +20,7 @@ object HomeTab : Tab {
|
|||||||
val title = LocalStrings.current.navigationHome
|
val title = LocalStrings.current.navigationHome
|
||||||
|
|
||||||
return TabOptions(
|
return TabOptions(
|
||||||
index = 0u,
|
index = TabNavigationSection.Home.toInt().toUShort(),
|
||||||
title = title,
|
title = title,
|
||||||
icon = icon,
|
icon = icon,
|
||||||
)
|
)
|
||||||
|
@ -8,6 +8,8 @@ import cafe.adriel.voyager.navigator.tab.Tab
|
|||||||
import cafe.adriel.voyager.navigator.tab.TabNavigator
|
import cafe.adriel.voyager.navigator.tab.TabNavigator
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInt
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.main.InboxScreen
|
import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.main.InboxScreen
|
||||||
|
|
||||||
object InboxTab : Tab {
|
object InboxTab : Tab {
|
||||||
@ -17,7 +19,7 @@ object InboxTab : Tab {
|
|||||||
val icon = rememberVectorPainter(Icons.Default.Inbox)
|
val icon = rememberVectorPainter(Icons.Default.Inbox)
|
||||||
val title = LocalStrings.current.navigationInbox
|
val title = LocalStrings.current.navigationInbox
|
||||||
return TabOptions(
|
return TabOptions(
|
||||||
index = 3u,
|
index = TabNavigationSection.Inbox.toInt().toUShort(),
|
||||||
title = title,
|
title = title,
|
||||||
icon = icon,
|
icon = icon,
|
||||||
)
|
)
|
||||||
|
@ -31,6 +31,7 @@ val profileTabModule =
|
|||||||
}
|
}
|
||||||
factory<ProfileSideMenuMviModel> {
|
factory<ProfileSideMenuMviModel> {
|
||||||
ProfileSideMenuViewModel(
|
ProfileSideMenuViewModel(
|
||||||
|
settingsRepository = get(),
|
||||||
lemmyValueCache = get(),
|
lemmyValueCache = get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ internal fun ProfileMenuContent(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
isModerator: Boolean = false,
|
isModerator: Boolean = false,
|
||||||
canCreateCommunity: Boolean = false,
|
canCreateCommunity: Boolean = false,
|
||||||
|
isBookmarksVisible: Boolean = true,
|
||||||
) {
|
) {
|
||||||
val notificationCenter = remember { getNotificationCenter() }
|
val notificationCenter = remember { getNotificationCenter() }
|
||||||
|
|
||||||
@ -42,14 +43,16 @@ internal fun ProfileMenuContent(
|
|||||||
notificationCenter.send(NotificationCenterEvent.ProfileSideMenuAction.ManageSubscriptions)
|
notificationCenter.send(NotificationCenterEvent.ProfileSideMenuAction.ManageSubscriptions)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
SettingsRow(
|
if (isBookmarksVisible) {
|
||||||
title = LocalStrings.current.navigationDrawerTitleBookmarks,
|
SettingsRow(
|
||||||
icon = Icons.Default.Bookmark,
|
title = LocalStrings.current.navigationDrawerTitleBookmarks,
|
||||||
onTap =
|
icon = Icons.Default.Bookmark,
|
||||||
rememberCallback {
|
onTap =
|
||||||
notificationCenter.send(NotificationCenterEvent.ProfileSideMenuAction.Bookmarks)
|
rememberCallback {
|
||||||
},
|
notificationCenter.send(NotificationCenterEvent.ProfileSideMenuAction.Bookmarks)
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
SettingsRow(
|
SettingsRow(
|
||||||
title = LocalStrings.current.navigationDrawerTitleDrafts,
|
title = LocalStrings.current.navigationDrawerTitleDrafts,
|
||||||
icon = Icons.Default.Drafts,
|
icon = Icons.Default.Drafts,
|
||||||
|
@ -11,6 +11,7 @@ interface ProfileSideMenuMviModel :
|
|||||||
data class State(
|
data class State(
|
||||||
val isModerator: Boolean = false,
|
val isModerator: Boolean = false,
|
||||||
val canCreateCommunity: Boolean = false,
|
val canCreateCommunity: Boolean = false,
|
||||||
|
val isBookmarksVisible: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed interface Effect
|
sealed interface Effect
|
||||||
|
@ -70,6 +70,7 @@ class ProfileSideMenuScreen : Screen {
|
|||||||
),
|
),
|
||||||
isModerator = uiState.isModerator,
|
isModerator = uiState.isModerator,
|
||||||
canCreateCommunity = uiState.canCreateCommunity,
|
canCreateCommunity = uiState.canCreateCommunity,
|
||||||
|
isBookmarksVisible = uiState.isBookmarksVisible,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.profile.menu
|
|||||||
|
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toTabNavigationSections
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.SettingsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.LemmyValueCache
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.LemmyValueCache
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
@ -9,6 +12,7 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ProfileSideMenuViewModel(
|
class ProfileSideMenuViewModel(
|
||||||
|
private val settingsRepository: SettingsRepository,
|
||||||
private val lemmyValueCache: LemmyValueCache,
|
private val lemmyValueCache: LemmyValueCache,
|
||||||
) : DefaultMviModel<ProfileSideMenuMviModel.Intent, ProfileSideMenuMviModel.State, ProfileSideMenuMviModel.Effect>(
|
) : DefaultMviModel<ProfileSideMenuMviModel.Intent, ProfileSideMenuMviModel.State, ProfileSideMenuMviModel.Effect>(
|
||||||
ProfileSideMenuMviModel.State(),
|
ProfileSideMenuMviModel.State(),
|
||||||
@ -39,6 +43,16 @@ class ProfileSideMenuViewModel(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
|
settingsRepository.currentBottomBarSections
|
||||||
|
.onEach { sectionIds ->
|
||||||
|
val isBookmarksInBottomBar =
|
||||||
|
sectionIds
|
||||||
|
.toTabNavigationSections()
|
||||||
|
.contains(TabNavigationSection.Bookmarks)
|
||||||
|
updateState {
|
||||||
|
it.copy(isBookmarksVisible = !isBookmarksInBottomBar)
|
||||||
|
}
|
||||||
|
}.launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import cafe.adriel.voyager.navigator.tab.Tab
|
|||||||
import cafe.adriel.voyager.navigator.tab.TabNavigator
|
import cafe.adriel.voyager.navigator.tab.TabNavigator
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInt
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.main.ProfileMainScreen
|
import com.github.diegoberaldin.raccoonforlemmy.feature.profile.main.ProfileMainScreen
|
||||||
|
|
||||||
object ProfileTab : Tab {
|
object ProfileTab : Tab {
|
||||||
@ -16,7 +18,7 @@ object ProfileTab : Tab {
|
|||||||
val icon = rememberVectorPainter(Icons.Default.AccountCircle)
|
val icon = rememberVectorPainter(Icons.Default.AccountCircle)
|
||||||
val title = LocalStrings.current.navigationProfile
|
val title = LocalStrings.current.navigationProfile
|
||||||
return TabOptions(
|
return TabOptions(
|
||||||
index = 2u,
|
index = TabNavigationSection.Profile.toInt().toUShort(),
|
||||||
title = title,
|
title = title,
|
||||||
icon = icon,
|
icon = icon,
|
||||||
)
|
)
|
||||||
|
@ -8,6 +8,8 @@ import cafe.adriel.voyager.navigator.Navigator
|
|||||||
import cafe.adriel.voyager.navigator.tab.Tab
|
import cafe.adriel.voyager.navigator.tab.Tab
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInt
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.explore.ExploreScreen
|
import com.github.diegoberaldin.raccoonforlemmy.unit.explore.ExploreScreen
|
||||||
|
|
||||||
object ExploreTab : Tab {
|
object ExploreTab : Tab {
|
||||||
@ -16,7 +18,7 @@ object ExploreTab : Tab {
|
|||||||
val icon = rememberVectorPainter(Icons.Default.Explore)
|
val icon = rememberVectorPainter(Icons.Default.Explore)
|
||||||
val title = LocalStrings.current.navigationSearch
|
val title = LocalStrings.current.navigationSearch
|
||||||
return TabOptions(
|
return TabOptions(
|
||||||
index = 1u,
|
index = TabNavigationSection.Explore.toInt().toUShort(),
|
||||||
title = title,
|
title = title,
|
||||||
icon = icon,
|
icon = icon,
|
||||||
)
|
)
|
||||||
|
@ -67,6 +67,7 @@ kotlin {
|
|||||||
implementation(projects.unit.choosecolor)
|
implementation(projects.unit.choosecolor)
|
||||||
implementation(projects.unit.choosefont)
|
implementation(projects.unit.choosefont)
|
||||||
implementation(projects.unit.configureswipeactions)
|
implementation(projects.unit.configureswipeactions)
|
||||||
|
implementation(projects.unit.configurenavbar)
|
||||||
implementation(projects.unit.configurecontentview)
|
implementation(projects.unit.configurecontentview)
|
||||||
implementation(projects.unit.filteredcontents)
|
implementation(projects.unit.filteredcontents)
|
||||||
implementation(projects.unit.medialist)
|
implementation(projects.unit.medialist)
|
||||||
@ -82,8 +83,14 @@ kotlin {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.github.diegoberaldin.raccoonforlemmy.feature.settings"
|
namespace = "com.github.diegoberaldin.raccoonforlemmy.feature.settings"
|
||||||
compileSdk = libs.versions.android.targetSdk.get().toInt()
|
compileSdk =
|
||||||
|
libs.versions.android.targetSdk
|
||||||
|
.get()
|
||||||
|
.toInt()
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
minSdk =
|
||||||
|
libs.versions.android.minSdk
|
||||||
|
.get()
|
||||||
|
.toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import cafe.adriel.voyager.navigator.Navigator
|
|||||||
import cafe.adriel.voyager.navigator.tab.Tab
|
import cafe.adriel.voyager.navigator.tab.Tab
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInt
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.settings.main.SettingsScreen
|
import com.github.diegoberaldin.raccoonforlemmy.feature.settings.main.SettingsScreen
|
||||||
|
|
||||||
object SettingsTab : Tab {
|
object SettingsTab : Tab {
|
||||||
@ -18,7 +20,7 @@ object SettingsTab : Tab {
|
|||||||
val title = LocalStrings.current.navigationSettings
|
val title = LocalStrings.current.navigationSettings
|
||||||
|
|
||||||
return TabOptions(
|
return TabOptions(
|
||||||
index = 4u,
|
index = TabNavigationSection.Settings.toInt().toUShort(),
|
||||||
title = title,
|
title = title,
|
||||||
icon = icon,
|
icon = icon,
|
||||||
)
|
)
|
||||||
|
@ -69,6 +69,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.utils.datetime.getPrettyDur
|
|||||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.fs.getFileSystemManager
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.fs.getFileSystemManager
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.toReadableName
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.toReadableName
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.ConfigureNavBarScreen
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -159,6 +160,7 @@ class AdvancedSettingsScreen : Screen {
|
|||||||
Modifier
|
Modifier
|
||||||
.padding(
|
.padding(
|
||||||
top = padding.calculateTopPadding(),
|
top = padding.calculateTopPadding(),
|
||||||
|
bottom = padding.calculateBottomPadding(),
|
||||||
).nestedScroll(scrollBehavior.nestedScrollConnection),
|
).nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@ -495,6 +497,14 @@ class AdvancedSettingsScreen : Screen {
|
|||||||
title = LocalStrings.current.settingsTitleExperimental,
|
title = LocalStrings.current.settingsTitleExperimental,
|
||||||
icon = Icons.Default.Science,
|
icon = Icons.Default.Science,
|
||||||
)
|
)
|
||||||
|
SettingsRow(
|
||||||
|
title = LocalStrings.current.settingsItemConfigureBottomNavigationBar,
|
||||||
|
disclosureIndicator = true,
|
||||||
|
onTap =
|
||||||
|
rememberCallback {
|
||||||
|
navigationCoordinator.pushScreen(ConfigureNavBarScreen())
|
||||||
|
},
|
||||||
|
)
|
||||||
if (uiState.alternateMarkdownRenderingItemVisible) {
|
if (uiState.alternateMarkdownRenderingItemVisible) {
|
||||||
// alternate Markdown rendering
|
// alternate Markdown rendering
|
||||||
SettingsSwitchRow(
|
SettingsSwitchRow(
|
||||||
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
@ -154,8 +155,8 @@ class SettingsScreen : Screen {
|
|||||||
Modifier
|
Modifier
|
||||||
.padding(
|
.padding(
|
||||||
top = padding.calculateTopPadding(),
|
top = padding.calculateTopPadding(),
|
||||||
bottom = padding.calculateBottomPadding(),
|
).navigationBarsPadding()
|
||||||
).nestedScroll(scrollBehavior.nestedScrollConnection),
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxSize().verticalScroll(scrollState),
|
modifier = Modifier.fillMaxSize().verticalScroll(scrollState),
|
||||||
|
@ -61,6 +61,7 @@ include(":unit:choosefont")
|
|||||||
include(":unit:communitydetail")
|
include(":unit:communitydetail")
|
||||||
include(":unit:communityinfo")
|
include(":unit:communityinfo")
|
||||||
include(":unit:configurecontentview")
|
include(":unit:configurecontentview")
|
||||||
|
include(":unit:configurenavbar")
|
||||||
include(":unit:configureswipeactions")
|
include(":unit:configureswipeactions")
|
||||||
include(":unit:createcomment")
|
include(":unit:createcomment")
|
||||||
include(":unit:createpost")
|
include(":unit:createpost")
|
||||||
|
@ -76,6 +76,7 @@ kotlin {
|
|||||||
implementation(projects.unit.communitydetail)
|
implementation(projects.unit.communitydetail)
|
||||||
implementation(projects.unit.communityinfo)
|
implementation(projects.unit.communityinfo)
|
||||||
implementation(projects.unit.configurecontentview)
|
implementation(projects.unit.configurecontentview)
|
||||||
|
implementation(projects.unit.configurenavbar)
|
||||||
implementation(projects.unit.configureswipeactions)
|
implementation(projects.unit.configureswipeactions)
|
||||||
implementation(projects.unit.createcomment)
|
implementation(projects.unit.createcomment)
|
||||||
implementation(projects.unit.createpost)
|
implementation(projects.unit.createpost)
|
||||||
|
@ -35,6 +35,7 @@ import com.github.diegoberaldin.raccoonforlemmy.unit.chat.di.chatModule
|
|||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.communitydetail.di.communityDetailModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.communitydetail.di.communityDetailModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.communityinfo.di.communityInfoModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.communityinfo.di.communityInfoModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.configurecontentview.di.configureContentViewModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configurecontentview.di.configureContentViewModule
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.di.configureNavBarModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.configureswipeactions.di.configureSwipeActionsModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configureswipeactions.di.configureSwipeActionsModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.createcomment.di.createCommentModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.createcomment.di.createCommentModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.createpost.di.createPostModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.createpost.di.createPostModule
|
||||||
@ -119,5 +120,6 @@ val sharedHelperModule =
|
|||||||
moderateWithReasonModule,
|
moderateWithReasonModule,
|
||||||
acknowledgementsModule,
|
acknowledgementsModule,
|
||||||
mediaListModule,
|
mediaListModule,
|
||||||
|
configureNavBarModule,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -57,8 +57,10 @@ import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.ProvideString
|
|||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.ComposeEvent
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.ComposeEvent
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.DrawerEvent
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.DrawerEvent
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.SideMenuEvents
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.SideMenuEvents
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getBottomNavItemsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getDrawerCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getDrawerCoordinator
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInts
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getAccountRepository
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getAccountRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.di.getAppConfigStore
|
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.di.getAppConfigStore
|
||||||
@ -108,9 +110,12 @@ fun App(onLoadingFinished: () -> Unit = {}) {
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val subscriptionsCache = remember { getSubscriptionsCache() }
|
val subscriptionsCache = remember { getSubscriptionsCache() }
|
||||||
val appConfigStore = remember { getAppConfigStore() }
|
val appConfigStore = remember { getAppConfigStore() }
|
||||||
|
val bottomNavItemsRepository = remember { getBottomNavItemsRepository() }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
val accountId = accountRepository.getActive()?.id
|
val lastActiveAccount = accountRepository.getActive()
|
||||||
|
val lastInstance = lastActiveAccount?.instance?.takeIf { it.isNotEmpty() }
|
||||||
|
val accountId = lastActiveAccount?.id
|
||||||
val currentSettings = settingsRepository.getSettings(accountId)
|
val currentSettings = settingsRepository.getSettings(accountId)
|
||||||
val seedColor =
|
val seedColor =
|
||||||
if (currentSettings.randomThemeColor) {
|
if (currentSettings.randomThemeColor) {
|
||||||
@ -122,8 +127,8 @@ fun App(onLoadingFinished: () -> Unit = {}) {
|
|||||||
currentSettings.customSeedColor
|
currentSettings.customSeedColor
|
||||||
}
|
}
|
||||||
settingsRepository.changeCurrentSettings(currentSettings.copy(customSeedColor = seedColor))
|
settingsRepository.changeCurrentSettings(currentSettings.copy(customSeedColor = seedColor))
|
||||||
val lastActiveAccount = accountRepository.getActive()
|
val bottomBarSections = bottomNavItemsRepository.get(accountId)
|
||||||
val lastInstance = lastActiveAccount?.instance?.takeIf { it.isNotEmpty() }
|
settingsRepository.changeCurrentBottomBarSections(bottomBarSections.toInts())
|
||||||
if (lastInstance != null) {
|
if (lastInstance != null) {
|
||||||
apiConfigurationRepository.changeInstance(lastInstance)
|
apiConfigurationRepository.changeInstance(lastInstance)
|
||||||
}
|
}
|
||||||
@ -132,6 +137,7 @@ fun App(onLoadingFinished: () -> Unit = {}) {
|
|||||||
appConfigStore.initialize()
|
appConfigStore.initialize()
|
||||||
|
|
||||||
hasBeenInitialized = true
|
hasBeenInitialized = true
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
delay(50)
|
delay(50)
|
||||||
onLoadingFinished()
|
onLoadingFinished()
|
||||||
|
@ -43,10 +43,10 @@ import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getDrawerCoor
|
|||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.home.ui.HomeTab
|
import com.github.diegoberaldin.raccoonforlemmy.feature.home.ui.HomeTab
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.feature.settings.main.SettingsScreen
|
import com.github.diegoberaldin.raccoonforlemmy.feature.settings.main.SettingsScreen
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.navigation.TabNavigationItem
|
import com.github.diegoberaldin.raccoonforlemmy.navigation.TabNavigationItem
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.navigation.toTab
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.manageaccounts.ManageAccountsScreen
|
import com.github.diegoberaldin.raccoonforlemmy.unit.manageaccounts.ManageAccountsScreen
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
@ -277,7 +277,8 @@ internal object MainScreen : Screen {
|
|||||||
),
|
),
|
||||||
tonalElevation = 0.dp,
|
tonalElevation = 0.dp,
|
||||||
) {
|
) {
|
||||||
for (section in uiState.bottomBarSections) {
|
// it must be done so (indexed), otherwise section gets remembered in tap callbacks
|
||||||
|
uiState.bottomBarSections.forEachIndexed { idx, section ->
|
||||||
TabNavigationItem(
|
TabNavigationItem(
|
||||||
section = section,
|
section = section,
|
||||||
withText = titleVisible,
|
withText = titleVisible,
|
||||||
@ -287,15 +288,17 @@ internal object MainScreen : Screen {
|
|||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
onClick =
|
onClick = {
|
||||||
rememberCallbackArgs { tab ->
|
val section = uiState.bottomBarSections[idx]
|
||||||
tabNavigator.current = tab
|
val tab = section.toTab()
|
||||||
navigationCoordinator.setCurrentSection(section)
|
tabNavigator.current = tab
|
||||||
},
|
navigationCoordinator.setCurrentSection(section)
|
||||||
onLongPress =
|
},
|
||||||
rememberCallbackArgs { tab ->
|
onLongPress = {
|
||||||
handleOnLongPress(tab, section)
|
val section = uiState.bottomBarSections[idx]
|
||||||
},
|
val tab = section.toTab()
|
||||||
|
handleOnLongPress(tab, section)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy
|
|||||||
|
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toTabNavigationSections
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
|
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.SettingsRepository
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.SettingsRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.inbox.InboxCoordinator
|
import com.github.diegoberaldin.raccoonforlemmy.domain.inbox.InboxCoordinator
|
||||||
@ -21,8 +20,6 @@ class MainViewModel(
|
|||||||
private val identityRepository: IdentityRepository,
|
private val identityRepository: IdentityRepository,
|
||||||
private val settingRepository: SettingsRepository,
|
private val settingRepository: SettingsRepository,
|
||||||
private val userRepository: UserRepository,
|
private val userRepository: UserRepository,
|
||||||
private val accountRepository: AccountRepository,
|
|
||||||
private val bottomNavItemsRepository: BottomNavItemsRepository,
|
|
||||||
private val notificationChecker: InboxNotificationChecker,
|
private val notificationChecker: InboxNotificationChecker,
|
||||||
private val lemmyValueCache: LemmyValueCache,
|
private val lemmyValueCache: LemmyValueCache,
|
||||||
) : DefaultMviModel<MainMviModel.Intent, MainMviModel.UiState, MainMviModel.Effect>(
|
) : DefaultMviModel<MainMviModel.Intent, MainMviModel.UiState, MainMviModel.Effect>(
|
||||||
@ -53,17 +50,20 @@ class MainViewModel(
|
|||||||
notificationChecker.stop()
|
notificationChecker.stop()
|
||||||
}
|
}
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
|
|
||||||
settingRepository.currentSettings
|
settingRepository.currentSettings
|
||||||
.onEach {
|
.onEach {
|
||||||
updateCustomProfileIcon()
|
updateCustomProfileIcon()
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
|
settingRepository.currentBottomBarSections
|
||||||
|
.onEach { sectionIds ->
|
||||||
|
val sections = sectionIds.toTabNavigationSections()
|
||||||
|
updateState { it.copy(bottomBarSections = sections) }
|
||||||
|
}.launchIn(this)
|
||||||
|
|
||||||
identityRepository.isLogged
|
identityRepository.isLogged
|
||||||
.onEach { isLogged ->
|
.onEach { isLogged ->
|
||||||
updateState { it.copy(isLogged = isLogged ?: false) }
|
updateState { it.copy(isLogged = isLogged ?: false) }
|
||||||
updateCustomProfileIcon()
|
updateCustomProfileIcon()
|
||||||
refreshBottomNavigationItems()
|
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,12 +106,4 @@ class MainViewModel(
|
|||||||
emitEffect(MainMviModel.Effect.ReadAllInboxSuccess)
|
emitEffect(MainMviModel.Effect.ReadAllInboxSuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshBottomNavigationItems() {
|
|
||||||
val accountId = accountRepository.getActive()?.id
|
|
||||||
val sections = bottomNavItemsRepository.get(accountId)
|
|
||||||
updateState {
|
|
||||||
it.copy(bottomBarSections = sections)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,6 @@ internal val internalSharedModule =
|
|||||||
identityRepository = get(),
|
identityRepository = get(),
|
||||||
settingRepository = get(),
|
settingRepository = get(),
|
||||||
userRepository = get(),
|
userRepository = get(),
|
||||||
accountRepository = get(),
|
|
||||||
bottomNavItemsRepository = get(),
|
|
||||||
notificationChecker = get(),
|
notificationChecker = get(),
|
||||||
lemmyValueCache = get(),
|
lemmyValueCache = get(),
|
||||||
)
|
)
|
||||||
|
@ -8,6 +8,8 @@ import cafe.adriel.voyager.navigator.Navigator
|
|||||||
import cafe.adriel.voyager.navigator.tab.Tab
|
import cafe.adriel.voyager.navigator.tab.Tab
|
||||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInt
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsScreen
|
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsScreen
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsType
|
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.FilteredContentsType
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.toInt
|
import com.github.diegoberaldin.raccoonforlemmy.unit.filteredcontents.toInt
|
||||||
@ -20,7 +22,7 @@ object BookmarksTab : Tab {
|
|||||||
val title = LocalStrings.current.navigationDrawerTitleBookmarks
|
val title = LocalStrings.current.navigationDrawerTitleBookmarks
|
||||||
|
|
||||||
return TabOptions(
|
return TabOptions(
|
||||||
index = 5u,
|
index = TabNavigationSection.Bookmarks.toInt().toUShort(),
|
||||||
title = title,
|
title = title,
|
||||||
icon = icon,
|
icon = icon,
|
||||||
)
|
)
|
||||||
|
@ -42,8 +42,8 @@ internal fun RowScope.TabNavigationItem(
|
|||||||
section: TabNavigationSection,
|
section: TabNavigationSection,
|
||||||
withText: Boolean = true,
|
withText: Boolean = true,
|
||||||
customIconUrl: String? = null,
|
customIconUrl: String? = null,
|
||||||
onClick: ((Tab) -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
onLongPress: ((Tab) -> Unit)? = null,
|
onLongPress: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||||
val unread by navigationCoordinator.inboxUnread.collectAsState()
|
val unread by navigationCoordinator.inboxUnread.collectAsState()
|
||||||
@ -51,10 +51,6 @@ internal fun RowScope.TabNavigationItem(
|
|||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val tab = section.toTab()
|
val tab = section.toTab()
|
||||||
|
|
||||||
fun handleClick() {
|
|
||||||
onClick?.invoke(tab)
|
|
||||||
}
|
|
||||||
|
|
||||||
val pointerInputModifier =
|
val pointerInputModifier =
|
||||||
Modifier.pointerInput(Unit) {
|
Modifier.pointerInput(Unit) {
|
||||||
detectTapGestures(
|
detectTapGestures(
|
||||||
@ -65,16 +61,18 @@ internal fun RowScope.TabNavigationItem(
|
|||||||
interactionSource.emit(PressInteraction.Release(press))
|
interactionSource.emit(PressInteraction.Release(press))
|
||||||
},
|
},
|
||||||
onTap = {
|
onTap = {
|
||||||
handleClick()
|
onClick?.invoke()
|
||||||
},
|
},
|
||||||
onLongPress = {
|
onLongPress = {
|
||||||
onLongPress?.invoke(tab)
|
onLongPress?.invoke()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
onClick = ::handleClick,
|
onClick = {
|
||||||
|
onClick?.invoke()
|
||||||
|
},
|
||||||
selected = section == currentSection,
|
selected = section == currentSection,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
icon = {
|
icon = {
|
||||||
@ -136,7 +134,7 @@ internal fun RowScope.TabNavigationItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TabNavigationSection.toTab(): Tab =
|
internal fun TabNavigationSection.toTab(): Tab =
|
||||||
when (this) {
|
when (this) {
|
||||||
TabNavigationSection.Explore -> ExploreTab
|
TabNavigationSection.Explore -> ExploreTab
|
||||||
TabNavigationSection.Profile -> ProfileTab
|
TabNavigationSection.Profile -> ProfileTab
|
||||||
|
@ -36,6 +36,7 @@ import com.github.diegoberaldin.raccoonforlemmy.unit.chat.di.chatModule
|
|||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.communitydetail.di.communityDetailModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.communitydetail.di.communityDetailModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.communityinfo.di.communityInfoModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.communityinfo.di.communityInfoModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.configurecontentview.di.configureContentViewModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configurecontentview.di.configureContentViewModule
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.di.configureNavBarModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.configureswipeactions.di.configureSwipeActionsModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configureswipeactions.di.configureSwipeActionsModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.createcomment.di.createCommentModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.createcomment.di.createCommentModule
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.unit.createpost.di.createPostModule
|
import com.github.diegoberaldin.raccoonforlemmy.unit.createpost.di.createPostModule
|
||||||
@ -122,6 +123,7 @@ fun initKoin() {
|
|||||||
moderateWithReasonModule,
|
moderateWithReasonModule,
|
||||||
acknowledgementsModule,
|
acknowledgementsModule,
|
||||||
mediaListModule,
|
mediaListModule,
|
||||||
|
configureNavBarModule,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.aspectRatio
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
@ -110,17 +111,18 @@ class AccountSettingsScreen : Screen {
|
|||||||
var confirmBackWithUnsavedChangesDialog by remember { mutableStateOf(false) }
|
var confirmBackWithUnsavedChangesDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(model) {
|
LaunchedEffect(model) {
|
||||||
model.effects.onEach { evt ->
|
model.effects
|
||||||
when (evt) {
|
.onEach { evt ->
|
||||||
AccountSettingsMviModel.Effect.Failure -> {
|
when (evt) {
|
||||||
snackbarHostState.showSnackbar(errorMessage)
|
AccountSettingsMviModel.Effect.Failure -> {
|
||||||
}
|
snackbarHostState.showSnackbar(errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
AccountSettingsMviModel.Effect.Success -> {
|
AccountSettingsMviModel.Effect.Success -> {
|
||||||
snackbarHostState.showSnackbar(successMessage)
|
snackbarHostState.showSnackbar(successMessage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}.launchIn(this)
|
||||||
}.launchIn(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DisposableEffect(key) {
|
DisposableEffect(key) {
|
||||||
@ -214,8 +216,7 @@ class AccountSettingsScreen : Screen {
|
|||||||
Modifier
|
Modifier
|
||||||
.padding(
|
.padding(
|
||||||
top = padding.calculateTopPadding(),
|
top = padding.calculateTopPadding(),
|
||||||
bottom = Spacing.m,
|
).navigationBarsPadding()
|
||||||
)
|
|
||||||
.then(
|
.then(
|
||||||
if (settings.hideNavigationBarWhileScrolling) {
|
if (settings.hideNavigationBarWhileScrolling) {
|
||||||
Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
|
78
unit/configurenavbar/build.gradle.kts
Normal file
78
unit/configurenavbar/build.gradle.kts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlin.multiplatform)
|
||||||
|
alias(libs.plugins.android.library)
|
||||||
|
alias(libs.plugins.jetbrains.compose)
|
||||||
|
alias(libs.plugins.compose.compiler)
|
||||||
|
alias(libs.plugins.detekt)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
|
||||||
|
kotlin {
|
||||||
|
applyDefaultHierarchyTemplate()
|
||||||
|
androidTarget {
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget.set(JvmTarget.JVM_1_8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listOf(
|
||||||
|
iosX64(),
|
||||||
|
iosArm64(),
|
||||||
|
iosSimulatorArm64(),
|
||||||
|
).forEach {
|
||||||
|
it.binaries.framework {
|
||||||
|
baseName = "unit.configurenavbar"
|
||||||
|
isStatic = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val commonMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(compose.runtime)
|
||||||
|
implementation(compose.foundation)
|
||||||
|
implementation(compose.material)
|
||||||
|
implementation(compose.material3)
|
||||||
|
implementation(compose.materialIconsExtended)
|
||||||
|
|
||||||
|
implementation(libs.koin.core)
|
||||||
|
implementation(libs.reorderable)
|
||||||
|
implementation(libs.voyager.navigator)
|
||||||
|
implementation(libs.voyager.screenmodel)
|
||||||
|
implementation(libs.voyager.koin)
|
||||||
|
|
||||||
|
implementation(projects.core.appearance)
|
||||||
|
implementation(projects.core.architecture)
|
||||||
|
implementation(projects.core.commonui.components)
|
||||||
|
implementation(projects.core.commonui.modals)
|
||||||
|
implementation(projects.core.l10n)
|
||||||
|
implementation(projects.core.navigation)
|
||||||
|
implementation(projects.core.notifications)
|
||||||
|
implementation(projects.core.persistence)
|
||||||
|
implementation(projects.core.utils)
|
||||||
|
|
||||||
|
implementation(projects.domain.identity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val commonTest by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar"
|
||||||
|
compileSdk =
|
||||||
|
libs.versions.android.targetSdk
|
||||||
|
.get()
|
||||||
|
.toInt()
|
||||||
|
defaultConfig {
|
||||||
|
minSdk =
|
||||||
|
libs.versions.android.minSdk
|
||||||
|
.get()
|
||||||
|
.toInt()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import cafe.adriel.voyager.core.model.ScreenModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
interface ConfigureNavBarMviModel :
|
||||||
|
MviModel<ConfigureNavBarMviModel.Intent, ConfigureNavBarMviModel.UiState, ConfigureNavBarMviModel.Effect>,
|
||||||
|
ScreenModel {
|
||||||
|
sealed interface Intent {
|
||||||
|
data object Reset : Intent
|
||||||
|
|
||||||
|
data object HapticFeedback : Intent
|
||||||
|
|
||||||
|
data class SwapItems(
|
||||||
|
val from: Int,
|
||||||
|
val to: Int,
|
||||||
|
) : Intent
|
||||||
|
|
||||||
|
data class Delete(
|
||||||
|
val section: TabNavigationSection,
|
||||||
|
) : Intent
|
||||||
|
|
||||||
|
data object Save : Intent
|
||||||
|
}
|
||||||
|
|
||||||
|
data class UiState(
|
||||||
|
val sections: List<TabNavigationSection> = emptyList(),
|
||||||
|
val availableSections: List<TabNavigationSection> = emptyList(),
|
||||||
|
val hasUnsavedChanges: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed interface Effect
|
||||||
|
}
|
@ -0,0 +1,279 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
|
import cafe.adriel.voyager.koin.getScreenModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SelectTabNavigationSectionBottomSheet
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.di.getNavigationCoordinator
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInt
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toReadableName
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.di.getSettingsRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.composable.ConfigureAddAction
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.composable.ConfigureNavBarItem
|
||||||
|
import sh.calvin.reorderable.ReorderableItem
|
||||||
|
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||||
|
|
||||||
|
class ConfigureNavBarScreen : Screen {
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val model = getScreenModel<ConfigureNavBarMviModel>()
|
||||||
|
val uiState by model.uiState.collectAsState()
|
||||||
|
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||||
|
val topAppBarState = rememberTopAppBarState()
|
||||||
|
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarState)
|
||||||
|
val settingsRepository = remember { getSettingsRepository() }
|
||||||
|
val settings by settingsRepository.currentSettings.collectAsState()
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
val reorderableLazyColumnState =
|
||||||
|
rememberReorderableLazyListState(lazyListState) { from, to ->
|
||||||
|
model.reduce(
|
||||||
|
ConfigureNavBarMviModel.Intent.SwapItems(
|
||||||
|
from = from.index - 1,
|
||||||
|
to = to.index - 1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
var confirmBackWithUnsavedChangesDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
DisposableEffect(key) {
|
||||||
|
navigationCoordinator.setCanGoBackCallback {
|
||||||
|
if (uiState.hasUnsavedChanges) {
|
||||||
|
confirmBackWithUnsavedChangesDialog = true
|
||||||
|
return@setCanGoBackCallback false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
onDispose {
|
||||||
|
navigationCoordinator.setCanGoBackCallback(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.background(MaterialTheme.colorScheme.background)
|
||||||
|
.padding(Spacing.xs),
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(horizontal = Spacing.s),
|
||||||
|
text = LocalStrings.current.settingsItemConfigureBottomNavigationBar,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
if (navigationCoordinator.canPop.value) {
|
||||||
|
Image(
|
||||||
|
modifier =
|
||||||
|
Modifier.onClick(
|
||||||
|
onClick = {
|
||||||
|
if (uiState.hasUnsavedChanges) {
|
||||||
|
confirmBackWithUnsavedChangesDialog = true
|
||||||
|
} else {
|
||||||
|
navigationCoordinator.popScreen()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
imageVector = Icons.AutoMirrored.Default.ArrowBack,
|
||||||
|
contentDescription = null,
|
||||||
|
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
TextButton(
|
||||||
|
contentPadding =
|
||||||
|
PaddingValues(
|
||||||
|
horizontal = Spacing.xs,
|
||||||
|
vertical = Spacing.xxs,
|
||||||
|
),
|
||||||
|
onClick = {
|
||||||
|
model.reduce(ConfigureNavBarMviModel.Intent.Reset)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = LocalStrings.current.buttonReset,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { padding ->
|
||||||
|
Column(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(
|
||||||
|
top = padding.calculateTopPadding(),
|
||||||
|
).navigationBarsPadding()
|
||||||
|
.then(
|
||||||
|
if (settings.hideNavigationBarWhileScrolling) {
|
||||||
|
Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
},
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.weight(1f).fillMaxWidth(),
|
||||||
|
state = lazyListState,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(Spacing.xs),
|
||||||
|
) {
|
||||||
|
if (uiState.sections.isEmpty()) {
|
||||||
|
item {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(top = Spacing.xs),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
text = LocalStrings.current.messageEmptyList,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item {
|
||||||
|
// workaround for https://github.com/Calvin-LL/Reorderable/issues/4
|
||||||
|
ReorderableItem(
|
||||||
|
state = reorderableLazyColumnState,
|
||||||
|
key = "dummy",
|
||||||
|
enabled = false,
|
||||||
|
modifier = Modifier.fillMaxWidth().height(Dp.Hairline),
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items(
|
||||||
|
items = uiState.sections,
|
||||||
|
key = { it.toInt().toString() },
|
||||||
|
) { section ->
|
||||||
|
ReorderableItem(
|
||||||
|
state = reorderableLazyColumnState,
|
||||||
|
key = section.toInt().toString(),
|
||||||
|
) { isDragging ->
|
||||||
|
val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp)
|
||||||
|
Surface(
|
||||||
|
shadowElevation = elevation,
|
||||||
|
) {
|
||||||
|
ConfigureNavBarItem(
|
||||||
|
reorderableScope = this,
|
||||||
|
title = section.toReadableName(),
|
||||||
|
onDragStarted =
|
||||||
|
rememberCallback(model) {
|
||||||
|
model.reduce(ConfigureNavBarMviModel.Intent.HapticFeedback)
|
||||||
|
},
|
||||||
|
onDelete =
|
||||||
|
rememberCallback(model) {
|
||||||
|
model.reduce(ConfigureNavBarMviModel.Intent.Delete(section))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (uiState.availableSections.isNotEmpty() && uiState.sections.size < 5) {
|
||||||
|
item {
|
||||||
|
ConfigureAddAction(
|
||||||
|
onAdd =
|
||||||
|
rememberCallback {
|
||||||
|
val available = model.uiState.value.availableSections
|
||||||
|
val sheet =
|
||||||
|
SelectTabNavigationSectionBottomSheet(values = available)
|
||||||
|
navigationCoordinator.showBottomSheet(sheet)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(vertical = Spacing.m),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
enabled = uiState.hasUnsavedChanges,
|
||||||
|
onClick = {
|
||||||
|
model.reduce(ConfigureNavBarMviModel.Intent.Save)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(text = LocalStrings.current.actionSave)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (confirmBackWithUnsavedChangesDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {
|
||||||
|
confirmBackWithUnsavedChangesDialog = false
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
confirmBackWithUnsavedChangesDialog = false
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(text = LocalStrings.current.buttonNoStay)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
confirmBackWithUnsavedChangesDialog = false
|
||||||
|
navigationCoordinator.popScreen()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(text = LocalStrings.current.buttonYesQuit)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = LocalStrings.current.messageUnsavedChanges)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,150 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar
|
||||||
|
|
||||||
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.BottomNavItemsRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toInts
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toTabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.SettingsRepository
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.vibrate.HapticFeedback
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
internal class ConfigureNavBarViewModel(
|
||||||
|
private val accountRepository: AccountRepository,
|
||||||
|
private val identityRepository: IdentityRepository,
|
||||||
|
private val bottomNavItemsRepository: BottomNavItemsRepository,
|
||||||
|
private val settingsRepository: SettingsRepository,
|
||||||
|
private val hapticFeedback: HapticFeedback,
|
||||||
|
private val notificationCenter: NotificationCenter,
|
||||||
|
) : DefaultMviModel<ConfigureNavBarMviModel.Intent, ConfigureNavBarMviModel.UiState, ConfigureNavBarMviModel.Effect>(
|
||||||
|
initialState = ConfigureNavBarMviModel.UiState(),
|
||||||
|
),
|
||||||
|
ConfigureNavBarMviModel {
|
||||||
|
init {
|
||||||
|
screenModelScope.launch {
|
||||||
|
notificationCenter
|
||||||
|
.subscribe(NotificationCenterEvent.TabNavigationSectionSelected::class)
|
||||||
|
.onEach { evt ->
|
||||||
|
evt.sectionId.toTabNavigationSection()?.also { section ->
|
||||||
|
handleAdd(section)
|
||||||
|
}
|
||||||
|
}.launchIn(this)
|
||||||
|
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reduce(intent: ConfigureNavBarMviModel.Intent) {
|
||||||
|
when (intent) {
|
||||||
|
ConfigureNavBarMviModel.Intent.HapticFeedback -> hapticFeedback.vibrate()
|
||||||
|
ConfigureNavBarMviModel.Intent.Reset -> handleReset()
|
||||||
|
is ConfigureNavBarMviModel.Intent.Delete -> handleDelete(intent.section)
|
||||||
|
is ConfigureNavBarMviModel.Intent.SwapItems -> handleSwap(intent.from, intent.to)
|
||||||
|
ConfigureNavBarMviModel.Intent.Save -> save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun refresh() {
|
||||||
|
val accountId = accountRepository.getActive()?.id
|
||||||
|
val currentSections = bottomNavItemsRepository.get(accountId)
|
||||||
|
updateState {
|
||||||
|
it.copy(
|
||||||
|
sections = currentSections,
|
||||||
|
availableSections = getAvailableSections(currentSections),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAvailableSections(excludeSections: List<TabNavigationSection> = emptyList()): List<TabNavigationSection> {
|
||||||
|
val availableSections =
|
||||||
|
buildList {
|
||||||
|
this += TabNavigationSection.Home
|
||||||
|
this += TabNavigationSection.Explore
|
||||||
|
this += TabNavigationSection.Inbox
|
||||||
|
this += TabNavigationSection.Profile
|
||||||
|
this += TabNavigationSection.Settings
|
||||||
|
if (identityRepository.isLogged.value == true) {
|
||||||
|
this += TabNavigationSection.Bookmarks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return availableSections.filter { section -> section !in excludeSections }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleReset() {
|
||||||
|
screenModelScope.launch {
|
||||||
|
val oldSections = uiState.value.sections
|
||||||
|
val newSections = BottomNavItemsRepository.DEFAULT_ITEMS
|
||||||
|
updateState {
|
||||||
|
it.copy(
|
||||||
|
sections = newSections,
|
||||||
|
availableSections = getAvailableSections(newSections),
|
||||||
|
hasUnsavedChanges = newSections != oldSections,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleAdd(section: TabNavigationSection) {
|
||||||
|
val newSections = uiState.value.sections + section
|
||||||
|
screenModelScope.launch {
|
||||||
|
updateState {
|
||||||
|
it.copy(
|
||||||
|
sections = newSections,
|
||||||
|
availableSections = getAvailableSections(newSections),
|
||||||
|
hasUnsavedChanges = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleDelete(section: TabNavigationSection) {
|
||||||
|
val newSections = uiState.value.sections - section
|
||||||
|
screenModelScope.launch {
|
||||||
|
updateState {
|
||||||
|
it.copy(
|
||||||
|
sections = newSections,
|
||||||
|
availableSections = getAvailableSections(newSections),
|
||||||
|
hasUnsavedChanges = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSwap(
|
||||||
|
from: Int,
|
||||||
|
to: Int,
|
||||||
|
) {
|
||||||
|
val newSections =
|
||||||
|
uiState.value.sections.toMutableList().apply {
|
||||||
|
val element = removeAt(from)
|
||||||
|
add(to, element)
|
||||||
|
}
|
||||||
|
screenModelScope.launch {
|
||||||
|
updateState {
|
||||||
|
it.copy(
|
||||||
|
sections = newSections,
|
||||||
|
hasUnsavedChanges = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun save() {
|
||||||
|
screenModelScope.launch {
|
||||||
|
val currentSections = uiState.value.sections
|
||||||
|
val accountId = accountRepository.getActive()?.id
|
||||||
|
bottomNavItemsRepository.update(accountId, currentSections)
|
||||||
|
updateState {
|
||||||
|
it.copy(hasUnsavedChanges = false)
|
||||||
|
}
|
||||||
|
settingsRepository.changeCurrentBottomBarSections(currentSections.toInts())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.composable
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Add
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.ancillaryTextAlpha
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConfigureAddAction(onAdd: () -> Unit) {
|
||||||
|
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)
|
||||||
|
Row(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(
|
||||||
|
horizontal = Spacing.s,
|
||||||
|
vertical = Spacing.xs,
|
||||||
|
).onClick(onClick = onAdd),
|
||||||
|
horizontalArrangement = Arrangement.SpaceAround,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.size(IconSize.m),
|
||||||
|
imageVector = Icons.Outlined.Add,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = ancillaryColor,
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(Spacing.xs))
|
||||||
|
Text(
|
||||||
|
text = LocalStrings.current.buttonAdd,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.composable
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
|
import androidx.compose.ui.layout.positionInParent
|
||||||
|
import androidx.compose.ui.unit.DpOffset
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.IconSize
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.ancillaryTextAlpha
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.CustomDropDown
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.l10n.messages.LocalStrings
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
|
||||||
|
import sh.calvin.reorderable.ReorderableCollectionItemScope
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun ConfigureNavBarItem(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
reorderableScope: ReorderableCollectionItemScope,
|
||||||
|
title: String,
|
||||||
|
onDragStarted: (() -> Unit)? = null,
|
||||||
|
onDelete: (() -> Unit)? = null,
|
||||||
|
) {
|
||||||
|
var optionsOffset by remember { mutableStateOf(Offset.Zero) }
|
||||||
|
var optionsMenuOpen by remember { mutableStateOf(false) }
|
||||||
|
val fullColor = MaterialTheme.colorScheme.onBackground
|
||||||
|
val ancillaryColor = MaterialTheme.colorScheme.onBackground.copy(alpha = ancillaryTextAlpha)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier =
|
||||||
|
modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(
|
||||||
|
horizontal = Spacing.m,
|
||||||
|
vertical = Spacing.s,
|
||||||
|
),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
color = fullColor,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
Box {
|
||||||
|
Icon(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.size(IconSize.m)
|
||||||
|
.padding(Spacing.xs)
|
||||||
|
.then(
|
||||||
|
with(reorderableScope) {
|
||||||
|
Modifier.draggableHandle(
|
||||||
|
onDragStarted = {
|
||||||
|
onDragStarted?.invoke()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
).onGloballyPositioned {
|
||||||
|
optionsOffset = it.positionInParent()
|
||||||
|
}.onClick(
|
||||||
|
onClick = {
|
||||||
|
optionsMenuOpen = true
|
||||||
|
},
|
||||||
|
),
|
||||||
|
imageVector = Icons.Default.MoreVert,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = ancillaryColor,
|
||||||
|
)
|
||||||
|
|
||||||
|
CustomDropDown(
|
||||||
|
expanded = optionsMenuOpen,
|
||||||
|
onDismiss = {
|
||||||
|
optionsMenuOpen = false
|
||||||
|
},
|
||||||
|
offset =
|
||||||
|
DpOffset(
|
||||||
|
x = optionsOffset.x.toLocalDp(),
|
||||||
|
y = optionsOffset.y.toLocalDp(),
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = {
|
||||||
|
Text(LocalStrings.current.commentActionDelete)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
optionsMenuOpen = false
|
||||||
|
onDelete?.invoke()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.di
|
||||||
|
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.ConfigureNavBarMviModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.unit.configurenavbar.ConfigureNavBarViewModel
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val configureNavBarModule =
|
||||||
|
module {
|
||||||
|
single<ConfigureNavBarMviModel> {
|
||||||
|
ConfigureNavBarViewModel(
|
||||||
|
accountRepository = get(),
|
||||||
|
identityRepository = get(),
|
||||||
|
bottomNavItemsRepository = get(),
|
||||||
|
settingsRepository = get(),
|
||||||
|
hapticFeedback = get(),
|
||||||
|
notificationCenter = get(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -205,22 +205,24 @@ object ModalDrawerContent : Tab {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item {
|
if (uiState.isSettingsVisible) {
|
||||||
DrawerShortcut(
|
item {
|
||||||
title = LocalStrings.current.navigationSettings,
|
DrawerShortcut(
|
||||||
icon = Icons.Default.Settings,
|
title = LocalStrings.current.navigationSettings,
|
||||||
onSelected =
|
icon = Icons.Default.Settings,
|
||||||
rememberCallback(coordinator) {
|
onSelected =
|
||||||
scope.launch {
|
rememberCallback(coordinator) {
|
||||||
focusManager.clearFocus()
|
scope.launch {
|
||||||
navigationCoordinator.popUntilRoot()
|
focusManager.clearFocus()
|
||||||
coordinator.toggleDrawer()
|
navigationCoordinator.popUntilRoot()
|
||||||
delay(50)
|
coordinator.toggleDrawer()
|
||||||
|
delay(50)
|
||||||
|
|
||||||
coordinator.sendEvent(DrawerEvent.OpenSettings)
|
coordinator.sendEvent(DrawerEvent.OpenSettings)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ interface ModalDrawerMviModel :
|
|||||||
val searchText: String = "",
|
val searchText: String = "",
|
||||||
val isFiltering: Boolean = false,
|
val isFiltering: Boolean = false,
|
||||||
val enableToggleFavorite: Boolean = false,
|
val enableToggleFavorite: Boolean = false,
|
||||||
|
val isSettingsVisible: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed interface Effect
|
sealed interface Effect
|
||||||
|
@ -4,6 +4,8 @@ import cafe.adriel.voyager.core.model.screenModelScope
|
|||||||
import com.diegoberaldin.raccoonforlemmy.domain.lemmy.pagination.CommunityPaginationManager
|
import com.diegoberaldin.raccoonforlemmy.domain.lemmy.pagination.CommunityPaginationManager
|
||||||
import com.diegoberaldin.raccoonforlemmy.domain.lemmy.pagination.CommunityPaginationSpecification
|
import com.diegoberaldin.raccoonforlemmy.domain.lemmy.pagination.CommunityPaginationSpecification
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.TabNavigationSection
|
||||||
|
import com.github.diegoberaldin.raccoonforlemmy.core.navigation.toTabNavigationSections
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterEvent
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.FavoriteCommunityModel
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.FavoriteCommunityModel
|
||||||
@ -98,6 +100,14 @@ class ModalDrawerViewModel(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
|
settingsRepository.currentBottomBarSections
|
||||||
|
.onEach { sectionIds ->
|
||||||
|
val isSettingsInBottomBar =
|
||||||
|
sectionIds.toTabNavigationSections().contains(TabNavigationSection.Settings)
|
||||||
|
updateState {
|
||||||
|
it.copy(isSettingsVisible = !isSettingsInBottomBar)
|
||||||
|
}
|
||||||
|
}.launchIn(this)
|
||||||
|
|
||||||
uiState
|
uiState
|
||||||
.map { it.searchText }
|
.map { it.searchText }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user