Mute one contact's stories, without blocking them
Android half of ios#33. Same shape as iOS: the muted set lives in the vault beside the audience list, because who somebody has muted is a statement about their social graph and shared preferences are readable by anything that reaches the file. The filter runs on the way out of `stories()`, after `saveStories` has already written the live set back. A muted contact's stories are still received and still stored — dropping them on receipt would be cheaper, but unmuting would then show an empty strip until their next post, and muting would quietly mean something other than what it says. `Vault.visibleStories` never drops the signed-in user, even if their own id is in the set. `setStoryMuted` refuses to put it there, but a restored backup from a build without that guard could carry one, and hiding somebody's own posts from them reads as the posts having failed to send. Long press on a story bubble to hide or restore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bf44c15266
commit
0c1af3e45f
@@ -2355,6 +2355,7 @@ private fun VoiceBubble(ref: io.flamenet.e2e.Attachment.Ref, model: MessengerVie
|
||||
* would be telling people a story is safe to post when it is not.
|
||||
*/
|
||||
@Composable
|
||||
@OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||
fun ColumnScope.StoryStrip(
|
||||
model: MessengerViewModel,
|
||||
phase: MessengerViewModel.Phase.SignedIn,
|
||||
@@ -2431,7 +2432,18 @@ fun ColumnScope.StoryStrip(
|
||||
.padding(if (story.seen) 0.dp else 2.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Palette.AvatarFill)
|
||||
.clickable { model.openStory(story) },
|
||||
// Long press hides this contact's stories (ios#33).
|
||||
// On the bubble because the moment somebody wants this
|
||||
// is the moment they are looking at the bubble.
|
||||
.combinedClickable(
|
||||
onClick = { model.openStory(story) },
|
||||
onLongClick = {
|
||||
if (!story.mine) {
|
||||
model.setStoryMuted(
|
||||
story.authorId, !model.isStoryMuted(story.authorId))
|
||||
}
|
||||
},
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
|
||||
@@ -1913,6 +1913,20 @@ class MessengerViewModel(app: Application) : AndroidViewModel(app) {
|
||||
* would need the relay to know who may read it, which is a list of your
|
||||
* contacts handed to the server for a feature that works without it.
|
||||
*/
|
||||
/**
|
||||
* Hide or restore one contact's stories (ios#33).
|
||||
*
|
||||
* Local only — no envelope and no relay call, so the author is never told.
|
||||
*/
|
||||
fun setStoryMuted(authorId: Long, muted: Boolean) = viewModelScope.launch {
|
||||
val current = session ?: return@launch
|
||||
withContext(Dispatchers.IO) { runCatching { current.setStoryMuted(authorId, muted) } }
|
||||
refreshStories()
|
||||
}
|
||||
|
||||
/** Whether this contact's stories are hidden on this device. */
|
||||
fun isStoryMuted(authorId: Long): Boolean = session?.isStoryMuted(authorId) ?: false
|
||||
|
||||
/** Change who may see this account's stories. */
|
||||
fun setStoryAudience(audience: Session.StoryAudience) = viewModelScope.launch {
|
||||
val current = session ?: return@launch
|
||||
|
||||
@@ -820,8 +820,29 @@ class Session(
|
||||
val live = vault.loadStories().filter { it.isActive(now) }
|
||||
// Written back so expired bytes leave the device rather than lingering
|
||||
// until the next post.
|
||||
//
|
||||
// ⚠️ Saved BEFORE the mute filter, deliberately. A muted contact's
|
||||
// stories are still stored: hiding is a display choice, and dropping
|
||||
// them here would make unmuting show an empty strip until their next
|
||||
// post — muting would then quietly mean something other than it says.
|
||||
vault.saveStories(live, now)
|
||||
return live.sortedByDescending { it.postedAt }
|
||||
return Vault.visibleStories(
|
||||
live.sortedByDescending { it.postedAt }, vault.mutedStoryAuthors(), userId)
|
||||
}
|
||||
|
||||
/** Whether this contact's stories are hidden on this device (ios#33). */
|
||||
fun isStoryMuted(authorId: Long): Boolean = authorId in vault.mutedStoryAuthors()
|
||||
|
||||
/**
|
||||
* Hide or restore one contact's stories. Local only; nothing is sent.
|
||||
*
|
||||
* ⚠️ Refuses to mute the signed-in user. See [Vault.visibleStories].
|
||||
*/
|
||||
fun setStoryMuted(authorId: Long, muted: Boolean) {
|
||||
if (authorId == userId) return
|
||||
val current = vault.mutedStoryAuthors()
|
||||
val next = if (muted) current + authorId else current - authorId
|
||||
if (next != current) vault.saveMutedStoryAuthors(next)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user