Improve ui vm separation
parent
b211c8bffb
commit
17f41d76b8
@ -0,0 +1,84 @@
|
||||
package org.thoughtcrime.securesms.conversation.disappearingmessages
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.thoughtcrime.securesms.ui.GetString
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
|
||||
enum class Event {
|
||||
SUCCESS, FAIL
|
||||
}
|
||||
|
||||
data class State(
|
||||
val isGroup: Boolean = false,
|
||||
val isSelfAdmin: Boolean = true,
|
||||
val address: Address? = null,
|
||||
val isNoteToSelf: Boolean = false,
|
||||
val expiryMode: ExpiryMode? = null,
|
||||
val isNewConfigEnabled: Boolean = true,
|
||||
val persistedMode: ExpiryMode? = null,
|
||||
val showDebugOptions: Boolean = false
|
||||
) {
|
||||
val subtitle get() = when {
|
||||
isGroup || isNoteToSelf -> GetString(R.string.activity_disappearing_messages_subtitle_sent)
|
||||
else -> GetString(R.string.activity_disappearing_messages_subtitle)
|
||||
}
|
||||
|
||||
val typeOptionsHidden get() = isNoteToSelf || (isGroup && isNewConfigEnabled)
|
||||
|
||||
val duration get() = expiryMode?.duration
|
||||
val expiryType get() = expiryMode?.type
|
||||
|
||||
val isTimeOptionsEnabled = isNoteToSelf || isSelfAdmin && (isNewConfigEnabled || expiryType == ExpiryType.LEGACY)
|
||||
}
|
||||
|
||||
|
||||
enum class ExpiryType(
|
||||
private val createMode: (Long) -> ExpiryMode,
|
||||
@StringRes val title: Int,
|
||||
@StringRes val subtitle: Int? = null,
|
||||
@StringRes val contentDescription: Int = title,
|
||||
) {
|
||||
NONE(
|
||||
{ ExpiryMode.NONE },
|
||||
R.string.expiration_off,
|
||||
contentDescription = R.string.AccessibilityId_disable_disappearing_messages,
|
||||
),
|
||||
LEGACY(
|
||||
ExpiryMode::Legacy,
|
||||
R.string.expiration_type_disappear_legacy,
|
||||
contentDescription = R.string.expiration_type_disappear_legacy_description
|
||||
),
|
||||
AFTER_READ(
|
||||
ExpiryMode::AfterRead,
|
||||
R.string.expiration_type_disappear_after_read,
|
||||
R.string.expiration_type_disappear_after_read_description,
|
||||
R.string.expiration_type_disappear_after_read_description
|
||||
),
|
||||
AFTER_SEND(
|
||||
ExpiryMode::AfterSend,
|
||||
R.string.expiration_type_disappear_after_send,
|
||||
R.string.expiration_type_disappear_after_read_description,
|
||||
R.string.expiration_type_disappear_after_send_description
|
||||
);
|
||||
|
||||
fun mode(seconds: Long) = if (seconds != 0L) createMode(seconds) else ExpiryMode.NONE
|
||||
fun mode(duration: Duration) = mode(duration.inWholeSeconds)
|
||||
|
||||
fun defaultMode(persistedMode: ExpiryMode?) = when(this) {
|
||||
persistedMode?.type -> persistedMode
|
||||
AFTER_READ -> mode(12.hours)
|
||||
else -> mode(1.days)
|
||||
}
|
||||
}
|
||||
|
||||
val ExpiryMode.type: ExpiryType get() = when(this) {
|
||||
is ExpiryMode.Legacy -> ExpiryType.LEGACY
|
||||
is ExpiryMode.AfterSend -> ExpiryType.AFTER_SEND
|
||||
is ExpiryMode.AfterRead -> ExpiryType.AFTER_READ
|
||||
else -> ExpiryType.NONE
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package org.thoughtcrime.securesms.conversation.disappearingmessages.ui
|
||||
|
||||
import network.loki.messenger.R
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
|
||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.State
|
||||
import org.thoughtcrime.securesms.ui.GetString
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
fun State.toUiState() = UiState(
|
||||
cards = listOfNotNull(
|
||||
typeOptions()?.let { ExpiryOptionsCard(GetString(R.string.activity_disappearing_messages_delete_type), it) },
|
||||
timeOptions()?.let { ExpiryOptionsCard(GetString(R.string.activity_disappearing_messages_timer), it) }
|
||||
),
|
||||
showGroupFooter = isGroup && isNewConfigEnabled,
|
||||
showSetButton = isSelfAdmin
|
||||
)
|
||||
|
||||
private fun State.typeOptions(): List<ExpiryRadioOption>? = if (typeOptionsHidden) null else {
|
||||
buildList {
|
||||
add(offTypeOption())
|
||||
if (!isNewConfigEnabled) add(legacyTypeOption())
|
||||
if (!isGroup) add(afterReadTypeOption())
|
||||
add(afterSendTypeOption())
|
||||
}
|
||||
}
|
||||
|
||||
private fun State.timeOptions(): List<ExpiryRadioOption>? {
|
||||
val type = takeUnless {
|
||||
it.typeOptionsHidden
|
||||
}?.expiryType ?: if (isNewConfigEnabled) ExpiryType.AFTER_SEND else ExpiryType.LEGACY
|
||||
|
||||
return when (type) {
|
||||
ExpiryType.AFTER_READ -> afterReadModes
|
||||
ExpiryType.AFTER_SEND -> afterSendModes
|
||||
ExpiryType.LEGACY -> legacyModes
|
||||
else -> null
|
||||
}?.map { timeOption(it) }?.let {
|
||||
buildList {
|
||||
if (typeOptionsHidden) add(offTypeOption())
|
||||
addAll(debugOptions())
|
||||
addAll(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun State.offTypeOption() = typeOption(ExpiryType.NONE)
|
||||
private fun State.legacyTypeOption() = typeOption(ExpiryType.LEGACY)
|
||||
private fun State.afterReadTypeOption() = newTypeOption(ExpiryType.AFTER_READ)
|
||||
private fun State.afterSendTypeOption() = newTypeOption(ExpiryType.AFTER_SEND)
|
||||
private fun State.newTypeOption(type: ExpiryType) = typeOption(type, isNewConfigEnabled && isSelfAdmin)
|
||||
|
||||
private fun State.typeOption(
|
||||
type: ExpiryType,
|
||||
enabled: Boolean = isSelfAdmin,
|
||||
) = ExpiryRadioOption(
|
||||
value = type.defaultMode(persistedMode),
|
||||
title = GetString(type.title),
|
||||
subtitle = type.subtitle?.let(::GetString),
|
||||
contentDescription = GetString(type.contentDescription),
|
||||
selected = expiryType == type,
|
||||
enabled = enabled
|
||||
)
|
||||
|
||||
private fun debugTimes(isDebug: Boolean) = if (isDebug) listOf(10.seconds, 1.minutes) else emptyList()
|
||||
private fun debugModes(isDebug: Boolean, type: ExpiryType) =
|
||||
debugTimes(isDebug).map { type.mode(it.inWholeSeconds) }
|
||||
private fun State.debugOptions(): List<ExpiryRadioOption> =
|
||||
debugModes(showDebugOptions, expiryType.takeIf { it == ExpiryType.AFTER_READ } ?: ExpiryType.AFTER_SEND)
|
||||
.map { timeOption(it, subtitle = GetString("for testing purposes")) }
|
||||
|
||||
private val afterSendTimes = listOf(12.hours, 1.days, 7.days, 14.days)
|
||||
|
||||
private val afterSendModes = afterSendTimes.map { it.inWholeSeconds }.map(ExpiryMode::AfterSend)
|
||||
private val legacyModes = afterSendTimes.map { it.inWholeSeconds }.map(ExpiryMode::Legacy)
|
||||
|
||||
private val afterReadTimes = buildList {
|
||||
add(5.minutes)
|
||||
add(1.hours)
|
||||
addAll(afterSendTimes)
|
||||
}
|
||||
private val afterReadModes = afterReadTimes.map { it.inWholeSeconds }.map(ExpiryMode::AfterRead)
|
||||
|
||||
private fun State.timeOption(
|
||||
mode: ExpiryMode,
|
||||
title: GetString = GetString(mode.duration),
|
||||
subtitle: GetString? = null,
|
||||
) = ExpiryRadioOption(
|
||||
value = mode,
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
contentDescription = title,
|
||||
selected = expiryMode == mode,
|
||||
enabled = isTimeOptionsEnabled
|
||||
)
|
@ -0,0 +1,32 @@
|
||||
package org.thoughtcrime.securesms.conversation.disappearingmessages.ui
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.thoughtcrime.securesms.ui.GetString
|
||||
import org.thoughtcrime.securesms.ui.RadioOption
|
||||
|
||||
typealias ExpiryOptionsCard = OptionsCard<ExpiryMode>
|
||||
|
||||
data class UiState(
|
||||
val cards: List<ExpiryOptionsCard> = emptyList(),
|
||||
val showGroupFooter: Boolean = false,
|
||||
val showSetButton: Boolean = true
|
||||
) {
|
||||
constructor(
|
||||
vararg cards: ExpiryOptionsCard,
|
||||
showGroupFooter: Boolean = false,
|
||||
showSetButton: Boolean = true,
|
||||
): this(
|
||||
cards.asList(),
|
||||
showGroupFooter,
|
||||
showSetButton
|
||||
)
|
||||
}
|
||||
|
||||
data class OptionsCard<T>(
|
||||
val title: GetString,
|
||||
val options: List<RadioOption<T>>
|
||||
) {
|
||||
constructor(title: GetString, vararg options: RadioOption<T>): this(title, options.asList())
|
||||
constructor(@StringRes title: Int, vararg options: RadioOption<T>): this(GetString(title), options.asList())
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package org.thoughtcrime.securesms.ui
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) {
|
||||
if (pagerState.pageCount >= 2) Card(
|
||||
shape = RoundedCornerShape(50.dp),
|
||||
backgroundColor = Color.Black.copy(alpha = 0.4f),
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Box(modifier = Modifier.padding(8.dp)) {
|
||||
com.google.accompanist.pager.HorizontalPagerIndicator(
|
||||
pagerState = pagerState,
|
||||
pageCount = pagerState.pageCount,
|
||||
activeColor = Color.White,
|
||||
inactiveColor = classicDarkColors[5])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun RowScope.CarouselPrevButton(pagerState: PagerState) {
|
||||
CarouselButton(pagerState, pagerState.canScrollBackward, R.drawable.ic_prev, -1)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun RowScope.CarouselNextButton(pagerState: PagerState) {
|
||||
CarouselButton(pagerState, pagerState.canScrollForward, R.drawable.ic_next, 1)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun RowScope.CarouselButton(
|
||||
pagerState: PagerState,
|
||||
enabled: Boolean,
|
||||
@DrawableRes id: Int,
|
||||
delta: Int
|
||||
) {
|
||||
if (pagerState.pageCount <= 1) Spacer(modifier = Modifier.width(32.dp))
|
||||
else {
|
||||
val animationScope = rememberCoroutineScope()
|
||||
IconButton(
|
||||
modifier = Modifier
|
||||
.width(40.dp)
|
||||
.align(Alignment.CenterVertically),
|
||||
enabled = enabled,
|
||||
onClick = { animationScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + delta) } }) {
|
||||
Icon(
|
||||
painter = painterResource(id = id),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue