mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-23 10:20:25 +01:00
Improve the Banner system.
This commit is contained in:
@@ -16,7 +16,6 @@ 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
|
||||
@@ -58,10 +57,10 @@ class ConversationBannerView @JvmOverloads constructor(
|
||||
orientation = VERTICAL
|
||||
}
|
||||
|
||||
fun collectAndShowBanners(flows: Iterable<Flow<Banner>>) {
|
||||
fun collectAndShowBanners(flows: List<Banner<*>>) {
|
||||
val bannerManager = BannerManager(flows)
|
||||
show(stub = bannerStub) {
|
||||
bannerManager.setContent(this)
|
||||
bannerManager.updateContent(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.ConcatAdapter
|
||||
import androidx.recyclerview.widget.ConversationLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@@ -80,6 +82,10 @@ import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
@@ -106,8 +112,6 @@ import org.thoughtcrime.securesms.badges.gifts.OpenableGift
|
||||
import org.thoughtcrime.securesms.badges.gifts.OpenableGiftItemDecoration
|
||||
import org.thoughtcrime.securesms.badges.gifts.viewgift.received.ViewReceivedGiftBottomSheet
|
||||
import org.thoughtcrime.securesms.badges.gifts.viewgift.sent.ViewSentGiftBottomSheet
|
||||
import org.thoughtcrime.securesms.banner.banners.ServiceOutageBanner
|
||||
import org.thoughtcrime.securesms.banner.banners.UnauthorizedBanner
|
||||
import org.thoughtcrime.securesms.calls.YouAreAlreadyInACallSnackbar
|
||||
import org.thoughtcrime.securesms.components.AnimatingToggle
|
||||
import org.thoughtcrime.securesms.components.ComposeText
|
||||
@@ -311,7 +315,6 @@ import org.thoughtcrime.securesms.util.MessageConstraintsUtil.isValidEditMessage
|
||||
import org.thoughtcrime.securesms.util.PlayStoreUtil
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentUtil
|
||||
import org.thoughtcrime.securesms.util.SharedPreferencesLifecycleObserver
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics
|
||||
import org.thoughtcrime.securesms.util.StorageUtil
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
@@ -1025,33 +1028,26 @@ class ConversationFragment :
|
||||
val conversationBannerListener = ConversationBannerListener()
|
||||
binding.conversationBanner.listener = conversationBannerListener
|
||||
|
||||
val unauthorizedProducer = UnauthorizedBanner.Producer(requireContext())
|
||||
val serviceOutageProducer = ServiceOutageBanner.Producer(requireContext())
|
||||
lifecycle.addObserver(
|
||||
SharedPreferencesLifecycleObserver(
|
||||
requireContext(),
|
||||
mapOf(
|
||||
TextSecurePreferences.UNAUTHORIZED_RECEIVED to { unauthorizedProducer.queryAndEmit() },
|
||||
TextSecurePreferences.SERVICE_OUTAGE to { serviceOutageProducer.queryAndEmit() }
|
||||
lifecycleScope.launch {
|
||||
viewModel
|
||||
.getBannerFlows(
|
||||
context = requireContext(),
|
||||
groupJoinClickListener = conversationBannerListener::reviewJoinRequestsAction,
|
||||
onSuggestionAddMembers = {
|
||||
conversationGroupViewModel.groupRecordSnapshot?.let { groupRecord ->
|
||||
GroupsV1MigrationSuggestionsDialog.show(requireActivity(), groupRecord.id.requireV2(), groupRecord.gv1MigrationSuggestions)
|
||||
}
|
||||
},
|
||||
onSuggestionNoThanks = conversationGroupViewModel::onSuggestedMembersBannerDismissed,
|
||||
bubbleClickListener = conversationBannerListener::changeBubbleSettingAction
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val bannerFlows = viewModel.getBannerFlows(
|
||||
context = requireContext(),
|
||||
unauthorizedFlow = unauthorizedProducer.flow,
|
||||
serviceOutageStatusFlow = serviceOutageProducer.flow,
|
||||
groupJoinClickListener = conversationBannerListener::reviewJoinRequestsAction,
|
||||
onAddMembers = {
|
||||
conversationGroupViewModel.groupRecordSnapshot?.let { groupRecord ->
|
||||
GroupsV1MigrationSuggestionsDialog.show(requireActivity(), groupRecord.id.requireV2(), groupRecord.gv1MigrationSuggestions)
|
||||
.distinctUntilChanged()
|
||||
.flowWithLifecycle(viewLifecycleOwner.lifecycle)
|
||||
.flowOn(Dispatchers.Main)
|
||||
.collect {
|
||||
binding.conversationBanner.collectAndShowBanners(it)
|
||||
}
|
||||
},
|
||||
onNoThanks = conversationGroupViewModel::onSuggestedMembersBannerDismissed,
|
||||
bubbleClickListener = conversationBannerListener::changeBubbleSettingAction
|
||||
)
|
||||
|
||||
binding.conversationBanner.collectAndShowBanners(bannerFlows)
|
||||
}
|
||||
|
||||
if (TextSecurePreferences.getServiceOutage(context)) {
|
||||
AppDependencies.jobManager.add(ServiceOutageDetectionJob())
|
||||
|
||||
@@ -29,12 +29,13 @@ 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.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flatMapConcat
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.rx3.asFlow
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.paging.ProxyPagingController
|
||||
@@ -309,30 +310,42 @@ class ConversationViewModel(
|
||||
})
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun getBannerFlows(context: Context, unauthorizedFlow: Flow<UnauthorizedBanner>, serviceOutageStatusFlow: Flow<ServiceOutageBanner>, groupJoinClickListener: () -> Unit, onAddMembers: () -> Unit, onNoThanks: () -> Unit, bubbleClickListener: (Boolean) -> Unit): List<Flow<Banner>> {
|
||||
val pendingGroupJoinFlow: Flow<PendingGroupJoinRequestsBanner> = merge(
|
||||
flow {
|
||||
emit(PendingGroupJoinRequestsBanner(false, 0, {}, {}))
|
||||
},
|
||||
groupRecordFlow.flatMapConcat { PendingGroupJoinRequestsBanner.createFlow(it.actionableRequestingMembersCount, groupJoinClickListener) }
|
||||
)
|
||||
fun getBannerFlows(
|
||||
context: Context,
|
||||
groupJoinClickListener: () -> Unit,
|
||||
onSuggestionAddMembers: () -> Unit,
|
||||
onSuggestionNoThanks: () -> Unit,
|
||||
bubbleClickListener: (Boolean) -> Unit
|
||||
): Flow<List<Banner<*>>> {
|
||||
val pendingGroupJoinFlow: Flow<PendingGroupJoinRequestsBanner> = groupRecordFlow
|
||||
.map {
|
||||
PendingGroupJoinRequestsBanner(
|
||||
suggestionsSize = it.actionableRequestingMembersCount,
|
||||
onViewClicked = groupJoinClickListener
|
||||
)
|
||||
}
|
||||
|
||||
val groupV1SuggestionsFlow = merge(
|
||||
flow {
|
||||
emit(GroupsV1MigrationSuggestionsBanner(0, {}, {}))
|
||||
},
|
||||
groupRecordFlow.flatMapConcat { GroupsV1MigrationSuggestionsBanner.createFlow(it.gv1MigrationSuggestions.size, onAddMembers, onNoThanks) }
|
||||
)
|
||||
val groupV1SuggestionsFlow = groupRecordFlow
|
||||
.map {
|
||||
GroupsV1MigrationSuggestionsBanner(
|
||||
suggestionsSize = it.gv1MigrationSuggestions.size,
|
||||
onAddMembers = onSuggestionAddMembers,
|
||||
onNoThanks = onSuggestionNoThanks
|
||||
)
|
||||
}
|
||||
|
||||
return listOf(
|
||||
OutdatedBuildBanner.createFlow(context, OutdatedBuildBanner.ExpiryStatus.EXPIRED_ONLY),
|
||||
unauthorizedFlow,
|
||||
serviceOutageStatusFlow,
|
||||
pendingGroupJoinFlow,
|
||||
groupV1SuggestionsFlow,
|
||||
BubbleOptOutBanner.createFlow(inBubble = repository.isInBubble, bubbleClickListener)
|
||||
return combine(
|
||||
listOf(
|
||||
flowOf(OutdatedBuildBanner()),
|
||||
flowOf(UnauthorizedBanner(context)),
|
||||
flowOf(ServiceOutageBanner(context)),
|
||||
pendingGroupJoinFlow,
|
||||
groupV1SuggestionsFlow,
|
||||
flowOf(BubbleOptOutBanner(inBubble = repository.isInBubble, actionListener = bubbleClickListener))
|
||||
),
|
||||
transform = { it.toList() }
|
||||
)
|
||||
.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
||||
fun onChatBoundsChanged(bounds: Rect) {
|
||||
|
||||
Reference in New Issue
Block a user