mirror of
https://github.com/LiveFastEatTrashRaccoon/RaccoonForLemmy.git
synced 2025-02-09 12:48:42 +01:00
feat: zombie mode (#107)
This commit is contained in:
parent
76d582f014
commit
eb990ccd82
@ -2,6 +2,7 @@ package com.github.diegoberaldin.raccoonforlemmy.core.commonui.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.animateIntAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
@ -64,7 +65,10 @@ fun FloatingActionButtonMenu(
|
||||
}
|
||||
val numberOfItems by animateIntAsState(
|
||||
targetValue = if (fabExpanded) items.size else 0,
|
||||
animationSpec = tween(250 * items.size)
|
||||
animationSpec = tween(
|
||||
durationMillis = 150 * items.size,
|
||||
easing = LinearEasing,
|
||||
)
|
||||
)
|
||||
val indices: List<Int> = if (numberOfItems == 0) {
|
||||
emptyList()
|
||||
|
@ -43,6 +43,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.BottomS
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.PostCardBody
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.components.ScaledContent
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getCommunityInfoViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.getPrettyNumber
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.prettifyDate
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
@ -115,37 +116,55 @@ class CommunityInfoScreen(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = Icons.Default.Padding,
|
||||
title = stringResource(MR.strings.community_info_posts),
|
||||
value = uiState.community.posts.toString(),
|
||||
value = uiState.community.posts.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
)
|
||||
CommunityInfoItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = Icons.Default.Reply,
|
||||
title = stringResource(MR.strings.community_info_comments),
|
||||
value = uiState.community.comments.toString(),
|
||||
value = uiState.community.comments.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
)
|
||||
CommunityInfoItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = Icons.Default.Group,
|
||||
title = stringResource(MR.strings.community_info_subscribers),
|
||||
value = uiState.community.subscribers.toString(),
|
||||
value = uiState.community.subscribers.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
)
|
||||
CommunityInfoItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = Icons.Default.CalendarViewMonth,
|
||||
title = stringResource(MR.strings.community_info_monthly_active_users),
|
||||
value = uiState.community.monthlyActiveUsers.toString(),
|
||||
value = uiState.community.monthlyActiveUsers.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
)
|
||||
CommunityInfoItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = Icons.Default.CalendarViewWeek,
|
||||
title = stringResource(MR.strings.community_info_weekly_active_users),
|
||||
value = uiState.community.weeklyActiveUsers.toString(),
|
||||
value = uiState.community.weeklyActiveUsers.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
)
|
||||
CommunityInfoItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
icon = Icons.Default.CalendarViewDay,
|
||||
title = stringResource(MR.strings.community_info_daily_active_users),
|
||||
value = uiState.community.dailyActiveUsers.toString(),
|
||||
value = uiState.community.dailyActiveUsers.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
)
|
||||
|
||||
Divider()
|
||||
|
@ -30,6 +30,8 @@ interface CommunityDetailMviModel :
|
||||
data object Block : Intent
|
||||
data object BlockInstance : Intent
|
||||
data object ClearRead : Intent
|
||||
data class StartZombieMode(val index: Int) : Intent
|
||||
data object PauseZombieMode : Intent
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
@ -48,11 +50,13 @@ interface CommunityDetailMviModel :
|
||||
val fullHeightImages: Boolean = true,
|
||||
val separateUpAndDownVotes: Boolean = false,
|
||||
val autoLoadImages: Boolean = true,
|
||||
val zombieModeActive: Boolean = false,
|
||||
)
|
||||
|
||||
sealed interface Effect {
|
||||
data object BlockSuccess : Effect
|
||||
data class BlockError(val message: String?) : Effect
|
||||
data object BackToTop : Effect
|
||||
data class ZombieModeTick(val index: Int) : Effect
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@ -24,6 +25,8 @@ import androidx.compose.material.icons.filled.ArrowCircleUp
|
||||
import androidx.compose.material.icons.filled.ClearAll
|
||||
import androidx.compose.material.icons.filled.Create
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.icons.filled.Sync
|
||||
import androidx.compose.material.icons.filled.SyncDisabled
|
||||
import androidx.compose.material.icons.outlined.AddCircleOutline
|
||||
import androidx.compose.material.icons.outlined.CheckCircle
|
||||
import androidx.compose.material.icons.outlined.Pending
|
||||
@ -181,10 +184,10 @@ class CommunityDetailScreen(
|
||||
)
|
||||
}
|
||||
LaunchedEffect(model) {
|
||||
model.effects.onEach {
|
||||
when (it) {
|
||||
model.effects.onEach { effect ->
|
||||
when (effect) {
|
||||
is CommunityDetailMviModel.Effect.BlockError -> {
|
||||
snackbarHostState.showSnackbar(it.message ?: genericError)
|
||||
snackbarHostState.showSnackbar(effect.message ?: genericError)
|
||||
}
|
||||
|
||||
CommunityDetailMviModel.Effect.BlockSuccess -> {
|
||||
@ -192,8 +195,12 @@ class CommunityDetailScreen(
|
||||
}
|
||||
|
||||
CommunityDetailMviModel.Effect.BackToTop -> {
|
||||
scope.launch {
|
||||
lazyListState.scrollToItem(0)
|
||||
lazyListState.scrollToItem(0)
|
||||
}
|
||||
|
||||
is CommunityDetailMviModel.Effect.ZombieModeTick -> {
|
||||
if (effect.index >= 0) {
|
||||
lazyListState.scrollToItem(effect.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -283,6 +290,28 @@ class CommunityDetailScreen(
|
||||
) {
|
||||
FloatingActionButtonMenu(
|
||||
items = buildList {
|
||||
if (uiState.zombieModeActive) {
|
||||
this += FloatingActionButtonMenuItem(
|
||||
icon = Icons.Default.SyncDisabled,
|
||||
text = stringResource(MR.strings.action_deactivate_zombie_mode),
|
||||
onSelected = rememberCallback(model) {
|
||||
model.reduce(CommunityDetailMviModel.Intent.PauseZombieMode)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
this += FloatingActionButtonMenuItem(
|
||||
icon = Icons.Default.Sync,
|
||||
text = stringResource(MR.strings.action_activate_zombie_mode),
|
||||
onSelected = rememberCallback(model) {
|
||||
val idx = lazyListState.firstVisibleItemIndex
|
||||
model.reduce(
|
||||
CommunityDetailMviModel.Intent.StartZombieMode(
|
||||
idx + 1// header
|
||||
)
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
this += FloatingActionButtonMenuItem(
|
||||
icon = Icons.Default.ExpandLess,
|
||||
text = stringResource(MR.strings.action_back_to_top),
|
||||
@ -343,45 +372,46 @@ class CommunityDetailScreen(
|
||||
) {
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
userScrollEnabled = !uiState.zombieModeActive,
|
||||
) {
|
||||
item {
|
||||
CommunityHeader(
|
||||
community = uiState.community,
|
||||
autoLoadImages = uiState.autoLoadImages,
|
||||
options = listOf(
|
||||
stringResource(MR.strings.community_detail_info),
|
||||
stringResource(MR.strings.community_detail_instance_info),
|
||||
stringResource(MR.strings.community_detail_block),
|
||||
stringResource(MR.strings.community_detail_block_instance),
|
||||
),
|
||||
onOpenImage = rememberCallbackArgs { url ->
|
||||
navigationCoordinator.getRootNavigator()
|
||||
?.push(ZoomableImageScreen(url))
|
||||
},
|
||||
onOptionSelected = rememberCallbackArgs { optionIdx ->
|
||||
when (optionIdx) {
|
||||
3 -> model.reduce(CommunityDetailMviModel.Intent.BlockInstance)
|
||||
2 -> model.reduce(CommunityDetailMviModel.Intent.Block)
|
||||
Column {
|
||||
CommunityHeader(
|
||||
community = uiState.community,
|
||||
autoLoadImages = uiState.autoLoadImages,
|
||||
options = listOf(
|
||||
stringResource(MR.strings.community_detail_info),
|
||||
stringResource(MR.strings.community_detail_instance_info),
|
||||
stringResource(MR.strings.community_detail_block),
|
||||
stringResource(MR.strings.community_detail_block_instance),
|
||||
),
|
||||
onOpenImage = rememberCallbackArgs { url ->
|
||||
navigationCoordinator.getRootNavigator()
|
||||
?.push(ZoomableImageScreen(url))
|
||||
},
|
||||
onOptionSelected = rememberCallbackArgs { optionIdx ->
|
||||
when (optionIdx) {
|
||||
3 -> model.reduce(CommunityDetailMviModel.Intent.BlockInstance)
|
||||
2 -> model.reduce(CommunityDetailMviModel.Intent.Block)
|
||||
|
||||
1 -> {
|
||||
navigationCoordinator.getRootNavigator()?.push(
|
||||
InstanceInfoScreen(
|
||||
url = uiState.community.instanceUrl,
|
||||
),
|
||||
)
|
||||
}
|
||||
1 -> {
|
||||
navigationCoordinator.getRootNavigator()?.push(
|
||||
InstanceInfoScreen(
|
||||
url = uiState.community.instanceUrl,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
navigationCoordinator.getBottomNavigator()?.show(
|
||||
CommunityInfoScreen(uiState.community),
|
||||
)
|
||||
else -> {
|
||||
navigationCoordinator.getBottomNavigator()?.show(
|
||||
CommunityInfoScreen(uiState.community),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(Spacing.m))
|
||||
},
|
||||
)
|
||||
Spacer(modifier = Modifier.height(Spacing.m))
|
||||
}
|
||||
}
|
||||
if (uiState.posts.isEmpty() && uiState.loading) {
|
||||
items(5) {
|
||||
|
@ -6,6 +6,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.SettingsRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.HapticFeedback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.ShareHelper
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.ZombieModeHelper
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
|
||||
@ -33,6 +34,7 @@ class CommunityDetailViewModel(
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val shareHelper: ShareHelper,
|
||||
private val hapticFeedback: HapticFeedback,
|
||||
private val zombieModeHelper: ZombieModeHelper,
|
||||
) : CommunityDetailMviModel,
|
||||
MviModel<CommunityDetailMviModel.Intent, CommunityDetailMviModel.UiState, CommunityDetailMviModel.Effect> by mvi {
|
||||
|
||||
@ -69,6 +71,10 @@ class CommunityDetailViewModel(
|
||||
}
|
||||
}.launchIn(this)
|
||||
|
||||
zombieModeHelper.index.onEach { index ->
|
||||
mvi.emitEffect(CommunityDetailMviModel.Effect.ZombieModeTick(index))
|
||||
}.launchIn(this)
|
||||
|
||||
if (uiState.value.currentUserId == null) {
|
||||
val user = siteRepository.getCurrentUser(auth)
|
||||
mvi.updateState { it.copy(currentUserId = user?.id ?: 0) }
|
||||
@ -136,6 +142,16 @@ class CommunityDetailViewModel(
|
||||
hide(post = post)
|
||||
}
|
||||
}
|
||||
|
||||
CommunityDetailMviModel.Intent.PauseZombieMode -> {
|
||||
mvi.updateState { it.copy(zombieModeActive = false) }
|
||||
zombieModeHelper.pause()
|
||||
}
|
||||
|
||||
is CommunityDetailMviModel.Intent.StartZombieMode -> {
|
||||
mvi.updateState { it.copy(zombieModeActive = true) }
|
||||
zombieModeHelper.start(intent.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,10 +37,13 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.getPrettyNumber
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@Composable
|
||||
fun CommunityHeader(
|
||||
@ -201,7 +204,10 @@ fun CommunityHeader(
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = community.subscribers.toString(),
|
||||
text = community.subscribers.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@ -213,7 +219,10 @@ fun CommunityHeader(
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = community.monthlyActiveUsers.toString(),
|
||||
text = community.monthlyActiveUsers.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
|
@ -40,11 +40,14 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.getPrettyNumber
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.prettifyDate
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@Composable
|
||||
fun UserHeader(
|
||||
@ -203,7 +206,10 @@ fun UserHeader(
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = postScore.toString(),
|
||||
text = postScore.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
@ -218,7 +224,10 @@ fun UserHeader(
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = commentScore.toString(),
|
||||
text = commentScore.getPrettyNumber(
|
||||
thousandLabel = stringResource(MR.strings.profile_thousand_short),
|
||||
millionLabel = stringResource(MR.strings.profile_million_short),
|
||||
),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
|
@ -31,9 +31,14 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedIt
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.saveditems.SavedItemsViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailMviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailViewModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.di.utilsModule
|
||||
import org.koin.dsl.module
|
||||
|
||||
val commonUiModule = module {
|
||||
includes(
|
||||
utilsModule,
|
||||
)
|
||||
|
||||
single<NavigationCoordinator> {
|
||||
DefaultNavigationCoordinator()
|
||||
}
|
||||
@ -73,6 +78,7 @@ val commonUiModule = module {
|
||||
settingsRepository = get(),
|
||||
shareHelper = get(),
|
||||
hapticFeedback = get(),
|
||||
zombieModeHelper = get(),
|
||||
)
|
||||
}
|
||||
factory<CommunityInfoMviModel> { params ->
|
||||
|
@ -0,0 +1,103 @@
|
||||
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.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.BottomSheetHandle
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.getPrettyDuration
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class DurationBottomSheet(
|
||||
private val values: List<Duration> = listOf(
|
||||
1.seconds,
|
||||
2.seconds,
|
||||
2.5.seconds,
|
||||
3.seconds,
|
||||
3.5.seconds,
|
||||
4.seconds,
|
||||
4.5.seconds,
|
||||
5.seconds,
|
||||
),
|
||||
) : Screen {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigationCoordinator = remember { getNavigationCoordinator() }
|
||||
val notificationCenter = remember { getNotificationCenter() }
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
top = Spacing.s,
|
||||
start = Spacing.s,
|
||||
end = Spacing.s,
|
||||
bottom = Spacing.m,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
BottomSheetHandle()
|
||||
Text(
|
||||
modifier = Modifier.padding(start = Spacing.s, top = Spacing.s),
|
||||
text = stringResource(MR.strings.settings_zombie_mode_interval),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(Spacing.xxxs),
|
||||
) {
|
||||
for (value in values) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = Spacing.s,
|
||||
vertical = Spacing.s,
|
||||
).fillMaxWidth().onClick(
|
||||
rememberCallback {
|
||||
notificationCenter.getObserver(
|
||||
NotificationCenterContractKeys.ChangeZombieInterval
|
||||
)?.also {
|
||||
it.invoke(value)
|
||||
}
|
||||
navigationCoordinator.getBottomNavigator()?.hide()
|
||||
},
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = value.getPrettyDuration(
|
||||
secondsLabel = stringResource(MR.strings.post_second_short),
|
||||
minutesLabel = stringResource(MR.strings.post_minute_short),
|
||||
hoursLabel = stringResource(MR.strings.post_hour_short),
|
||||
),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ import com.github.diegoberaldin.raccoonforlemmy.resources.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
class SortBottomSheet(
|
||||
private val contract: String = NotificationCenterContractKeys.ChangeSortType,
|
||||
private val values: List<SortType> = listOf(
|
||||
SortType.Active,
|
||||
SortType.Hot,
|
||||
@ -70,6 +71,7 @@ class SortBottomSheet(
|
||||
SortBottomSheetMain(
|
||||
values = values,
|
||||
expandTop = expandTop,
|
||||
contract = contract,
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -77,6 +79,7 @@ class SortBottomSheet(
|
||||
}
|
||||
|
||||
internal class SortBottomSheetMain(
|
||||
private val contract: String,
|
||||
private val values: List<SortType>,
|
||||
private val expandTop: Boolean = false,
|
||||
) : Screen {
|
||||
@ -108,13 +111,9 @@ internal class SortBottomSheetMain(
|
||||
.onClick(
|
||||
rememberCallback {
|
||||
if (value == SortType.Top.Generic && expandTop) {
|
||||
navigator.push(
|
||||
SortBottomSheetTop()
|
||||
)
|
||||
navigator.push(SortBottomSheetTop(contract = contract))
|
||||
} else {
|
||||
notificationCenter.getAllObservers(
|
||||
NotificationCenterContractKeys.ChangeSortType
|
||||
).forEach {
|
||||
notificationCenter.getAllObservers(contract).forEach {
|
||||
it.invoke(value)
|
||||
}
|
||||
navigationCoordinator.getBottomNavigator()?.hide()
|
||||
@ -149,6 +148,7 @@ internal class SortBottomSheetMain(
|
||||
}
|
||||
|
||||
internal class SortBottomSheetTop(
|
||||
private val contract: String,
|
||||
private val values: List<SortType> = listOf(
|
||||
SortType.Top.PastHour,
|
||||
SortType.Top.Past6Hours,
|
||||
@ -201,11 +201,10 @@ internal class SortBottomSheetTop(
|
||||
.onClick(
|
||||
rememberCallback {
|
||||
notificationCenter.getAllObservers(
|
||||
NotificationCenterContractKeys.ChangeSortType
|
||||
)
|
||||
.forEach {
|
||||
it.invoke(value)
|
||||
}
|
||||
contract,
|
||||
).forEach {
|
||||
it.invoke(value)
|
||||
}
|
||||
navigationCoordinator.getBottomNavigator()?.hide()
|
||||
},
|
||||
),
|
||||
|
@ -2,11 +2,13 @@ package com.github.diegoberaldin.raccoonforlemmy.core.notifications
|
||||
|
||||
object NotificationCenterContractKeys {
|
||||
const val ChangeSortType = "changeSortType"
|
||||
const val ChangeCommentSortType = "changeCommentSortType"
|
||||
const val ChangeFeedType = "changeFeedType"
|
||||
const val ChangeInboxType = "changeInboxType"
|
||||
const val ChangeTheme = "changeTheme"
|
||||
const val ChangeFontSize = "changeFontSize"
|
||||
const val ChangeFontFamily = "changeFontFamily"
|
||||
const val ChangeZombieInterval = "changeZombieInterval"
|
||||
const val ChangeLanguage = "changeLanguage"
|
||||
const val ChangePostLayout = "changePostLayout"
|
||||
const val Logout = "logout"
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.persistence.data
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.JavaSerializable
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
data class SettingsModel(
|
||||
val id: Long? = null,
|
||||
@ -27,4 +29,5 @@ data class SettingsModel(
|
||||
val autoLoadImages: Boolean = true,
|
||||
val autoExpandComments: Boolean = true,
|
||||
val hideNavigationBarWhileScrolling: Boolean = true,
|
||||
val zombieModeInterval: Duration = 2.5.seconds,
|
||||
) : JavaSerializable
|
||||
|
@ -8,6 +8,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
private object KeyStoreKeys {
|
||||
const val UiTheme = "uiTheme"
|
||||
@ -33,6 +34,7 @@ private object KeyStoreKeys {
|
||||
const val UpvoteColor = "upvoteColor"
|
||||
const val DownVoteColor = "downVoteColor"
|
||||
const val HideNavigationBarWhileScrolling = "hideNavigationBarWhileScrolling"
|
||||
const val ZombieModeInterval = "zombieModeInterval"
|
||||
}
|
||||
|
||||
internal class DefaultSettingsRepository(
|
||||
@ -71,7 +73,9 @@ internal class DefaultSettingsRepository(
|
||||
upvoteColor = settings.upvoteColor?.toLong(),
|
||||
downvoteColor = settings.downvoteColor?.toLong(),
|
||||
hideNavigationBarWhileScrolling = if (settings.hideNavigationBarWhileScrolling) 1 else 0,
|
||||
)
|
||||
zombieModeInterval = settings.zombieModeInterval.inWholeMilliseconds,
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getSettings(accountId: Long?): SettingsModel =
|
||||
@ -104,6 +108,7 @@ internal class DefaultSettingsRepository(
|
||||
upvoteColor = if (!keyStore.containsKey(KeyStoreKeys.UpvoteColor)) null else keyStore[KeyStoreKeys.UpvoteColor, 0],
|
||||
downvoteColor = if (!keyStore.containsKey(KeyStoreKeys.DownVoteColor)) null else keyStore[KeyStoreKeys.DownVoteColor, 0],
|
||||
hideNavigationBarWhileScrolling = keyStore[KeyStoreKeys.HideNavigationBarWhileScrolling, true],
|
||||
zombieModeInterval = keyStore[KeyStoreKeys.ZombieModeInterval, 2500].milliseconds,
|
||||
)
|
||||
} else {
|
||||
val entity = db.settingsQueries.getBy(accountId).executeAsOneOrNull()
|
||||
@ -165,6 +170,10 @@ internal class DefaultSettingsRepository(
|
||||
KeyStoreKeys.HideNavigationBarWhileScrolling,
|
||||
settings.hideNavigationBarWhileScrolling
|
||||
)
|
||||
keyStore.save(
|
||||
KeyStoreKeys.ZombieModeInterval,
|
||||
settings.zombieModeInterval.inWholeMilliseconds,
|
||||
)
|
||||
} else {
|
||||
db.settingsQueries.update(
|
||||
theme = settings.theme?.toLong(),
|
||||
@ -191,6 +200,7 @@ internal class DefaultSettingsRepository(
|
||||
upvoteColor = settings.upvoteColor?.toLong(),
|
||||
downvoteColor = settings.downvoteColor?.toLong(),
|
||||
hideNavigationBarWhileScrolling = if (settings.hideNavigationBarWhileScrolling) 1L else 0L,
|
||||
zombieModeInterval = settings.zombieModeInterval.inWholeMilliseconds,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -225,4 +235,5 @@ private fun GetBy.toModel() = SettingsModel(
|
||||
upvoteColor = upvoteColor?.toInt(),
|
||||
downvoteColor = downvoteColor?.toInt(),
|
||||
hideNavigationBarWhileScrolling = hideNavigationBarWhileScrolling != 0L,
|
||||
zombieModeInterval = zombieModeInterval.milliseconds,
|
||||
)
|
||||
|
@ -23,6 +23,7 @@ CREATE TABLE SettingsEntity (
|
||||
upvoteColor INTEGER DEFAULT NULL,
|
||||
downvoteColor INTEGER DEFAULT NULL,
|
||||
hideNavigationBarWhileScrolling INTEGER NOT NULL DEFAULT 1,
|
||||
zombieModeInterval INTEGER NOT NULL DEFAULT 2500,
|
||||
account_id INTEGER,
|
||||
FOREIGN KEY (account_id) REFERENCES AccountEntity(id) ON DELETE CASCADE,
|
||||
UNIQUE(account_id)
|
||||
@ -53,6 +54,7 @@ INSERT OR IGNORE INTO SettingsEntity (
|
||||
upvoteColor,
|
||||
downvoteColor,
|
||||
hideNavigationBarWhileScrolling,
|
||||
zombieModeInterval,
|
||||
account_id
|
||||
) VALUES (
|
||||
?,
|
||||
@ -78,6 +80,7 @@ INSERT OR IGNORE INTO SettingsEntity (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
);
|
||||
|
||||
@ -105,7 +108,8 @@ SET theme = ?,
|
||||
fullHeightImages = ?,
|
||||
upvoteColor = ?,
|
||||
downvoteColor = ?,
|
||||
hideNavigationBarWhileScrolling = ?
|
||||
hideNavigationBarWhileScrolling = ?,
|
||||
zombieModeInterval = ?
|
||||
WHERE account_id = ?;
|
||||
|
||||
getBy:
|
||||
@ -133,6 +137,7 @@ SELECT
|
||||
fullHeightImages,
|
||||
upvoteColor,
|
||||
downvoteColor,
|
||||
hideNavigationBarWhileScrolling
|
||||
hideNavigationBarWhileScrolling,
|
||||
zombieModeInterval
|
||||
FROM SettingsEntity
|
||||
WHERE account_id = ?;
|
@ -1,2 +1,5 @@
|
||||
ALTER TABLE SettingsEntity
|
||||
ADD COLUMN hideNavigationBarWhileScrolling INTEGER NOT NULL DEFAULT 1;
|
||||
|
||||
ALTER TABLE SettingsEntity
|
||||
ADD COLUMN zombieModeInterval INTEGER NOT NULL DEFAULT 2500;
|
@ -15,7 +15,8 @@ import io.ktor.utils.io.core.readBytes
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.round
|
||||
import kotlin.time.Duration
|
||||
|
||||
@Composable
|
||||
fun String.toLanguageName() = when (this) {
|
||||
@ -43,10 +44,75 @@ fun Int.getPrettyNumber(
|
||||
millionLabel: String,
|
||||
thousandLabel: String,
|
||||
): String {
|
||||
val value = this
|
||||
return when {
|
||||
this > 1_000_000 -> (((this / 1_000_000.0) * 10).roundToInt() / 10.0).toString() + millionLabel
|
||||
this > 1_000 -> (((this / 1_000.0) * 10).roundToInt() / 10.0).toString() + thousandLabel
|
||||
else -> this.toString()
|
||||
value > 1_000_000 -> buildString {
|
||||
val rounded = round((value / 1_000_000.0) * 10) / 10
|
||||
if (rounded % 1 <= 0) {
|
||||
append(rounded.toInt())
|
||||
} else {
|
||||
append(rounded)
|
||||
}
|
||||
append(millionLabel)
|
||||
}
|
||||
|
||||
value > 1_000 -> buildString {
|
||||
val rounded = round((value / 1_000.0) * 10) / 10
|
||||
if (rounded % 1 <= 0) {
|
||||
append(rounded.toInt())
|
||||
} else {
|
||||
append(rounded)
|
||||
}
|
||||
append(thousandLabel)
|
||||
}
|
||||
|
||||
else -> buildString {
|
||||
append(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Duration.getPrettyDuration(
|
||||
secondsLabel: String,
|
||||
minutesLabel: String,
|
||||
hoursLabel: String,
|
||||
): String = when {
|
||||
inWholeHours > 0 -> buildString {
|
||||
append(inWholeHours)
|
||||
append(hoursLabel)
|
||||
val remainderMinutes = inWholeMinutes % 60
|
||||
val remainderSeconds = inWholeSeconds % 60
|
||||
if (remainderMinutes > 0 || remainderSeconds > 0) {
|
||||
append(" ")
|
||||
append(remainderMinutes)
|
||||
append(minutesLabel)
|
||||
}
|
||||
if (remainderSeconds > 0) {
|
||||
append(" ")
|
||||
append(remainderSeconds)
|
||||
append(secondsLabel)
|
||||
}
|
||||
}
|
||||
|
||||
inWholeMinutes > 0 -> buildString {
|
||||
append(inWholeMinutes)
|
||||
append(minutesLabel)
|
||||
val remainderSeconds = inWholeSeconds % 60
|
||||
if (remainderSeconds > 0) {
|
||||
append(" ")
|
||||
append(remainderSeconds)
|
||||
append(secondsLabel)
|
||||
}
|
||||
}
|
||||
|
||||
else -> buildString {
|
||||
val rounded = round((inWholeMilliseconds / 1000.0) * 10.0) / 10.0
|
||||
if (rounded % 1 <= 0) {
|
||||
append(rounded.toInt())
|
||||
} else {
|
||||
append(rounded)
|
||||
}
|
||||
append(secondsLabel)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,48 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.utils
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
interface ZombieModeHelper {
|
||||
|
||||
val index: Flow<Int>
|
||||
|
||||
fun start(
|
||||
initialValue: Int = 0,
|
||||
interval: Duration = 2.5.seconds,
|
||||
)
|
||||
|
||||
fun pause()
|
||||
}
|
||||
|
||||
internal class DefaultZombieModeHelper : ZombieModeHelper {
|
||||
private val scope = CoroutineScope(SupervisorJob())
|
||||
override val index = MutableStateFlow(-1)
|
||||
private var delayInterval: Duration = 2.5.seconds
|
||||
private var job: Job? = null
|
||||
|
||||
override fun start(initialValue: Int, interval: Duration) {
|
||||
index.value = initialValue
|
||||
delayInterval = interval
|
||||
job = scope.launch {
|
||||
while (isActive) {
|
||||
delay(delayInterval)
|
||||
index.update { (it + 1).coerceAtLeast(0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun pause() {
|
||||
job?.cancel()
|
||||
index.value = -1
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.github.diegoberaldin.raccoonforlemmy.core.utils.di
|
||||
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.DefaultZombieModeHelper
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.ZombieModeHelper
|
||||
import org.koin.dsl.module
|
||||
|
||||
val utilsModule = module {
|
||||
factory<ZombieModeHelper> {
|
||||
DefaultZombieModeHelper()
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ val homeTabModule = module {
|
||||
shareHelper = get(),
|
||||
notificationCenter = get(),
|
||||
hapticFeedback = get(),
|
||||
zombieModeHelper = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ interface PostListMviModel :
|
||||
data class MarkAsRead(val id: Int) : Intent
|
||||
data class Hide(val id: Int) : Intent
|
||||
data object ClearRead : Intent
|
||||
data class StartZombieMode(val index: Int) : Intent
|
||||
data object PauseZombieMode : Intent
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
@ -46,9 +48,11 @@ interface PostListMviModel :
|
||||
val fullHeightImages: Boolean = true,
|
||||
val separateUpAndDownVotes: Boolean = false,
|
||||
val autoLoadImages: Boolean = true,
|
||||
val zombieModeActive: Boolean = false,
|
||||
)
|
||||
|
||||
sealed interface Effect {
|
||||
data object BackToTop : Effect
|
||||
data class ZombieModeTick(val index: Int) : Effect
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ import androidx.compose.material.icons.filled.ArrowCircleDown
|
||||
import androidx.compose.material.icons.filled.ArrowCircleUp
|
||||
import androidx.compose.material.icons.filled.ClearAll
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.icons.filled.Sync
|
||||
import androidx.compose.material.icons.filled.SyncDisabled
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
@ -129,11 +131,17 @@ class PostListScreen : Screen {
|
||||
}.launchIn(this)
|
||||
}
|
||||
LaunchedEffect(model) {
|
||||
model.effects.onEach {
|
||||
when (it) {
|
||||
model.effects.onEach { effect ->
|
||||
when (effect) {
|
||||
PostListMviModel.Effect.BackToTop -> {
|
||||
lazyListState.scrollToItem(0)
|
||||
}
|
||||
|
||||
is PostListMviModel.Effect.ZombieModeTick -> {
|
||||
if (effect.index >= 0) {
|
||||
lazyListState.scrollToItem(effect.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.launchIn(this)
|
||||
}
|
||||
@ -210,6 +218,24 @@ class PostListScreen : Screen {
|
||||
FloatingActionButtonMenu(
|
||||
modifier = Modifier.padding(bottom = Dimensions.topBarHeight),
|
||||
items = buildList {
|
||||
if (uiState.zombieModeActive) {
|
||||
this += FloatingActionButtonMenuItem(
|
||||
icon = Icons.Default.SyncDisabled,
|
||||
text = stringResource(MR.strings.action_deactivate_zombie_mode),
|
||||
onSelected = rememberCallback(model) {
|
||||
model.reduce(PostListMviModel.Intent.PauseZombieMode)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
this += FloatingActionButtonMenuItem(
|
||||
icon = Icons.Default.Sync,
|
||||
text = stringResource(MR.strings.action_activate_zombie_mode),
|
||||
onSelected = rememberCallback(model) {
|
||||
val idx = lazyListState.firstVisibleItemIndex
|
||||
model.reduce(PostListMviModel.Intent.StartZombieMode(idx))
|
||||
},
|
||||
)
|
||||
}
|
||||
this += FloatingActionButtonMenuItem(
|
||||
icon = Icons.Default.ExpandLess,
|
||||
text = stringResource(MR.strings.action_back_to_top),
|
||||
@ -267,6 +293,7 @@ class PostListScreen : Screen {
|
||||
) {
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
userScrollEnabled = !uiState.zombieModeActive,
|
||||
) {
|
||||
if (uiState.posts.isEmpty() && uiState.loading) {
|
||||
items(5) {
|
||||
|
@ -8,6 +8,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationC
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.SettingsRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.HapticFeedback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.ShareHelper
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.ZombieModeHelper
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.ApiConfigurationRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType
|
||||
@ -36,6 +37,7 @@ class PostListViewModel(
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val notificationCenter: NotificationCenter,
|
||||
private val hapticFeedback: HapticFeedback,
|
||||
private val zombieModeHelper: ZombieModeHelper,
|
||||
) : PostListMviModel,
|
||||
MviModel<PostListMviModel.Intent, PostListMviModel.UiState, PostListMviModel.Effect> by mvi {
|
||||
|
||||
@ -108,6 +110,10 @@ class PostListViewModel(
|
||||
}
|
||||
}.launchIn(this)
|
||||
|
||||
zombieModeHelper.index.onEach { index ->
|
||||
mvi.emitEffect(PostListMviModel.Effect.ZombieModeTick(index))
|
||||
}.launchIn(this)
|
||||
|
||||
val auth = identityRepository.authToken.value.orEmpty()
|
||||
val user = siteRepository.getCurrentUser(auth)
|
||||
mvi.updateState { it.copy(currentUserId = user?.id ?: 0) }
|
||||
@ -184,7 +190,21 @@ class PostListViewModel(
|
||||
}
|
||||
|
||||
PostListMviModel.Intent.ClearRead -> clearRead()
|
||||
is PostListMviModel.Intent.Hide -> hide(post = uiState.value.posts.first { it.id == intent.id })
|
||||
is PostListMviModel.Intent.Hide -> {
|
||||
uiState.value.posts.firstOrNull { it.id == intent.id }?.also { post ->
|
||||
hide(post = post)
|
||||
}
|
||||
}
|
||||
|
||||
PostListMviModel.Intent.PauseZombieMode -> {
|
||||
mvi.updateState { it.copy(zombieModeActive = false) }
|
||||
zombieModeHelper.pause()
|
||||
}
|
||||
|
||||
is PostListMviModel.Intent.StartZombieMode -> {
|
||||
mvi.updateState { it.copy(zombieModeActive = true) }
|
||||
zombieModeHelper.start(intent.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,8 @@ import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.UiTheme
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType
|
||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
interface SettingsMviModel :
|
||||
MviModel<SettingsMviModel.Intent, SettingsMviModel.UiState, SettingsMviModel.Effect>,
|
||||
@ -39,6 +41,7 @@ interface SettingsMviModel :
|
||||
data class ChangeAutoExpandComments(val value: Boolean) : Intent
|
||||
data class ChangeFullHeightImages(val value: Boolean) : Intent
|
||||
data class ChangeHideNavigationBarWhileScrolling(val value: Boolean) : Intent
|
||||
data class ChangeZombieModeInterval(val value: Duration) : Intent
|
||||
}
|
||||
|
||||
data class UiState(
|
||||
@ -68,6 +71,7 @@ interface SettingsMviModel :
|
||||
val autoExpandComments: Boolean = false,
|
||||
val fullHeightImages: Boolean = false,
|
||||
val hideNavigationBarWhileScrolling: Boolean = true,
|
||||
val zombieModeInterval: Duration = 2.5.seconds,
|
||||
)
|
||||
|
||||
sealed interface Effect
|
||||
|
@ -52,6 +52,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getDrawerCoordi
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.getNavigationCoordinator
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.ColorBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.ColorPickerDialog
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.DurationBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.FontFamilyBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.FontScaleBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.LanguageBottomSheet
|
||||
@ -61,6 +62,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.SortBottomS
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.modals.ThemeBottomSheet
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.getPrettyDuration
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.onClick
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallback
|
||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.rememberCallbackArgs
|
||||
@ -85,6 +87,7 @@ import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration
|
||||
|
||||
class SettingsScreen : Screen {
|
||||
|
||||
@ -229,12 +232,25 @@ class SettingsScreen : Screen {
|
||||
)
|
||||
)
|
||||
}
|
||||
}, key, NotificationCenterContractKeys.ChangeSortType
|
||||
}, key, NotificationCenterContractKeys.ChangeCommentSortType
|
||||
)
|
||||
notificationCenter.addObserver(
|
||||
{
|
||||
infoDialogOpened = false
|
||||
}, key, NotificationCenterContractKeys.CloseDialog
|
||||
},
|
||||
key, NotificationCenterContractKeys.CloseDialog,
|
||||
)
|
||||
notificationCenter.addObserver(
|
||||
{
|
||||
(it as? Duration)?.also { value ->
|
||||
model.reduce(
|
||||
SettingsMviModel.Intent.ChangeZombieModeInterval(
|
||||
value,
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
key, NotificationCenterContractKeys.ChangeZombieInterval
|
||||
)
|
||||
}
|
||||
|
||||
@ -460,6 +476,7 @@ class SettingsScreen : Screen {
|
||||
onTap = rememberCallback {
|
||||
val sheet = SortBottomSheet(
|
||||
expandTop = true,
|
||||
contract = NotificationCenterContractKeys.ChangeSortType,
|
||||
)
|
||||
navigationCoordinator.getBottomNavigator()?.show(sheet)
|
||||
},
|
||||
@ -471,6 +488,7 @@ class SettingsScreen : Screen {
|
||||
value = uiState.defaultCommentSortType.toReadableName(),
|
||||
onTap = rememberCallback {
|
||||
val sheet = SortBottomSheet(
|
||||
contract = NotificationCenterContractKeys.ChangeCommentSortType,
|
||||
values = listOf(
|
||||
SortType.Hot,
|
||||
SortType.Top.Generic,
|
||||
@ -488,6 +506,20 @@ class SettingsScreen : Screen {
|
||||
title = stringResource(MR.strings.settings_section_behaviour),
|
||||
)
|
||||
|
||||
// zombie mode interval
|
||||
SettingsRow(
|
||||
title = stringResource(MR.strings.settings_zombie_mode_interval),
|
||||
value = uiState.zombieModeInterval.getPrettyDuration(
|
||||
secondsLabel = stringResource(MR.strings.post_second_short),
|
||||
minutesLabel = stringResource(MR.strings.post_minute_short),
|
||||
hoursLabel = stringResource(MR.strings.post_hour_short),
|
||||
),
|
||||
onTap = rememberCallback {
|
||||
val sheet = DurationBottomSheet()
|
||||
navigationCoordinator.getBottomNavigator()?.show(sheet)
|
||||
},
|
||||
)
|
||||
|
||||
// swipe actions
|
||||
SettingsSwitchRow(
|
||||
title = stringResource(MR.strings.settings_enable_swipe_actions),
|
||||
|
@ -28,6 +28,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration
|
||||
|
||||
class SettingsViewModel(
|
||||
private val mvi: DefaultMviModel<SettingsMviModel.Intent, SettingsMviModel.UiState, SettingsMviModel.Effect>,
|
||||
@ -195,6 +196,10 @@ class SettingsViewModel(
|
||||
is SettingsMviModel.Intent.ChangeHideNavigationBarWhileScrolling -> changeHideNavigationBarWhileScrolling(
|
||||
intent.value
|
||||
)
|
||||
|
||||
is SettingsMviModel.Intent.ChangeZombieModeInterval -> changeZombieModeInterval(
|
||||
intent.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,6 +446,16 @@ class SettingsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeZombieModeInterval(value: Duration) {
|
||||
mvi.updateState { it.copy(zombieModeInterval = value) }
|
||||
mvi.scope?.launch {
|
||||
val settings = settingsRepository.currentSettings.value.copy(
|
||||
zombieModeInterval = value
|
||||
)
|
||||
saveSettings(settings)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun saveSettings(settings: SettingsModel) {
|
||||
val accountId = accountRepository.getActive()?.id
|
||||
settingsRepository.updateSettings(settings, accountId)
|
||||
|
@ -1,217 +1,220 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="action_back_to_top">Back to top</string>
|
||||
<string name="action_chat">Send message</string>
|
||||
<string name="action_clear_read">Clear read</string>
|
||||
<string name="action_create_post">Create post</string>
|
||||
<string name="action_reply">Reply</string>
|
||||
<string name="button_close">Close</string>
|
||||
<string name="button_confirm">Confirm</string>
|
||||
<string name="button_load">Load</string>
|
||||
<string name="button_reset">Reset</string>
|
||||
<string name="button_retry">Retry</string>
|
||||
<string name="comment_action_delete">Delete</string>
|
||||
<string name="community_detail_block">Block</string>
|
||||
<string name="community_detail_block_instance">Block instance</string>
|
||||
<string name="community_detail_info">Community info</string>
|
||||
<string name="community_detail_instance_info">Instance details</string>
|
||||
<string name="community_info_comments">comments</string>
|
||||
<string name="community_info_daily_active_users">active users (day)</string>
|
||||
<string name="community_info_monthly_active_users">active users (month)</string>
|
||||
<string name="community_info_posts">posts</string>
|
||||
<string name="community_info_subscribers">subscribers</string>
|
||||
<string name="community_info_weekly_active_users">active users (week)</string>
|
||||
<string name="create_comment_body">Comment body</string>
|
||||
<string name="create_comment_title">New comment</string>
|
||||
<string name="create_post_body">Post body</string>
|
||||
<string name="create_post_name">Post title</string>
|
||||
<string name="create_post_nsfw">NSFW</string>
|
||||
<string name="create_post_tab_editor">Editor</string>
|
||||
<string name="create_post_tab_preview">Preview</string>
|
||||
<string name="create_post_title">New post</string>
|
||||
<string name="create_post_url">URL</string>
|
||||
<string name="create_report_placeholder">Report text (optional)</string>
|
||||
<string name="create_report_title_comment">Report comment</string>
|
||||
<string name="create_report_title_post">Report post</string>
|
||||
<string name="dialog_raw_content_text">Text</string>
|
||||
<string name="dialog_raw_content_title">Title</string>
|
||||
<string name="dialog_raw_content_url">URL</string>
|
||||
<string name="dialog_title_change_instance">Change instance</string>
|
||||
<string name="dialog_title_raw_content">Raw content</string>
|
||||
<string name="explore_result_type_all">All</string>
|
||||
<string name="explore_result_type_comments">Comments</string>
|
||||
<string name="explore_result_type_communities">Communities</string>
|
||||
<string name="explore_result_type_posts">Posts</string>
|
||||
<string name="explore_result_type_users">Users</string>
|
||||
<string name="explore_search_placeholder">Search</string>
|
||||
<string name="home_instance_via">via %1$s</string>
|
||||
<string name="home_listing_title">Feeds</string>
|
||||
<string name="home_listing_type_all">All</string>
|
||||
<string name="home_listing_type_local">Local</string>
|
||||
<string name="home_listing_type_subscribed">Subscribed</string>
|
||||
<string name="home_sort_title">Sort by</string>
|
||||
<string name="home_sort_type_active">Active</string>
|
||||
<string name="home_sort_type_controversial">Controversial</string>
|
||||
<string name="home_sort_type_hot">Hot</string>
|
||||
<string name="home_sort_type_most_comments">Most comments</string>
|
||||
<string name="home_sort_type_new">New</string>
|
||||
<string name="home_sort_type_new_comments">New comments</string>
|
||||
<string name="home_sort_type_old">Old</string>
|
||||
<string name="home_sort_type_scaled">Scaled</string>
|
||||
<string name="home_sort_type_top">Top</string>
|
||||
<string name="home_sort_type_top_12_hours">Top 12 hours</string>
|
||||
<string name="home_sort_type_top_12_hours_short">12h</string>
|
||||
<string name="home_sort_type_top_6_hours">Top 6 hours</string>
|
||||
<string name="home_sort_type_top_6_hours_short">6h</string>
|
||||
<string name="home_sort_type_top_day">Top day</string>
|
||||
<string name="home_sort_type_top_day_short">day</string>
|
||||
<string name="home_sort_type_top_hour">Top hour</string>
|
||||
<string name="home_sort_type_top_hour_short">1h</string>
|
||||
<string name="home_sort_type_top_month">Top month</string>
|
||||
<string name="home_sort_type_top_month_short">month</string>
|
||||
<string name="home_sort_type_top_week">Top week</string>
|
||||
<string name="home_sort_type_top_week_short">week</string>
|
||||
<string name="home_sort_type_top_year">Top year</string>
|
||||
<string name="home_sort_type_top_year_short">year</string>
|
||||
<string name="inbox_chat_message">Message</string>
|
||||
<string name="inbox_item_mention">mentioned you in</string>
|
||||
<string name="inbox_item_reply_comment">replied to your comment in</string>
|
||||
<string name="inbox_item_reply_post">replied to your post in</string>
|
||||
<string name="inbox_listing_type_all">All</string>
|
||||
<string name="inbox_listing_type_title">Inbox type</string>
|
||||
<string name="inbox_listing_type_unread">Unread</string>
|
||||
<string name="inbox_not_logged_message">You are currently not logged in.\nPlease add an account
|
||||
from the profile screen to see your inbox.
|
||||
</string>
|
||||
<string name="inbox_section_mentions">Mentions</string>
|
||||
<string name="inbox_section_messages">Messages</string>
|
||||
<string name="inbox_section_replies">Replies</string>
|
||||
<string name="instance_detail_communities">Communities</string>
|
||||
<string name="instance_detail_title">Instance: %1$s</string>
|
||||
<string name="lang">en</string>
|
||||
<string name="language_de" translatable="false">Deutsch</string>
|
||||
<string name="language_el" translatable="false">Ελληνικά</string>
|
||||
<string name="language_en" translatable="false">English</string>
|
||||
<string name="language_es" translatable="false">Español</string>
|
||||
<string name="language_fr" translatable="false">Français</string>
|
||||
<string name="language_it" translatable="false">Italiano</string>
|
||||
<string name="language_pt" translatable="false">Português</string>
|
||||
<string name="language_ro" translatable="false">Română</string>
|
||||
<string name="login_field_instance_name">Instance name</string>
|
||||
<string name="login_field_label_optional">(optional)</string>
|
||||
<string name="login_field_password">Password</string>
|
||||
<string name="login_field_token">TOTP 2FA token</string>
|
||||
<string name="login_field_user_name">Username (or email)</string>
|
||||
<string name="manage_accounts_button_add">Add account</string>
|
||||
<string name="manage_accounts_title">Manage accounts</string>
|
||||
<string name="manage_subscriptions_header_multicommunities">Multi-communities</string>
|
||||
<string name="manage_subscriptions_header_subscriptions">Subscriptions</string>
|
||||
<string name="message_empty_comments">It\'s too silent there.\nWould you like to be the one who
|
||||
writes the first comment?
|
||||
</string>
|
||||
<string name="message_empty_list">No items to display</string>
|
||||
<string name="message_error_loading_comments">An error has occurred loading comments.</string>
|
||||
<string name="message_generic_error">Generic error</string>
|
||||
<string name="message_image_loading_error">Image loading error</string>
|
||||
<string name="message_invalid_field">Invalid field</string>
|
||||
<string name="message_missing_field">Missing field</string>
|
||||
<string name="message_operation_successful">Operation completed succcessfully</string>
|
||||
<string name="multi_community_editor_communities">Communities</string>
|
||||
<string name="multi_community_editor_icon">Icon</string>
|
||||
<string name="multi_community_editor_name">Name</string>
|
||||
<string name="multi_community_editor_title">Multi-community editor</string>
|
||||
<string name="navigation_drawer_anonymous">Anonymous</string>
|
||||
<string name="navigation_drawer_title_bookmarks">Saved</string>
|
||||
<string name="navigation_drawer_title_subscriptions">Subscriptions</string>
|
||||
<string name="navigation_home">Posts</string>
|
||||
<string name="navigation_inbox">Inbox</string>
|
||||
<string name="navigation_profile">Profile</string>
|
||||
<string name="navigation_search">Explore</string>
|
||||
<string name="navigation_settings">Settings</string>
|
||||
<string name="post_action_edit">Edit</string>
|
||||
<string name="post_action_hide">Hide</string>
|
||||
<string name="post_action_report">Report…</string>
|
||||
<string name="post_action_see_raw">View raw…</string>
|
||||
<string name="post_action_share">Share…</string>
|
||||
<string name="post_detail_cross_posts">also posted to:</string>
|
||||
<string name="post_detail_load_more_comments">Load more comments…</string>
|
||||
<string name="post_hour_short">h</string>
|
||||
<string name="post_minute_short">m</string>
|
||||
<string name="post_second_short">s</string>
|
||||
<string name="profile_button_login">Login</string>
|
||||
<string name="profile_day_short">d</string>
|
||||
<string name="profile_million_short">m</string>
|
||||
<string name="profile_month_short">m</string>
|
||||
<string name="profile_not_logged_message">You are currently not logged in.\nPlease add an
|
||||
account to continue.
|
||||
</string>
|
||||
<string name="profile_section_comments">Comments</string>
|
||||
<string name="profile_section_posts">Posts</string>
|
||||
<string name="profile_thousand_short">k</string>
|
||||
<string name="profile_year_short">y</string>
|
||||
<string name="settings_about">About this app…</string>
|
||||
<string name="settings_about_app_version">App version</string>
|
||||
<string name="settings_about_changelog">View full changelog</string>
|
||||
<string name="settings_about_report_github">Report a bug (GitHub)</string>
|
||||
<string name="settings_about_report_email">Report a bug (email)</string>
|
||||
<string name="settings_about_view_github">View on GitHub</string>
|
||||
<string name="settings_about_view_lemmy">Lemmy community</string>
|
||||
<string name="settings_auto_expand_comments">Automatically expand comments</string>
|
||||
<string name="settings_auto_load_images">Automatically load images</string>
|
||||
<string name="settings_blur_nsfw">Blur NSFW images</string>
|
||||
<string name="settings_color_aquamarine">🐬 Distracted dolphin</string>
|
||||
<string name="settings_color_banana">🦔 Hilarious hedgehog</string>
|
||||
<string name="settings_color_blue">🐳 Witty whale</string>
|
||||
<string name="settings_color_custom">Custom</string>
|
||||
<string name="settings_color_dialog_alpha">A</string>
|
||||
<string name="settings_color_dialog_blue">B</string>
|
||||
<string name="settings_color_dialog_green">G</string>
|
||||
<string name="settings_color_dialog_red">R</string>
|
||||
<string name="settings_color_dialog_title">Pick a color</string>
|
||||
<string name="settings_color_gray">🦝 Ravenous raccoon</string>
|
||||
<string name="settings_color_green">🐸 Frolicsome frog</string>
|
||||
<string name="settings_color_orange">🦊 Fiery fox</string>
|
||||
<string name="settings_color_pink">🦄 Unique unicorn</string>
|
||||
<string name="settings_color_purple">🐙 Oceanic octopus</string>
|
||||
<string name="settings_color_red">🦀 Crunchy crab</string>
|
||||
<string name="settings_color_white">🐼 Bambling bear</string>
|
||||
<string name="settings_content_font_large">Large</string>
|
||||
<string name="settings_content_font_larger">Extra large</string>
|
||||
<string name="settings_content_font_largest">Double extra large</string>
|
||||
<string name="settings_content_font_normal">Normal</string>
|
||||
<string name="settings_content_font_scale">Content text size</string>
|
||||
<string name="settings_content_font_small">Small</string>
|
||||
<string name="settings_content_font_smaller">Extra small</string>
|
||||
<string name="settings_content_font_smallest">Double extra small</string>
|
||||
<string name="settings_custom_seed_color">Custom theme color</string>
|
||||
<string name="settings_default_comment_sort_type">Default comment sort type</string>
|
||||
<string name="settings_default_listing_type">Default feed type</string>
|
||||
<string name="settings_default_post_sort_type">Default post sort type</string>
|
||||
<string name="settings_downvote_color">Downvote color</string>
|
||||
<string name="settings_dynamic_colors">Use dynamic colors</string>
|
||||
<string name="settings_enable_crash_report">Enable crash reporting</string>
|
||||
<string name="settings_enable_swipe_actions">Enable swipe actions</string>
|
||||
<string name="settings_full_height_images">Full height images</string>
|
||||
<string name="settings_include_nsfw">Include NSFW contents</string>
|
||||
<string name="settings_language">Language</string>
|
||||
<string name="settings_navigation_bar_titles_visible">Show navigation bar titles</string>
|
||||
<string name="settings_open_url_external">Open URLs in external browser</string>
|
||||
<string name="settings_post_layout">Post layout</string>
|
||||
<string name="settings_post_layout_card">Card</string>
|
||||
<string name="settings_post_layout_compact">Compact</string>
|
||||
<string name="settings_post_layout_full">Full</string>
|
||||
<string name="settings_section_appearance">Look and feel</string>
|
||||
<string name="settings_section_behaviour">Behaviour</string>
|
||||
<string name="settings_section_debug">Debug</string>
|
||||
<string name="settings_section_feed">Posts and comments</string>
|
||||
<string name="settings_section_nsfw">NSFW</string>
|
||||
<string name="settings_separate_up_and_downvotes">Separate upvotes / downvotes</string>
|
||||
<string name="settings_theme_black">Pure black</string>
|
||||
<string name="settings_theme_dark">Dark</string>
|
||||
<string name="settings_theme_light">Light</string>
|
||||
<string name="settings_ui_font_family">UI font</string>
|
||||
<string name="settings_ui_font_scale">UI text size</string>
|
||||
<string name="settings_ui_theme">UI theme</string>
|
||||
<string name="settings_upvote_color">Upvote color</string>
|
||||
<string name="settings_hide_navigation_bar">Hide navigation bar when scrolling</string>
|
||||
<string name="action_back_to_top">Back to top</string>
|
||||
<string name="action_chat">Send message</string>
|
||||
<string name="action_clear_read">Clear read</string>
|
||||
<string name="action_create_post">Create post</string>
|
||||
<string name="action_reply">Reply</string>
|
||||
<string name="action_activate_zombie_mode">Activate zombie mode</string>
|
||||
<string name="action_deactivate_zombie_mode">Deactivate zombie mode</string>
|
||||
<string name="button_close">Close</string>
|
||||
<string name="button_confirm">Confirm</string>
|
||||
<string name="button_load">Load</string>
|
||||
<string name="button_reset">Reset</string>
|
||||
<string name="button_retry">Retry</string>
|
||||
<string name="comment_action_delete">Delete</string>
|
||||
<string name="community_detail_block">Block</string>
|
||||
<string name="community_detail_block_instance">Block instance</string>
|
||||
<string name="community_detail_info">Community info</string>
|
||||
<string name="community_detail_instance_info">Instance details</string>
|
||||
<string name="community_info_comments">comments</string>
|
||||
<string name="community_info_daily_active_users">active users (day)</string>
|
||||
<string name="community_info_monthly_active_users">active users (month)</string>
|
||||
<string name="community_info_posts">posts</string>
|
||||
<string name="community_info_subscribers">subscribers</string>
|
||||
<string name="community_info_weekly_active_users">active users (week)</string>
|
||||
<string name="create_comment_body">Comment body</string>
|
||||
<string name="create_comment_title">New comment</string>
|
||||
<string name="create_post_body">Post body</string>
|
||||
<string name="create_post_name">Post title</string>
|
||||
<string name="create_post_nsfw">NSFW</string>
|
||||
<string name="create_post_tab_editor">Editor</string>
|
||||
<string name="create_post_tab_preview">Preview</string>
|
||||
<string name="create_post_title">New post</string>
|
||||
<string name="create_post_url">URL</string>
|
||||
<string name="create_report_placeholder">Report text (optional)</string>
|
||||
<string name="create_report_title_comment">Report comment</string>
|
||||
<string name="create_report_title_post">Report post</string>
|
||||
<string name="dialog_raw_content_text">Text</string>
|
||||
<string name="dialog_raw_content_title">Title</string>
|
||||
<string name="dialog_raw_content_url">URL</string>
|
||||
<string name="dialog_title_change_instance">Change instance</string>
|
||||
<string name="dialog_title_raw_content">Raw content</string>
|
||||
<string name="explore_result_type_all">All</string>
|
||||
<string name="explore_result_type_comments">Comments</string>
|
||||
<string name="explore_result_type_communities">Communities</string>
|
||||
<string name="explore_result_type_posts">Posts</string>
|
||||
<string name="explore_result_type_users">Users</string>
|
||||
<string name="explore_search_placeholder">Search</string>
|
||||
<string name="home_instance_via">via %1$s</string>
|
||||
<string name="home_listing_title">Feeds</string>
|
||||
<string name="home_listing_type_all">All</string>
|
||||
<string name="home_listing_type_local">Local</string>
|
||||
<string name="home_listing_type_subscribed">Subscribed</string>
|
||||
<string name="home_sort_title">Sort by</string>
|
||||
<string name="home_sort_type_active">Active</string>
|
||||
<string name="home_sort_type_controversial">Controversial</string>
|
||||
<string name="home_sort_type_hot">Hot</string>
|
||||
<string name="home_sort_type_most_comments">Most comments</string>
|
||||
<string name="home_sort_type_new">New</string>
|
||||
<string name="home_sort_type_new_comments">New comments</string>
|
||||
<string name="home_sort_type_old">Old</string>
|
||||
<string name="home_sort_type_scaled">Scaled</string>
|
||||
<string name="home_sort_type_top">Top</string>
|
||||
<string name="home_sort_type_top_12_hours">Top 12 hours</string>
|
||||
<string name="home_sort_type_top_12_hours_short">12h</string>
|
||||
<string name="home_sort_type_top_6_hours">Top 6 hours</string>
|
||||
<string name="home_sort_type_top_6_hours_short">6h</string>
|
||||
<string name="home_sort_type_top_day">Top day</string>
|
||||
<string name="home_sort_type_top_day_short">day</string>
|
||||
<string name="home_sort_type_top_hour">Top hour</string>
|
||||
<string name="home_sort_type_top_hour_short">1h</string>
|
||||
<string name="home_sort_type_top_month">Top month</string>
|
||||
<string name="home_sort_type_top_month_short">month</string>
|
||||
<string name="home_sort_type_top_week">Top week</string>
|
||||
<string name="home_sort_type_top_week_short">week</string>
|
||||
<string name="home_sort_type_top_year">Top year</string>
|
||||
<string name="home_sort_type_top_year_short">year</string>
|
||||
<string name="inbox_chat_message">Message</string>
|
||||
<string name="inbox_item_mention">mentioned you in</string>
|
||||
<string name="inbox_item_reply_comment">replied to your comment in</string>
|
||||
<string name="inbox_item_reply_post">replied to your post in</string>
|
||||
<string name="inbox_listing_type_all">All</string>
|
||||
<string name="inbox_listing_type_title">Inbox type</string>
|
||||
<string name="inbox_listing_type_unread">Unread</string>
|
||||
<string name="inbox_not_logged_message">You are currently not logged in.\nPlease add an account
|
||||
from the profile screen to see your inbox.
|
||||
</string>
|
||||
<string name="inbox_section_mentions">Mentions</string>
|
||||
<string name="inbox_section_messages">Messages</string>
|
||||
<string name="inbox_section_replies">Replies</string>
|
||||
<string name="instance_detail_communities">Communities</string>
|
||||
<string name="instance_detail_title">Instance: %1$s</string>
|
||||
<string name="lang">en</string>
|
||||
<string name="language_de" translatable="false">Deutsch</string>
|
||||
<string name="language_el" translatable="false">Ελληνικά</string>
|
||||
<string name="language_en" translatable="false">English</string>
|
||||
<string name="language_es" translatable="false">Español</string>
|
||||
<string name="language_fr" translatable="false">Français</string>
|
||||
<string name="language_it" translatable="false">Italiano</string>
|
||||
<string name="language_pt" translatable="false">Português</string>
|
||||
<string name="language_ro" translatable="false">Română</string>
|
||||
<string name="login_field_instance_name">Instance name</string>
|
||||
<string name="login_field_label_optional">(optional)</string>
|
||||
<string name="login_field_password">Password</string>
|
||||
<string name="login_field_token">TOTP 2FA token</string>
|
||||
<string name="login_field_user_name">Username (or email)</string>
|
||||
<string name="manage_accounts_button_add">Add account</string>
|
||||
<string name="manage_accounts_title">Manage accounts</string>
|
||||
<string name="manage_subscriptions_header_multicommunities">Multi-communities</string>
|
||||
<string name="manage_subscriptions_header_subscriptions">Subscriptions</string>
|
||||
<string name="message_empty_comments">It\'s too silent there.\nWould you like to be the one who
|
||||
writes the first comment?
|
||||
</string>
|
||||
<string name="message_empty_list">No items to display</string>
|
||||
<string name="message_error_loading_comments">An error has occurred loading comments.</string>
|
||||
<string name="message_generic_error">Generic error</string>
|
||||
<string name="message_image_loading_error">Image loading error</string>
|
||||
<string name="message_invalid_field">Invalid field</string>
|
||||
<string name="message_missing_field">Missing field</string>
|
||||
<string name="message_operation_successful">Operation completed succcessfully</string>
|
||||
<string name="multi_community_editor_communities">Communities</string>
|
||||
<string name="multi_community_editor_icon">Icon</string>
|
||||
<string name="multi_community_editor_name">Name</string>
|
||||
<string name="multi_community_editor_title">Multi-community editor</string>
|
||||
<string name="navigation_drawer_anonymous">Anonymous</string>
|
||||
<string name="navigation_drawer_title_bookmarks">Saved</string>
|
||||
<string name="navigation_drawer_title_subscriptions">Subscriptions</string>
|
||||
<string name="navigation_home">Posts</string>
|
||||
<string name="navigation_inbox">Inbox</string>
|
||||
<string name="navigation_profile">Profile</string>
|
||||
<string name="navigation_search">Explore</string>
|
||||
<string name="navigation_settings">Settings</string>
|
||||
<string name="post_action_edit">Edit</string>
|
||||
<string name="post_action_hide">Hide</string>
|
||||
<string name="post_action_report">Report…</string>
|
||||
<string name="post_action_see_raw">View raw…</string>
|
||||
<string name="post_action_share">Share…</string>
|
||||
<string name="post_detail_cross_posts">also posted to:</string>
|
||||
<string name="post_detail_load_more_comments">Load more comments…</string>
|
||||
<string name="post_hour_short">h</string>
|
||||
<string name="post_minute_short">m</string>
|
||||
<string name="post_second_short">s</string>
|
||||
<string name="profile_button_login">Login</string>
|
||||
<string name="profile_day_short">d</string>
|
||||
<string name="profile_million_short">m</string>
|
||||
<string name="profile_month_short">m</string>
|
||||
<string name="profile_not_logged_message">You are currently not logged in.\nPlease add an
|
||||
account to continue.
|
||||
</string>
|
||||
<string name="profile_section_comments">Comments</string>
|
||||
<string name="profile_section_posts">Posts</string>
|
||||
<string name="profile_thousand_short">k</string>
|
||||
<string name="profile_year_short">y</string>
|
||||
<string name="settings_about">About this app…</string>
|
||||
<string name="settings_about_app_version">App version</string>
|
||||
<string name="settings_about_changelog">View full changelog</string>
|
||||
<string name="settings_about_report_github">Report a bug (GitHub)</string>
|
||||
<string name="settings_about_report_email">Report a bug (email)</string>
|
||||
<string name="settings_about_view_github">View on GitHub</string>
|
||||
<string name="settings_about_view_lemmy">Lemmy community</string>
|
||||
<string name="settings_auto_expand_comments">Automatically expand comments</string>
|
||||
<string name="settings_auto_load_images">Automatically load images</string>
|
||||
<string name="settings_blur_nsfw">Blur NSFW images</string>
|
||||
<string name="settings_color_aquamarine">🐬 Distracted dolphin</string>
|
||||
<string name="settings_color_banana">🦔 Hilarious hedgehog</string>
|
||||
<string name="settings_color_blue">🐳 Witty whale</string>
|
||||
<string name="settings_color_custom">Custom</string>
|
||||
<string name="settings_color_dialog_alpha">A</string>
|
||||
<string name="settings_color_dialog_blue">B</string>
|
||||
<string name="settings_color_dialog_green">G</string>
|
||||
<string name="settings_color_dialog_red">R</string>
|
||||
<string name="settings_color_dialog_title">Pick a color</string>
|
||||
<string name="settings_color_gray">🦝 Ravenous raccoon</string>
|
||||
<string name="settings_color_green">🐸 Frolicsome frog</string>
|
||||
<string name="settings_color_orange">🦊 Fiery fox</string>
|
||||
<string name="settings_color_pink">🦄 Unique unicorn</string>
|
||||
<string name="settings_color_purple">🐙 Oceanic octopus</string>
|
||||
<string name="settings_color_red">🦀 Crunchy crab</string>
|
||||
<string name="settings_color_white">🐼 Bambling bear</string>
|
||||
<string name="settings_content_font_large">Large</string>
|
||||
<string name="settings_content_font_larger">Extra large</string>
|
||||
<string name="settings_content_font_largest">Double extra large</string>
|
||||
<string name="settings_content_font_normal">Normal</string>
|
||||
<string name="settings_content_font_scale">Content text size</string>
|
||||
<string name="settings_content_font_small">Small</string>
|
||||
<string name="settings_content_font_smaller">Extra small</string>
|
||||
<string name="settings_content_font_smallest">Double extra small</string>
|
||||
<string name="settings_custom_seed_color">Custom theme color</string>
|
||||
<string name="settings_default_comment_sort_type">Default comment sort type</string>
|
||||
<string name="settings_default_listing_type">Default feed type</string>
|
||||
<string name="settings_default_post_sort_type">Default post sort type</string>
|
||||
<string name="settings_downvote_color">Downvote color</string>
|
||||
<string name="settings_dynamic_colors">Use dynamic colors</string>
|
||||
<string name="settings_enable_crash_report">Enable crash reporting</string>
|
||||
<string name="settings_enable_swipe_actions">Enable swipe actions</string>
|
||||
<string name="settings_full_height_images">Full height images</string>
|
||||
<string name="settings_include_nsfw">Include NSFW contents</string>
|
||||
<string name="settings_language">Language</string>
|
||||
<string name="settings_navigation_bar_titles_visible">Show navigation bar titles</string>
|
||||
<string name="settings_open_url_external">Open URLs in external browser</string>
|
||||
<string name="settings_post_layout">Post layout</string>
|
||||
<string name="settings_post_layout_card">Card</string>
|
||||
<string name="settings_post_layout_compact">Compact</string>
|
||||
<string name="settings_post_layout_full">Full</string>
|
||||
<string name="settings_section_appearance">Look and feel</string>
|
||||
<string name="settings_section_behaviour">Behaviour</string>
|
||||
<string name="settings_section_debug">Debug</string>
|
||||
<string name="settings_section_feed">Posts and comments</string>
|
||||
<string name="settings_section_nsfw">NSFW</string>
|
||||
<string name="settings_separate_up_and_downvotes">Separate upvotes / downvotes</string>
|
||||
<string name="settings_theme_black">Pure black</string>
|
||||
<string name="settings_theme_dark">Dark</string>
|
||||
<string name="settings_theme_light">Light</string>
|
||||
<string name="settings_ui_font_family">UI font</string>
|
||||
<string name="settings_ui_font_scale">UI text size</string>
|
||||
<string name="settings_ui_theme">UI theme</string>
|
||||
<string name="settings_upvote_color">Upvote color</string>
|
||||
<string name="settings_hide_navigation_bar">Hide navigation bar when scrolling</string>
|
||||
<string name="settings_zombie_mode_interval">Zombie mode interval duration</string>
|
||||
</resources>
|
@ -5,6 +5,8 @@
|
||||
<string name="action_clear_read">Gelesene löschen</string>
|
||||
<string name="action_create_post">Beitrag erstellen</string>
|
||||
<string name="action_reply">Antwort</string>
|
||||
<string name="action_activate_zombie_mode">Aktivieren den Zombie-Modus</string>
|
||||
<string name="action_deactivate_zombie_mode">Deaktivieren den Zombie-Modus</string>
|
||||
<string name="button_close">Schließen</string>
|
||||
<string name="button_confirm">Bestätigen</string>
|
||||
<string name="button_load">Belastung</string>
|
||||
@ -210,4 +212,5 @@
|
||||
<string name="settings_ui_theme">ui-Thema</string>
|
||||
<string name="settings_upvote_color">Farbe für Upvotes</string>
|
||||
<string name="settings_hide_navigation_bar">Navigationsleiste beim Scrollen ausblenden</string>
|
||||
<string name="settings_zombie_mode_interval">Dauer des Intervalls des Zombie-Modus</string>
|
||||
</resources>
|
@ -1,20 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="action_back_to_top">Πίσω στην κορυφή</string>
|
||||
<string name="action_chat">Αποστολή μηνύματος</string>
|
||||
<string name="action_clear_read">Εκκαθάριση αναγνωσμένων</string>
|
||||
<string name="action_create_post">Δημιουργία ανάρτησης</string>
|
||||
<string name="action_reply">Απάντηση</string>
|
||||
<string name="button_close">Κλείσιμο</string>
|
||||
<string name="button_confirm">Επιβεβαίωση</string>
|
||||
<string name="button_load">Φόρτωση</string>
|
||||
<string name="button_reset">Επαναφορά</string>
|
||||
<string name="action_chat">Αποστείλετε μήνυμα</string>
|
||||
<string name="action_clear_read">Εκκαθαρίστε αναγνωσμένα</string>
|
||||
<string name="action_create_post">Δημιουργήστε ανάρτηση</string>
|
||||
<string name="action_activate_zombie_mode">Ενεργοποιήστε λειτουργία ζόμπι</string>
|
||||
<string name="action_deactivate_zombie_mode">Απενεργοποιήστε λειτουργία ζόμπι</string>
|
||||
<string name="action_reply">Απάντηστε</string>
|
||||
<string name="button_close">Κλείσε</string>
|
||||
<string name="button_confirm">Επιβεβαίωσε</string>
|
||||
<string name="button_load">Φόρτωσε</string>
|
||||
<string name="button_reset">Επανάφερε</string>
|
||||
<string name="button_retry">Προσπάθησε ξανά</string>
|
||||
<string name="comment_action_delete">Διαγραφή</string>
|
||||
<string name="community_detail_block">Φραγή</string>
|
||||
<string name="community_detail_block_instance">Φραγή παράδειγματος</string>
|
||||
<string name="comment_action_delete">Διάγραψε</string>
|
||||
<string name="community_detail_block">Απόκλεισε</string>
|
||||
<string name="community_detail_block_instance">Απόκλεισε διακομιστή</string>
|
||||
<string name="community_detail_info">Πληροφορίες κοινότητας</string>
|
||||
<string name="community_detail_instance_info">Λεπτομέρειες παραδείγματος</string>
|
||||
<string name="community_detail_instance_info">Πληροφορίες διακομιστή</string>
|
||||
<string name="community_info_comments">σχόλια</string>
|
||||
<string name="community_info_daily_active_users">ενεργοί χρήστες (ημέρα)</string>
|
||||
<string name="community_info_monthly_active_users">ενεργοί χρήστες (μήνας)</string>
|
||||
@ -87,9 +89,9 @@
|
||||
<string name="inbox_section_messages">Μηνύματα</string>
|
||||
<string name="inbox_section_replies">Απαντήσεις</string>
|
||||
<string name="instance_detail_communities">Κοινότητες</string>
|
||||
<string name="instance_detail_title">Παράδειγμα: %1$s</string>
|
||||
<string name="instance_detail_title">Διακομιστής: %1$s</string>
|
||||
<string name="lang">el</string>
|
||||
<string name="login_field_instance_name">Όνομα παραδείγματος</string>
|
||||
<string name="login_field_instance_name">Όνομα διακομιστή</string>
|
||||
<string name="login_field_label_optional">(προαιρετικό)</string>
|
||||
<string name="login_field_password">Κωδικός πρόσβασης</string>
|
||||
<string name="login_field_token">Τοκεν 2FA TOTP</string>
|
||||
@ -212,4 +214,6 @@
|
||||
<string name="settings_upvote_color">Χρώμα ψήφου ανώτερου</string>
|
||||
<string name="settings_hide_navigation_bar">Απόκρυψη της γραμμής πλοήγησης κατά την κύλιση
|
||||
</string>
|
||||
<string name="settings_zombie_mode_interval">Διάρκεια του διαστήματος της λειτουργίας ζόμπι
|
||||
</string>
|
||||
</resources>
|
@ -5,6 +5,8 @@
|
||||
<string name="action_clear_read">Esconder leídos</string>
|
||||
<string name="action_create_post">Nueva publicación</string>
|
||||
<string name="action_reply">Responder</string>
|
||||
<string name="action_activate_zombie_mode">Activar modo zombie</string>
|
||||
<string name="action_deactivate_zombie_mode">Desactivar modo zombie</string>
|
||||
<string name="button_close">Cerrar</string>
|
||||
<string name="button_confirm">Confirmar</string>
|
||||
<string name="button_load">Cargar</string>
|
||||
@ -212,4 +214,5 @@
|
||||
<string name="settings_upvote_color">Color votos positivos</string>
|
||||
<string name="settings_hide_navigation_bar">Ocultar la barra de navegación al desplazarse
|
||||
</string>
|
||||
<string name="settings_zombie_mode_interval">Duración del intervalo modo zombie</string>
|
||||
</resources>
|
@ -5,6 +5,8 @@
|
||||
<string name="action_clear_read">Effacer les publications lues</string>
|
||||
<string name="action_create_post">Créer une publication</string>
|
||||
<string name="action_reply">Répondre</string>
|
||||
<string name="action_activate_zombie_mode">Activer le mode zombie</string>
|
||||
<string name="action_deactivate_zombie_mode">Désactiver le mode zombie</string>
|
||||
<string name="button_close">Fermer</string>
|
||||
<string name="button_confirm">Confirmer</string>
|
||||
<string name="button_load">Charger</string>
|
||||
@ -210,4 +212,5 @@
|
||||
<string name="settings_upvote_color">Couleur votes positifs</string>
|
||||
<string name="settings_hide_navigation_bar">Masquer la barre de navigation lors du défilement
|
||||
</string>
|
||||
<string name="settings_zombie_mode_interval">Durée de l\'intervalle du mode zombie</string>
|
||||
</resources>
|
@ -4,6 +4,8 @@
|
||||
<string name="action_chat">Invia messaggio</string>
|
||||
<string name="action_clear_read">Nascondi letti</string>
|
||||
<string name="action_create_post">Crea post</string>
|
||||
<string name="action_activate_zombie_mode">Attiva modalità zombie</string>
|
||||
<string name="action_deactivate_zombie_mode">Disattiva modalità zombie</string>
|
||||
<string name="action_reply">Rispondi</string>
|
||||
<string name="button_close">Chiudi</string>
|
||||
<string name="button_confirm">Conferma</string>
|
||||
@ -210,4 +212,5 @@
|
||||
<string name="settings_upvote_color">Colore voti positivi</string>
|
||||
<string name="settings_hide_navigation_bar">Nascondi barra di navigazione durante lo scroll
|
||||
</string>
|
||||
<string name="settings_zombie_mode_interval">Durata intevallo modalità zombie</string>
|
||||
</resources>
|
@ -5,6 +5,8 @@
|
||||
<string name="action_clear_read">Limpar lidas</string>
|
||||
<string name="action_create_post">Criar publicação</string>
|
||||
<string name="action_reply">Responder</string>
|
||||
<string name="action_activate_zombie_mode">Ativar modo zumbi</string>
|
||||
<string name="action_deactivate_zombie_mode">Desativar modo zumbi</string>
|
||||
<string name="button_close">Fechar</string>
|
||||
<string name="button_confirm">Confirmar</string>
|
||||
<string name="button_load">Carregar</string>
|
||||
@ -207,4 +209,5 @@
|
||||
<string name="settings_ui_theme">Tema da interface</string>
|
||||
<string name="settings_upvote_color">Cor votos positivos</string>
|
||||
<string name="settings_hide_navigation_bar">Ocultar a barra de navegação ao rolar</string>
|
||||
<string name="settings_zombie_mode_interval">Duração do intervalo do modo zumbi</string>
|
||||
</resources>
|
@ -5,6 +5,8 @@
|
||||
<string name="action_clear_read">Ascunde citite</string>
|
||||
<string name="action_create_post">Creează postare</string>
|
||||
<string name="action_reply">Răspunde</string>
|
||||
<string name="action_activate_zombie_mode">Activează modul zombie</string>
|
||||
<string name="action_deactivate_zombie_mode">Dezactiează modul zombie</string>
|
||||
<string name="button_close">Închide</string>
|
||||
<string name="button_confirm">Confirmă</string>
|
||||
<string name="button_load">Încărcă</string>
|
||||
@ -208,4 +210,5 @@
|
||||
<string name="settings_ui_theme">Tema UI</string>
|
||||
<string name="settings_upvote_color">Culoare voturilor pozitive</string>
|
||||
<string name="settings_hide_navigation_bar">Ascunde bara de navigare la derulare</string>
|
||||
<string name="settings_zombie_mode_interval">Durată intervalului al modului zombie</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user