Story privacy screen updates.

This commit is contained in:
Alex Hart
2022-08-18 10:11:42 -03:00
committed by Cody Henthorne
parent 15e52a8b88
commit f341e02fb7
74 changed files with 800 additions and 423 deletions

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.contacts.paged
import org.thoughtcrime.securesms.contacts.HeaderAction
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.recipients.Recipient
/**
@@ -12,7 +13,7 @@ sealed class ContactSearchData(val contactSearchKey: ContactSearchKey) {
*
* Note that if the recipient is a group, it's participant list size is used instead of viewerCount.
*/
data class Story(val recipient: Recipient, val viewerCount: Int) : ContactSearchData(ContactSearchKey.RecipientSearchKey.Story(recipient.id))
data class Story(val recipient: Recipient, val viewerCount: Int, val privacyMode: DistributionListPrivacyMode) : ContactSearchData(ContactSearchKey.RecipientSearchKey.Story(recipient.id))
/**
* A row displaying a known recipient.

View File

@@ -10,6 +10,7 @@ import org.thoughtcrime.securesms.components.AvatarImageView
import org.thoughtcrime.securesms.components.FromTextView
import org.thoughtcrime.securesms.components.menu.ActionItem
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
@@ -26,6 +27,26 @@ private typealias RecipientClickListener = (View, ContactSearchData.KnownRecipie
* Mapping Models and View Holders for ContactSearchData
*/
object ContactSearchItems {
fun registerStoryItems(
mappingAdapter: MappingAdapter,
displayCheckBox: Boolean = false,
storyListener: StoryClickListener,
storyContextMenuCallbacks: StoryContextMenuCallbacks? = null
) {
mappingAdapter.registerFactory(
StoryModel::class.java,
LayoutFactory({ StoryViewHolder(it, displayCheckBox, storyListener, storyContextMenuCallbacks) }, R.layout.contact_search_item)
)
}
fun registerHeaders(mappingAdapter: MappingAdapter) {
mappingAdapter.registerFactory(
HeaderModel::class.java,
LayoutFactory({ HeaderViewHolder(it) }, R.layout.contact_search_section_header)
)
}
fun register(
mappingAdapter: MappingAdapter,
displayCheckBox: Boolean,
@@ -34,18 +55,12 @@ object ContactSearchItems {
storyContextMenuCallbacks: StoryContextMenuCallbacks,
expandListener: (ContactSearchData.Expand) -> Unit
) {
mappingAdapter.registerFactory(
StoryModel::class.java,
LayoutFactory({ StoryViewHolder(it, displayCheckBox, storyListener, storyContextMenuCallbacks) }, R.layout.contact_search_item)
)
registerStoryItems(mappingAdapter, displayCheckBox, storyListener, storyContextMenuCallbacks)
mappingAdapter.registerFactory(
RecipientModel::class.java,
LayoutFactory({ KnownRecipientViewHolder(it, displayCheckBox, recipientListener) }, R.layout.contact_search_item)
)
mappingAdapter.registerFactory(
HeaderModel::class.java,
LayoutFactory({ HeaderViewHolder(it) }, R.layout.contact_search_section_header)
)
registerHeaders(mappingAdapter)
mappingAdapter.registerFactory(
ExpandModel::class.java,
LayoutFactory({ ExpandViewHolder(it, expandListener) }, R.layout.contacts_expand_item)
@@ -92,7 +107,12 @@ object ContactSearchItems {
}
}
private class StoryViewHolder(itemView: View, displayCheckBox: Boolean, onClick: StoryClickListener, private val storyContextMenuCallbacks: StoryContextMenuCallbacks) : BaseRecipientViewHolder<StoryModel, ContactSearchData.Story>(itemView, displayCheckBox, onClick) {
private class StoryViewHolder(
itemView: View,
displayCheckBox: Boolean,
onClick: StoryClickListener,
private val storyContextMenuCallbacks: StoryContextMenuCallbacks?
) : BaseRecipientViewHolder<StoryModel, ContactSearchData.Story>(itemView, displayCheckBox, onClick) {
override fun isSelected(model: StoryModel): Boolean = model.isSelected
override fun getData(model: StoryModel): ContactSearchData.Story = model.story
override fun getRecipient(model: StoryModel): Recipient = model.story.recipient
@@ -109,13 +129,11 @@ object ContactSearchItems {
if (model.story.recipient.isMyStory && !model.hasBeenNotified) {
number.setText(R.string.ContactSearchItems__tap_to_choose_your_viewers)
} else {
val pluralId = when {
model.story.recipient.isGroup -> R.plurals.ContactSearchItems__group_story_d_viewers
model.story.recipient.isMyStory -> R.plurals.SelectViewersFragment__d_viewers
else -> R.plurals.ContactSearchItems__private_story_d_viewers
number.text = when {
model.story.recipient.isGroup -> context.resources.getQuantityString(R.plurals.ContactSearchItems__group_story_d_viewers, count, count)
model.story.recipient.isMyStory -> context.resources.getQuantityString(R.plurals.ContactSearchItems__my_story_s_dot_d_viewers, count, presentPrivacyMode(model.story.privacyMode), count)
else -> context.resources.getQuantityString(R.plurals.ContactSearchItems__private_story_d_viewers, count, count)
}
number.text = context.resources.getQuantityString(pluralId, count, count)
}
}
@@ -128,11 +146,15 @@ object ContactSearchItems {
}
override fun bindLongPress(model: StoryModel) {
if (storyContextMenuCallbacks == null) {
return
}
itemView.setOnLongClickListener {
val actions: List<ActionItem> = when {
model.story.recipient.isMyStory -> getMyStoryContextMenuActions(model)
model.story.recipient.isGroup -> getGroupStoryContextMenuActions(model)
model.story.recipient.isDistributionList -> getPrivateStoryContextMenuActions(model)
model.story.recipient.isMyStory -> getMyStoryContextMenuActions(model, storyContextMenuCallbacks)
model.story.recipient.isGroup -> getGroupStoryContextMenuActions(model, storyContextMenuCallbacks)
model.story.recipient.isDistributionList -> getPrivateStoryContextMenuActions(model, storyContextMenuCallbacks)
else -> error("Unsupported story target. Not a group or distribution list.")
}
@@ -144,32 +166,40 @@ object ContactSearchItems {
}
}
private fun getMyStoryContextMenuActions(model: StoryModel): List<ActionItem> {
private fun getMyStoryContextMenuActions(model: StoryModel, callbacks: StoryContextMenuCallbacks): List<ActionItem> {
return listOf(
ActionItem(R.drawable.ic_settings_24, context.getString(R.string.ContactSearchItems__story_settings)) {
storyContextMenuCallbacks.onOpenStorySettings(model.story)
callbacks.onOpenStorySettings(model.story)
}
)
}
private fun getGroupStoryContextMenuActions(model: StoryModel): List<ActionItem> {
private fun getGroupStoryContextMenuActions(model: StoryModel, callbacks: StoryContextMenuCallbacks): List<ActionItem> {
return listOf(
ActionItem(R.drawable.ic_minus_circle_20, context.getString(R.string.ContactSearchItems__remove_story)) {
storyContextMenuCallbacks.onRemoveGroupStory(model.story, model.isSelected)
callbacks.onRemoveGroupStory(model.story, model.isSelected)
}
)
}
private fun getPrivateStoryContextMenuActions(model: StoryModel): List<ActionItem> {
private fun getPrivateStoryContextMenuActions(model: StoryModel, callbacks: StoryContextMenuCallbacks): List<ActionItem> {
return listOf(
ActionItem(R.drawable.ic_settings_24, context.getString(R.string.ContactSearchItems__story_settings)) {
storyContextMenuCallbacks.onOpenStorySettings(model.story)
callbacks.onOpenStorySettings(model.story)
},
ActionItem(R.drawable.ic_delete_24, context.getString(R.string.ContactSearchItems__delete_story), R.color.signal_colorError) {
storyContextMenuCallbacks.onDeletePrivateStory(model.story, model.isSelected)
callbacks.onDeletePrivateStory(model.story, model.isSelected)
}
)
}
private fun presentPrivacyMode(privacyMode: DistributionListPrivacyMode): String {
return when (privacyMode) {
DistributionListPrivacyMode.ONLY_WITH -> context.getString(R.string.ChooseInitialMyStoryMembershipFragment__only_share_with)
DistributionListPrivacyMode.ALL_EXCEPT -> context.getString(R.string.ChooseInitialMyStoryMembershipFragment__all_signal_connections_except)
DistributionListPrivacyMode.ALL -> context.getString(R.string.ChooseInitialMyStoryMembershipFragment__all_signal_connections)
}
}
}
/**

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.contacts.paged
import android.database.Cursor
import org.signal.paging.PagedDataSource
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.keyvalue.StorySend
import java.util.concurrent.TimeUnit
@@ -157,7 +158,9 @@ class ContactSearchPagedDataSource(
endIndex = endIndex,
cursorRowToData = {
val recipient = contactSearchPagedDataSourceRepository.getRecipientFromDistributionListCursor(it)
ContactSearchData.Story(recipient, contactSearchPagedDataSourceRepository.getDistributionListMembershipCount(recipient))
val count = contactSearchPagedDataSourceRepository.getDistributionListMembershipCount(recipient)
val privacyMode = contactSearchPagedDataSourceRepository.getPrivacyModeFromDistributionListCursor(it)
ContactSearchData.Story(recipient, count, privacyMode)
},
extraData = getFilteredGroupStories(section, query)
)
@@ -201,7 +204,7 @@ class ContactSearchPagedDataSource(
endIndex = endIndex,
cursorRowToData = {
if (section.returnAsGroupStories) {
ContactSearchData.Story(contactSearchPagedDataSourceRepository.getRecipientFromGroupCursor(cursor), 0)
ContactSearchData.Story(contactSearchPagedDataSourceRepository.getRecipientFromGroupCursor(cursor), 0, DistributionListPrivacyMode.ALL)
} else {
ContactSearchData.KnownRecipient(contactSearchPagedDataSourceRepository.getRecipientFromGroupCursor(cursor))
}

View File

@@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.database.DistributionListDatabase
import org.thoughtcrime.securesms.database.GroupDatabase
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.keyvalue.StorySend
import org.thoughtcrime.securesms.recipients.Recipient
@@ -76,6 +77,10 @@ open class ContactSearchPagedDataSourceRepository(
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, DistributionListDatabase.RECIPIENT_ID)))
}
open fun getPrivacyModeFromDistributionListCursor(cursor: Cursor): DistributionListPrivacyMode {
return DistributionListPrivacyMode.deserialize(CursorUtil.requireLong(cursor, DistributionListDatabase.PRIVACY_MODE))
}
open fun getRecipientFromThreadCursor(cursor: Cursor): Recipient {
return Recipient.resolved(RecipientId.from(CursorUtil.requireLong(cursor, ThreadDatabase.RECIPIENT_ID)))
}
@@ -95,7 +100,7 @@ open class ContactSearchPagedDataSourceRepository(
open fun getGroupStories(): Set<ContactSearchData.Story> {
return SignalDatabase.groups.groupsToDisplayAsStories.map {
val recipient = Recipient.resolved(SignalDatabase.recipients.getOrInsertFromGroupId(it))
ContactSearchData.Story(recipient, recipient.participantIds.size)
ContactSearchData.Story(recipient, recipient.participantIds.size, DistributionListPrivacyMode.ALL)
}.toSet()
}

View File

@@ -11,6 +11,7 @@ import org.signal.paging.LivePagedData
import org.signal.paging.PagedData
import org.signal.paging.PagingConfig
import org.signal.paging.PagingController
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.groups.SelectionLimits
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.livedata.Store
@@ -98,7 +99,7 @@ class ContactSearchViewModel(
state.copy(
groupStories = state.groupStories + groupStories.map {
val recipient = Recipient.resolved(it.recipientId)
ContactSearchData.Story(recipient, recipient.participantIds.size)
ContactSearchData.Story(recipient, recipient.participantIds.size, DistributionListPrivacyMode.ALL)
}
)
}