kopia lustrzana https://github.com/ryukoposting/Signal-Android
Implement Stories read receipt off state.
rodzic
f3873c8a7c
commit
e412cac419
|
@ -62,6 +62,7 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
|
||||||
StartLocation.NOTIFICATION_PROFILE_DETAILS -> AppSettingsFragmentDirections.actionDirectToNotificationProfileDetails(
|
StartLocation.NOTIFICATION_PROFILE_DETAILS -> AppSettingsFragmentDirections.actionDirectToNotificationProfileDetails(
|
||||||
EditNotificationProfileScheduleFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).profileId
|
EditNotificationProfileScheduleFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).profileId
|
||||||
)
|
)
|
||||||
|
StartLocation.PRIVACY -> AppSettingsFragmentDirections.actionDirectToPrivacy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +169,9 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createNotificationProfile(context: Context): Intent = getIntentForStartLocation(context, StartLocation.CREATE_NOTIFICATION_PROFILE)
|
fun createNotificationProfile(context: Context): Intent = getIntentForStartLocation(context, StartLocation.CREATE_NOTIFICATION_PROFILE)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun privacy(context: Context): Intent = getIntentForStartLocation(context, StartLocation.PRIVACY)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun notificationProfileDetails(context: Context, profileId: Long): Intent {
|
fun notificationProfileDetails(context: Context, profileId: Long): Intent {
|
||||||
val arguments = EditNotificationProfileScheduleFragmentArgs.Builder(profileId, false)
|
val arguments = EditNotificationProfileScheduleFragmentArgs.Builder(profileId, false)
|
||||||
|
@ -197,7 +201,8 @@ class AppSettingsActivity : DSLSettingsActivity(), DonationPaymentComponent {
|
||||||
MANAGE_SUBSCRIPTIONS(8),
|
MANAGE_SUBSCRIPTIONS(8),
|
||||||
NOTIFICATION_PROFILES(9),
|
NOTIFICATION_PROFILES(9),
|
||||||
CREATE_NOTIFICATION_PROFILE(10),
|
CREATE_NOTIFICATION_PROFILE(10),
|
||||||
NOTIFICATION_PROFILE_DETAILS(11);
|
NOTIFICATION_PROFILE_DETAILS(11),
|
||||||
|
PRIVACY(12);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromCode(code: Int?): StartLocation {
|
fun fromCode(code: Int?): StartLocation {
|
||||||
|
|
|
@ -339,7 +339,7 @@ class StoryViewerPageFragment :
|
||||||
if (state.posts.isNotEmpty() && state.selectedPostIndex in state.posts.indices) {
|
if (state.posts.isNotEmpty() && state.selectedPostIndex in state.posts.indices) {
|
||||||
val post = state.posts[state.selectedPostIndex]
|
val post = state.posts[state.selectedPostIndex]
|
||||||
|
|
||||||
presentViewsAndReplies(post, state.replyState)
|
presentViewsAndReplies(post, state.replyState, state.isReceiptsEnabled)
|
||||||
presentSenderAvatar(senderAvatar, post)
|
presentSenderAvatar(senderAvatar, post)
|
||||||
presentGroupAvatar(groupAvatar, post)
|
presentGroupAvatar(groupAvatar, post)
|
||||||
presentFrom(from, post)
|
presentFrom(from, post)
|
||||||
|
@ -449,6 +449,7 @@ class StoryViewerPageFragment :
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
viewModel.setIsFragmentResumed(true)
|
viewModel.setIsFragmentResumed(true)
|
||||||
|
viewModel.checkReadReceiptState()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
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) {
|
if (replyState == StoryViewerPageState.ReplyState.NONE) {
|
||||||
viewsAndReplies.visible = false
|
viewsAndReplies.visible = false
|
||||||
return
|
return
|
||||||
|
@ -835,6 +836,7 @@ class StoryViewerPageFragment :
|
||||||
val replies = resources.getQuantityString(R.plurals.StoryViewerFragment__d_replies, post.replyCount, post.replyCount)
|
val replies = resources.getQuantityString(R.plurals.StoryViewerFragment__d_replies, post.replyCount, post.replyCount)
|
||||||
|
|
||||||
if (Recipient.self() == post.sender) {
|
if (Recipient.self() == post.sender) {
|
||||||
|
if (isReceiptsEnabled) {
|
||||||
if (post.replyCount == 0) {
|
if (post.replyCount == 0) {
|
||||||
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
||||||
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
||||||
|
@ -844,6 +846,16 @@ class StoryViewerPageFragment :
|
||||||
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
||||||
viewsAndReplies.text = getString(R.string.StoryViewerFragment__s_s, views, replies)
|
viewsAndReplies.text = getString(R.string.StoryViewerFragment__s_s, views, replies)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
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) {
|
} else if (post.replyCount > 0) {
|
||||||
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
viewsAndReplies.setIconResource(R.drawable.ic_chevron_end_24)
|
||||||
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
viewsAndReplies.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
import org.thoughtcrime.securesms.util.Base64
|
import org.thoughtcrime.securesms.util.Base64
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open for testing.
|
* Open for testing.
|
||||||
|
@ -35,6 +36,8 @@ open class StoryViewerPageRepository(context: Context) {
|
||||||
|
|
||||||
private val context = context.applicationContext
|
private val context = context.applicationContext
|
||||||
|
|
||||||
|
fun isReadReceiptsEnabled(): Boolean = TextSecurePreferences.isReadReceiptsEnabled(context)
|
||||||
|
|
||||||
private fun getStoryRecords(recipientId: RecipientId, isUnviewedOnly: Boolean): Observable<List<MessageRecord>> {
|
private fun getStoryRecords(recipientId: RecipientId, isUnviewedOnly: Boolean): Observable<List<MessageRecord>> {
|
||||||
return Observable.create { emitter ->
|
return Observable.create { emitter ->
|
||||||
val recipient = Recipient.resolved(recipientId)
|
val recipient = Recipient.resolved(recipientId)
|
||||||
|
|
|
@ -6,7 +6,8 @@ data class StoryViewerPageState(
|
||||||
val replyState: ReplyState = ReplyState.NONE,
|
val replyState: ReplyState = ReplyState.NONE,
|
||||||
val isFirstPage: Boolean = false,
|
val isFirstPage: Boolean = false,
|
||||||
val isDisplayingInitialState: 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
|
* Indicates which Reply method is available when the user swipes on the dialog
|
||||||
|
|
|
@ -28,7 +28,7 @@ class StoryViewerPageViewModel(
|
||||||
private val repository: StoryViewerPageRepository
|
private val repository: StoryViewerPageRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val store = RxStore(StoryViewerPageState())
|
private val store = RxStore(StoryViewerPageState(isReceiptsEnabled = repository.isReadReceiptsEnabled()))
|
||||||
private val disposables = CompositeDisposable()
|
private val disposables = CompositeDisposable()
|
||||||
private val storyViewerDialogSubject: Subject<Optional<StoryViewerDialog>> = PublishSubject.create()
|
private val storyViewerDialogSubject: Subject<Optional<StoryViewerDialog>> = PublishSubject.create()
|
||||||
|
|
||||||
|
@ -46,6 +46,16 @@ class StoryViewerPageViewModel(
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkReadReceiptState() {
|
||||||
|
val isReceiptsEnabledInState = getStateSnapshot().isReceiptsEnabled
|
||||||
|
val isReceiptsEnabledInRepository = repository.isReadReceiptsEnabled()
|
||||||
|
if (isReceiptsEnabledInState xor isReceiptsEnabledInRepository) {
|
||||||
|
store.update {
|
||||||
|
it.copy(isReceiptsEnabled = isReceiptsEnabledInRepository)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
disposables.clear()
|
disposables.clear()
|
||||||
disposables += repository.getStoryPostsFor(recipientId, isUnviewedOnly).subscribe { posts ->
|
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.DSLConfiguration
|
||||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
||||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
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.components.settings.configure
|
||||||
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerChild
|
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerChild
|
||||||
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerParent
|
import org.thoughtcrime.securesms.stories.viewer.reply.StoryViewsAndRepliesPagerParent
|
||||||
|
@ -37,15 +38,28 @@ class StoryViewsFragment :
|
||||||
StoryViewItem.register(adapter)
|
StoryViewItem.register(adapter)
|
||||||
|
|
||||||
val emptyNotice: View = requireView().findViewById(R.id.empty_notice)
|
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)
|
onPageSelected(findListener<StoryViewsAndRepliesPagerParent>()?.selectedChild ?: StoryViewsAndRepliesPagerParent.Child.VIEWS)
|
||||||
|
|
||||||
viewModel.state.observe(viewLifecycleOwner) {
|
viewModel.state.observe(viewLifecycleOwner) {
|
||||||
emptyNotice.visible = it.loadState == StoryViewsState.LoadState.READY && it.views.isEmpty()
|
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())
|
adapter.submitList(getConfiguration(it).toMappingModelList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
viewModel.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPageSelected(child: StoryViewsAndRepliesPagerParent.Child) {
|
override fun onPageSelected(child: StoryViewsAndRepliesPagerParent.Child) {
|
||||||
recyclerView?.isNestedScrollingEnabled = child == StoryViewsAndRepliesPagerParent.Child.VIEWS
|
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.database.SignalDatabase
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
|
|
||||||
class StoryViewsRepository {
|
class StoryViewsRepository {
|
||||||
|
|
||||||
|
fun isReadReceiptsEnabled(): Boolean = TextSecurePreferences.isReadReceiptsEnabled(ApplicationDependencies.getApplication())
|
||||||
|
|
||||||
fun getViews(storyId: Long): Observable<List<StoryViewItemData>> {
|
fun getViews(storyId: Long): Observable<List<StoryViewItemData>> {
|
||||||
return Observable.create<List<StoryViewItemData>> { emitter ->
|
return Observable.create<List<StoryViewItemData>> { emitter ->
|
||||||
fun refresh() {
|
fun refresh() {
|
||||||
|
|
|
@ -6,6 +6,7 @@ data class StoryViewsState(
|
||||||
) {
|
) {
|
||||||
enum class LoadState {
|
enum class LoadState {
|
||||||
INIT,
|
INIT,
|
||||||
READY
|
READY,
|
||||||
|
DISABLED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,15 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||||
import org.thoughtcrime.securesms.util.livedata.Store
|
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()
|
private val disposables = CompositeDisposable()
|
||||||
|
|
||||||
val state: LiveData<StoryViewsState> = store.stateLiveData
|
val state: LiveData<StoryViewsState> = store.stateLiveData
|
||||||
|
|
||||||
init {
|
fun refresh() {
|
||||||
|
if (repository.isReadReceiptsEnabled()) {
|
||||||
disposables += repository.getViews(storyId).subscribe { data ->
|
disposables += repository.getViews(storyId).subscribe { data ->
|
||||||
store.update {
|
store.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
|
@ -23,6 +24,13 @@ class StoryViewsViewModel(storyId: Long, repository: StoryViewsRepository) : Vie
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
store.update {
|
||||||
|
it.copy(
|
||||||
|
loadState = StoryViewsState.LoadState.DISABLED
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="@color/signal_colorOnSurface_12" android:state_enabled="false" />
|
||||||
|
<item android:color="@color/signal_colorOutline" />
|
||||||
|
</selector>
|
|
@ -17,6 +17,45 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/disabled_notice"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:constraint_referenced_ids="disabled_label,disabled_button"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/disabled_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||||
|
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/StoryViewsFragment__enable_read_receipts_to_see_whos_viewed_your_story"
|
||||||
|
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/disabled_button"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/disabled_button"
|
||||||
|
style="@style/Signal.Widget.Button.Medium.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/StoryViewsFragment__go_to_settings"
|
||||||
|
android:textColor="@color/signal_colorOnSurface"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/disabled_label" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recycler"
|
android:id="@+id/recycler"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -535,6 +535,16 @@
|
||||||
app:popUpTo="@id/app_settings"
|
app:popUpTo="@id/app_settings"
|
||||||
app:popUpToInclusive="true" />
|
app:popUpToInclusive="true" />
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_direct_to_privacy"
|
||||||
|
app:destination="@id/privacySettingsFragment"
|
||||||
|
app:enterAnim="@anim/fragment_open_enter"
|
||||||
|
app:exitAnim="@anim/fragment_open_exit"
|
||||||
|
app:popEnterAnim="@anim/fragment_close_enter"
|
||||||
|
app:popExitAnim="@anim/fragment_close_exit"
|
||||||
|
app:popUpTo="@id/app_settings"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
|
|
||||||
<!-- endregion -->
|
<!-- endregion -->
|
||||||
|
|
||||||
<!-- Internal Settings -->
|
<!-- Internal Settings -->
|
||||||
|
|
|
@ -127,6 +127,11 @@
|
||||||
<item name="strokeWidth">0dp</item>
|
<item name="strokeWidth">0dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Signal.Widget.Button.Medium.OutlinedButton" parent="Signal.Widget.Button.Medium.Secondary">
|
||||||
|
<item name="strokeColor">@color/button_outline_color_selector</item>
|
||||||
|
<item name="strokeWidth">1.5dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="Signal.Widget.Button.Base.Tonal" parent="Widget.Material3.Button.TonalButton">
|
<style name="Signal.Widget.Button.Base.Tonal" parent="Widget.Material3.Button.TonalButton">
|
||||||
<item name="android:insetTop">0dp</item>
|
<item name="android:insetTop">0dp</item>
|
||||||
<item name="android:insetBottom">0dp</item>
|
<item name="android:insetBottom">0dp</item>
|
||||||
|
|
|
@ -4590,6 +4590,8 @@
|
||||||
<item quantity="one">%1$d reply</item>
|
<item quantity="one">%1$d reply</item>
|
||||||
<item quantity="other">%1$d replies</item>
|
<item quantity="other">%1$d replies</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<!-- Used when view receipts are disabled -->
|
||||||
|
<string name="StoryViewerPageFragment__views_off">Views off</string>
|
||||||
<!-- Used to join views and replies when both exist on a story item -->
|
<!-- Used to join views and replies when both exist on a story item -->
|
||||||
<string name="StoryViewerFragment__s_s">%1$s %2$s</string>
|
<string name="StoryViewerFragment__s_s">%1$s %2$s</string>
|
||||||
<!-- Displayed when viewing a post you sent -->
|
<!-- Displayed when viewing a post you sent -->
|
||||||
|
@ -4602,6 +4604,10 @@
|
||||||
<string name="StoryViewerPageFragment__reply_to_group">Reply to group</string>
|
<string name="StoryViewerPageFragment__reply_to_group">Reply to group</string>
|
||||||
<!-- Displayed when a story has no views -->
|
<!-- Displayed when a story has no views -->
|
||||||
<string name="StoryViewsFragment__no_views_yet">No views yet</string>
|
<string name="StoryViewsFragment__no_views_yet">No views yet</string>
|
||||||
|
<!-- Displayed when user has disabled receipts -->
|
||||||
|
<string name="StoryViewsFragment__enable_read_receipts_to_see_whos_viewed_your_story">Enable read receipts to see who\'s viewed your stories.</string>
|
||||||
|
<!-- Button label displayed when user has disabled receipts -->
|
||||||
|
<string name="StoryViewsFragment__go_to_settings">Go to settings</string>
|
||||||
<!-- Displayed when a story has no replies yet -->
|
<!-- Displayed when a story has no replies yet -->
|
||||||
<string name="StoryGroupReplyFragment__no_replies_yet">No replies yet</string>
|
<string name="StoryGroupReplyFragment__no_replies_yet">No replies yet</string>
|
||||||
<!-- Displayed for each user that reacted to a story when viewing replies -->
|
<!-- Displayed for each user that reacted to a story when viewing replies -->
|
||||||
|
|
Ładowanie…
Reference in New Issue