mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-20 07:18:25 +00:00
Improve ui vm separation
This commit is contained in:
parent
b211c8bffb
commit
17f41d76b8
@ -15,6 +15,7 @@ import network.loki.messenger.R
|
|||||||
import network.loki.messenger.databinding.ActivityDisappearingMessagesBinding
|
import network.loki.messenger.databinding.ActivityDisappearingMessagesBinding
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.DisappearingMessages
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.DisappearingMessages
|
||||||
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.UiState
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase
|
import org.thoughtcrime.securesms.database.RecipientDatabase
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||||
import org.thoughtcrime.securesms.ui.AppTheme
|
import org.thoughtcrime.securesms.ui.AppTheme
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.disappearingmessages
|
package org.thoughtcrime.securesms.conversation.disappearingmessages
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@ -18,62 +17,20 @@ import kotlinx.coroutines.flow.stateIn
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import network.loki.messenger.BuildConfig
|
import network.loki.messenger.BuildConfig
|
||||||
import network.loki.messenger.R
|
|
||||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||||
import org.session.libsession.messaging.messages.ExpirationConfiguration
|
import org.session.libsession.messaging.messages.ExpirationConfiguration
|
||||||
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
|
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
|
||||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.utilities.Address
|
|
||||||
import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol
|
import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.ExpiryCallbacks
|
||||||
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.UiState
|
||||||
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.toUiState
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||||
import org.thoughtcrime.securesms.database.Storage
|
import org.thoughtcrime.securesms.database.Storage
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||||
import org.thoughtcrime.securesms.ui.GetString
|
|
||||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||||
import kotlin.time.Duration
|
|
||||||
import kotlin.time.Duration.Companion.days
|
|
||||||
import kotlin.time.Duration.Companion.hours
|
|
||||||
import kotlin.time.Duration.Companion.minutes
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Callbacks {
|
|
||||||
fun onSetClick(): Any?
|
|
||||||
fun setMode(mode: ExpiryMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
object NoOpCallbacks: Callbacks {
|
|
||||||
override fun onSetClick() {}
|
|
||||||
override fun setMode(mode: ExpiryMode) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DisappearingMessagesViewModel(
|
class DisappearingMessagesViewModel(
|
||||||
private val threadId: Long,
|
private val threadId: Long,
|
||||||
@ -85,7 +42,7 @@ class DisappearingMessagesViewModel(
|
|||||||
private val storage: Storage,
|
private val storage: Storage,
|
||||||
isNewConfigEnabled: Boolean,
|
isNewConfigEnabled: Boolean,
|
||||||
showDebugOptions: Boolean
|
showDebugOptions: Boolean
|
||||||
) : AndroidViewModel(application), Callbacks {
|
) : AndroidViewModel(application), ExpiryCallbacks {
|
||||||
|
|
||||||
private val _event = Channel<Event>()
|
private val _event = Channel<Event>()
|
||||||
val event = _event.receiveAsFlow()
|
val event = _event.receiveAsFlow()
|
||||||
@ -99,7 +56,7 @@ class DisappearingMessagesViewModel(
|
|||||||
val state = _state.asStateFlow()
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
val uiState = _state
|
val uiState = _state
|
||||||
.map(::UiState)
|
.map(State::toUiState)
|
||||||
.stateIn(viewModelScope, SharingStarted.Eagerly, UiState())
|
.stateIn(viewModelScope, SharingStarted.Eagerly, UiState())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -122,7 +79,7 @@ class DisappearingMessagesViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setMode(mode: ExpiryMode) = _state.update { it.copy(expiryMode = mode) }
|
override fun setValue(value: ExpiryMode) = _state.update { it.copy(expiryMode = value) }
|
||||||
|
|
||||||
override fun onSetClick() = viewModelScope.launch {
|
override fun onSetClick() = viewModelScope.launch {
|
||||||
val state = _state.value
|
val state = _state.value
|
||||||
@ -178,177 +135,3 @@ class DisappearingMessagesViewModel(
|
|||||||
) as T
|
) as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class UiState(
|
|
||||||
val cards: List<CardModel> = emptyList(),
|
|
||||||
val showGroupFooter: Boolean = false,
|
|
||||||
val showSetButton: Boolean = true
|
|
||||||
) {
|
|
||||||
constructor(state: State): this(
|
|
||||||
cards = listOfNotNull(
|
|
||||||
typeOptions(state)?.let { CardModel(GetString(R.string.activity_disappearing_messages_delete_type), it) },
|
|
||||||
timeOptions(state)?.let { CardModel(GetString(R.string.activity_disappearing_messages_timer), it) }
|
|
||||||
),
|
|
||||||
showGroupFooter = state.isGroup && state.isNewConfigEnabled,
|
|
||||||
showSetButton = state.isSelfAdmin
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
vararg cards: CardModel,
|
|
||||||
showGroupFooter: Boolean = false,
|
|
||||||
showSetButton: Boolean = true,
|
|
||||||
): this(
|
|
||||||
cards.asList(),
|
|
||||||
showGroupFooter,
|
|
||||||
showSetButton
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class CardModel(
|
|
||||||
val title: GetString,
|
|
||||||
val options: List<OptionModel>
|
|
||||||
) {
|
|
||||||
constructor(title: GetString, vararg options: OptionModel): this(title, options.asList())
|
|
||||||
constructor(@StringRes title: Int, vararg options: OptionModel): this(GetString(title), options.asList())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun offTypeOption(state: State) = typeOption(ExpiryType.NONE, state)
|
|
||||||
fun legacyTypeOption(state: State) = typeOption(ExpiryType.LEGACY, state)
|
|
||||||
fun afterReadTypeOption(state: State) = newTypeOption(ExpiryType.AFTER_READ, state)
|
|
||||||
fun afterSendTypeOption(state: State) = newTypeOption(ExpiryType.AFTER_SEND, state)
|
|
||||||
private fun newTypeOption(type: ExpiryType, state: State) = typeOption(type, state, state.run { isNewConfigEnabled && isSelfAdmin })
|
|
||||||
|
|
||||||
private fun typeOptions(state: State) = state.takeUnless { it.typeOptionsHidden }?.run {
|
|
||||||
listOfNotNull(
|
|
||||||
offTypeOption(state),
|
|
||||||
takeUnless { isNewConfigEnabled }?.let(::legacyTypeOption),
|
|
||||||
takeUnless { isGroup }?.let(::afterReadTypeOption),
|
|
||||||
afterSendTypeOption(state)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun typeOption(
|
|
||||||
type: ExpiryType,
|
|
||||||
state: State,
|
|
||||||
enabled: Boolean = state.isSelfAdmin,
|
|
||||||
) = OptionModel(
|
|
||||||
value = type.defaultMode(state.persistedMode),
|
|
||||||
title = GetString(type.title),
|
|
||||||
subtitle = type.subtitle?.let(::GetString),
|
|
||||||
contentDescription = GetString(type.contentDescription),
|
|
||||||
selected = state.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 debugOptions(state: State): List<OptionModel> =
|
|
||||||
debugModes(state.showDebugOptions, state.expiryType.takeIf { it == ExpiryType.AFTER_READ } ?: ExpiryType.AFTER_SEND)
|
|
||||||
.map { timeOption(it, state, subtitle = GetString("for testing purposes")) }
|
|
||||||
|
|
||||||
val defaultTimes = listOf(12.hours, 1.days, 7.days, 14.days)
|
|
||||||
|
|
||||||
val afterSendTimes = defaultTimes
|
|
||||||
val afterSendModes = afterSendTimes.map { it.inWholeSeconds }.map(ExpiryMode::AfterSend)
|
|
||||||
val legacyModes = afterSendTimes.map { it.inWholeSeconds }.map(ExpiryMode::Legacy)
|
|
||||||
fun afterSendOptions(state: State) = afterSendModes.map { timeOption(it, state) }
|
|
||||||
fun legacyOptions(state: State) = legacyModes.map { timeOption(it, state) }
|
|
||||||
|
|
||||||
val afterReadTimes = buildList {
|
|
||||||
add(5.minutes)
|
|
||||||
add(1.hours)
|
|
||||||
addAll(defaultTimes)
|
|
||||||
}
|
|
||||||
val afterReadModes = afterReadTimes.map { it.inWholeSeconds }.map(ExpiryMode::AfterRead)
|
|
||||||
fun afterReadOptions(state: State) = afterReadModes.map { timeOption(it, state) }
|
|
||||||
|
|
||||||
private fun timeOptions(
|
|
||||||
state: State
|
|
||||||
): List<OptionModel>? {
|
|
||||||
val type = state.takeUnless {
|
|
||||||
it.typeOptionsHidden
|
|
||||||
}?.expiryType ?: if (state.isNewConfigEnabled) ExpiryType.AFTER_SEND else ExpiryType.LEGACY
|
|
||||||
|
|
||||||
return when (type) {
|
|
||||||
ExpiryType.AFTER_READ -> afterReadOptions(state)
|
|
||||||
ExpiryType.AFTER_SEND -> afterSendOptions(state)
|
|
||||||
ExpiryType.LEGACY -> legacyOptions(state)
|
|
||||||
else -> null
|
|
||||||
}?.let {
|
|
||||||
buildList {
|
|
||||||
if (state.typeOptionsHidden) add(offTypeOption(state))
|
|
||||||
addAll(debugOptions(state))
|
|
||||||
addAll(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun timeOption(
|
|
||||||
mode: ExpiryMode,
|
|
||||||
state: State,
|
|
||||||
title: GetString = GetString(mode.duration),
|
|
||||||
subtitle: GetString? = null,
|
|
||||||
) = OptionModel(
|
|
||||||
value = mode,
|
|
||||||
title = title,
|
|
||||||
subtitle = subtitle,
|
|
||||||
contentDescription = title,
|
|
||||||
selected = state.expiryMode == mode,
|
|
||||||
enabled = state.isTimeOptionsEnabled
|
|
||||||
)
|
|
||||||
|
|
||||||
data class OptionModel(
|
|
||||||
val value: ExpiryMode,
|
|
||||||
val title: GetString,
|
|
||||||
val subtitle: GetString? = null,
|
|
||||||
val contentDescription: GetString = title,
|
|
||||||
val selected: Boolean = false,
|
|
||||||
val enabled: Boolean = true,
|
|
||||||
)
|
|
||||||
|
|
||||||
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,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
|
||||||
|
)
|
@ -4,11 +4,7 @@ import androidx.compose.foundation.layout.Arrangement
|
|||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
@ -20,32 +16,25 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.Callbacks
|
import org.thoughtcrime.securesms.ui.Callbacks
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.CardModel
|
import org.thoughtcrime.securesms.ui.NoOpCallbacks
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
|
import org.thoughtcrime.securesms.ui.OptionsCard
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.NoOpCallbacks
|
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.State
|
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.UiState
|
|
||||||
import org.thoughtcrime.securesms.ui.CellNoMargin
|
|
||||||
import org.thoughtcrime.securesms.ui.Divider
|
|
||||||
import org.thoughtcrime.securesms.ui.OutlineButton
|
import org.thoughtcrime.securesms.ui.OutlineButton
|
||||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
import org.thoughtcrime.securesms.ui.RadioOption
|
||||||
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
|
|
||||||
import org.thoughtcrime.securesms.ui.TitledRadioButton
|
|
||||||
import org.thoughtcrime.securesms.ui.fadingEdges
|
import org.thoughtcrime.securesms.ui.fadingEdges
|
||||||
|
|
||||||
|
typealias ExpiryCallbacks = Callbacks<ExpiryMode>
|
||||||
|
typealias ExpiryRadioOption = RadioOption<ExpiryMode>
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DisappearingMessages(
|
fun DisappearingMessages(
|
||||||
state: UiState,
|
state: UiState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
callbacks: Callbacks = NoOpCallbacks
|
callbacks: ExpiryCallbacks = NoOpCallbacks
|
||||||
) {
|
) {
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
@ -81,18 +70,3 @@ fun DisappearingMessages(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun OptionsCard(card: CardModel, callbacks: Callbacks) {
|
|
||||||
Text(text = card.title())
|
|
||||||
CellNoMargin {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.heightIn(max = 5000.dp)
|
|
||||||
) {
|
|
||||||
itemsIndexed(card.options) { i, it ->
|
|
||||||
if (i != 0) Divider()
|
|
||||||
TitledRadioButton(it) { callbacks.setMode(it.value) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -11,7 +11,6 @@ import network.loki.messenger.R
|
|||||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.State
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.State
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.UiState
|
|
||||||
import org.thoughtcrime.securesms.ui.PreviewTheme
|
import org.thoughtcrime.securesms.ui.PreviewTheme
|
||||||
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
|
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ fun PreviewStates(
|
|||||||
) {
|
) {
|
||||||
PreviewTheme(R.style.Classic_Dark) {
|
PreviewTheme(R.style.Classic_Dark) {
|
||||||
DisappearingMessages(
|
DisappearingMessages(
|
||||||
UiState(state)
|
state.toUiState()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +55,7 @@ fun PreviewThemes(
|
|||||||
) {
|
) {
|
||||||
PreviewTheme(themeResId) {
|
PreviewTheme(themeResId) {
|
||||||
DisappearingMessages(
|
DisappearingMessages(
|
||||||
UiState(State(expiryMode = ExpiryMode.AfterSend(43200))),
|
State(expiryMode = ExpiryMode.AfterSend(43200)).toUiState(),
|
||||||
modifier = Modifier.size(400.dp, 600.dp)
|
modifier = Modifier.size(400.dp, 600.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
|
}
|
81
app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt
Normal file
81
app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt
Normal file
@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,16 +2,13 @@ package org.thoughtcrime.securesms.ui
|
|||||||
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.ScrollState
|
import androidx.compose.foundation.ScrollState
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@ -20,21 +17,20 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.pager.PagerState
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.ButtonColors
|
import androidx.compose.material.ButtonColors
|
||||||
import androidx.compose.material.ButtonDefaults
|
import androidx.compose.material.ButtonDefaults
|
||||||
import androidx.compose.material.Card
|
import androidx.compose.material.Card
|
||||||
import androidx.compose.material.Colors
|
import androidx.compose.material.Colors
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.IconButton
|
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.OutlinedButton
|
import androidx.compose.material.OutlinedButton
|
||||||
import androidx.compose.material.RadioButton
|
import androidx.compose.material.RadioButton
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.TextButton
|
import androidx.compose.material.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
@ -49,14 +45,46 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import com.google.accompanist.pager.HorizontalPagerIndicator
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.components.ProfilePictureView
|
import org.thoughtcrime.securesms.components.ProfilePictureView
|
||||||
import org.thoughtcrime.securesms.conversation.disappearingmessages.OptionModel
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
interface Callbacks<in T> {
|
||||||
|
fun onSetClick(): Any?
|
||||||
|
fun setValue(value: T)
|
||||||
|
}
|
||||||
|
|
||||||
|
object NoOpCallbacks: Callbacks<Any> {
|
||||||
|
override fun onSetClick() {}
|
||||||
|
override fun setValue(value: Any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RadioOption<T>(
|
||||||
|
val value: T,
|
||||||
|
val title: GetString,
|
||||||
|
val subtitle: GetString? = null,
|
||||||
|
val contentDescription: GetString = title,
|
||||||
|
val selected: Boolean = false,
|
||||||
|
val enabled: Boolean = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> OptionsCard(card: OptionsCard<T>, callbacks: Callbacks<T>) {
|
||||||
|
Text(text = card.title())
|
||||||
|
CellNoMargin {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.heightIn(max = 5000.dp)
|
||||||
|
) {
|
||||||
|
itemsIndexed(card.options) { i, it ->
|
||||||
|
if (i != 0) Divider()
|
||||||
|
TitledRadioButton(it) { callbacks.setValue(it.value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ItemButton(
|
fun ItemButton(
|
||||||
text: String,
|
text: String,
|
||||||
@ -115,7 +143,7 @@ fun CellWithPaddingAndMargin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TitledRadioButton(option: OptionModel, onClick: () -> Unit) {
|
fun <T> TitledRadioButton(option: RadioOption<T>, onClick: () -> Unit) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -210,63 +238,6 @@ fun Modifier.fadingEdges(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@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)) {
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Divider() {
|
fun Divider() {
|
||||||
androidx.compose.material.Divider(
|
androidx.compose.material.Divider(
|
||||||
|
@ -24,6 +24,9 @@ import org.session.libsession.utilities.TextSecurePreferences
|
|||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.utilities.guava.Optional
|
import org.session.libsignal.utilities.guava.Optional
|
||||||
import org.thoughtcrime.securesms.MainCoroutineRule
|
import org.thoughtcrime.securesms.MainCoroutineRule
|
||||||
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.ExpiryRadioOption
|
||||||
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.OptionsCard
|
||||||
|
import org.thoughtcrime.securesms.conversation.disappearingmessages.ui.UiState
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||||
import org.thoughtcrime.securesms.database.Storage
|
import org.thoughtcrime.securesms.database.Storage
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||||
@ -83,7 +86,7 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_timer,
|
R.string.activity_disappearing_messages_timer,
|
||||||
typeOption(ExpiryMode.NONE, selected = true),
|
typeOption(ExpiryMode.NONE, selected = true),
|
||||||
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
||||||
@ -122,7 +125,7 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_timer,
|
R.string.activity_disappearing_messages_timer,
|
||||||
typeOption(ExpiryMode.NONE, selected = true),
|
typeOption(ExpiryMode.NONE, selected = true),
|
||||||
timeOption(ExpiryType.LEGACY, 12.hours),
|
timeOption(ExpiryType.LEGACY, 12.hours),
|
||||||
@ -161,7 +164,7 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_timer,
|
R.string.activity_disappearing_messages_timer,
|
||||||
typeOption(ExpiryMode.NONE, selected = true),
|
typeOption(ExpiryMode.NONE, selected = true),
|
||||||
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
||||||
@ -201,7 +204,7 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_timer,
|
R.string.activity_disappearing_messages_timer,
|
||||||
typeOption(ExpiryMode.NONE, enabled = false, selected = true),
|
typeOption(ExpiryMode.NONE, enabled = false, selected = true),
|
||||||
timeOption(ExpiryType.AFTER_SEND, 12.hours, enabled = false),
|
timeOption(ExpiryType.AFTER_SEND, 12.hours, enabled = false),
|
||||||
@ -243,7 +246,7 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_delete_type,
|
R.string.activity_disappearing_messages_delete_type,
|
||||||
typeOption(ExpiryMode.NONE, selected = true),
|
typeOption(ExpiryMode.NONE, selected = true),
|
||||||
typeOption(12.hours, ExpiryType.AFTER_READ),
|
typeOption(12.hours, ExpiryType.AFTER_READ),
|
||||||
@ -282,14 +285,14 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_delete_type,
|
R.string.activity_disappearing_messages_delete_type,
|
||||||
typeOption(ExpiryMode.NONE),
|
typeOption(ExpiryMode.NONE),
|
||||||
typeOption(time, ExpiryType.AFTER_READ),
|
typeOption(time, ExpiryType.AFTER_READ),
|
||||||
typeOption(time, ExpiryType.AFTER_SEND, selected = true)
|
typeOption(time, ExpiryType.AFTER_SEND, selected = true)
|
||||||
),
|
),
|
||||||
CardModel(
|
OptionsCard(
|
||||||
GetString(R.string.activity_disappearing_messages_timer),
|
R.string.activity_disappearing_messages_timer,
|
||||||
timeOption(ExpiryType.AFTER_SEND, 12.hours, selected = true),
|
timeOption(ExpiryType.AFTER_SEND, 12.hours, selected = true),
|
||||||
timeOption(ExpiryType.AFTER_SEND, 1.days),
|
timeOption(ExpiryType.AFTER_SEND, 1.days),
|
||||||
timeOption(ExpiryType.AFTER_SEND, 7.days),
|
timeOption(ExpiryType.AFTER_SEND, 7.days),
|
||||||
@ -328,15 +331,15 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_delete_type,
|
R.string.activity_disappearing_messages_delete_type,
|
||||||
typeOption(ExpiryMode.NONE),
|
typeOption(ExpiryMode.NONE),
|
||||||
typeOption(time, ExpiryType.LEGACY, selected = true),
|
typeOption(time, ExpiryType.LEGACY, selected = true),
|
||||||
typeOption(12.hours, ExpiryType.AFTER_READ, enabled = false),
|
typeOption(12.hours, ExpiryType.AFTER_READ, enabled = false),
|
||||||
typeOption(1.days, ExpiryType.AFTER_SEND, enabled = false)
|
typeOption(1.days, ExpiryType.AFTER_SEND, enabled = false)
|
||||||
),
|
),
|
||||||
CardModel(
|
OptionsCard(
|
||||||
GetString(R.string.activity_disappearing_messages_timer),
|
R.string.activity_disappearing_messages_timer,
|
||||||
timeOption(ExpiryType.LEGACY, 12.hours, selected = true),
|
timeOption(ExpiryType.LEGACY, 12.hours, selected = true),
|
||||||
timeOption(ExpiryType.LEGACY, 1.days),
|
timeOption(ExpiryType.LEGACY, 1.days),
|
||||||
timeOption(ExpiryType.LEGACY, 7.days),
|
timeOption(ExpiryType.LEGACY, 7.days),
|
||||||
@ -375,14 +378,14 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_delete_type,
|
R.string.activity_disappearing_messages_delete_type,
|
||||||
typeOption(ExpiryMode.NONE),
|
typeOption(ExpiryMode.NONE),
|
||||||
typeOption(12.hours, ExpiryType.AFTER_READ),
|
typeOption(12.hours, ExpiryType.AFTER_READ),
|
||||||
typeOption(time, ExpiryType.AFTER_SEND, selected = true)
|
typeOption(time, ExpiryType.AFTER_SEND, selected = true)
|
||||||
),
|
),
|
||||||
CardModel(
|
OptionsCard(
|
||||||
GetString(R.string.activity_disappearing_messages_timer),
|
R.string.activity_disappearing_messages_timer,
|
||||||
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
||||||
timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true),
|
timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true),
|
||||||
timeOption(ExpiryType.AFTER_SEND, 7.days),
|
timeOption(ExpiryType.AFTER_SEND, 7.days),
|
||||||
@ -422,14 +425,14 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_delete_type,
|
R.string.activity_disappearing_messages_delete_type,
|
||||||
typeOption(ExpiryMode.NONE),
|
typeOption(ExpiryMode.NONE),
|
||||||
typeOption(1.days, ExpiryType.AFTER_READ, selected = true),
|
typeOption(1.days, ExpiryType.AFTER_READ, selected = true),
|
||||||
typeOption(time, ExpiryType.AFTER_SEND)
|
typeOption(time, ExpiryType.AFTER_SEND)
|
||||||
),
|
),
|
||||||
CardModel(
|
OptionsCard(
|
||||||
GetString(R.string.activity_disappearing_messages_timer),
|
R.string.activity_disappearing_messages_timer,
|
||||||
timeOption(ExpiryType.AFTER_READ, 5.minutes),
|
timeOption(ExpiryType.AFTER_READ, 5.minutes),
|
||||||
timeOption(ExpiryType.AFTER_READ, 1.hours),
|
timeOption(ExpiryType.AFTER_READ, 1.hours),
|
||||||
timeOption(ExpiryType.AFTER_READ, 12.hours),
|
timeOption(ExpiryType.AFTER_READ, 12.hours),
|
||||||
@ -452,7 +455,7 @@ class DisappearingMessagesViewModelTest {
|
|||||||
|
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
|
|
||||||
viewModel.setMode(afterSendMode(1.days))
|
viewModel.setValue(afterSendMode(1.days))
|
||||||
|
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
|
|
||||||
@ -475,14 +478,14 @@ class DisappearingMessagesViewModelTest {
|
|||||||
viewModel.uiState.value
|
viewModel.uiState.value
|
||||||
).isEqualTo(
|
).isEqualTo(
|
||||||
UiState(
|
UiState(
|
||||||
CardModel(
|
OptionsCard(
|
||||||
R.string.activity_disappearing_messages_delete_type,
|
R.string.activity_disappearing_messages_delete_type,
|
||||||
typeOption(ExpiryMode.NONE),
|
typeOption(ExpiryMode.NONE),
|
||||||
typeOption(12.hours, ExpiryType.AFTER_READ),
|
typeOption(12.hours, ExpiryType.AFTER_READ),
|
||||||
typeOption(1.days, ExpiryType.AFTER_SEND, selected = true)
|
typeOption(1.days, ExpiryType.AFTER_SEND, selected = true)
|
||||||
),
|
),
|
||||||
CardModel(
|
OptionsCard(
|
||||||
GetString(R.string.activity_disappearing_messages_timer),
|
R.string.activity_disappearing_messages_timer,
|
||||||
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
timeOption(ExpiryType.AFTER_SEND, 12.hours),
|
||||||
timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true),
|
timeOption(ExpiryType.AFTER_SEND, 1.days, selected = true),
|
||||||
timeOption(ExpiryType.AFTER_SEND, 7.days),
|
timeOption(ExpiryType.AFTER_SEND, 7.days),
|
||||||
@ -497,7 +500,7 @@ class DisappearingMessagesViewModelTest {
|
|||||||
time: Duration,
|
time: Duration,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
selected: Boolean = false
|
selected: Boolean = false
|
||||||
) = OptionModel(
|
) = ExpiryRadioOption(
|
||||||
value = type.mode(time),
|
value = type.mode(time),
|
||||||
title = GetString(time),
|
title = GetString(time),
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
@ -564,7 +567,7 @@ fun typeOption(time: Duration, type: ExpiryType, selected: Boolean = false, enab
|
|||||||
typeOption(type.mode(time), selected, enabled)
|
typeOption(type.mode(time), selected, enabled)
|
||||||
|
|
||||||
fun typeOption(mode: ExpiryMode, selected: Boolean = false, enabled: Boolean = true) =
|
fun typeOption(mode: ExpiryMode, selected: Boolean = false, enabled: Boolean = true) =
|
||||||
OptionModel(
|
ExpiryRadioOption(
|
||||||
mode,
|
mode,
|
||||||
GetString(mode.type.title),
|
GetString(mode.type.title),
|
||||||
mode.type.subtitle?.let(::GetString),
|
mode.type.subtitle?.let(::GetString),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user