Add Banners to all reminder usages behind remote config.

This commit is contained in:
Nicholas Tinsley
2024-08-09 17:41:02 -04:00
committed by mtang-signal
parent f296fcd716
commit e2e6a73e8d
18 changed files with 253 additions and 62 deletions

View File

@@ -14,8 +14,12 @@ import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.compose.ui.platform.ComposeView
import androidx.core.transition.addListener
import kotlinx.coroutines.flow.Flow
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.banner.Banner
import org.thoughtcrime.securesms.banner.BannerManager
import org.thoughtcrime.securesms.components.identity.UnverifiedBannerView
import org.thoughtcrime.securesms.components.reminder.Reminder
import org.thoughtcrime.securesms.components.reminder.ReminderView
@@ -47,6 +51,7 @@ class ConversationBannerView @JvmOverloads constructor(
) : LinearLayoutCompat(context, attrs, defStyleAttr) {
private val unverifiedBannerStub: Stub<UnverifiedBannerView> by lazy { ViewUtil.findStubById(this, R.id.unverified_banner_stub) }
private val reminderStub: Stub<ReminderView> by lazy { ViewUtil.findStubById(this, R.id.reminder_stub) }
private val bannerStub: Stub<ComposeView> by lazy { ViewUtil.findStubById(this, R.id.banner_stub) }
private val reviewBannerStub: Stub<ReviewBannerView> by lazy { ViewUtil.findStubById(this, R.id.review_banner_stub) }
private val voiceNotePlayerStub: Stub<View> by lazy { ViewUtil.findStubById(this, R.id.voice_note_player_stub) }
@@ -56,6 +61,13 @@ class ConversationBannerView @JvmOverloads constructor(
orientation = VERTICAL
}
fun collectAndShowBanners(flows: Iterable<Flow<Banner>>) {
val bannerManager = BannerManager(flows)
show(stub = bannerStub) {
bannerManager.setContent(this)
}
}
fun showReminder(reminder: Reminder) {
show(
stub = reminderStub

View File

@@ -228,6 +228,7 @@ import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationInfoBotto
import org.thoughtcrime.securesms.groups.ui.migration.GroupsV1MigrationSuggestionsDialog
import org.thoughtcrime.securesms.groups.v2.GroupBlockJoinRequestResult
import org.thoughtcrime.securesms.invites.InviteActions
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob
import org.thoughtcrime.securesms.keyboard.KeyboardPage
import org.thoughtcrime.securesms.keyboard.KeyboardPagerFragment
import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel
@@ -1016,17 +1017,38 @@ class ConversationFragment :
VoiceMessageRecordingSessionCallbacks()
)
binding.conversationBanner.listener = ConversationBannerListener()
viewModel
.reminder
.subscribeBy { reminder ->
if (reminder.isPresent) {
binding.conversationBanner.showReminder(reminder.get())
} else {
binding.conversationBanner.clearReminder()
}
val conversationBannerListener = ConversationBannerListener()
binding.conversationBanner.listener = conversationBannerListener
if (RemoteConfig.newBannerUi) {
val bannerFlows = viewModel.getBannerFlows(
context = requireContext(),
groupJoinClickListener = conversationBannerListener::reviewJoinRequestsAction,
onAddMembers = {
conversationGroupViewModel.groupRecordSnapshot?.let { groupRecord ->
GroupsV1MigrationSuggestionsDialog.show(requireActivity(), groupRecord.id.requireV2(), groupRecord.gv1MigrationSuggestions)
}
},
onNoThanks = conversationGroupViewModel::onSuggestedMembersBannerDismissed,
bubbleClickListener = conversationBannerListener::changeBubbleSettingAction
)
binding.conversationBanner.collectAndShowBanners(bannerFlows)
if (TextSecurePreferences.getServiceOutage(context)) {
AppDependencies.jobManager.add(ServiceOutageDetectionJob())
}
.addTo(disposables)
} else {
viewModel
.reminder
.subscribeBy { reminder ->
if (reminder.isPresent) {
binding.conversationBanner.showReminder(reminder.get())
} else {
binding.conversationBanner.clearReminder()
}
}
.addTo(disposables)
}
viewModel
.identityRecordsObservable

View File

@@ -29,9 +29,25 @@ import io.reactivex.rxjava3.schedulers.Schedulers
import io.reactivex.rxjava3.subjects.BehaviorSubject
import io.reactivex.rxjava3.subjects.PublishSubject
import io.reactivex.rxjava3.subjects.Subject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flatMap
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.rx3.asFlow
import org.signal.core.util.concurrent.subscribeWithSubject
import org.signal.core.util.orNull
import org.signal.paging.ProxyPagingController
import org.thoughtcrime.securesms.banner.Banner
import org.thoughtcrime.securesms.banner.banners.BubbleOptOutBanner
import org.thoughtcrime.securesms.banner.banners.GroupsV1MigrationSuggestionsBanner
import org.thoughtcrime.securesms.banner.banners.OutdatedBuildBanner
import org.thoughtcrime.securesms.banner.banners.PendingGroupJoinRequestsBanner
import org.thoughtcrime.securesms.banner.banners.PendingGroupJoinRequestsBanner.Producer
import org.thoughtcrime.securesms.banner.banners.ServiceOutageBanner
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner
import org.thoughtcrime.securesms.components.reminder.Reminder
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.conversation.ConversationMessage
@@ -42,6 +58,7 @@ import org.thoughtcrime.securesms.conversation.v2.data.ConversationElementKey
import org.thoughtcrime.securesms.conversation.v2.items.ChatColorsDrawable
import org.thoughtcrime.securesms.database.DatabaseObserver
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.database.model.IdentityRecord
import org.thoughtcrime.securesms.database.model.Mention
import org.thoughtcrime.securesms.database.model.MessageId
@@ -155,6 +172,8 @@ class ConversationViewModel(
private val refreshReminder: Subject<Unit> = PublishSubject.create()
val reminder: Observable<Optional<Reminder>>
private val groupRecordFlow: Flow<GroupRecord?>
private val refreshIdentityRecords: Subject<Unit> = PublishSubject.create()
private val identityRecordsStore: RxStore<IdentityRecordsState> = RxStore(IdentityRecordsState())
val identityRecordsObservable: Observable<IdentityRecordsState> = identityRecordsStore.stateFlowable.toObservable()
@@ -276,6 +295,8 @@ class ConversationViewModel(
.flatMapMaybe { groupRecord -> repository.getReminder(groupRecord.orNull()) }
.observeOn(AndroidSchedulers.mainThread())
groupRecordFlow = recipientRepository.groupRecord.subscribeOn(Schedulers.io()).asFlow().map { it.orNull() }
Observable.combineLatest(
refreshIdentityRecords.startWithItem(Unit).observeOn(Schedulers.io()),
recipient,
@@ -299,6 +320,36 @@ class ConversationViewModel(
})
}
@OptIn(ExperimentalCoroutinesApi::class)
fun getBannerFlows(context: Context, groupJoinClickListener: () -> Unit, onAddMembers: () -> Unit, onNoThanks: () -> Unit, bubbleClickListener: (Boolean) -> Unit): List<Flow<Banner>> {
val pendingGroupJoinFlow = groupRecordFlow.flatMapConcat {
flow {
if (it == null) {
emit(PendingGroupJoinRequestsBanner(false, 0, {}, {}))
} else {
emitAll(Producer(it.actionableRequestingMembersCount, groupJoinClickListener).flow)
}
}
}
val groupV1SuggestionsFlow = groupRecordFlow.map {
if (it == null) {
GroupsV1MigrationSuggestionsBanner(0, {}, {})
} else {
GroupsV1MigrationSuggestionsBanner(it.gv1MigrationSuggestions.size, onAddMembers, onNoThanks)
}
}
return listOf(
OutdatedBuildBanner.createFlow(context, OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
UnauthorizedBanner.createFlow(context),
ServiceOutageBanner.createFlow(context),
pendingGroupJoinFlow,
groupV1SuggestionsFlow,
BubbleOptOutBanner.createFlow(inBubble = true, bubbleClickListener)
)
}
fun onChatBoundsChanged(bounds: Rect) {
chatBounds.onNext(bounds)
}