mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-25 04:06:14 +00:00
Implement Stories read receipt off state.
This commit is contained in:
committed by
Greyson Parrelli
parent
f3873c8a7c
commit
e412cac419
@@ -62,6 +62,7 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
|
||||
StartLocation.NOTIFICATION_PROFILE_DETAILS -> AppSettingsFragmentDirections.actionDirectToNotificationProfileDetails(
|
||||
EditNotificationProfileScheduleFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).profileId
|
||||
)
|
||||
StartLocation.PRIVACY -> AppSettingsFragmentDirections.actionDirectToPrivacy()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +169,9 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
|
||||
@JvmStatic
|
||||
fun createNotificationProfile(context: Context): Intent = getIntentForStartLocation(context, StartLocation.CREATE_NOTIFICATION_PROFILE)
|
||||
|
||||
@JvmStatic
|
||||
fun privacy(context: Context): Intent = getIntentForStartLocation(context, StartLocation.PRIVACY)
|
||||
|
||||
@JvmStatic
|
||||
fun notificationProfileDetails(context: Context, profileId: Long): Intent {
|
||||
val arguments = EditNotificationProfileScheduleFragmentArgs.Builder(profileId, false)
|
||||
@@ -197,7 +201,8 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
|
||||
MANAGE_SUBSCRIPTIONS(8),
|
||||
NOTIFICATION_PROFILES(9),
|
||||
CREATE_NOTIFICATION_PROFILE(10),
|
||||
NOTIFICATION_PROFILE_DETAILS(11);
|
||||
NOTIFICATION_PROFILE_DETAILS(11),
|
||||
PRIVACY(12);
|
||||
|
||||
companion object {
|
||||
fun fromCode(code: Int?): StartLocation {
|
||||
|
||||
@@ -339,7 +339,7 @@ class StoryViewerPageFragment :
|
||||
if (state.posts.isNotEmpty() && state.selectedPostIndex in state.posts.indices) {
|
||||
val post = state.posts[state.selectedPostIndex]
|
||||
|
||||
presentViewsAndReplies(post, state.replyState)
|
||||
presentViewsAndReplies(post, state.replyState, state.isReceiptsEnabled)
|
||||
presentSenderAvatar(senderAvatar, post)
|
||||
presentGroupAvatar(groupAvatar, post)
|
||||
presentFrom(from, post)
|
||||
@@ -449,6 +449,7 @@ class StoryViewerPageFragment :
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.setIsFragmentResumed(true)
|
||||
viewModel.checkReadReceiptState()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@@ -823,7 +824,7 @@ class StoryViewerPageFragment :
|
||||
}
|
||||
}
|
||||
|
||||
private fun presentViewsAndReplies(post: StoryPost, replyState: StoryViewerPageState.ReplyState) {
|
||||
private fun presentViewsAndReplies(post: StoryPost, replyState: StoryViewerPageState.ReplyState, isReceiptsEnabled: Boolean) {
|
||||
if (replyState == StoryViewerPageState.ReplyState.NONE) {
|
||||
viewsAndReplies.visible = false
|
||||
return
|
||||
@@ -835,14 +836,25 @@ class StoryViewerPageFragment :
|
||||
val replies = resources.getQuantityString(R.plurals.StoryViewerFragment__d_replies, post.replyCount, post.replyCount)
|
||||
|
||||
if (Recipient.self() == post.sender) {
|
||||
if (post.replyCount == 0) {
|
||||
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
||||
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
||||
viewsAndReplies.text = views
|
||||
if (isReceiptsEnabled) {
|
||||
if (post.replyCount == 0) {
|
||||
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
||||
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
||||
viewsAndReplies.text = views
|
||||
} else {
|
||||
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
||||
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
||||
viewsAndReplies.text = getString(R.string.StoryViewerFragment__s_s, views, replies)
|
||||
}
|
||||
} else {
|
||||
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
||||
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
||||
viewsAndReplies.text = getString(R.string.StoryViewerFragment__s_s, views, replies)
|
||||
if (post.replyCount == 0) {
|
||||
viewsAndReplies.icon = null
|
||||
viewsAndReplies.setText(R.string.StoryViewerPageFragment__views_off)
|
||||
} else {
|
||||
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
||||
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
||||
viewsAndReplies.text = replies
|
||||
}
|
||||
}
|
||||
} else if (post.replyCount > 0) {
|
||||
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.stories.Stories
|
||||
import org.thoughtcrime.securesms.util.Base64
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
|
||||
/**
|
||||
* Open for testing.
|
||||
@@ -35,6 +36,8 @@ open class StoryViewerPageRepository(context: Context) {
|
||||
|
||||
private val context = context.applicationContext
|
||||
|
||||
fun isReadReceiptsEnabled(): Boolean = TextSecurePreferences.isReadReceiptsEnabled(context)
|
||||
|
||||
private fun getStoryRecords(recipientId: RecipientId, isUnviewedOnly: Boolean): Observable<List<MessageRecord>> {
|
||||
return Observable.create { emitter ->
|
||||
val recipient = Recipient.resolved(recipientId)
|
||||
|
||||
@@ -6,7 +6,8 @@ data class StoryViewerPageState(
|
||||
val replyState: ReplyState = ReplyState.NONE,
|
||||
val isFirstPage: Boolean = false,
|
||||
val isDisplayingInitialState: Boolean = false,
|
||||
val isReady: Boolean = false
|
||||
val isReady: Boolean = false,
|
||||
val isReceiptsEnabled: Boolean
|
||||
) {
|
||||
/**
|
||||
* Indicates which Reply method is available when the user swipes on the dialog
|
||||
|
||||
@@ -28,7 +28,7 @@ class StoryViewerPageViewModel(
|
||||
private val repository: StoryViewerPageRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val store = RxStore(StoryViewerPageState())
|
||||
private val store = RxStore(StoryViewerPageState(isReceiptsEnabled = repository.isReadReceiptsEnabled()))
|
||||
private val disposables = CompositeDisposable()
|
||||
private val storyViewerDialogSubject: Subject<Optional<StoryViewerDialog>> = PublishSubject.create()
|
||||
|
||||
@@ -46,6 +46,16 @@ class StoryViewerPageViewModel(
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun checkReadReceiptState() {
|
||||
val isReceiptsEnabledInState = getStateSnapshot().isReceiptsEnabled
|
||||
val isReceiptsEnabledInRepository = repository.isReadReceiptsEnabled()
|
||||
if (isReceiptsEnabledInState xor isReceiptsEnabledInRepository) {
|
||||
store.update {
|
||||
it.copy(isReceiptsEnabled = isReceiptsEnabledInRepository)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
disposables.clear()
|
||||
disposables += repository.getStoryPostsFor(recipientId, isUnviewedOnly).subscribe { posts ->
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerChild
|
||||
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerParent
|
||||
@@ -37,15 +38,28 @@ class StoryViewsFragment :
|
||||
StoryViewItem.register(adapter)
|
||||
|
||||
val emptyNotice: View = requireView().findViewById(R.id.empty_notice)
|
||||
val disabledNotice: View = requireView().findViewById(R.id.disabled_notice)
|
||||
val disabledButton: View = requireView().findViewById(R.id.disabled_button)
|
||||
|
||||
disabledButton.setOnClickListener {
|
||||
startActivity(AppSettingsActivity.privacy(requireContext()))
|
||||
}
|
||||
|
||||
onPageSelected(findListener<StoryViewsAndRepliesPagerParent>()?.selectedChild ?: StoryViewsAndRepliesPagerParent.Child.VIEWS)
|
||||
|
||||
viewModel.state.observe(viewLifecycleOwner) {
|
||||
emptyNotice.visible = it.loadState == StoryViewsState.LoadState.READY && it.views.isEmpty()
|
||||
disabledNotice.visible = it.loadState == StoryViewsState.LoadState.DISABLED
|
||||
recyclerView?.visible = it.loadState == StoryViewsState.LoadState.READY
|
||||
adapter.submitList(getConfiguration(it).toMappingModelList())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
override fun onPageSelected(child: StoryViewsAndRepliesPagerParent.Child) {
|
||||
recyclerView?.isNestedScrollingEnabled = child == StoryViewsAndRepliesPagerParent.Child.VIEWS
|
||||
}
|
||||
|
||||
@@ -7,8 +7,12 @@ import org.thoughtcrime.securesms.database.GroupReceiptDatabase
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
|
||||
class StoryViewsRepository {
|
||||
|
||||
fun isReadReceiptsEnabled(): Boolean = TextSecurePreferences.isReadReceiptsEnabled(ApplicationDependencies.getApplication())
|
||||
|
||||
fun getViews(storyId: Long): Observable<List<StoryViewItemData>> {
|
||||
return Observable.create<List<StoryViewItemData>> { emitter ->
|
||||
fun refresh() {
|
||||
|
||||
@@ -6,6 +6,7 @@ data class StoryViewsState(
|
||||
) {
|
||||
enum class LoadState {
|
||||
INIT,
|
||||
READY
|
||||
READY,
|
||||
DISABLED
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,19 +7,27 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
|
||||
class StoryViewsViewModel(storyId: Long, repository: StoryViewsRepository) : ViewModel() {
|
||||
class StoryViewsViewModel(private val storyId: Long, private val repository: StoryViewsRepository) : ViewModel() {
|
||||
|
||||
private val store = Store(StoryViewsState())
|
||||
private val store = Store(StoryViewsState(StoryViewsState.LoadState.INIT))
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
val state: LiveData<StoryViewsState> = store.stateLiveData
|
||||
|
||||
init {
|
||||
disposables += repository.getViews(storyId).subscribe { data ->
|
||||
fun refresh() {
|
||||
if (repository.isReadReceiptsEnabled()) {
|
||||
disposables += repository.getViews(storyId).subscribe { data ->
|
||||
store.update {
|
||||
it.copy(
|
||||
views = data,
|
||||
loadState = StoryViewsState.LoadState.READY
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
store.update {
|
||||
it.copy(
|
||||
views = data,
|
||||
loadState = StoryViewsState.LoadState.READY
|
||||
loadState = StoryViewsState.LoadState.DISABLED
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user