Merge pull request #2349 from element-hq/feature/bma/disableTyping
"Share presence" setting
This commit is contained in:
commit
3c36868f85
35 changed files with 197 additions and 58 deletions
2
changelog.d/2241.feature
Normal file
2
changelog.d/2241.feature
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Change "Read receipts" advanced setting used to send private Read Receipt to "Share presence" settings.
|
||||||
|
When disabled, private Read Receipts will be sent, and no typing notification will be sent. Also Read Receipts and typing notifications will not be rendered in the timeline.
|
||||||
|
|
@ -23,6 +23,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|
@ -37,6 +38,7 @@ import io.element.android.features.messages.impl.attachments.Attachment
|
||||||
import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError
|
import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError
|
||||||
import io.element.android.features.messages.impl.mentions.MentionSuggestion
|
import io.element.android.features.messages.impl.mentions.MentionSuggestion
|
||||||
import io.element.android.features.messages.impl.mentions.MentionSuggestionsProcessor
|
import io.element.android.features.messages.impl.mentions.MentionSuggestionsProcessor
|
||||||
|
import io.element.android.features.preferences.api.store.SessionPreferencesStore
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||||
|
|
@ -86,6 +88,7 @@ class MessageComposerPresenter @Inject constructor(
|
||||||
private val room: MatrixRoom,
|
private val room: MatrixRoom,
|
||||||
private val mediaPickerProvider: PickerProvider,
|
private val mediaPickerProvider: PickerProvider,
|
||||||
private val featureFlagService: FeatureFlagService,
|
private val featureFlagService: FeatureFlagService,
|
||||||
|
private val sessionPreferencesStore: SessionPreferencesStore,
|
||||||
private val localMediaFactory: LocalMediaFactory,
|
private val localMediaFactory: LocalMediaFactory,
|
||||||
private val mediaSender: MediaSender,
|
private val mediaSender: MediaSender,
|
||||||
private val snackbarDispatcher: SnackbarDispatcher,
|
private val snackbarDispatcher: SnackbarDispatcher,
|
||||||
|
|
@ -147,6 +150,8 @@ class MessageComposerPresenter @Inject constructor(
|
||||||
var showAttachmentSourcePicker: Boolean by remember { mutableStateOf(false) }
|
var showAttachmentSourcePicker: Boolean by remember { mutableStateOf(false) }
|
||||||
var showTextFormatting: Boolean by remember { mutableStateOf(false) }
|
var showTextFormatting: Boolean by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val sendTypingNotifications by sessionPreferencesStore.isSendTypingNotificationsEnabled().collectAsState(initial = true)
|
||||||
|
|
||||||
LaunchedEffect(messageComposerContext.composerMode) {
|
LaunchedEffect(messageComposerContext.composerMode) {
|
||||||
when (val modeValue = messageComposerContext.composerMode) {
|
when (val modeValue = messageComposerContext.composerMode) {
|
||||||
is MessageComposerMode.Edit ->
|
is MessageComposerMode.Edit ->
|
||||||
|
|
@ -212,7 +217,9 @@ class MessageComposerPresenter @Inject constructor(
|
||||||
// Declare that the user is not typing anymore when the composer is disposed
|
// Declare that the user is not typing anymore when the composer is disposed
|
||||||
onDispose {
|
onDispose {
|
||||||
appCoroutineScope.launch {
|
appCoroutineScope.launch {
|
||||||
room.typingNotice(false)
|
if (sendTypingNotifications) {
|
||||||
|
room.typingNotice(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -310,8 +317,10 @@ class MessageComposerPresenter @Inject constructor(
|
||||||
analyticsService.trackError(event.error)
|
analyticsService.trackError(event.error)
|
||||||
}
|
}
|
||||||
is MessageComposerEvents.TypingNotice -> {
|
is MessageComposerEvents.TypingNotice -> {
|
||||||
localCoroutineScope.launch {
|
if (sendTypingNotifications) {
|
||||||
room.typingNotice(event.isTyping)
|
localCoroutineScope.launch {
|
||||||
|
room.typingNotice(event.isTyping)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is MessageComposerEvents.SuggestionReceived -> {
|
is MessageComposerEvents.SuggestionReceived -> {
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ class TimelinePresenter @AssistedInject constructor(
|
||||||
val keyBackupState by encryptionService.backupStateStateFlow.collectAsState()
|
val keyBackupState by encryptionService.backupStateStateFlow.collectAsState()
|
||||||
|
|
||||||
val isSendPublicReadReceiptsEnabled by sessionPreferencesStore.isSendPublicReadReceiptsEnabled().collectAsState(initial = true)
|
val isSendPublicReadReceiptsEnabled by sessionPreferencesStore.isSendPublicReadReceiptsEnabled().collectAsState(initial = true)
|
||||||
|
val renderReadReceipts by sessionPreferencesStore.isRenderReadReceiptsEnabled().collectAsState(initial = true)
|
||||||
|
|
||||||
val sessionState by remember {
|
val sessionState by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
|
|
@ -183,6 +184,7 @@ class TimelinePresenter @AssistedInject constructor(
|
||||||
highlightedEventId = highlightedEventId.value,
|
highlightedEventId = highlightedEventId.value,
|
||||||
paginationState = paginationState,
|
paginationState = paginationState,
|
||||||
timelineItems = timelineItems,
|
timelineItems = timelineItems,
|
||||||
|
renderReadReceipts = renderReadReceipts,
|
||||||
newEventState = newItemState.value,
|
newEventState = newItemState.value,
|
||||||
sessionState = sessionState,
|
sessionState = sessionState,
|
||||||
eventSink = { handleEvents(it) }
|
eventSink = { handleEvents(it) }
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||||
data class TimelineState(
|
data class TimelineState(
|
||||||
val timelineItems: ImmutableList<TimelineItem>,
|
val timelineItems: ImmutableList<TimelineItem>,
|
||||||
val timelineRoomInfo: TimelineRoomInfo,
|
val timelineRoomInfo: TimelineRoomInfo,
|
||||||
|
val renderReadReceipts: Boolean,
|
||||||
val highlightedEventId: EventId?,
|
val highlightedEventId: EventId?,
|
||||||
val paginationState: MatrixTimeline.PaginationState,
|
val paginationState: MatrixTimeline.PaginationState,
|
||||||
val newEventState: NewEventState,
|
val newEventState: NewEventState,
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ import kotlin.random.Random
|
||||||
fun aTimelineState(timelineItems: ImmutableList<TimelineItem> = persistentListOf()) = TimelineState(
|
fun aTimelineState(timelineItems: ImmutableList<TimelineItem> = persistentListOf()) = TimelineState(
|
||||||
timelineItems = timelineItems,
|
timelineItems = timelineItems,
|
||||||
timelineRoomInfo = aTimelineRoomInfo(),
|
timelineRoomInfo = aTimelineRoomInfo(),
|
||||||
|
renderReadReceipts = false,
|
||||||
paginationState = MatrixTimeline.PaginationState(
|
paginationState = MatrixTimeline.PaginationState(
|
||||||
isBackPaginating = false,
|
isBackPaginating = false,
|
||||||
hasMoreToLoadBackwards = true,
|
hasMoreToLoadBackwards = true,
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,7 @@ fun TimelineView(
|
||||||
TimelineItemRow(
|
TimelineItemRow(
|
||||||
timelineItem = timelineItem,
|
timelineItem = timelineItem,
|
||||||
timelineRoomInfo = state.timelineRoomInfo,
|
timelineRoomInfo = state.timelineRoomInfo,
|
||||||
|
renderReadReceipts = state.renderReadReceipts,
|
||||||
isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true &&
|
isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true &&
|
||||||
state.timelineItems.first().identifier() == timelineItem.identifier(),
|
state.timelineItems.first().identifier() == timelineItem.identifier(),
|
||||||
highlightedItem = state.highlightedEventId?.value,
|
highlightedItem = state.highlightedEventId?.value,
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,13 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
internal fun ATimelineItemEventRow(
|
internal fun ATimelineItemEventRow(
|
||||||
event: TimelineItem.Event,
|
event: TimelineItem.Event,
|
||||||
timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(),
|
timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(),
|
||||||
|
renderReadReceipts: Boolean = false,
|
||||||
isLastOutgoingMessage: Boolean = false,
|
isLastOutgoingMessage: Boolean = false,
|
||||||
isHighlighted: Boolean = false,
|
isHighlighted: Boolean = false,
|
||||||
) = TimelineItemEventRow(
|
) = TimelineItemEventRow(
|
||||||
event = event,
|
event = event,
|
||||||
timelineRoomInfo = timelineRoomInfo,
|
timelineRoomInfo = timelineRoomInfo,
|
||||||
|
renderReadReceipts = renderReadReceipts,
|
||||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||||
isHighlighted = isHighlighted,
|
isHighlighted = isHighlighted,
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ import kotlin.math.roundToInt
|
||||||
fun TimelineItemEventRow(
|
fun TimelineItemEventRow(
|
||||||
event: TimelineItem.Event,
|
event: TimelineItem.Event,
|
||||||
timelineRoomInfo: TimelineRoomInfo,
|
timelineRoomInfo: TimelineRoomInfo,
|
||||||
|
renderReadReceipts: Boolean,
|
||||||
isLastOutgoingMessage: Boolean,
|
isLastOutgoingMessage: Boolean,
|
||||||
isHighlighted: Boolean,
|
isHighlighted: Boolean,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
|
|
@ -223,6 +224,7 @@ fun TimelineItemEventRow(
|
||||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||||
receipts = event.readReceiptState.receipts,
|
receipts = event.readReceiptState.receipts,
|
||||||
),
|
),
|
||||||
|
renderReadReceipts = renderReadReceipts,
|
||||||
onReadReceiptsClicked = { onReadReceiptClick(event) },
|
onReadReceiptsClicked = { onReadReceiptClick(event) },
|
||||||
modifier = Modifier.padding(top = 4.dp),
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ internal fun TimelineItemEventRowWithRRPreview(
|
||||||
timelineItemReactions = aTimelineItemReactions(count = 0),
|
timelineItemReactions = aTimelineItemReactions(count = 0),
|
||||||
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
||||||
),
|
),
|
||||||
|
renderReadReceipts = true,
|
||||||
isLastOutgoingMessage = false,
|
isLastOutgoingMessage = false,
|
||||||
)
|
)
|
||||||
// A message from current user
|
// A message from current user
|
||||||
|
|
@ -60,6 +61,7 @@ internal fun TimelineItemEventRowWithRRPreview(
|
||||||
timelineItemReactions = aTimelineItemReactions(count = 0),
|
timelineItemReactions = aTimelineItemReactions(count = 0),
|
||||||
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
||||||
),
|
),
|
||||||
|
renderReadReceipts = true,
|
||||||
isLastOutgoingMessage = false,
|
isLastOutgoingMessage = false,
|
||||||
)
|
)
|
||||||
// Another message from current user
|
// Another message from current user
|
||||||
|
|
@ -73,6 +75,7 @@ internal fun TimelineItemEventRowWithRRPreview(
|
||||||
timelineItemReactions = aTimelineItemReactions(count = 0),
|
timelineItemReactions = aTimelineItemReactions(count = 0),
|
||||||
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
readReceiptState = TimelineItemReadReceipts(state.receipts),
|
||||||
),
|
),
|
||||||
|
renderReadReceipts = true,
|
||||||
isLastOutgoingMessage = true,
|
isLastOutgoingMessage = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import io.element.android.libraries.matrix.api.core.UserId
|
||||||
fun TimelineItemGroupedEventsRow(
|
fun TimelineItemGroupedEventsRow(
|
||||||
timelineItem: TimelineItem.GroupedEvents,
|
timelineItem: TimelineItem.GroupedEvents,
|
||||||
timelineRoomInfo: TimelineRoomInfo,
|
timelineRoomInfo: TimelineRoomInfo,
|
||||||
|
renderReadReceipts: Boolean,
|
||||||
isLastOutgoingMessage: Boolean,
|
isLastOutgoingMessage: Boolean,
|
||||||
highlightedItem: String?,
|
highlightedItem: String?,
|
||||||
sessionState: SessionState,
|
sessionState: SessionState,
|
||||||
|
|
@ -70,6 +71,7 @@ fun TimelineItemGroupedEventsRow(
|
||||||
timelineItem = timelineItem,
|
timelineItem = timelineItem,
|
||||||
timelineRoomInfo = timelineRoomInfo,
|
timelineRoomInfo = timelineRoomInfo,
|
||||||
highlightedItem = highlightedItem,
|
highlightedItem = highlightedItem,
|
||||||
|
renderReadReceipts = renderReadReceipts,
|
||||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||||
sessionState = sessionState,
|
sessionState = sessionState,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
|
|
@ -93,6 +95,7 @@ private fun TimelineItemGroupedEventsRowContent(
|
||||||
timelineItem: TimelineItem.GroupedEvents,
|
timelineItem: TimelineItem.GroupedEvents,
|
||||||
timelineRoomInfo: TimelineRoomInfo,
|
timelineRoomInfo: TimelineRoomInfo,
|
||||||
highlightedItem: String?,
|
highlightedItem: String?,
|
||||||
|
renderReadReceipts: Boolean,
|
||||||
isLastOutgoingMessage: Boolean,
|
isLastOutgoingMessage: Boolean,
|
||||||
sessionState: SessionState,
|
sessionState: SessionState,
|
||||||
onClick: (TimelineItem.Event) -> Unit,
|
onClick: (TimelineItem.Event) -> Unit,
|
||||||
|
|
@ -124,6 +127,7 @@ private fun TimelineItemGroupedEventsRowContent(
|
||||||
TimelineItemRow(
|
TimelineItemRow(
|
||||||
timelineItem = subGroupEvent,
|
timelineItem = subGroupEvent,
|
||||||
timelineRoomInfo = timelineRoomInfo,
|
timelineRoomInfo = timelineRoomInfo,
|
||||||
|
renderReadReceipts = renderReadReceipts,
|
||||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||||
highlightedItem = highlightedItem,
|
highlightedItem = highlightedItem,
|
||||||
sessionState = sessionState,
|
sessionState = sessionState,
|
||||||
|
|
@ -141,13 +145,14 @@ private fun TimelineItemGroupedEventsRowContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (renderReadReceipts) {
|
||||||
TimelineItemReadReceiptView(
|
TimelineItemReadReceiptView(
|
||||||
state = ReadReceiptViewState(
|
state = ReadReceiptViewState(
|
||||||
sendState = null,
|
sendState = null,
|
||||||
isLastOutgoingMessage = false,
|
isLastOutgoingMessage = false,
|
||||||
receipts = timelineItem.aggregatedReadReceipts,
|
receipts = timelineItem.aggregatedReadReceipts,
|
||||||
),
|
),
|
||||||
|
renderReadReceipts = true,
|
||||||
onReadReceiptsClicked = onExpandGroupClick
|
onReadReceiptsClicked = onExpandGroupClick
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -163,6 +168,7 @@ internal fun TimelineItemGroupedEventsRowContentExpandedPreview() = ElementPrevi
|
||||||
timelineItem = aGroupedEvents(withReadReceipts = true),
|
timelineItem = aGroupedEvents(withReadReceipts = true),
|
||||||
timelineRoomInfo = aTimelineRoomInfo(),
|
timelineRoomInfo = aTimelineRoomInfo(),
|
||||||
highlightedItem = null,
|
highlightedItem = null,
|
||||||
|
renderReadReceipts = true,
|
||||||
isLastOutgoingMessage = false,
|
isLastOutgoingMessage = false,
|
||||||
sessionState = aSessionState(),
|
sessionState = aSessionState(),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -187,6 +193,7 @@ internal fun TimelineItemGroupedEventsRowContentCollapsePreview() = ElementPrevi
|
||||||
timelineItem = aGroupedEvents(withReadReceipts = true),
|
timelineItem = aGroupedEvents(withReadReceipts = true),
|
||||||
timelineRoomInfo = aTimelineRoomInfo(),
|
timelineRoomInfo = aTimelineRoomInfo(),
|
||||||
highlightedItem = null,
|
highlightedItem = null,
|
||||||
|
renderReadReceipts = true,
|
||||||
isLastOutgoingMessage = false,
|
isLastOutgoingMessage = false,
|
||||||
sessionState = aSessionState(),
|
sessionState = aSessionState(),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.core.UserId
|
||||||
internal fun TimelineItemRow(
|
internal fun TimelineItemRow(
|
||||||
timelineItem: TimelineItem,
|
timelineItem: TimelineItem,
|
||||||
timelineRoomInfo: TimelineRoomInfo,
|
timelineRoomInfo: TimelineRoomInfo,
|
||||||
|
renderReadReceipts: Boolean,
|
||||||
isLastOutgoingMessage: Boolean,
|
isLastOutgoingMessage: Boolean,
|
||||||
highlightedItem: String?,
|
highlightedItem: String?,
|
||||||
sessionState: SessionState,
|
sessionState: SessionState,
|
||||||
|
|
@ -58,6 +59,7 @@ internal fun TimelineItemRow(
|
||||||
if (timelineItem.content is TimelineItemStateContent) {
|
if (timelineItem.content is TimelineItemStateContent) {
|
||||||
TimelineItemStateEventRow(
|
TimelineItemStateEventRow(
|
||||||
event = timelineItem,
|
event = timelineItem,
|
||||||
|
renderReadReceipts = renderReadReceipts,
|
||||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||||
isHighlighted = highlightedItem == timelineItem.identifier(),
|
isHighlighted = highlightedItem == timelineItem.identifier(),
|
||||||
onClick = { onClick(timelineItem) },
|
onClick = { onClick(timelineItem) },
|
||||||
|
|
@ -70,6 +72,7 @@ internal fun TimelineItemRow(
|
||||||
TimelineItemEventRow(
|
TimelineItemEventRow(
|
||||||
event = timelineItem,
|
event = timelineItem,
|
||||||
timelineRoomInfo = timelineRoomInfo,
|
timelineRoomInfo = timelineRoomInfo,
|
||||||
|
renderReadReceipts = renderReadReceipts,
|
||||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||||
isHighlighted = highlightedItem == timelineItem.identifier(),
|
isHighlighted = highlightedItem == timelineItem.identifier(),
|
||||||
onClick = { onClick(timelineItem) },
|
onClick = { onClick(timelineItem) },
|
||||||
|
|
@ -91,6 +94,7 @@ internal fun TimelineItemRow(
|
||||||
TimelineItemGroupedEventsRow(
|
TimelineItemGroupedEventsRow(
|
||||||
timelineItem = timelineItem,
|
timelineItem = timelineItem,
|
||||||
timelineRoomInfo = timelineRoomInfo,
|
timelineRoomInfo = timelineRoomInfo,
|
||||||
|
renderReadReceipts = renderReadReceipts,
|
||||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||||
highlightedItem = highlightedItem,
|
highlightedItem = highlightedItem,
|
||||||
sessionState = sessionState,
|
sessionState = sessionState,
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ import kotlinx.collections.immutable.toPersistentList
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemStateEventRow(
|
fun TimelineItemStateEventRow(
|
||||||
event: TimelineItem.Event,
|
event: TimelineItem.Event,
|
||||||
|
renderReadReceipts: Boolean,
|
||||||
isLastOutgoingMessage: Boolean,
|
isLastOutgoingMessage: Boolean,
|
||||||
isHighlighted: Boolean,
|
isHighlighted: Boolean,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
|
|
@ -90,6 +91,7 @@ fun TimelineItemStateEventRow(
|
||||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||||
receipts = event.readReceiptState.receipts,
|
receipts = event.readReceiptState.receipts,
|
||||||
),
|
),
|
||||||
|
renderReadReceipts = renderReadReceipts,
|
||||||
onReadReceiptsClicked = { onReadReceiptsClick(event) },
|
onReadReceiptsClicked = { onReadReceiptsClick(event) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -107,6 +109,7 @@ internal fun TimelineItemStateEventRowPreview() = ElementPreview {
|
||||||
receipts = listOf(aReadReceiptData(0)).toPersistentList(),
|
receipts = listOf(aReadReceiptData(0)).toPersistentList(),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
renderReadReceipts = true,
|
||||||
isLastOutgoingMessage = false,
|
isLastOutgoingMessage = false,
|
||||||
isHighlighted = false,
|
isHighlighted = false,
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
|
||||||
|
|
@ -58,20 +58,23 @@ import kotlinx.collections.immutable.ImmutableList
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemReadReceiptView(
|
fun TimelineItemReadReceiptView(
|
||||||
state: ReadReceiptViewState,
|
state: ReadReceiptViewState,
|
||||||
|
renderReadReceipts: Boolean,
|
||||||
onReadReceiptsClicked: () -> Unit,
|
onReadReceiptsClicked: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
if (state.receipts.isNotEmpty()) {
|
if (state.receipts.isNotEmpty()) {
|
||||||
ReadReceiptsRow(modifier = modifier) {
|
if (renderReadReceipts) {
|
||||||
ReadReceiptsAvatars(
|
ReadReceiptsRow(modifier = modifier) {
|
||||||
receipts = state.receipts,
|
ReadReceiptsAvatars(
|
||||||
modifier = Modifier
|
receipts = state.receipts,
|
||||||
.clip(RoundedCornerShape(4.dp))
|
modifier = Modifier
|
||||||
.clickable {
|
.clip(RoundedCornerShape(4.dp))
|
||||||
onReadReceiptsClicked()
|
.clickable {
|
||||||
}
|
onReadReceiptsClicked()
|
||||||
.padding(2.dp)
|
}
|
||||||
)
|
.padding(2.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
when (state.sendState) {
|
when (state.sendState) {
|
||||||
|
|
@ -206,6 +209,7 @@ internal fun TimelineItemReactionsViewPreview(
|
||||||
) = ElementPreview {
|
) = ElementPreview {
|
||||||
TimelineItemReadReceiptView(
|
TimelineItemReadReceiptView(
|
||||||
state = state,
|
state = state,
|
||||||
|
renderReadReceipts = true,
|
||||||
onReadReceiptsClicked = {},
|
onReadReceiptsClicked = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -675,6 +675,7 @@ class MessagesPresenterTest {
|
||||||
room = matrixRoom,
|
room = matrixRoom,
|
||||||
mediaPickerProvider = FakePickerProvider(),
|
mediaPickerProvider = FakePickerProvider(),
|
||||||
featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.NotificationSettings.key to true)),
|
featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.NotificationSettings.key to true)),
|
||||||
|
sessionPreferencesStore = InMemorySessionPreferencesStore(),
|
||||||
localMediaFactory = FakeLocalMediaFactory(mockMediaUrl),
|
localMediaFactory = FakeLocalMediaFactory(mockMediaUrl),
|
||||||
mediaSender = mediaSender,
|
mediaSender = mediaSender,
|
||||||
snackbarDispatcher = SnackbarDispatcher(),
|
snackbarDispatcher = SnackbarDispatcher(),
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,13 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
|
||||||
|
import io.element.android.features.preferences.api.store.SessionPreferencesStore
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
|
import io.element.android.libraries.featureflag.test.InMemorySessionPreferencesStore
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||||
|
|
@ -888,6 +890,24 @@ class MessageComposerPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - handle typing notice event when sending typing notice is disabled`() = runTest {
|
||||||
|
val room = FakeMatrixRoom()
|
||||||
|
val store = InMemorySessionPreferencesStore(
|
||||||
|
isSendTypingNotificationsEnabled = false
|
||||||
|
)
|
||||||
|
val presenter = createPresenter(room = room, sessionPreferencesStore = store, coroutineScope = this)
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
val initialState = awaitFirstItem()
|
||||||
|
assertThat(room.typingRecord).isEmpty()
|
||||||
|
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(true))
|
||||||
|
initialState.eventSink.invoke(MessageComposerEvents.TypingNotice(false))
|
||||||
|
assertThat(room.typingRecord).isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun ReceiveTurbine<MessageComposerState>.backToNormalMode(state: MessageComposerState, skipCount: Int = 0): MessageComposerState {
|
private suspend fun ReceiveTurbine<MessageComposerState>.backToNormalMode(state: MessageComposerState, skipCount: Int = 0): MessageComposerState {
|
||||||
state.eventSink.invoke(MessageComposerEvents.CloseSpecialMode)
|
state.eventSink.invoke(MessageComposerEvents.CloseSpecialMode)
|
||||||
skipItems(skipCount)
|
skipItems(skipCount)
|
||||||
|
|
@ -901,6 +921,7 @@ class MessageComposerPresenterTest {
|
||||||
room: MatrixRoom = FakeMatrixRoom(),
|
room: MatrixRoom = FakeMatrixRoom(),
|
||||||
pickerProvider: PickerProvider = this.pickerProvider,
|
pickerProvider: PickerProvider = this.pickerProvider,
|
||||||
featureFlagService: FeatureFlagService = this.featureFlagService,
|
featureFlagService: FeatureFlagService = this.featureFlagService,
|
||||||
|
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
|
||||||
mediaPreProcessor: MediaPreProcessor = this.mediaPreProcessor,
|
mediaPreProcessor: MediaPreProcessor = this.mediaPreProcessor,
|
||||||
snackbarDispatcher: SnackbarDispatcher = this.snackbarDispatcher,
|
snackbarDispatcher: SnackbarDispatcher = this.snackbarDispatcher,
|
||||||
permissionPresenter: PermissionsPresenter = FakePermissionsPresenter(),
|
permissionPresenter: PermissionsPresenter = FakePermissionsPresenter(),
|
||||||
|
|
@ -909,6 +930,7 @@ class MessageComposerPresenterTest {
|
||||||
room,
|
room,
|
||||||
pickerProvider,
|
pickerProvider,
|
||||||
featureFlagService,
|
featureFlagService,
|
||||||
|
sessionPreferencesStore,
|
||||||
localMediaFactory,
|
localMediaFactory,
|
||||||
MediaSender(mediaPreProcessor, room),
|
MediaSender(mediaPreProcessor, room),
|
||||||
snackbarDispatcher,
|
snackbarDispatcher,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import io.element.android.compound.theme.Theme
|
||||||
sealed interface AdvancedSettingsEvents {
|
sealed interface AdvancedSettingsEvents {
|
||||||
data class SetRichTextEditorEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
data class SetRichTextEditorEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
||||||
data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
||||||
data class SetSendPublicReadReceiptsEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
data class SetSharePresenceEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
||||||
data object ChangeTheme : AdvancedSettingsEvents
|
data object ChangeTheme : AdvancedSettingsEvents
|
||||||
data object CancelChangeTheme : AdvancedSettingsEvents
|
data object CancelChangeTheme : AdvancedSettingsEvents
|
||||||
data class SetTheme(val theme: Theme) : AdvancedSettingsEvents
|
data class SetTheme(val theme: Theme) : AdvancedSettingsEvents
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ class AdvancedSettingsPresenter @Inject constructor(
|
||||||
val isDeveloperModeEnabled by appPreferencesStore
|
val isDeveloperModeEnabled by appPreferencesStore
|
||||||
.isDeveloperModeEnabledFlow()
|
.isDeveloperModeEnabledFlow()
|
||||||
.collectAsState(initial = false)
|
.collectAsState(initial = false)
|
||||||
val isSendPublicReadReceiptsEnabled by sessionPreferencesStore
|
val isSharePresenceEnabled by sessionPreferencesStore
|
||||||
.isSendPublicReadReceiptsEnabled()
|
.isSharePresenceEnabled()
|
||||||
.collectAsState(initial = true)
|
.collectAsState(initial = true)
|
||||||
val theme by remember {
|
val theme by remember {
|
||||||
appPreferencesStore.getThemeFlow().mapToTheme()
|
appPreferencesStore.getThemeFlow().mapToTheme()
|
||||||
|
|
@ -60,8 +60,8 @@ class AdvancedSettingsPresenter @Inject constructor(
|
||||||
is AdvancedSettingsEvents.SetDeveloperModeEnabled -> localCoroutineScope.launch {
|
is AdvancedSettingsEvents.SetDeveloperModeEnabled -> localCoroutineScope.launch {
|
||||||
appPreferencesStore.setDeveloperModeEnabled(event.enabled)
|
appPreferencesStore.setDeveloperModeEnabled(event.enabled)
|
||||||
}
|
}
|
||||||
is AdvancedSettingsEvents.SetSendPublicReadReceiptsEnabled -> localCoroutineScope.launch {
|
is AdvancedSettingsEvents.SetSharePresenceEnabled -> localCoroutineScope.launch {
|
||||||
sessionPreferencesStore.setSendPublicReadReceipts(event.enabled)
|
sessionPreferencesStore.setSharePresence(event.enabled)
|
||||||
}
|
}
|
||||||
AdvancedSettingsEvents.CancelChangeTheme -> showChangeThemeDialog = false
|
AdvancedSettingsEvents.CancelChangeTheme -> showChangeThemeDialog = false
|
||||||
AdvancedSettingsEvents.ChangeTheme -> showChangeThemeDialog = true
|
AdvancedSettingsEvents.ChangeTheme -> showChangeThemeDialog = true
|
||||||
|
|
@ -75,7 +75,7 @@ class AdvancedSettingsPresenter @Inject constructor(
|
||||||
return AdvancedSettingsState(
|
return AdvancedSettingsState(
|
||||||
isRichTextEditorEnabled = isRichTextEditorEnabled,
|
isRichTextEditorEnabled = isRichTextEditorEnabled,
|
||||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||||
isSendPublicReadReceiptsEnabled = isSendPublicReadReceiptsEnabled,
|
isSharePresenceEnabled = isSharePresenceEnabled,
|
||||||
theme = theme,
|
theme = theme,
|
||||||
showChangeThemeDialog = showChangeThemeDialog,
|
showChangeThemeDialog = showChangeThemeDialog,
|
||||||
eventSink = { handleEvents(it) }
|
eventSink = { handleEvents(it) }
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import io.element.android.compound.theme.Theme
|
||||||
data class AdvancedSettingsState(
|
data class AdvancedSettingsState(
|
||||||
val isRichTextEditorEnabled: Boolean,
|
val isRichTextEditorEnabled: Boolean,
|
||||||
val isDeveloperModeEnabled: Boolean,
|
val isDeveloperModeEnabled: Boolean,
|
||||||
val isSendPublicReadReceiptsEnabled: Boolean,
|
val isSharePresenceEnabled: Boolean,
|
||||||
val theme: Theme,
|
val theme: Theme,
|
||||||
val showChangeThemeDialog: Boolean,
|
val showChangeThemeDialog: Boolean,
|
||||||
val eventSink: (AdvancedSettingsEvents) -> Unit
|
val eventSink: (AdvancedSettingsEvents) -> Unit
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ fun aAdvancedSettingsState(
|
||||||
) = AdvancedSettingsState(
|
) = AdvancedSettingsState(
|
||||||
isRichTextEditorEnabled = isRichTextEditorEnabled,
|
isRichTextEditorEnabled = isRichTextEditorEnabled,
|
||||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||||
isSendPublicReadReceiptsEnabled = isSendPublicReadReceiptsEnabled,
|
isSharePresenceEnabled = isSendPublicReadReceiptsEnabled,
|
||||||
theme = Theme.System,
|
theme = Theme.System,
|
||||||
showChangeThemeDialog = showChangeThemeDialog,
|
showChangeThemeDialog = showChangeThemeDialog,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
|
|
|
||||||
|
|
@ -83,15 +83,15 @@ fun AdvancedSettingsView(
|
||||||
)
|
)
|
||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(text = stringResource(id = R.string.screen_advanced_settings_send_read_receipts))
|
Text(text = stringResource(id = R.string.screen_advanced_settings_share_presence))
|
||||||
},
|
},
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
Text(text = stringResource(id = R.string.screen_advanced_settings_send_read_receipts_description))
|
Text(text = stringResource(id = R.string.screen_advanced_settings_share_presence_description))
|
||||||
},
|
},
|
||||||
trailingContent = ListItemContent.Switch(
|
trailingContent = ListItemContent.Switch(
|
||||||
checked = state.isSendPublicReadReceiptsEnabled,
|
checked = state.isSharePresenceEnabled,
|
||||||
),
|
),
|
||||||
onClick = { state.eventSink(AdvancedSettingsEvents.SetSendPublicReadReceiptsEnabled(!state.isSendPublicReadReceiptsEnabled)) }
|
onClick = { state.eventSink(AdvancedSettingsEvents.SetSharePresenceEnabled(!state.isSharePresenceEnabled)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
<string name="screen_advanced_settings_rich_text_editor_description">"Disable the rich text editor to type Markdown manually."</string>
|
<string name="screen_advanced_settings_rich_text_editor_description">"Disable the rich text editor to type Markdown manually."</string>
|
||||||
<string name="screen_advanced_settings_send_read_receipts">"Read receipts"</string>
|
<string name="screen_advanced_settings_send_read_receipts">"Read receipts"</string>
|
||||||
<string name="screen_advanced_settings_send_read_receipts_description">"If turned off, your read receipts won\'t be sent to anyone. You will still receive read receipts from other users."</string>
|
<string name="screen_advanced_settings_send_read_receipts_description">"If turned off, your read receipts won\'t be sent to anyone. You will still receive read receipts from other users."</string>
|
||||||
|
<string name="screen_advanced_settings_share_presence">"Share presence"</string>
|
||||||
|
<string name="screen_advanced_settings_share_presence_description">"If turned off, you won’t be able to send or receive read receipts or typing notifications"</string>
|
||||||
<string name="screen_advanced_settings_view_source_description">"Enable option to view message source in the timeline."</string>
|
<string name="screen_advanced_settings_view_source_description">"Enable option to view message source in the timeline."</string>
|
||||||
<string name="screen_edit_profile_display_name">"Display name"</string>
|
<string name="screen_edit_profile_display_name">"Display name"</string>
|
||||||
<string name="screen_edit_profile_display_name_placeholder">"Your display name"</string>
|
<string name="screen_edit_profile_display_name_placeholder">"Your display name"</string>
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class AdvancedSettingsPresenterTest {
|
||||||
assertThat(initialState.isDeveloperModeEnabled).isFalse()
|
assertThat(initialState.isDeveloperModeEnabled).isFalse()
|
||||||
assertThat(initialState.isRichTextEditorEnabled).isFalse()
|
assertThat(initialState.isRichTextEditorEnabled).isFalse()
|
||||||
assertThat(initialState.showChangeThemeDialog).isFalse()
|
assertThat(initialState.showChangeThemeDialog).isFalse()
|
||||||
assertThat(initialState.isSendPublicReadReceiptsEnabled).isTrue()
|
assertThat(initialState.isSharePresenceEnabled).isTrue()
|
||||||
assertThat(initialState.theme).isEqualTo(Theme.System)
|
assertThat(initialState.theme).isEqualTo(Theme.System)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -79,17 +79,17 @@ class AdvancedSettingsPresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - send public read receipts off on`() = runTest {
|
fun `present - share presence off on`() = runTest {
|
||||||
val presenter = createAdvancedSettingsPresenter()
|
val presenter = createAdvancedSettingsPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitLastSequentialItem()
|
val initialState = awaitLastSequentialItem()
|
||||||
assertThat(initialState.isSendPublicReadReceiptsEnabled).isTrue()
|
assertThat(initialState.isSharePresenceEnabled).isTrue()
|
||||||
initialState.eventSink.invoke(AdvancedSettingsEvents.SetSendPublicReadReceiptsEnabled(false))
|
initialState.eventSink.invoke(AdvancedSettingsEvents.SetSharePresenceEnabled(false))
|
||||||
assertThat(awaitItem().isSendPublicReadReceiptsEnabled).isFalse()
|
assertThat(awaitItem().isSharePresenceEnabled).isFalse()
|
||||||
initialState.eventSink.invoke(AdvancedSettingsEvents.SetSendPublicReadReceiptsEnabled(true))
|
initialState.eventSink.invoke(AdvancedSettingsEvents.SetSharePresenceEnabled(true))
|
||||||
assertThat(awaitItem().isSendPublicReadReceiptsEnabled).isTrue()
|
assertThat(awaitItem().isSharePresenceEnabled).isTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,20 @@ package io.element.android.features.preferences.api.store
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface SessionPreferencesStore {
|
interface SessionPreferencesStore {
|
||||||
|
suspend fun setSharePresence(enabled: Boolean)
|
||||||
|
fun isSharePresenceEnabled(): Flow<Boolean>
|
||||||
|
|
||||||
suspend fun setSendPublicReadReceipts(enabled: Boolean)
|
suspend fun setSendPublicReadReceipts(enabled: Boolean)
|
||||||
fun isSendPublicReadReceiptsEnabled(): Flow<Boolean>
|
fun isSendPublicReadReceiptsEnabled(): Flow<Boolean>
|
||||||
|
|
||||||
|
suspend fun setRenderReadReceipts(enabled: Boolean)
|
||||||
|
fun isRenderReadReceiptsEnabled(): Flow<Boolean>
|
||||||
|
|
||||||
|
suspend fun setSendTypingNotifications(enabled: Boolean)
|
||||||
|
fun isSendTypingNotificationsEnabled(): Flow<Boolean>
|
||||||
|
|
||||||
|
suspend fun setRenderTypingNotifications(enabled: Boolean)
|
||||||
|
fun isRenderTypingNotificationsEnabled(): Flow<Boolean>
|
||||||
|
|
||||||
suspend fun clear()
|
suspend fun clear()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class DefaultSessionPreferencesStore(
|
class DefaultSessionPreferencesStore(
|
||||||
|
|
@ -43,13 +45,41 @@ class DefaultSessionPreferencesStore(
|
||||||
return context.preferencesDataStoreFile("session_${hashedUserId}_preferences")
|
return context.preferencesDataStoreFile("session_${hashedUserId}_preferences")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val sharePresenceKey = booleanPreferencesKey("sharePresence")
|
||||||
private val sendPublicReadReceiptsKey = booleanPreferencesKey("sendPublicReadReceipts")
|
private val sendPublicReadReceiptsKey = booleanPreferencesKey("sendPublicReadReceipts")
|
||||||
|
private val renderReadReceiptsKey = booleanPreferencesKey("renderReadReceipts")
|
||||||
|
private val sendTypingNotificationsKey = booleanPreferencesKey("sendTypingNotifications")
|
||||||
|
private val renderTypingNotificationsKey = booleanPreferencesKey("renderTypingNotifications")
|
||||||
|
|
||||||
private val dataStoreFile = storeFile(context, sessionId)
|
private val dataStoreFile = storeFile(context, sessionId)
|
||||||
private val store = PreferenceDataStoreFactory.create(scope = sessionCoroutineScope) { dataStoreFile }
|
private val store = PreferenceDataStoreFactory.create(scope = sessionCoroutineScope) { dataStoreFile }
|
||||||
|
|
||||||
|
override suspend fun setSharePresence(enabled: Boolean) {
|
||||||
|
update(sharePresenceKey, enabled)
|
||||||
|
// Also update all the other settings
|
||||||
|
setSendPublicReadReceipts(enabled)
|
||||||
|
setRenderReadReceipts(enabled)
|
||||||
|
setSendTypingNotifications(enabled)
|
||||||
|
setRenderTypingNotifications(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSharePresenceEnabled(): Flow<Boolean> {
|
||||||
|
// Migration, if sendPublicReadReceiptsKey was false, consider that sharing presence is false.
|
||||||
|
return get(sharePresenceKey) { runBlocking { isSendPublicReadReceiptsEnabled().first() } }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun setSendPublicReadReceipts(enabled: Boolean) = update(sendPublicReadReceiptsKey, enabled)
|
override suspend fun setSendPublicReadReceipts(enabled: Boolean) = update(sendPublicReadReceiptsKey, enabled)
|
||||||
override fun isSendPublicReadReceiptsEnabled(): Flow<Boolean> = get(sendPublicReadReceiptsKey, true)
|
override fun isSendPublicReadReceiptsEnabled(): Flow<Boolean> = get(sendPublicReadReceiptsKey) { true }
|
||||||
|
|
||||||
|
override suspend fun setRenderReadReceipts(enabled: Boolean) = update(renderReadReceiptsKey, enabled)
|
||||||
|
override fun isRenderReadReceiptsEnabled(): Flow<Boolean> = get(renderReadReceiptsKey) { true }
|
||||||
|
|
||||||
|
override suspend fun setSendTypingNotifications(enabled: Boolean) = update(sendTypingNotificationsKey, enabled)
|
||||||
|
override fun isSendTypingNotificationsEnabled(): Flow<Boolean> = get(sendTypingNotificationsKey) { true }
|
||||||
|
|
||||||
|
override suspend fun setRenderTypingNotifications(enabled: Boolean) = update(renderTypingNotificationsKey, enabled)
|
||||||
|
override fun isRenderTypingNotificationsEnabled(): Flow<Boolean> = get(renderTypingNotificationsKey) { true }
|
||||||
|
|
||||||
override suspend fun clear() {
|
override suspend fun clear() {
|
||||||
dataStoreFile.safeDelete()
|
dataStoreFile.safeDelete()
|
||||||
|
|
@ -59,7 +89,7 @@ class DefaultSessionPreferencesStore(
|
||||||
store.edit { prefs -> prefs[key] = value }
|
store.edit { prefs -> prefs[key] = value }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> get(key: Preferences.Key<T>, default: T): Flow<T> {
|
private fun <T> get(key: Preferences.Key<T>, default: () -> T): Flow<T> {
|
||||||
return store.data.map { prefs -> prefs[key] ?: default }
|
return store.data.map { prefs -> prefs[key] ?: default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,19 +21,50 @@ import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
class InMemorySessionPreferencesStore(
|
class InMemorySessionPreferencesStore(
|
||||||
|
isSharePresenceEnabled: Boolean = true,
|
||||||
isSendPublicReadReceiptsEnabled: Boolean = true,
|
isSendPublicReadReceiptsEnabled: Boolean = true,
|
||||||
|
isRenderReadReceiptsEnabled: Boolean = true,
|
||||||
|
isSendTypingNotificationsEnabled: Boolean = true,
|
||||||
|
isRenderTypingNotificationsEnabled: Boolean = true,
|
||||||
) : SessionPreferencesStore {
|
) : SessionPreferencesStore {
|
||||||
|
private val isSharePresenceEnabled = MutableStateFlow(isSharePresenceEnabled)
|
||||||
private val isSendPublicReadReceiptsEnabled = MutableStateFlow(isSendPublicReadReceiptsEnabled)
|
private val isSendPublicReadReceiptsEnabled = MutableStateFlow(isSendPublicReadReceiptsEnabled)
|
||||||
|
private val isRenderReadReceiptsEnabled = MutableStateFlow(isRenderReadReceiptsEnabled)
|
||||||
|
private val isSendTypingNotificationsEnabled = MutableStateFlow(isSendTypingNotificationsEnabled)
|
||||||
|
private val isRenderTypingNotificationsEnabled = MutableStateFlow(isRenderTypingNotificationsEnabled)
|
||||||
var clearCallCount = 0
|
var clearCallCount = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
override suspend fun setSharePresence(enabled: Boolean) {
|
||||||
|
isSharePresenceEnabled.tryEmit(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSharePresenceEnabled(): Flow<Boolean> = isSharePresenceEnabled
|
||||||
|
|
||||||
override suspend fun setSendPublicReadReceipts(enabled: Boolean) {
|
override suspend fun setSendPublicReadReceipts(enabled: Boolean) {
|
||||||
isSendPublicReadReceiptsEnabled.tryEmit(enabled)
|
isSendPublicReadReceiptsEnabled.tryEmit(enabled)
|
||||||
}
|
}
|
||||||
override fun isSendPublicReadReceiptsEnabled(): Flow<Boolean> {
|
|
||||||
return isSendPublicReadReceiptsEnabled
|
override fun isSendPublicReadReceiptsEnabled(): Flow<Boolean> = isSendPublicReadReceiptsEnabled
|
||||||
|
|
||||||
|
override suspend fun setRenderReadReceipts(enabled: Boolean) {
|
||||||
|
isRenderReadReceiptsEnabled.tryEmit(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isRenderReadReceiptsEnabled(): Flow<Boolean> = isRenderReadReceiptsEnabled
|
||||||
|
|
||||||
|
override suspend fun setSendTypingNotifications(enabled: Boolean) {
|
||||||
|
isSendTypingNotificationsEnabled.tryEmit(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSendTypingNotificationsEnabled(): Flow<Boolean> = isSendTypingNotificationsEnabled
|
||||||
|
|
||||||
|
override suspend fun setRenderTypingNotifications(enabled: Boolean) {
|
||||||
|
isRenderTypingNotificationsEnabled.tryEmit(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isRenderTypingNotificationsEnabled(): Flow<Boolean> = isRenderTypingNotificationsEnabled
|
||||||
|
|
||||||
override suspend fun clear() {
|
override suspend fun clear() {
|
||||||
clearCallCount++
|
clearCallCount++
|
||||||
isSendPublicReadReceiptsEnabled.tryEmit(true)
|
isSendPublicReadReceiptsEnabled.tryEmit(true)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:da3f8614862ddacfd9b54afcd3bf091bec335e1489d7cbffc826059a89ae5478
|
oid sha256:41d892f8a97bb9d89d6a6128c7acae4d5c509dadee0e1758c7fec9d76bc4216d
|
||||||
size 58453
|
size 56386
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:1fd599f2e93432d5d5555f086b60059e84452a0da3c2a69d932042b7ee4d3bac
|
oid sha256:478e8c2d3dd29baf88f2641813760c623631d53446f31bca2f1372c05d4ca914
|
||||||
size 57922
|
size 55856
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9e9641228e0482c8e5500bb999b180e79cbfcb6a13e121f3be68aae5c58a6839
|
oid sha256:6e174f5f0b15343f5f02cc716cb371aa89d87e298104ac8e5df83bdab2db573d
|
||||||
size 57953
|
size 55887
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:1c1c5b00098e5860d4c69ef9a652483ea1192d9fa10f5449fb817769ab054183
|
oid sha256:d044628791909fdb8f621b3693cb63726e6a1dbc1fd6e12e1b3a4e3c9be1e868
|
||||||
size 36128
|
size 36288
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4fe6625641cf06c45a817e9d10bc0d7296aa59160fc5a3e9dc28304e5b794125
|
oid sha256:62762b55b56ee307e15a2aa488e8426714771d6e9d76d68ee82185b3420a348f
|
||||||
size 57943
|
size 55831
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e85b252d0d6c166f987974ebcf9a6b58b924316733b868ad0ed5a61413bda381
|
oid sha256:923619c9af298ce1a250c5094429b6b820b560c62a55ec8baa40144478e42358
|
||||||
size 54577
|
size 53102
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4f246ce9f60b25d9739d73e2aed98132dfb3a0a694acbf320ff796f35f805fdc
|
oid sha256:1125869fdf539f15103bef1a380e46af9b56c44262a1986009099e2700206038
|
||||||
size 54273
|
size 52799
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d0403a4bb8da3d36922cb5362977af9b3052624525def05e5236f4f922384c31
|
oid sha256:ecee6cd60257a931f717c8ae23f256ded8dd6ac8fa7af7430082c6a3f837b274
|
||||||
size 54297
|
size 52821
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:824774a1e19974090de4cb32e06a7f984b5db238c3dc8d04a35d3457e647ac84
|
oid sha256:8449c20314175d6e829df65b2b1ba3cf36944a679b2e360ee5d2ed4d8333ad07
|
||||||
size 31999
|
size 32149
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c0820b462a0dc103b66aef78528ae5ce13a55e2614a7a197e06713366c422893
|
oid sha256:5c38a6f66d48174c6e2b3ac8525906cd371030f15ac3ca8727a8ca83f528916e
|
||||||
size 54313
|
size 52823
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue