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 sourceType: 'module', 73 globals: { 74 ...globals.browser, 75 }, 76 parserOptions: { 77 parser: tsParser,
··· 72 sourceType: 'module', 73 globals: { 74 ...globals.browser, 75 + ...globals.node, 76 }, 77 parserOptions: { 78 parser: tsParser,
-1
jest/jestSetup.js
··· 114 }, 115 })) 116 117 - jest.mock('../src/logger/bitdrift/lib', () => ({})) 118 jest.mock('../src/lib/statsig/statsig', () => ({}))
··· 114 }, 115 })) 116 117 jest.mock('../src/lib/statsig/statsig', () => ({}))
+4 -3
src/App.native.tsx
··· 1 import '#/logger/sentry/setup' 2 - import '#/logger/bitdrift/setup' 3 import '#/view/icons' 4 5 import React, {useEffect, useState} from 'react' ··· 66 import {Provider as PortalProvider} from '#/components/Portal' 67 import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' 68 import {ToastOutlet} from '#/components/Toast' 69 - import {Provider as AgeAssuranceV2Provider} from '#/ageAssurance' 70 - import {prefetchAgeAssuranceConfig} from '#/ageAssurance' 71 import {IS_ANDROID, IS_IOS} from '#/env' 72 import { 73 prefetchLiveEvents,
··· 1 import '#/logger/sentry/setup' 2 import '#/view/icons' 3 4 import React, {useEffect, useState} from 'react' ··· 65 import {Provider as PortalProvider} from '#/components/Portal' 66 import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' 67 import {ToastOutlet} from '#/components/Toast' 68 + import { 69 + prefetchAgeAssuranceConfig, 70 + Provider as AgeAssuranceV2Provider, 71 + } from '#/ageAssurance' 72 import {IS_ANDROID, IS_IOS} from '#/env' 73 import { 74 prefetchLiveEvents,
+3 -5
src/components/FeedInterstitials.tsx
··· 7 import {useNavigation} from '@react-navigation/native' 8 9 import {type NavigationProp} from '#/lib/routes/types' 10 - import {logEvent, useGate} from '#/lib/statsig/statsig' 11 import {logger} from '#/logger' 12 import {type MetricEvents} from '#/logger/metrics' 13 import {useModerationOpts} from '#/state/preferences/moderation-opts' ··· 435 }) { 436 const t = useTheme() 437 const {_} = useLingui() 438 - const gate = useGate() 439 const moderationOpts = useModerationOpts() 440 const {gtMobile} = useBreakpoints() 441 const followDialogControl = useDialogControl() ··· 443 const isLoading = isSuggestionsLoading || !moderationOpts 444 const isProfileHeaderContext = viewContext === 'profileHeader' 445 const isFeedContext = viewContext === 'feed' 446 - const showDismissButton = onDismiss && gate('suggested_users_dismiss') 447 448 const maxLength = gtMobile ? 3 : isProfileHeaderContext ? 12 : 6 449 const minLength = gtMobile ? 3 : 4 ··· 584 (hovered || pressed) && t.atoms.border_contrast_high, 585 ]}> 586 <ProfileCard.Outer> 587 - {showDismissButton && ( 588 <Button 589 label={_(msg`Dismiss this suggestion`)} 590 onPress={e => { 591 e.preventDefault() 592 - onDismiss!(profile.did) 593 logEvent('suggestedUser:dismiss', { 594 logContext: isFeedContext 595 ? 'InterstitialDiscover'
··· 7 import {useNavigation} from '@react-navigation/native' 8 9 import {type NavigationProp} from '#/lib/routes/types' 10 + import {logEvent} from '#/lib/statsig/statsig' 11 import {logger} from '#/logger' 12 import {type MetricEvents} from '#/logger/metrics' 13 import {useModerationOpts} from '#/state/preferences/moderation-opts' ··· 435 }) { 436 const t = useTheme() 437 const {_} = useLingui() 438 const moderationOpts = useModerationOpts() 439 const {gtMobile} = useBreakpoints() 440 const followDialogControl = useDialogControl() ··· 442 const isLoading = isSuggestionsLoading || !moderationOpts 443 const isProfileHeaderContext = viewContext === 'profileHeader' 444 const isFeedContext = viewContext === 'feed' 445 446 const maxLength = gtMobile ? 3 : isProfileHeaderContext ? 12 : 6 447 const minLength = gtMobile ? 3 : 4 ··· 582 (hovered || pressed) && t.atoms.border_contrast_high, 583 ]}> 584 <ProfileCard.Outer> 585 + {onDismiss && ( 586 <Button 587 label={_(msg`Dismiss this suggestion`)} 588 onPress={e => { 589 e.preventDefault() 590 + onDismiss(profile.did) 591 logEvent('suggestedUser:dismiss', { 592 logContext: isFeedContext 593 ? 'InterstitialDiscover'
+1 -8
src/components/PostControls/ShareMenu/index.tsx
··· 12 13 import {makeProfileLink} from '#/lib/routes/links' 14 import {shareUrl} from '#/lib/sharing' 15 - import {useGate} from '#/lib/statsig/statsig' 16 import {toShareUrl} from '#/lib/strings/url-helpers' 17 import {logger} from '#/logger' 18 import {type Shadow} from '#/state/cache/post-shadow' 19 import {useFeedFeedbackContext} from '#/state/feed-feedback' 20 import {EventStopper} from '#/view/com/util/EventStopper' 21 import {native} from '#/alf' 22 - import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as ArrowOutOfBoxIcon} from '#/components/icons/ArrowOutOfBox' 23 import {ArrowShareRight_Stroke2_Corner2_Rounded as ArrowShareRightIcon} from '#/components/icons/ArrowShareRight' 24 import {useMenuControl} from '#/components/Menu' 25 import * as Menu from '#/components/Menu' ··· 50 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 51 }): React.ReactNode => { 52 const {_} = useLingui() 53 - const gate = useGate() 54 const {feedDescriptor} = useFeedFeedbackContext() 55 - 56 - const ShareIcon = gate('alt_share_icon') 57 - ? ArrowShareRightIcon 58 - : ArrowOutOfBoxIcon 59 60 const menuControl = useMenuControl() 61 const [hasBeenOpen, setHasBeenOpen] = useState(false) ··· 114 {...props} 115 onLongPress={native(onNativeLongPress)} 116 hitSlop={hitSlop}> 117 - <PostControlButtonIcon icon={ShareIcon} /> 118 </PostControlButton> 119 ) 120 }}
··· 12 13 import {makeProfileLink} from '#/lib/routes/links' 14 import {shareUrl} from '#/lib/sharing' 15 import {toShareUrl} from '#/lib/strings/url-helpers' 16 import {logger} from '#/logger' 17 import {type Shadow} from '#/state/cache/post-shadow' 18 import {useFeedFeedbackContext} from '#/state/feed-feedback' 19 import {EventStopper} from '#/view/com/util/EventStopper' 20 import {native} from '#/alf' 21 import {ArrowShareRight_Stroke2_Corner2_Rounded as ArrowShareRightIcon} from '#/components/icons/ArrowShareRight' 22 import {useMenuControl} from '#/components/Menu' 23 import * as Menu from '#/components/Menu' ··· 48 logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' 49 }): React.ReactNode => { 50 const {_} = useLingui() 51 const {feedDescriptor} = useFeedFeedbackContext() 52 53 const menuControl = useMenuControl() 54 const [hasBeenOpen, setHasBeenOpen] = useState(false) ··· 107 {...props} 108 onLongPress={native(onNativeLongPress)} 109 hitSlop={hitSlop}> 110 + <PostControlButtonIcon icon={ArrowShareRightIcon} /> 111 </PostControlButton> 112 ) 113 }}
+1 -4
src/components/contacts/FindContactsBannerNUX.tsx
··· 6 import {useLingui} from '@lingui/react' 7 8 import {HITSLOP_10} from '#/lib/constants' 9 - import {useGate} from '#/lib/statsig/statsig' 10 import {logger} from '#/logger' 11 import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs' 12 import {atoms as a, useTheme} from '#/alf' ··· 89 const {mutate: save, variables} = useSaveNux() 90 const hidden = !!variables 91 const isFeatureEnabled = useIsFindContactsFeatureEnabledBasedOnGeolocation() 92 - const gate = useGate() 93 94 const visible = useMemo(() => { 95 if (IS_WEB) return false 96 if (hidden) return false 97 if (nux && nux.completed) return false 98 if (!isFeatureEnabled) return false 99 - if (gate('disable_settings_find_contacts')) return false 100 return true 101 - }, [hidden, nux, isFeatureEnabled, gate]) 102 103 const close = () => { 104 save({
··· 6 import {useLingui} from '@lingui/react' 7 8 import {HITSLOP_10} from '#/lib/constants' 9 import {logger} from '#/logger' 10 import {Nux, useNux, useSaveNux} from '#/state/queries/nuxs' 11 import {atoms as a, useTheme} from '#/alf' ··· 88 const {mutate: save, variables} = useSaveNux() 89 const hidden = !!variables 90 const isFeatureEnabled = useIsFindContactsFeatureEnabledBasedOnGeolocation() 91 92 const visible = useMemo(() => { 93 if (IS_WEB) return false 94 if (hidden) return false 95 if (nux && nux.completed) return false 96 if (!isFeatureEnabled) return false 97 return true 98 + }, [hidden, nux, isFeatureEnabled]) 99 100 const close = () => { 101 save({
+1 -2
src/components/dialogs/nuxs/LiveNowBetaDialog.tsx
··· 15 } from '#/components/dialogs/nuxs/utils' 16 import {Beaker_Stroke2_Corner2_Rounded as BeakerIcon} from '#/components/icons/Beaker' 17 import {Text} from '#/components/Typography' 18 - import {IS_WEB} from '#/env' 19 - import {IS_E2E} from '#/env' 20 21 export const enabled = createIsEnabledCheck(props => { 22 return (
··· 15 } from '#/components/dialogs/nuxs/utils' 16 import {Beaker_Stroke2_Corner2_Rounded as BeakerIcon} from '#/components/icons/Beaker' 17 import {Text} from '#/components/Typography' 18 + import {IS_E2E, IS_WEB} from '#/env' 19 20 export const enabled = createIsEnabledCheck(props => { 21 return (
+2 -14
src/lib/statsig/gates.ts
··· 1 export type Gate = 2 // Keep this alphabetic please. 3 - | 'alt_share_icon' 4 | 'debug_show_feedcontext' 5 - | 'debug_subscriptions' 6 - | 'disable_live_now_beta' 7 | 'disable_onboarding_find_contacts' 8 | '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'
··· 1 export type Gate = 2 // Keep this alphabetic please. 3 | 'debug_show_feedcontext' 4 + | 'is_bsky_team_member' // special, do not remove 5 | 'disable_onboarding_find_contacts' 6 | 'disable_settings_find_contacts' 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 import {logEvent} from '#/lib/statsig/statsig' 4 import {add} from '#/logger/logDump' 5 import {type MetricEvents} from '#/logger/metrics' 6 - import {bitdriftTransport} from '#/logger/transports/bitdrift' 7 import {consoleTransport} from '#/logger/transports/console' 8 import {sentryTransport} from '#/logger/transports/sentry' 9 import { ··· 13 type Transport, 14 } from '#/logger/types' 15 import {enabledLogLevels} from '#/logger/util' 16 - import {IS_NATIVE} from '#/env' 17 import {ENV} from '#/env' 18 19 export {type MetricEvents as Metrics} from '#/logger/metrics' ··· 21 const TRANSPORTS: Transport[] = (function configureTransports() { 22 switch (ENV) { 23 case 'production': { 24 - return [sentryTransport, IS_NATIVE && bitdriftTransport].filter( 25 - Boolean, 26 - ) as Transport[] 27 } 28 case 'test': { 29 return []
··· 3 import {logEvent} from '#/lib/statsig/statsig' 4 import {add} from '#/logger/logDump' 5 import {type MetricEvents} from '#/logger/metrics' 6 import {consoleTransport} from '#/logger/transports/console' 7 import {sentryTransport} from '#/logger/transports/sentry' 8 import { ··· 12 type Transport, 13 } from '#/logger/types' 14 import {enabledLogLevels} from '#/logger/util' 15 import {ENV} from '#/env' 16 17 export {type MetricEvents as Metrics} from '#/logger/metrics' ··· 19 const TRANSPORTS: Transport[] = (function configureTransports() { 20 switch (ENV) { 21 case 'production': { 22 + return [sentryTransport].filter(Boolean) 23 } 24 case 'test': { 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 VIDEO_SAVED_FEED, 21 } from '#/lib/constants' 22 import {useRequestNotificationsPermission} from '#/lib/notifications/notifications' 23 - import {logEvent, useGate} from '#/lib/statsig/statsig' 24 import {logger} from '#/logger' 25 import {useSetHasCheckedForStarterPack} from '#/state/preferences/used-starter-packs' 26 import {getAllListMembers} from '#/state/queries/list-members' ··· 61 const setActiveStarterPack = useSetActiveStarterPack() 62 const setHasCheckedForStarterPack = useSetHasCheckedForStarterPack() 63 const {startProgressGuide} = useProgressGuideControls() 64 - const gate = useGate() 65 66 const finishOnboarding = useCallback(async () => { 67 setSaving(true) ··· 114 ...TIMELINE_SAVED_FEED, 115 id: TID.nextStr(), 116 }, 117 - ] 118 - if (gate('onboarding_add_video_feed')) { 119 - feedsToSave.push({ 120 ...VIDEO_SAVED_FEED, 121 id: TID.nextStr(), 122 - }) 123 - } 124 125 // Any starter pack feeds will be pinned _after_ the defaults 126 if (starterPack && starterPack.feeds?.length) { ··· 200 setSaving(false) 201 setActiveStarterPack(undefined) 202 setHasCheckedForStarterPack(true) 203 - startProgressGuide( 204 - gate('old_postonboarding') ? 'like-10-and-follow-7' : 'follow-10', 205 - ) 206 dispatch({type: 'finish'}) 207 onboardDispatch({type: 'finish'}) 208 logEvent('onboarding:finished:nextPressed', { ··· 238 setActiveStarterPack, 239 setHasCheckedForStarterPack, 240 startProgressGuide, 241 - gate, 242 ]) 243 244 return (
··· 20 VIDEO_SAVED_FEED, 21 } from '#/lib/constants' 22 import {useRequestNotificationsPermission} from '#/lib/notifications/notifications' 23 + import {logEvent} from '#/lib/statsig/statsig' 24 import {logger} from '#/logger' 25 import {useSetHasCheckedForStarterPack} from '#/state/preferences/used-starter-packs' 26 import {getAllListMembers} from '#/state/queries/list-members' ··· 61 const setActiveStarterPack = useSetActiveStarterPack() 62 const setHasCheckedForStarterPack = useSetHasCheckedForStarterPack() 63 const {startProgressGuide} = useProgressGuideControls() 64 65 const finishOnboarding = useCallback(async () => { 66 setSaving(true) ··· 113 ...TIMELINE_SAVED_FEED, 114 id: TID.nextStr(), 115 }, 116 + { 117 ...VIDEO_SAVED_FEED, 118 id: TID.nextStr(), 119 + }, 120 + ] 121 122 // Any starter pack feeds will be pinned _after_ the defaults 123 if (starterPack && starterPack.feeds?.length) { ··· 197 setSaving(false) 198 setActiveStarterPack(undefined) 199 setHasCheckedForStarterPack(true) 200 + startProgressGuide('follow-10') 201 dispatch({type: 'finish'}) 202 onboardDispatch({type: 'finish'}) 203 logEvent('onboarding:finished:nextPressed', { ··· 233 setActiveStarterPack, 234 setHasCheckedForStarterPack, 235 startProgressGuide, 236 ]) 237 238 return (
+3 -7
src/screens/Onboarding/index.tsx
··· 23 import {useFindContactsFlowState} from '#/components/contacts/state' 24 import {Portal} from '#/components/Portal' 25 import {ScreenTransition} from '#/components/ScreenTransition' 26 - import {IS_NATIVE} from '#/env' 27 - import {ENV} from '#/env' 28 import {StepFindContacts} from './StepFindContacts' 29 import {StepFindContactsIntro} from './StepFindContactsIntro' 30 import {StepSuggestedAccounts} from './StepSuggestedAccounts' 31 import {StepSuggestedStarterpacks} from './StepSuggestedStarterpacks' 32 33 export function Onboarding() { 34 - const gate = useGate() 35 const t = useTheme() 36 37 const {contentLanguages} = useLanguagePrefs() 38 const probablySpeaksEnglish = useMemo(() => { ··· 41 }, [contentLanguages]) 42 43 // starter packs screen is currently geared towards english-speaking accounts 44 - const showSuggestedStarterpacks = 45 - ENV !== 'e2e' && 46 - probablySpeaksEnglish && 47 - gate('onboarding_suggested_starterpacks') 48 49 const findContactsEnabled = 50 useIsFindContactsFeatureEnabledBasedOnGeolocation()
··· 23 import {useFindContactsFlowState} from '#/components/contacts/state' 24 import {Portal} from '#/components/Portal' 25 import {ScreenTransition} from '#/components/ScreenTransition' 26 + import {ENV, IS_NATIVE} from '#/env' 27 import {StepFindContacts} from './StepFindContacts' 28 import {StepFindContactsIntro} from './StepFindContactsIntro' 29 import {StepSuggestedAccounts} from './StepSuggestedAccounts' 30 import {StepSuggestedStarterpacks} from './StepSuggestedStarterpacks' 31 32 export function Onboarding() { 33 const t = useTheme() 34 + const gate = useGate() 35 36 const {contentLanguages} = useLanguagePrefs() 37 const probablySpeaksEnglish = useMemo(() => { ··· 40 }, [contentLanguages]) 41 42 // starter packs screen is currently geared towards english-speaking accounts 43 + const showSuggestedStarterpacks = ENV !== 'e2e' && probablySpeaksEnglish 44 45 const findContactsEnabled = 46 useIsFindContactsFeatureEnabledBasedOnGeolocation()
+2 -5
src/screens/Settings/AppIconSettings/index.tsx
··· 7 8 import {PressableScale} from '#/lib/custom-animations/PressableScale' 9 import {type CommonNavigatorParams} from '#/lib/routes/types' 10 - import {useGate} from '#/lib/statsig/statsig' 11 import {AppIconImage} from '#/screens/Settings/AppIconSettings/AppIconImage' 12 import {type AppIconSet} from '#/screens/Settings/AppIconSettings/types' 13 import {useAppIconSets} from '#/screens/Settings/AppIconSettings/useAppIconSets' ··· 15 import * as Toggle from '#/components/forms/Toggle' 16 import * as Layout from '#/components/Layout' 17 import {Text} from '#/components/Typography' 18 - import {IS_ANDROID} from '#/env' 19 - import {IS_INTERNAL} from '#/env' 20 21 type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppIconSettings'> 22 export function AppIconSettingsScreen({}: Props) { 23 const t = useTheme() 24 const {_} = useLingui() 25 const sets = useAppIconSets() 26 - const gate = useGate() 27 const [currentAppIcon, setCurrentAppIcon] = useState(() => 28 getAppIconName(DynamicAppIcon.getAppIcon()), 29 ) ··· 86 ))} 87 </Group> 88 89 - {IS_INTERNAL && gate('debug_subscriptions') && ( 90 <> 91 <Text 92 style={[
··· 7 8 import {PressableScale} from '#/lib/custom-animations/PressableScale' 9 import {type CommonNavigatorParams} from '#/lib/routes/types' 10 import {AppIconImage} from '#/screens/Settings/AppIconSettings/AppIconImage' 11 import {type AppIconSet} from '#/screens/Settings/AppIconSettings/types' 12 import {useAppIconSets} from '#/screens/Settings/AppIconSettings/useAppIconSets' ··· 14 import * as Toggle from '#/components/forms/Toggle' 15 import * as Layout from '#/components/Layout' 16 import {Text} from '#/components/Typography' 17 + import {IS_ANDROID, IS_INTERNAL} from '#/env' 18 19 type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppIconSettings'> 20 export function AppIconSettingsScreen({}: Props) { 21 const t = useTheme() 22 const {_} = useLingui() 23 const sets = useAppIconSets() 24 const [currentAppIcon, setCurrentAppIcon] = useState(() => 25 getAppIconName(DynamicAppIcon.getAppIcon()), 26 ) ··· 83 ))} 84 </Group> 85 86 + {IS_INTERNAL && ( 87 <> 88 <Text 89 style={[
+2 -4
src/screens/Settings/Settings.tsx
··· 1 import {useState} from 'react' 2 - import {Alert, LayoutAnimation, Pressable, View} from 'react-native' 3 - import {Linking} from 'react-native' 4 import {useReducedMotion} from 'react-native-reanimated' 5 import {type AppBskyActorDefs, moderateProfile} from '@atproto/api' 6 import {msg, Trans} from '@lingui/macro' ··· 70 shouldShowVerificationCheckButton, 71 VerificationCheckButton, 72 } from '#/components/verification/VerificationCheckButton' 73 - import {IS_IOS, IS_NATIVE} from '#/env' 74 - import {IS_INTERNAL} from '#/env' 75 import {device, useStorage} from '#/storage' 76 import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged' 77
··· 1 import {useState} from 'react' 2 + import {Alert, LayoutAnimation, Linking, Pressable, View} from 'react-native' 3 import {useReducedMotion} from 'react-native-reanimated' 4 import {type AppBskyActorDefs, moderateProfile} from '@atproto/api' 5 import {msg, Trans} from '@lingui/macro' ··· 69 shouldShowVerificationCheckButton, 70 VerificationCheckButton, 71 } from '#/components/verification/VerificationCheckButton' 72 + import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env' 73 import {device, useStorage} from '#/storage' 74 import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged' 75
+6 -92
src/state/session/logging.ts
··· 1 import {type AtpSessionData, type AtpSessionEvent} from '@atproto/api' 2 - import {sha256} from 'js-sha256' 3 - import {Statsig} from 'statsig-react-native-expo' 4 5 - import {IS_INTERNAL} from '#/env' 6 import {type Schema} from '../persisted' 7 import {type Action, type State} from './reducer' 8 import {type SessionAccount} from './types' ··· 68 } 69 } 70 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 - }
··· 1 import {type AtpSessionData, type AtpSessionEvent} from '@atproto/api' 2 3 import {type Schema} from '../persisted' 4 import {type Action, type State} from './reducer' 5 import {type SessionAccount} from './types' ··· 65 } 66 } 67 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 import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants' 32 import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' 33 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 34 - import {logEvent, useGate} from '#/lib/statsig/statsig' 35 import {isNetworkError} from '#/lib/strings/errors' 36 import {logger} from '#/logger' 37 import {usePostAuthorShadowFilter} from '#/state/cache/profile-shadow' ··· 235 const {_} = useLingui() 236 const queryClient = useQueryClient() 237 const {currentAccount, hasSession} = useSession() 238 - const gate = useGate() 239 const initialNumToRender = useInitialNumToRender() 240 const feedFeedback = useFeedFeedbackContext() 241 const [isPTRing, setIsPTRing] = useState(false) ··· 522 if ( 523 hasSession && 524 (feedUriOrActorDid === DISCOVER_FEED_URI || 525 - feed === 'following') && 526 - gate('show_composer_prompt') 527 ) { 528 arr.push({ 529 type: 'composerPrompt', ··· 546 } else if (feedKind === 'following') { 547 if (sliceIndex === 0) { 548 // Show composer prompt for Following feed 549 - if (hasSession && gate('show_composer_prompt')) { 550 arr.push({ 551 type: 'composerPrompt', 552 key: 'composerPrompt-' + sliceIndex, ··· 680 hasPressedShowLessUris, 681 ageAssuranceBannerState, 682 isCurrentFeedAtStartupSelected, 683 - gate, 684 blockedOrMutedAuthors, 685 ]) 686
··· 31 import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants' 32 import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' 33 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 34 + import {logEvent} from '#/lib/statsig/statsig' 35 import {isNetworkError} from '#/lib/strings/errors' 36 import {logger} from '#/logger' 37 import {usePostAuthorShadowFilter} from '#/state/cache/profile-shadow' ··· 235 const {_} = useLingui() 236 const queryClient = useQueryClient() 237 const {currentAccount, hasSession} = useSession() 238 const initialNumToRender = useInitialNumToRender() 239 const feedFeedback = useFeedFeedbackContext() 240 const [isPTRing, setIsPTRing] = useState(false) ··· 521 if ( 522 hasSession && 523 (feedUriOrActorDid === DISCOVER_FEED_URI || 524 + feed === 'following') 525 ) { 526 arr.push({ 527 type: 'composerPrompt', ··· 544 } else if (feedKind === 'following') { 545 if (sliceIndex === 0) { 546 // Show composer prompt for Following feed 547 + if (hasSession) { 548 arr.push({ 549 type: 'composerPrompt', 550 key: 'composerPrompt-' + sliceIndex, ··· 678 hasPressedShowLessUris, 679 ageAssuranceBannerState, 680 isCurrentFeedAtStartupSelected, 681 blockedOrMutedAuthors, 682 ]) 683
+12 -25
src/view/com/posts/PostFeedItem.tsx
··· 9 type ModerationDecision, 10 RichText as RichTextAPI, 11 } from '@atproto/api' 12 - import {useNavigation} from '@react-navigation/native' 13 import {useQueryClient} from '@tanstack/react-query' 14 15 import {useActorStatus} from '#/lib/actor-status' ··· 18 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 19 import {usePalette} from '#/lib/hooks/usePalette' 20 import {makeProfileLink} from '#/lib/routes/links' 21 - import {type NavigationProp} from '#/lib/routes/types' 22 - import {useGate} from '#/lib/statsig/statsig' 23 import {countLines} from '#/lib/strings/helpers' 24 import {logger} from '#/logger' 25 import { ··· 51 import {DiscoverDebug} from '#/components/PostControls/DiscoverDebug' 52 import {RichText} from '#/components/RichText' 53 import {SubtleHover} from '#/components/SubtleHover' 54 - import {ENV} from '#/env' 55 import * as bsky from '#/types/bsky' 56 import {PostFeedReason} from './PostFeedReason' 57 ··· 164 }): React.ReactNode => { 165 const queryClient = useQueryClient() 166 const {openComposer} = useOpenComposer() 167 - const navigation = useNavigation<NavigationProp>() 168 const pal = usePalette('default') 169 - const gate = useGate() 170 171 const [hover, setHover] = useState(false) 172 173 - const [href, rkey] = useMemo(() => { 174 const urip = new AtUri(post.uri) 175 return [makeProfileLink(post.author, 'post', urip.rkey), urip.rkey] 176 }, [post.uri, post.author]) ··· 184 feedContext, 185 reqId, 186 }) 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 - } 205 } 206 207 const onOpenAuthor = () => {
··· 9 type ModerationDecision, 10 RichText as RichTextAPI, 11 } from '@atproto/api' 12 import {useQueryClient} from '@tanstack/react-query' 13 14 import {useActorStatus} from '#/lib/actor-status' ··· 17 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 18 import {usePalette} from '#/lib/hooks/usePalette' 19 import {makeProfileLink} from '#/lib/routes/links' 20 import {countLines} from '#/lib/strings/helpers' 21 import {logger} from '#/logger' 22 import { ··· 48 import {DiscoverDebug} from '#/components/PostControls/DiscoverDebug' 49 import {RichText} from '#/components/RichText' 50 import {SubtleHover} from '#/components/SubtleHover' 51 import * as bsky from '#/types/bsky' 52 import {PostFeedReason} from './PostFeedReason' 53 ··· 160 }): React.ReactNode => { 161 const queryClient = useQueryClient() 162 const {openComposer} = useOpenComposer() 163 const pal = usePalette('default') 164 165 const [hover, setHover] = useState(false) 166 167 + const [href] = useMemo(() => { 168 const urip = new AtUri(post.uri) 169 return [makeProfileLink(post.author, 'post', urip.rkey), urip.rkey] 170 }, [post.uri, post.author]) ··· 178 feedContext, 179 reqId, 180 }) 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 + }) 192 } 193 194 const onOpenAuthor = () => {
-6
src/view/com/util/load-latest/LoadLatestBtn.tsx
··· 8 import {useMinimalShellFabTransform} from '#/lib/hooks/useMinimalShellTransform' 9 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 10 import {clamp} from '#/lib/numbers' 11 - import {useGate} from '#/lib/statsig/statsig' 12 import {useSession} from '#/state/session' 13 import {atoms as a, useLayoutBreakpoints, useTheme, web} from '#/alf' 14 import {useInteractionState} from '#/components/hooks/useInteractionState' ··· 39 40 // move button inline if it starts overlapping the left nav 41 const isTallViewport = useMediaQuery({minHeight: 700}) 42 - 43 - const gate = useGate() 44 - if (gate('remove_show_latest_button')) { 45 - return null 46 - } 47 48 // 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 // it on both tablet and mobile since we are showing the bottom bar (see createNativeStackNavigatorWithAuth)
··· 8 import {useMinimalShellFabTransform} from '#/lib/hooks/useMinimalShellTransform' 9 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 10 import {clamp} from '#/lib/numbers' 11 import {useSession} from '#/state/session' 12 import {atoms as a, useLayoutBreakpoints, useTheme, web} from '#/alf' 13 import {useInteractionState} from '#/components/hooks/useInteractionState' ··· 38 39 // move button inline if it starts overlapping the left nav 40 const isTallViewport = useMediaQuery({minHeight: 700}) 41 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 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 import {usePalette} from '#/lib/hooks/usePalette' 19 import {clamp} from '#/lib/numbers' 20 import {getTabState, TabState} from '#/lib/routes/helpers' 21 - import {useGate} from '#/lib/statsig/statsig' 22 import {emitSoftReset} from '#/state/events' 23 - import {useHomeBadge} from '#/state/home-badge' 24 import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 25 import {useUnreadNotifications} from '#/state/queries/notifications/unread' 26 import {useProfileQuery} from '#/state/queries/profile' ··· 43 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 44 HomeOpen_Stoke2_Corner0_Rounded as Home, 45 } 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' 48 import { 49 Message_Stroke2_Corner0_Rounded as Message, 50 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, ··· 72 const dedupe = useDedupe() 73 const accountSwitchControl = useDialogControl() 74 const playHaptic = useHaptics() 75 - const hasHomeBadge = useHomeBadge() 76 - const gate = useGate() 77 const hideBorder = useHideBottomBarBorder() 78 const iconWidth = 28 79 ··· 172 /> 173 ) 174 } 175 - hasNew={hasHomeBadge && gate('remove_show_latest_button')} 176 onPress={onPressHome} 177 accessibilityRole="tab" 178 accessibilityLabel={_(msg`Home`)}
··· 18 import {usePalette} from '#/lib/hooks/usePalette' 19 import {clamp} from '#/lib/numbers' 20 import {getTabState, TabState} from '#/lib/routes/helpers' 21 import {emitSoftReset} from '#/state/events' 22 import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 23 import {useUnreadNotifications} from '#/state/queries/notifications/unread' 24 import {useProfileQuery} from '#/state/queries/profile' ··· 41 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 42 HomeOpen_Stoke2_Corner0_Rounded as Home, 43 } from '#/components/icons/HomeOpen' 44 + import { 45 + MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, 46 + MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, 47 + } from '#/components/icons/MagnifyingGlass' 48 import { 49 Message_Stroke2_Corner0_Rounded as Message, 50 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, ··· 72 const dedupe = useDedupe() 73 const accountSwitchControl = useDialogControl() 74 const playHaptic = useHaptics() 75 const hideBorder = useHideBottomBarBorder() 76 const iconWidth = 28 77 ··· 170 /> 171 ) 172 } 173 onPress={onPressHome} 174 accessibilityRole="tab" 175 accessibilityLabel={_(msg`Home`)}
+5 -10
src/view/shell/bottom-bar/BottomBarWeb.tsx
··· 10 import {getCurrentRoute, isTab} from '#/lib/routes/helpers' 11 import {makeProfileLink} from '#/lib/routes/links' 12 import {type CommonNavigatorParams} from '#/lib/routes/types' 13 - import {useGate} from '#/lib/statsig/statsig' 14 - import {useHomeBadge} from '#/state/home-badge' 15 import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 16 import {useUnreadNotifications} from '#/state/queries/notifications/unread' 17 import {useSession} from '#/state/session' ··· 31 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 32 HomeOpen_Stoke2_Corner0_Rounded as Home, 33 } 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' 36 import { 37 Message_Stroke2_Corner0_Rounded as Message, 38 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, ··· 57 58 const unreadMessageCount = useUnreadMessageCount() 59 const notificationCountStr = useUnreadNotifications() 60 - const hasHomeBadge = useHomeBadge() 61 - const gate = useGate() 62 63 const showSignIn = React.useCallback(() => { 64 closeAllActiveElements() ··· 86 onLayout={event => footerHeight.set(event.nativeEvent.layout.height)}> 87 {hasSession ? ( 88 <> 89 - <NavItem 90 - routeName="Home" 91 - href="/" 92 - hasNew={hasHomeBadge && gate('remove_show_latest_button')}> 93 {({isActive}) => { 94 const Icon = isActive ? HomeFilled : Home 95 return (
··· 10 import {getCurrentRoute, isTab} from '#/lib/routes/helpers' 11 import {makeProfileLink} from '#/lib/routes/links' 12 import {type CommonNavigatorParams} from '#/lib/routes/types' 13 import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 14 import {useUnreadNotifications} from '#/state/queries/notifications/unread' 15 import {useSession} from '#/state/session' ··· 29 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 30 HomeOpen_Stoke2_Corner0_Rounded as Home, 31 } from '#/components/icons/HomeOpen' 32 + import { 33 + MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, 34 + MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, 35 + } from '#/components/icons/MagnifyingGlass' 36 import { 37 Message_Stroke2_Corner0_Rounded as Message, 38 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, ··· 57 58 const unreadMessageCount = useUnreadMessageCount() 59 const notificationCountStr = useUnreadNotifications() 60 61 const showSignIn = React.useCallback(() => { 62 closeAllActiveElements() ··· 84 onLayout={event => footerHeight.set(event.nativeEvent.layout.height)}> 85 {hasSession ? ( 86 <> 87 + <NavItem routeName="Home" href="/"> 88 {({isActive}) => { 89 const Icon = isActive ? HomeFilled : Home 90 return (
+4 -7
src/view/shell/desktop/LeftNav.tsx
··· 16 type CommonNavigatorParams, 17 type NavigationProp, 18 } from '#/lib/routes/types' 19 - import {useGate} from '#/lib/statsig/statsig' 20 import {sanitizeDisplayName} from '#/lib/strings/display-names' 21 import {isInvalidHandle, sanitizeHandle} from '#/lib/strings/handles' 22 import {emitSoftReset} from '#/state/events' 23 - import {useHomeBadge} from '#/state/home-badge' 24 import {useFetchHandle} from '#/state/queries/handle' 25 import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 26 import {useUnreadNotifications} from '#/state/queries/notifications/unread' ··· 55 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 56 HomeOpen_Stoke2_Corner0_Rounded as Home, 57 } 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' 60 import { 61 Message_Stroke2_Corner0_Rounded as Message, 62 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, ··· 617 const {isDesktop} = useWebMediaQueries() 618 const {leftNavMinimal, centerColumnOffset} = useLayoutBreakpoints() 619 const numUnreadNotifications = useUnreadNotifications() 620 - const hasHomeBadge = useHomeBadge() 621 - const gate = useGate() 622 623 if (!hasSession && !isDesktop) { 624 return null ··· 654 <> 655 <NavItem 656 href="/" 657 - hasNew={hasHomeBadge && gate('remove_show_latest_button')} 658 icon={ 659 <Home 660 aria-hidden={true}
··· 16 type CommonNavigatorParams, 17 type NavigationProp, 18 } from '#/lib/routes/types' 19 import {sanitizeDisplayName} from '#/lib/strings/display-names' 20 import {isInvalidHandle, sanitizeHandle} from '#/lib/strings/handles' 21 import {emitSoftReset} from '#/state/events' 22 import {useFetchHandle} from '#/state/queries/handle' 23 import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' 24 import {useUnreadNotifications} from '#/state/queries/notifications/unread' ··· 53 HomeOpen_Filled_Corner0_Rounded as HomeFilled, 54 HomeOpen_Stoke2_Corner0_Rounded as Home, 55 } from '#/components/icons/HomeOpen' 56 + import { 57 + MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, 58 + MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, 59 + } from '#/components/icons/MagnifyingGlass' 60 import { 61 Message_Stroke2_Corner0_Rounded as Message, 62 Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, ··· 617 const {isDesktop} = useWebMediaQueries() 618 const {leftNavMinimal, centerColumnOffset} = useLayoutBreakpoints() 619 const numUnreadNotifications = useUnreadNotifications() 620 621 if (!hasSession && !isDesktop) { 622 return null ··· 652 <> 653 <NavItem 654 href="/" 655 icon={ 656 <Home 657 aria-hidden={true}