Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client

[APP-1783] Clean up some feature gates and logging (#9729)

* Remove gates we don't need

* Clean up some logging

* Keep disable gates around for now

* Add missing gate

* Revert file

* Revert package changes

authored by

Eric Bailey and committed by
GitHub
bd9e8244 5989bf7f

+60 -278
+1
eslint.config.mjs
··· 72 72 sourceType: 'module', 73 73 globals: { 74 74 ...globals.browser, 75 + ...globals.node, 75 76 }, 76 77 parserOptions: { 77 78 parser: tsParser,
-1
jest/jestSetup.js
··· 114 114 }, 115 115 })) 116 116 117 - jest.mock('../src/logger/bitdrift/lib', () => ({})) 118 117 jest.mock('../src/lib/statsig/statsig', () => ({}))
+4 -3
src/App.native.tsx
··· 1 1 import '#/logger/sentry/setup' 2 - import '#/logger/bitdrift/setup' 3 2 import '#/view/icons' 4 3 5 4 import React, {useEffect, useState} from 'react' ··· 66 65 import {Provider as PortalProvider} from '#/components/Portal' 67 66 import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' 68 67 import {ToastOutlet} from '#/components/Toast' 69 - import {Provider as AgeAssuranceV2Provider} from '#/ageAssurance' 70 - import {prefetchAgeAssuranceConfig} from '#/ageAssurance' 68 + import { 69 + prefetchAgeAssuranceConfig, 70 + Provider as AgeAssuranceV2Provider, 71 + } from '#/ageAssurance' 71 72 import {IS_ANDROID, IS_IOS} from '#/env' 72 73 import { 73 74 prefetchLiveEvents,
+3 -5
src/components/FeedInterstitials.tsx
··· 7 7 import {useNavigation} from '@react-navigation/native' 8 8 9 9 import {type NavigationProp} from '#/lib/routes/types' 10 - import {logEvent, useGate} from '#/lib/statsig/statsig' 10 + import {logEvent} from '#/lib/statsig/statsig' 11 11 import {logger} from '#/logger' 12 12 import {type MetricEvents} from '#/logger/metrics' 13 13 import {useModerationOpts} from '#/state/preferences/moderation-opts' ··· 435 435 }) { 436 436 const t = useTheme() 437 437 const {_} = useLingui() 438 - const gate = useGate() 439 438 const moderationOpts = useModerationOpts() 440 439 const {gtMobile} = useBreakpoints() 441 440 const followDialogControl = useDialogControl() ··· 443 442 const isLoading = isSuggestionsLoading || !moderationOpts 444 443 const isProfileHeaderContext = viewContext === 'profileHeader' 445 444 const isFeedContext = viewContext === 'feed' 446 - const showDismissButton = onDismiss && gate('suggested_users_dismiss') 447 445 448 446 const maxLength = gtMobile ? 3 : isProfileHeaderContext ? 12 : 6 449 447 const minLength = gtMobile ? 3 : 4 ··· 584 582 (hovered || pressed) && t.atoms.border_contrast_high, 585 583 ]}> 586 584 <ProfileCard.Outer> 587 - {showDismissButton && ( 585 + {onDismiss && ( 588 586 <Button 589 587 label={_(msg`Dismiss this suggestion`)} 590 588 onPress={e => { 591 589 e.preventDefault() 592 - onDismiss!(profile.did) 590 + onDismiss(profile.did) 593 591 logEvent('suggestedUser:dismiss', { 594 592 logContext: isFeedContext 595 593 ? 'InterstitialDiscover'
+1 -8
src/components/PostControls/ShareMenu/index.tsx
··· 12 12 13 13 import {makeProfileLink} from '#/lib/routes/links' 14 14 import {shareUrl} from '#/lib/sharing' 15 - import {useGate} from '#/lib/statsig/statsig' 16 15 import {toShareUrl} from '#/lib/strings/url-helpers' 17 16 import {logger} from '#/logger' 18 17 import {type Shadow} from '#/state/cache/post-shadow' 19 18 import {useFeedFeedbackContext} from '#/state/feed-feedback' 20 19 import {EventStopper} from '#/view/com/util/EventStopper' 21 20 import {native} from '#/alf' 22 - import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox' 23 21 import {ArrowShareRight_Stroke2_Corner2_Rounded as ArrowShareRightIcon} from '#/components/icons/ArrowShareRight' 24 22 import {useMenuControl} from '#/components/Menu' 25 23 import * as Menu from '#/components/Menu' ··· 50 48 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 51 49 }): React.ReactNode => { 52 50 const {_} = useLingui() 53 - const gate = useGate() 54 51 const {feedDescriptor} = useFeedFeedbackContext() 55 - 56 - const ShareIcon = gate('alt_share_icon') 57 - ? ArrowShareRightIcon 58 - : ArrowOutOfBoxIcon 59 52 60 53 const menuControl = useMenuControl() 61 54 const [hasBeenOpen, setHasBeenOpen] = useState(false) ··· 114 107 {...props} 115 108 onLongPress={native(onNativeLongPress)} 116 109 hitSlop={hitSlop}> 117 - <PostControlButtonIcon icon={ShareIcon} /> 110 + <PostControlButtonIcon icon={ArrowShareRightIcon} /> 118 111 </PostControlButton> 119 112 ) 120 113 }}
+1 -4
src/components/contacts/FindContactsBannerNUX.tsx
··· 6 6 import {useLingui} from '@lingui/react' 7 7 8 8 import {HITSLOP_10} from '#/lib/constants' 9 - import {useGate} from '#/lib/statsig/statsig' 10 9 import {logger} from '#/logger' 11 10 import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs' 12 11 import {atoms as a, useTheme} from '#/alf' ··· 89 88 const {mutate: save, variables} = useSaveNux() 90 89 const hidden = !!variables 91 90 const isFeatureEnabled = useIsFindContactsFeatureEnabledBasedOnGeolocation() 92 - const gate = useGate() 93 91 94 92 const visible = useMemo(() => { 95 93 if (IS_WEB) return false 96 94 if (hidden) return false 97 95 if (nux && nux.completed) return false 98 96 if (!isFeatureEnabled) return false 99 - if (gate('disable_settings_find_contacts')) return false 100 97 return true 101 - }, [hidden, nux, isFeatureEnabled, gate]) 98 + }, [hidden, nux, isFeatureEnabled]) 102 99 103 100 const close = () => { 104 101 save({
+1 -2
src/components/dialogs/nuxs/LiveNowBetaDialog.tsx
··· 15 15 } from '#/components/dialogs/nuxs/utils' 16 16 import {Beaker_Stroke2_Corner2_Rounded as BeakerIcon} from '#/components/icons/Beaker' 17 17 import {Text} from '#/components/Typography' 18 - import {IS_WEB} from '#/env' 19 - import {IS_E2E} from '#/env' 18 + import {IS_E2E, IS_WEB} from '#/env' 20 19 21 20 export const enabled = createIsEnabledCheck(props => { 22 21 return (
+2 -14
src/lib/statsig/gates.ts
··· 1 1 export type Gate = 2 2 // Keep this alphabetic please. 3 - | 'alt_share_icon' 4 3 | 'debug_show_feedcontext' 5 - | 'debug_subscriptions' 6 - | 'disable_live_now_beta' 4 + | 'is_bsky_team_member' // special, do not remove 7 5 | 'disable_onboarding_find_contacts' 8 6 | 'disable_settings_find_contacts' 9 - | 'explore_show_suggested_feeds' 10 - | 'feed_reply_button_open_thread' 11 - | 'is_bsky_team_member' // special, do not remove 12 - | 'old_postonboarding' 13 - | 'onboarding_add_video_feed' 14 - | 'onboarding_suggested_starterpacks' 15 - | 'remove_show_latest_button' 16 - | 'show_composer_prompt' 17 - | 'suggested_users_dismiss' 18 - | 'test_gate_1' 19 - | 'test_gate_2' 7 + | 'disable_live_now_beta'
-1
src/logger/bitdrift/lib/index.ts
··· 1 - export {debug, error, info, warn} from '@bitdrift/react-native'
-4
src/logger/bitdrift/lib/index.web.ts
··· 1 - export function debug() {} 2 - export function error() {} 3 - export function info() {} 4 - export function warn() {}
-27
src/logger/bitdrift/setup/index.ts
··· 1 - import {init, SessionStrategy} from '@bitdrift/react-native' 2 - import {Statsig} from 'statsig-react-native-expo' 3 - 4 - import {initPromise} from '#/lib/statsig/statsig' 5 - import {BITDRIFT_API_KEY} from '#/env' 6 - 7 - initPromise.then(() => { 8 - let isEnabled = false 9 - let isNetworkEnabled = false 10 - try { 11 - if (Statsig.checkGate('enable_bitdrift_v2')) { 12 - isEnabled = true 13 - } 14 - if (Statsig.checkGate('enable_bitdrift_v2_networking')) { 15 - isNetworkEnabled = true 16 - } 17 - } catch (e) { 18 - // Statsig may complain about it being called too early. 19 - } 20 - if (isEnabled && BITDRIFT_API_KEY) { 21 - init(BITDRIFT_API_KEY, SessionStrategy.Activity, { 22 - url: 'https://api-bsky.bitdrift.io', 23 - // Only effects iOS, Android instrumentation is set via Gradle Plugin 24 - enableNetworkInstrumentation: isNetworkEnabled, 25 - }) 26 - } 27 - })
src/logger/bitdrift/setup/index.web.ts

This is a binary file and will not be displayed.

+1 -5
src/logger/index.ts
··· 3 3 import {logEvent} from '#/lib/statsig/statsig' 4 4 import {add} from '#/logger/logDump' 5 5 import {type MetricEvents} from '#/logger/metrics' 6 - import {bitdriftTransport} from '#/logger/transports/bitdrift' 7 6 import {consoleTransport} from '#/logger/transports/console' 8 7 import {sentryTransport} from '#/logger/transports/sentry' 9 8 import { ··· 13 12 type Transport, 14 13 } from '#/logger/types' 15 14 import {enabledLogLevels} from '#/logger/util' 16 - import {IS_NATIVE} from '#/env' 17 15 import {ENV} from '#/env' 18 16 19 17 export {type MetricEvents as Metrics} from '#/logger/metrics' ··· 21 19 const TRANSPORTS: Transport[] = (function configureTransports() { 22 20 switch (ENV) { 23 21 case 'production': { 24 - return [sentryTransport, IS_NATIVE && bitdriftTransport].filter( 25 - Boolean, 26 - ) as Transport[] 22 + return [sentryTransport].filter(Boolean) 27 23 } 28 24 case 'test': { 29 25 return []
-24
src/logger/transports/bitdrift.ts
··· 1 - import {debug, error, info, warn} from '#/logger/bitdrift/lib' 2 - import {LogLevel, type Transport} from '#/logger/types' 3 - import {prepareMetadata} from '#/logger/util' 4 - 5 - const logFunctions = { 6 - [LogLevel.Debug]: debug, 7 - [LogLevel.Info]: info, 8 - [LogLevel.Log]: info, 9 - [LogLevel.Warn]: warn, 10 - [LogLevel.Error]: error, 11 - } as const 12 - 13 - export const bitdriftTransport: Transport = ( 14 - level, 15 - context, 16 - message, 17 - metadata, 18 - ) => { 19 - const log = logFunctions[level] 20 - log(message.toString(), { 21 - __context__: context, 22 - ...prepareMetadata(metadata), 23 - }) 24 - }
+5 -11
src/screens/Onboarding/StepFinished/index.tsx
··· 20 20 VIDEO_SAVED_FEED, 21 21 } from '#/lib/constants' 22 22 import {useRequestNotificationsPermission} from '#/lib/notifications/notifications' 23 - import {logEvent, useGate} from '#/lib/statsig/statsig' 23 + import {logEvent} from '#/lib/statsig/statsig' 24 24 import {logger} from '#/logger' 25 25 import {useSetHasCheckedForStarterPack} from '#/state/preferences/used-starter-packs' 26 26 import {getAllListMembers} from '#/state/queries/list-members' ··· 61 61 const setActiveStarterPack = useSetActiveStarterPack() 62 62 const setHasCheckedForStarterPack = useSetHasCheckedForStarterPack() 63 63 const {startProgressGuide} = useProgressGuideControls() 64 - const gate = useGate() 65 64 66 65 const finishOnboarding = useCallback(async () => { 67 66 setSaving(true) ··· 114 113 ...TIMELINE_SAVED_FEED, 115 114 id: TID.nextStr(), 116 115 }, 117 - ] 118 - if (gate('onboarding_add_video_feed')) { 119 - feedsToSave.push({ 116 + { 120 117 ...VIDEO_SAVED_FEED, 121 118 id: TID.nextStr(), 122 - }) 123 - } 119 + }, 120 + ] 124 121 125 122 // Any starter pack feeds will be pinned _after_ the defaults 126 123 if (starterPack && starterPack.feeds?.length) { ··· 200 197 setSaving(false) 201 198 setActiveStarterPack(undefined) 202 199 setHasCheckedForStarterPack(true) 203 - startProgressGuide( 204 - gate('old_postonboarding') ? 'like-10-and-follow-7' : 'follow-10', 205 - ) 200 + startProgressGuide('follow-10') 206 201 dispatch({type: 'finish'}) 207 202 onboardDispatch({type: 'finish'}) 208 203 logEvent('onboarding:finished:nextPressed', { ··· 238 233 setActiveStarterPack, 239 234 setHasCheckedForStarterPack, 240 235 startProgressGuide, 241 - gate, 242 236 ]) 243 237 244 238 return (
+3 -7
src/screens/Onboarding/index.tsx
··· 23 23 import {useFindContactsFlowState} from '#/components/contacts/state' 24 24 import {Portal} from '#/components/Portal' 25 25 import {ScreenTransition} from '#/components/ScreenTransition' 26 - import {IS_NATIVE} from '#/env' 27 - import {ENV} from '#/env' 26 + import {ENV, IS_NATIVE} from '#/env' 28 27 import {StepFindContacts} from './StepFindContacts' 29 28 import {StepFindContactsIntro} from './StepFindContactsIntro' 30 29 import {StepSuggestedAccounts} from './StepSuggestedAccounts' 31 30 import {StepSuggestedStarterpacks} from './StepSuggestedStarterpacks' 32 31 33 32 export function Onboarding() { 34 - const gate = useGate() 35 33 const t = useTheme() 34 + const gate = useGate() 36 35 37 36 const {contentLanguages} = useLanguagePrefs() 38 37 const probablySpeaksEnglish = useMemo(() => { ··· 41 40 }, [contentLanguages]) 42 41 43 42 // starter packs screen is currently geared towards english-speaking accounts 44 - const showSuggestedStarterpacks = 45 - ENV !== 'e2e' && 46 - probablySpeaksEnglish && 47 - gate('onboarding_suggested_starterpacks') 43 + const showSuggestedStarterpacks = ENV !== 'e2e' && probablySpeaksEnglish 48 44 49 45 const findContactsEnabled = 50 46 useIsFindContactsFeatureEnabledBasedOnGeolocation()
+2 -5
src/screens/Settings/AppIconSettings/index.tsx
··· 7 7 8 8 import {PressableScale} from '#/lib/custom-animations/PressableScale' 9 9 import {type CommonNavigatorParams} from '#/lib/routes/types' 10 - import {useGate} from '#/lib/statsig/statsig' 11 10 import {AppIconImage} from '#/screens/Settings/AppIconSettings/AppIconImage' 12 11 import {type AppIconSet} from '#/screens/Settings/AppIconSettings/types' 13 12 import {useAppIconSets} from '#/screens/Settings/AppIconSettings/useAppIconSets' ··· 15 14 import * as Toggle from '#/components/forms/Toggle' 16 15 import * as Layout from '#/components/Layout' 17 16 import {Text} from '#/components/Typography' 18 - import {IS_ANDROID} from '#/env' 19 - import {IS_INTERNAL} from '#/env' 17 + import {IS_ANDROID, IS_INTERNAL} from '#/env' 20 18 21 19 type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppIconSettings'> 22 20 export function AppIconSettingsScreen({}: Props) { 23 21 const t = useTheme() 24 22 const {_} = useLingui() 25 23 const sets = useAppIconSets() 26 - const gate = useGate() 27 24 const [currentAppIcon, setCurrentAppIcon] = useState(() => 28 25 getAppIconName(DynamicAppIcon.getAppIcon()), 29 26 ) ··· 86 83 ))} 87 84 </Group> 88 85 89 - {IS_INTERNAL && gate('debug_subscriptions') && ( 86 + {IS_INTERNAL && ( 90 87 <> 91 88 <Text 92 89 style={[
+2 -4
src/screens/Settings/Settings.tsx
··· 1 1 import {useState} from 'react' 2 - import {Alert, LayoutAnimation, Pressable, View} from 'react-native' 3 - import {Linking} from 'react-native' 2 + import {Alert, LayoutAnimation, Linking, Pressable, View} from 'react-native' 4 3 import {useReducedMotion} from 'react-native-reanimated' 5 4 import {type AppBskyActorDefs, moderateProfile} from '@atproto/api' 6 5 import {msg, Trans} from '@lingui/macro' ··· 70 69 shouldShowVerificationCheckButton, 71 70 VerificationCheckButton, 72 71 } from '#/components/verification/VerificationCheckButton' 73 - import {IS_IOS, IS_NATIVE} from '#/env' 74 - import {IS_INTERNAL} from '#/env' 72 + import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env' 75 73 import {device, useStorage} from '#/storage' 76 74 import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged' 77 75
+6 -92
src/state/session/logging.ts
··· 1 1 import {type AtpSessionData, type AtpSessionEvent} from '@atproto/api' 2 - import {sha256} from 'js-sha256' 3 - import {Statsig} from 'statsig-react-native-expo' 4 2 5 - import {IS_INTERNAL} from '#/env' 6 3 import {type Schema} from '../persisted' 7 4 import {type Action, type State} from './reducer' 8 5 import {type SessionAccount} from './types' ··· 68 65 } 69 66 } 70 67 71 - let nextMessageIndex = 0 72 - const MAX_SLICE_LENGTH = 1000 73 - 74 - // Not gated. 75 - export function addSessionErrorLog(did: string, event: AtpSessionEvent) { 76 - try { 77 - if (!Statsig.initializeCalled() || !Statsig.getStableID()) { 78 - return 79 - } 80 - const stack = (new Error().stack ?? '').slice(0, MAX_SLICE_LENGTH) 81 - Statsig.logEvent('session:error', null, { 82 - did, 83 - event, 84 - stack, 85 - }) 86 - } catch (e) { 87 - console.error(e) 88 - } 89 - } 90 - 91 - export function addSessionDebugLog(log: Log) { 92 - try { 93 - if (!Statsig.initializeCalled() || !Statsig.getStableID()) { 94 - // Drop these logs for now. 95 - return 96 - } 97 - // DISABLING THIS GATE DUE TO EME @TODO EME-GATE 98 - if (!IS_INTERNAL) { 99 - return 100 - } 101 - // if (!Statsig.checkGate('debug_session')) { 102 - // return 103 - // } 104 - const messageIndex = nextMessageIndex++ 105 - const {type, ...content} = log 106 - let payload = JSON.stringify(content, replacer) 107 - 108 - let nextSliceIndex = 0 109 - while (payload.length > 0) { 110 - const sliceIndex = nextSliceIndex++ 111 - const slice = payload.slice(0, MAX_SLICE_LENGTH) 112 - payload = payload.slice(MAX_SLICE_LENGTH) 113 - Statsig.logEvent('session:debug', null, { 114 - realmId, 115 - messageIndex: String(messageIndex), 116 - messageType: type, 117 - sliceIndex: String(sliceIndex), 118 - slice, 119 - }) 120 - } 121 - } catch (e) { 122 - console.error(e) 123 - } 124 - } 125 - 126 - let agentIds = new WeakMap<object, string>() 127 - let realmId = Math.random().toString(36).slice(2) 128 - let nextAgentId = 1 129 - 130 - function getAgentId(agent: object) { 131 - let id = agentIds.get(agent) 132 - if (id === undefined) { 133 - id = realmId + '::' + nextAgentId++ 134 - agentIds.set(agent, id) 135 - } 136 - return id 137 - } 138 - 139 - function replacer(key: string, value: unknown) { 140 - if (typeof value === 'object' && value != null && 'api' in value) { 141 - return getAgentId(value) 142 - } 143 - if ( 144 - key === 'service' || 145 - key === 'email' || 146 - key === 'emailConfirmed' || 147 - key === 'emailAuthFactor' || 148 - key === 'pdsUrl' 149 - ) { 150 - return undefined 151 - } 152 - if ( 153 - typeof value === 'string' && 154 - (key === 'refreshJwt' || key === 'accessJwt') 155 - ) { 156 - return sha256(value) 157 - } 158 - return value 159 - } 68 + /** 69 + * Stubs, previously used to log session errors to Statsig. We may revive this 70 + * using Sentry or Bitdrift in the future. 71 + */ 72 + export function addSessionErrorLog(_did: string, _event: AtpSessionEvent) {} 73 + export function addSessionDebugLog(_log: Log) {}
+3 -6
src/view/com/posts/PostFeed.tsx
··· 31 31 import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants' 32 32 import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' 33 33 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 34 - import {logEvent, useGate} from '#/lib/statsig/statsig' 34 + import {logEvent} from '#/lib/statsig/statsig' 35 35 import {isNetworkError} from '#/lib/strings/errors' 36 36 import {logger} from '#/logger' 37 37 import {usePostAuthorShadowFilter} from '#/state/cache/profile-shadow' ··· 235 235 const {_} = useLingui() 236 236 const queryClient = useQueryClient() 237 237 const {currentAccount, hasSession} = useSession() 238 - const gate = useGate() 239 238 const initialNumToRender = useInitialNumToRender() 240 239 const feedFeedback = useFeedFeedbackContext() 241 240 const [isPTRing, setIsPTRing] = useState(false) ··· 522 521 if ( 523 522 hasSession && 524 523 (feedUriOrActorDid === DISCOVER_FEED_URI || 525 - feed === 'following') && 526 - gate('show_composer_prompt') 524 + feed === 'following') 527 525 ) { 528 526 arr.push({ 529 527 type: 'composerPrompt', ··· 546 544 } else if (feedKind === 'following') { 547 545 if (sliceIndex === 0) { 548 546 // Show composer prompt for Following feed 549 - if (hasSession && gate('show_composer_prompt')) { 547 + if (hasSession) { 550 548 arr.push({ 551 549 type: 'composerPrompt', 552 550 key: 'composerPrompt-' + sliceIndex, ··· 680 678 hasPressedShowLessUris, 681 679 ageAssuranceBannerState, 682 680 isCurrentFeedAtStartupSelected, 683 - gate, 684 681 blockedOrMutedAuthors, 685 682 ]) 686 683
+12 -25
src/view/com/posts/PostFeedItem.tsx
··· 9 9 type ModerationDecision, 10 10 RichText as RichTextAPI, 11 11 } from '@atproto/api' 12 - import {useNavigation} from '@react-navigation/native' 13 12 import {useQueryClient} from '@tanstack/react-query' 14 13 15 14 import {useActorStatus} from '#/lib/actor-status' ··· 18 17 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 19 18 import {usePalette} from '#/lib/hooks/usePalette' 20 19 import {makeProfileLink} from '#/lib/routes/links' 21 - import {type NavigationProp} from '#/lib/routes/types' 22 - import {useGate} from '#/lib/statsig/statsig' 23 20 import {countLines} from '#/lib/strings/helpers' 24 21 import {logger} from '#/logger' 25 22 import { ··· 51 48 import {DiscoverDebug} from '#/components/PostControls/DiscoverDebug' 52 49 import {RichText} from '#/components/RichText' 53 50 import {SubtleHover} from '#/components/SubtleHover' 54 - import {ENV} from '#/env' 55 51 import * as bsky from '#/types/bsky' 56 52 import {PostFeedReason} from './PostFeedReason' 57 53 ··· 164 160 }): React.ReactNode => { 165 161 const queryClient = useQueryClient() 166 162 const {openComposer} = useOpenComposer() 167 - const navigation = useNavigation<NavigationProp>() 168 163 const pal = usePalette('default') 169 - const gate = useGate() 170 164 171 165 const [hover, setHover] = useState(false) 172 166 173 - const [href, rkey] = useMemo(() => { 167 + const [href] = useMemo(() => { 174 168 const urip = new AtUri(post.uri) 175 169 return [makeProfileLink(post.author, 'post', urip.rkey), urip.rkey] 176 170 }, [post.uri, post.author]) ··· 184 178 feedContext, 185 179 reqId, 186 180 }) 187 - if (gate('feed_reply_button_open_thread') && ENV !== 'e2e') { 188 - navigation.navigate('PostThread', { 189 - name: post.author.did, 190 - rkey, 191 - }) 192 - } else { 193 - openComposer({ 194 - replyTo: { 195 - uri: post.uri, 196 - cid: post.cid, 197 - text: record.text || '', 198 - author: post.author, 199 - embed: post.embed, 200 - moderation, 201 - langs: record.langs, 202 - }, 203 - }) 204 - } 181 + openComposer({ 182 + replyTo: { 183 + uri: post.uri, 184 + cid: post.cid, 185 + text: record.text || '', 186 + author: post.author, 187 + embed: post.embed, 188 + moderation, 189 + langs: record.langs, 190 + }, 191 + }) 205 192 } 206 193 207 194 const onOpenAuthor = () => {
-6
src/view/com/util/load-latest/LoadLatestBtn.tsx
··· 8 8 import {useMinimalShellFabTransform} from '#/lib/hooks/useMinimalShellTransform' 9 9 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 10 10 import {clamp} from '#/lib/numbers' 11 - import {useGate} from '#/lib/statsig/statsig' 12 11 import {useSession} from '#/state/session' 13 12 import {atoms as a, useLayoutBreakpoints, useTheme, web} from '#/alf' 14 13 import {useInteractionState} from '#/components/hooks/useInteractionState' ··· 39 38 40 39 // move button inline if it starts overlapping the left nav 41 40 const isTallViewport = useMediaQuery({minHeight: 700}) 42 - 43 - const gate = useGate() 44 - if (gate('remove_show_latest_button')) { 45 - return null 46 - } 47 41 48 42 // Adjust height of the fab if we have a session only on mobile web. If we don't have a session, we want to adjust 49 43 // it on both tablet and mobile since we are showing the bottom bar (see createNativeStackNavigatorWithAuth)
+4 -7
src/view/shell/bottom-bar/BottomBar.tsx
··· 18 18 import {usePalette} from '#/lib/hooks/usePalette' 19 19 import {clamp} from '#/lib/numbers' 20 20 import {getTabState, TabState} from '#/lib/routes/helpers' 21 - import {useGate} from '#/lib/statsig/statsig' 22 21 import {emitSoftReset} from '#/state/events' 23 - import {useHomeBadge} from '#/state/home-badge' 24 22 import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 25 23 import {useUnreadNotifications} from '#/state/queries/notifications/unread' 26 24 import {useProfileQuery} from '#/state/queries/profile' ··· 43 41 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 44 42 HomeOpen_Stoke2_Corner0_Rounded as Home, 45 43 } from '#/components/icons/HomeOpen' 46 - import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass' 47 - import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass' 44 + import { 45 + MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, 46 + MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, 47 + } from '#/components/icons/MagnifyingGlass' 48 48 import { 49 49 Message_Stroke2_Corner0_Rounded as Message, 50 50 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, ··· 72 72 const dedupe = useDedupe() 73 73 const accountSwitchControl = useDialogControl() 74 74 const playHaptic = useHaptics() 75 - const hasHomeBadge = useHomeBadge() 76 - const gate = useGate() 77 75 const hideBorder = useHideBottomBarBorder() 78 76 const iconWidth = 28 79 77 ··· 172 170 /> 173 171 ) 174 172 } 175 - hasNew={hasHomeBadge && gate('remove_show_latest_button')} 176 173 onPress={onPressHome} 177 174 accessibilityRole="tab" 178 175 accessibilityLabel={_(msg`Home`)}
+5 -10
src/view/shell/bottom-bar/BottomBarWeb.tsx
··· 10 10 import {getCurrentRoute, isTab} from '#/lib/routes/helpers' 11 11 import {makeProfileLink} from '#/lib/routes/links' 12 12 import {type CommonNavigatorParams} from '#/lib/routes/types' 13 - import {useGate} from '#/lib/statsig/statsig' 14 - import {useHomeBadge} from '#/state/home-badge' 15 13 import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 16 14 import {useUnreadNotifications} from '#/state/queries/notifications/unread' 17 15 import {useSession} from '#/state/session' ··· 31 29 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 32 30 HomeOpen_Stoke2_Corner0_Rounded as Home, 33 31 } from '#/components/icons/HomeOpen' 34 - import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass' 35 - import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass' 32 + import { 33 + MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, 34 + MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, 35 + } from '#/components/icons/MagnifyingGlass' 36 36 import { 37 37 Message_Stroke2_Corner0_Rounded as Message, 38 38 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, ··· 57 57 58 58 const unreadMessageCount = useUnreadMessageCount() 59 59 const notificationCountStr = useUnreadNotifications() 60 - const hasHomeBadge = useHomeBadge() 61 - const gate = useGate() 62 60 63 61 const showSignIn = React.useCallback(() => { 64 62 closeAllActiveElements() ··· 86 84 onLayout={event => footerHeight.set(event.nativeEvent.layout.height)}> 87 85 {hasSession ? ( 88 86 <> 89 - <NavItem 90 - routeName="Home" 91 - href="/" 92 - hasNew={hasHomeBadge && gate('remove_show_latest_button')}> 87 + <NavItem routeName="Home" href="/"> 93 88 {({isActive}) => { 94 89 const Icon = isActive ? HomeFilled : Home 95 90 return (
+4 -7
src/view/shell/desktop/LeftNav.tsx
··· 16 16 type CommonNavigatorParams, 17 17 type NavigationProp, 18 18 } from '#/lib/routes/types' 19 - import {useGate} from '#/lib/statsig/statsig' 20 19 import {sanitizeDisplayName} from '#/lib/strings/display-names' 21 20 import {isInvalidHandle, sanitizeHandle} from '#/lib/strings/handles' 22 21 import {emitSoftReset} from '#/state/events' 23 - import {useHomeBadge} from '#/state/home-badge' 24 22 import {useFetchHandle} from '#/state/queries/handle' 25 23 import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 26 24 import {useUnreadNotifications} from '#/state/queries/notifications/unread' ··· 55 53 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 56 54 HomeOpen_Stoke2_Corner0_Rounded as Home, 57 55 } from '#/components/icons/HomeOpen' 58 - import {MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled} from '#/components/icons/MagnifyingGlass' 59 - import {MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass} from '#/components/icons/MagnifyingGlass' 56 + import { 57 + MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, 58 + MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, 59 + } from '#/components/icons/MagnifyingGlass' 60 60 import { 61 61 Message_Stroke2_Corner0_Rounded as Message, 62 62 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, ··· 617 617 const {isDesktop} = useWebMediaQueries() 618 618 const {leftNavMinimal, centerColumnOffset} = useLayoutBreakpoints() 619 619 const numUnreadNotifications = useUnreadNotifications() 620 - const hasHomeBadge = useHomeBadge() 621 - const gate = useGate() 622 620 623 621 if (!hasSession && !isDesktop) { 624 622 return null ··· 654 652 <> 655 653 <NavItem 656 654 href="/" 657 - hasNew={hasHomeBadge && gate('remove_show_latest_button')} 658 655 icon={ 659 656 <Home 660 657 aria-hidden={true}