From 4fdc08b3f2ec6ebe1a8ce3fa361d3354ea322be1 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 16 Sep 2026 12:13:04 -0400 Subject: [PATCH] chore(chat): retire the GroupChats flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Group chats reach the chat list unconditionally. The flag only ever filtered the list — sync was already unconditional — and it was unreachable outside internal builds: GroupChats inherited FeatureTrack.Internal, which the Labs screen hides without the beta override, over a default of false. So it gated nothing for anyone who was not staff, and staff are the only accounts the server places in a group today. Membership is server-assigned, so there is no client action to gate. When create, join and leave arrive they gate actions rather than visibility, which is a different flag. --- .../app/tipping/internal/TipFlowViewModel.kt | 15 +-------------- .../shared/chat/internal/RealChatCoordinator.kt | 5 ----- .../flipcash/shared/chat/GroupChatRoutingTest.kt | 2 -- .../com/flipcash/app/featureflags/FeatureFlag.kt | 11 ----------- 4 files changed, 1 insertion(+), 32 deletions(-) diff --git a/apps/flipcash/features/tipping/src/main/kotlin/com/flipcash/app/tipping/internal/TipFlowViewModel.kt b/apps/flipcash/features/tipping/src/main/kotlin/com/flipcash/app/tipping/internal/TipFlowViewModel.kt index 57257f7932..63455c5acf 100644 --- a/apps/flipcash/features/tipping/src/main/kotlin/com/flipcash/app/tipping/internal/TipFlowViewModel.kt +++ b/apps/flipcash/features/tipping/src/main/kotlin/com/flipcash/app/tipping/internal/TipFlowViewModel.kt @@ -4,8 +4,6 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import com.flipcash.app.core.bill.Scannable import com.flipcash.app.core.data.Loadable -import com.flipcash.app.featureflags.FeatureFlag -import com.flipcash.app.featureflags.FeatureFlagController import com.flipcash.app.core.extensions.onResult import com.flipcash.app.core.tipping.TipStep import com.flipcash.app.shareable.ShareSheetController @@ -21,18 +19,15 @@ import com.flipcash.shared.chat.ui.toConversationReference import com.flipcash.shared.tipping.TippingCoordinator import com.getcode.util.resources.ResourceHelper import com.getcode.view.BaseViewModel -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import javax.inject.Inject -@OptIn(ExperimentalCoroutinesApi::class) @HiltViewModel internal class TipFlowViewModel @Inject constructor( chatCoordinator: ChatCoordinator, @@ -41,7 +36,6 @@ internal class TipFlowViewModel @Inject constructor( tokenCoordinator: TokenCoordinator, shareable: ShareSheetController, tipCodePreviewCache: TipCodePreviewCache, - featureFlags: FeatureFlagController, private val resources: ResourceHelper, ) : BaseViewModel( initialState = State(), @@ -93,14 +87,7 @@ internal class TipFlowViewModel @Inject constructor( .launchIn(viewModelScope) combine( - featureFlags.observe(FeatureFlag.GroupChats) - .flatMapLatest { groupsOn -> - if (groupsOn) { - chatCoordinator.feed(ChatType.TIP_DM, ChatType.GROUP) - } else { - chatCoordinator.feed(ChatType.TIP_DM) - } - }, + chatCoordinator.feed(ChatType.TIP_DM, ChatType.GROUP), tokenCoordinator.tokens, ) { summaries, tokens -> val selfId = userManager.accountId diff --git a/apps/flipcash/shared/chat/src/main/kotlin/com/flipcash/shared/chat/internal/RealChatCoordinator.kt b/apps/flipcash/shared/chat/src/main/kotlin/com/flipcash/shared/chat/internal/RealChatCoordinator.kt index 740a0bd1c1..0286fc1683 100644 --- a/apps/flipcash/shared/chat/src/main/kotlin/com/flipcash/shared/chat/internal/RealChatCoordinator.kt +++ b/apps/flipcash/shared/chat/src/main/kotlin/com/flipcash/shared/chat/internal/RealChatCoordinator.kt @@ -123,11 +123,6 @@ class RealChatCoordinator @Inject constructor( feedDelegate.observeFeedFromDb() feedDelegate.syncFeed() groupFeedDelegate.initialize(scope) - // Synced unconditionally. The GroupChats flag filters the chat list, and every read of - // this cache is already scoped to the chat types its caller asked for, so a group sitting - // in the database with the flag off is invisible rather than latent. Gating the fetch - // instead made the toggle do nothing until the process restarted, and contradicted the - // roster-change routing below, which was never gated. groupFeedDelegate.syncGroupFeed() eventStreamDelegate.open() eventStreamDelegate.startHeartbeat { feedDelegate.syncFeed() } diff --git a/apps/flipcash/shared/chat/src/test/kotlin/com/flipcash/shared/chat/GroupChatRoutingTest.kt b/apps/flipcash/shared/chat/src/test/kotlin/com/flipcash/shared/chat/GroupChatRoutingTest.kt index 042100d7ae..c54d907b31 100644 --- a/apps/flipcash/shared/chat/src/test/kotlin/com/flipcash/shared/chat/GroupChatRoutingTest.kt +++ b/apps/flipcash/shared/chat/src/test/kotlin/com/flipcash/shared/chat/GroupChatRoutingTest.kt @@ -167,8 +167,6 @@ class GroupChatRoutingTest { subject.onUserLoggedIn(mockk(relaxed = true)) runCurrent() - // Unconditional: the GroupChats flag filters the list, it does not decide whether the - // cache is populated. Gating the fetch here made the toggle a no-op until the next launch. coVerify { groupFeedDelegate.syncGroupFeed() } subject.teardown() } diff --git a/apps/flipcash/shared/featureflags/src/main/kotlin/com/flipcash/app/featureflags/FeatureFlag.kt b/apps/flipcash/shared/featureflags/src/main/kotlin/com/flipcash/app/featureflags/FeatureFlag.kt index 9b36ac8873..08703a32dc 100644 --- a/apps/flipcash/shared/featureflags/src/main/kotlin/com/flipcash/app/featureflags/FeatureFlag.kt +++ b/apps/flipcash/shared/featureflags/src/main/kotlin/com/flipcash/app/featureflags/FeatureFlag.kt @@ -117,15 +117,6 @@ sealed interface FeatureFlag { override val persistLogOut: Boolean = false } - @FeatureFlagMarker - data object GroupChats: FeatureFlag { - override val key: String = "group_chats_enabled" - override val default: Boolean = false - override val launched: Boolean = false - override val visible: Boolean = true - override val persistLogOut: Boolean = false - } - companion object { val entries: List> get() = FeatureFlagEntries.entries @@ -148,7 +139,6 @@ val FeatureFlag<*>.title: String FeatureFlag.ContactPickerMode -> "Contact Picker Mode" FeatureFlag.ShowNetworkState -> "Network Offline Indicator" FeatureFlag.FrostedTipCard -> "Frosted Tip Card" - FeatureFlag.GroupChats -> "Group Chats" } val FeatureFlag<*>.message: String @@ -162,7 +152,6 @@ val FeatureFlag<*>.message: String FeatureFlag.ContactPickerMode -> "When enabled, contacts will be accessed via the system contact picker instead of requesting full READ_CONTACTS permission" FeatureFlag.ShowNetworkState -> "When enabled, you'll gain the ability to see the network state on the Scanner when offline" FeatureFlag.FrostedTipCard -> "When enabled, the tip card in the scanner renders as frosted glass over a blurred snapshot of the camera instead of a solid card" - FeatureFlag.GroupChats -> "When enabled, group chats appear in your chat list alongside your direct messages" }