From 513dfcc8b580f629c1ed95d6149aba88cac5ad4a Mon Sep 17 00:00:00 2001 From: syui Date: Sun, 7 Dec 2025 19:18:28 +0900 Subject: [PATCH] test gemini --- ios/patching/001-social-app-ios-config.patch | 168 ++----- ...social-app-ios-splash-license-footer.patch | 143 ++---- ...-social-app-ios-bypass-age-assurance.patch | 38 +- .../021-social-app-ios-clean-feed.patch | 445 ++++++++++++++++++ task.md | 4 + 5 files changed, 538 insertions(+), 260 deletions(-) create mode 100644 ios/patching/021-social-app-ios-clean-feed.patch create mode 100644 task.md diff --git a/ios/patching/001-social-app-ios-config.patch b/ios/patching/001-social-app-ios-config.patch index 6526b61..4c78864 100644 --- a/ios/patching/001-social-app-ios-config.patch +++ b/ios/patching/001-social-app-ios-config.patch @@ -1,150 +1,44 @@ -diff --git a/Dockerfile b/Dockerfile -index 371e8402c..2e139503e 100644 ---- a/Dockerfile -+++ b/Dockerfile -@@ -66,7 +66,8 @@ RUN \. "$NVM_DIR/nvm.sh" && \ - echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env && \ - echo "EXPO_PUBLIC_SENTRY_DSN=$EXPO_PUBLIC_SENTRY_DSN" >> .env && \ - npm install --global yarn && \ -- yarn && \ -+ yarn config set registry https://registry.npmjs.org/ && \ -+ yarn install --frozen-lockfile --network-timeout 100000 && \ - yarn intl:build 2>&1 | tee i18n.log && \ - if grep -q "invalid syntax" "i18n.log"; then echo "\n\nFound compilation errors!\n\n" && exit 1; else echo "\n\nNo compile errors!\n\n"; fi && \ - SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN SENTRY_RELEASE=$EXPO_PUBLIC_RELEASE_VERSION SENTRY_DIST=$EXPO_PUBLIC_BUNDLE_IDENTIFIER yarn build-web diff --git a/app.config.js b/app.config.js -index 246d8abd3..a6582864b 100644 +index 246d8abd3..56f42202b 100644 --- a/app.config.js +++ b/app.config.js -@@ -33,8 +33,8 @@ module.exports = function (_config) { - return { - expo: { - version: VERSION, -- name: 'Bluesky', -- slug: 'bluesky', -+ name: 'Aiat', -+ slug: 'aiat', - scheme: 'bluesky', - owner: 'blueskysocial', +@@ -40,7 +40,7 @@ module.exports = function (_config) { runtimeVersion: { -@@ -45,15 +45,20 @@ module.exports = function (_config) { + policy: 'appVersion', + }, +- icon: './assets/app-icons/ios_icon_default_next.png', ++ icon: './assets/icon.png', + userInterfaceStyle: 'automatic', primaryColor: '#1083fe', newArchEnabled: false, - ios: { -+ infoPlist: { -+ NSAppTransportSecurity: { -+ NSAllowsArbitraryLoads: true, -+ }, -+ }, - supportsTablet: false, -- bundleIdentifier: 'xyz.blueskyweb.app', -+ bundleIdentifier: 'ai.syui.at', - config: { - usesNonExemptEncryption: false, - }, - icon: - PLATFORM === 'web' // web build doesn't like .icon files - ? './assets/app-icons/ios_icon_default_next.png' -- : './assets/app-icons/ios_icon_default.icon', -+ : './assets/app-icons/ios_icon_default_next.png', - infoPlist: { - UIBackgroundModes: ['remote-notification'], - NSCameraUsageDescription: -@@ -113,7 +118,7 @@ module.exports = function (_config) { - entitlements: { - 'com.apple.developer.kernel.increased-memory-limit': true, - 'com.apple.developer.kernel.extended-virtual-addressing': true, -- 'com.apple.security.application-groups': 'group.app.bsky', -+ 'com.apple.security.application-groups': 'group.ai.syui.at', - }, - privacyManifests: { - NSPrivacyCollectedDataTypes: [ -@@ -182,7 +187,7 @@ module.exports = function (_config) { - backgroundColor: '#006AFF', - }, - googleServicesFile: './google-services.json', -- package: 'xyz.blueskyweb.app', -+ package: 'ai.syui.at', - intentFilters: [ - { - action: 'VIEW', -@@ -220,7 +225,7 @@ module.exports = function (_config) { +@@ -213,9 +213,9 @@ module.exports = function (_config) { + : undefined, + codeSigningMetadata: UPDATES_ENABLED + ? { +- keyid: 'main', +- alg: 'rsa-v1_5-sha256', +- } ++ keyid: 'main', ++ alg: 'rsa-v1_5-sha256', ++ } + : undefined, checkAutomatically: 'NEVER', }, - plugins: [ -- 'expo-video', -+ 'expo-video', './plugins/withCodeSignEntitlements.js', - 'expo-localization', +@@ -225,7 +225,7 @@ module.exports = function (_config) { 'expo-web-browser', [ -@@ -239,6 +244,11 @@ module.exports = function (_config) { - 'expo-build-properties', - { - ios: { -+ infoPlist: { -+ NSAppTransportSecurity: { -+ NSAllowsArbitraryLoads: true, -+ }, -+ }, - deploymentTarget: '15.1', - buildReactNativeFromSource: true, + 'react-native-edge-to-edge', +- {android: {enforceNavigationBarContrast: false}}, ++ { android: { enforceNavigationBarContrast: false } }, + ], + USE_SENTRY && [ + '@sentry/react-native/expo', +@@ -386,7 +386,7 @@ module.exports = function (_config) { }, -@@ -264,7 +274,6 @@ module.exports = function (_config) { - networkInstrumentation: true, }, ], -- './plugins/starterPackAppClipExtension/withStarterPackAppClip.js', - './plugins/withGradleJVMHeapSizeIncrease.js', - './plugins/withAndroidManifestLargeHeapPlugin.js', - './plugins/withAndroidManifestFCMIconPlugin.js', -@@ -296,6 +305,11 @@ module.exports = function (_config) { - 'expo-splash-screen', - { - ios: { -+ infoPlist: { -+ NSAppTransportSecurity: { -+ NSAllowsArbitraryLoads: true, -+ }, -+ }, - enableFullScreenImage_legacy: true, - backgroundColor: '#ffffff', - image: './assets/splash.png', -@@ -394,29 +408,30 @@ module.exports = function (_config) { - build: { - experimental: { - ios: { -+ infoPlist: { -+ NSAppTransportSecurity: { -+ NSAllowsArbitraryLoads: true, -+ }, -+ }, - appExtensions: [ - { - targetName: 'Share-with-Bluesky', -- bundleIdentifier: 'xyz.blueskyweb.app.Share-with-Bluesky', -+ bundleIdentifier: 'ai.syui.at.Share-with-Bluesky', - entitlements: { - 'com.apple.security.application-groups': [ -- 'group.app.bsky', -+ 'group.ai.syui.at', - ], - }, - }, - { - targetName: 'BlueskyNSE', -- bundleIdentifier: 'xyz.blueskyweb.app.BlueskyNSE', -+ bundleIdentifier: 'ai.syui.at.BlueskyNSE', - entitlements: { - 'com.apple.security.application-groups': [ -- 'group.app.bsky', -+ 'group.ai.syui.at', - ], - }, - }, -- { -- targetName: 'BlueskyClip', -- bundleIdentifier: 'xyz.blueskyweb.app.AppClip', -- }, - ], - }, - }, +- ['expo-screen-orientation', {initialOrientation: 'PORTRAIT_UP'}], ++ ['expo-screen-orientation', { initialOrientation: 'PORTRAIT_UP' }], + ['expo-location'], + ].filter(Boolean), + extra: { diff --git a/ios/patching/011-social-app-ios-splash-license-footer.patch b/ios/patching/011-social-app-ios-splash-license-footer.patch index a8f182e..086172e 100644 --- a/ios/patching/011-social-app-ios-splash-license-footer.patch +++ b/ios/patching/011-social-app-ios-splash-license-footer.patch @@ -1,37 +1,54 @@ diff --git a/src/view/com/auth/SplashScreen.tsx b/src/view/com/auth/SplashScreen.tsx -index 3442d1bdf..2b059af52 100644 +index 3442d1bdf..034310a17 100644 --- a/src/view/com/auth/SplashScreen.tsx +++ b/src/view/com/auth/SplashScreen.tsx -@@ -40,16 +40,6 @@ export const SplashScreen = ({ - - - -- -- -- What's up? -- - +@@ -1,18 +1,18 @@ +-import {View} from 'react-native' +-import Animated, {FadeIn, FadeOut} from 'react-native-reanimated' +-import {useSafeAreaInsets} from 'react-native-safe-area-context' +-import {msg, Trans} from '@lingui/macro' +-import {useLingui} from '@lingui/react' ++import { View } from 'react-native' ++import Animated, { FadeIn, FadeOut } from 'react-native-reanimated' ++import { useSafeAreaInsets } from 'react-native-safe-area-context' ++import { msg, Trans } from '@lingui/macro' ++import { useLingui } from '@lingui/react' - -@@ -102,6 +92,17 @@ export const SplashScreen = ({ +-import {useHaptics} from '#/lib/haptics' +-import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' +-import {CenteredView} from '#/view/com/util/Views' +-import {Logo} from '#/view/icons/Logo' +-import {Logotype} from '#/view/icons/Logotype' +-import {atoms as a, useTheme} from '#/alf' +-import {AppLanguageDropdown} from '#/components/AppLanguageDropdown' +-import {Button, ButtonText} from '#/components/Button' +-import {Text} from '#/components/Typography' ++import { useHaptics } from '#/lib/haptics' ++import { ErrorBoundary } from '#/view/com/util/ErrorBoundary' ++import { CenteredView } from '#/view/com/util/Views' ++import { Logo } from '#/view/icons/Logo' ++import { Logotype } from '#/view/icons/Logotype' ++import { atoms as a, useTheme } from '#/alf' ++import { AppLanguageDropdown } from '#/components/AppLanguageDropdown' ++import { Button, ButtonText } from '#/components/Button' ++import { Text } from '#/components/Typography' + + export const SplashScreen = ({ + onPressSignin, +@@ -22,7 +22,7 @@ export const SplashScreen = ({ + onPressCreateAccount: () => void + }) => { + const t = useTheme() +- const {_} = useLingui() ++ const { _ } = useLingui() + + const playHaptic = useHaptics() + const insets = useSafeAreaInsets() +@@ -102,7 +102,18 @@ export const SplashScreen = ({ +- + + - ++ -diff --git a/src/view/com/auth/SplashScreen.web.tsx b/src/view/com/auth/SplashScreen.web.tsx -index 22dd23d7f..08cd8aa33 100644 ---- a/src/view/com/auth/SplashScreen.web.tsx -+++ b/src/view/com/auth/SplashScreen.web.tsx -@@ -3,7 +3,9 @@ import {Pressable, View} from 'react-native' - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' - import {msg, Trans} from '@lingui/macro' - import {useLingui} from '@lingui/react' -+import {useNavigation} from '@react-navigation/native' - -+import {NavigationProp} from '#/lib/routes/types' - import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' - import {useKawaiiMode} from '#/state/preferences/kawaii' - import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' -@@ -93,15 +95,6 @@ export const SplashScreen = ({ - - - )} -- -- -- What's up? -- - - - -- -- Business -- -- -- Blog -- -- -- -- Jobs -- -- -- - - -+ -+ © syui -+ -+ - - - ) + diff --git a/ios/patching/020-social-app-ios-bypass-age-assurance.patch b/ios/patching/020-social-app-ios-bypass-age-assurance.patch index 7744943..fc15abc 100644 --- a/ios/patching/020-social-app-ios-bypass-age-assurance.patch +++ b/ios/patching/020-social-app-ios-bypass-age-assurance.patch @@ -1,5 +1,7 @@ ---- a/b/src/ageAssurance/index.tsx 2025-12-07 15:18:15 -+++ b/src/ageAssurance/index.tsx 2025-12-07 15:18:16 +diff --git a/src/ageAssurance/index.tsx b/src/ageAssurance/index.tsx +index 9a0a9c9d5..5da399795 100644 +--- a/src/ageAssurance/index.tsx ++++ b/src/ageAssurance/index.tsx @@ -1,10 +1,10 @@ -import {createContext, useCallback, useContext, useEffect, useMemo} from 'react' +import { createContext, useCallback, useContext, useEffect, useMemo } from 'react' @@ -17,7 +19,7 @@ import { useAgeAssuranceState, useOnAgeAssuranceAccessUpdate, -@@ -14,7 +14,7 @@ +@@ -14,7 +14,7 @@ import { type AgeAssuranceState, AgeAssuranceStatus, } from '#/ageAssurance/types' @@ -26,7 +28,7 @@ export { prefetchConfig as prefetchAgeAssuranceConfig, -@@ -23,7 +23,7 @@ +@@ -23,7 +23,7 @@ export { usePatchOtherRequiredData as usePatchAgeAssuranceOtherRequiredData, usePatchServerState as usePatchAgeAssuranceServerState, } from '#/ageAssurance/data' @@ -35,7 +37,7 @@ const AgeAssuranceStateContext = createContext<{ Access: typeof AgeAssuranceAccess -@@ -56,7 +56,7 @@ +@@ -56,7 +56,7 @@ export function useAgeAssurance() { return useContext(AgeAssuranceStateContext) } @@ -44,7 +46,7 @@ return ( -@@ -66,9 +66,9 @@ +@@ -66,9 +66,9 @@ export function Provider({children}: {children: React.ReactNode}) { ) } @@ -56,7 +58,7 @@ const getAndRegisterPushToken = useGetAndRegisterPushToken() const handleAccessUpdate = useCallback( -@@ -82,28 +82,25 @@ +@@ -82,7 +82,7 @@ function InnerProvider({children}: {children: React.ReactNode}) { useOnAgeAssuranceAccessUpdate(handleAccessUpdate) useEffect(() => { @@ -65,14 +67,7 @@ }, [state]) return ( - { -- const chatDisabled = state.access !== AgeAssuranceAccess.Full -- const isUnderage = data?.birthdate -- ? isUserUnderAdultAge(data.birthdate) -- : true -- const adultContentDisabled = -- state.access !== AgeAssuranceAccess.Full || isUnderage +@@ -97,7 +97,10 @@ function InnerProvider({children}: {children: React.ReactNode}) { return { Access: AgeAssuranceAccess, Status: AgeAssuranceStatus, @@ -82,14 +77,5 @@ + access: AgeAssuranceAccess.Full, + }, flags: { -- adultContentDisabled, -- chatDisabled, -+ adultContentDisabled: false, -+ chatDisabled: false, - }, - } -- }, [state, data])}> -+ }, [state])}> - {children} - - ) + adultContentDisabled, + chatDisabled, diff --git a/ios/patching/021-social-app-ios-clean-feed.patch b/ios/patching/021-social-app-ios-clean-feed.patch new file mode 100644 index 0000000..8f3c3b6 --- /dev/null +++ b/ios/patching/021-social-app-ios-clean-feed.patch @@ -0,0 +1,445 @@ +diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx +index e058e2883..1d6bc4f45 100644 +--- a/src/view/screens/Home.tsx ++++ b/src/view/screens/Home.tsx +@@ -1,342 +1,139 @@ + import React from 'react' +-import {ActivityIndicator, StyleSheet} from 'react-native' +-import {useFocusEffect} from '@react-navigation/native' +- +-import {PROD_DEFAULT_FEED} from '#/lib/constants' +-import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' +-import {useOTAUpdates} from '#/lib/hooks/useOTAUpdates' +-import {useSetTitle} from '#/lib/hooks/useSetTitle' +-import {useRequestNotificationsPermission} from '#/lib/notifications/notifications' +-import { +- type HomeTabNavigatorParams, +- type NativeStackScreenProps, +-} from '#/lib/routes/types' +-import {logEvent} from '#/lib/statsig/statsig' +-import {isWeb} from '#/platform/detection' +-import {emitSoftReset} from '#/state/events' +-import { +- type SavedFeedSourceInfo, +- usePinnedFeedsInfos, +-} from '#/state/queries/feed' +-import {type FeedDescriptor, type FeedParams} from '#/state/queries/post-feed' +-import {usePreferencesQuery} from '#/state/queries/preferences' +-import {type UsePreferencesQueryResponse} from '#/state/queries/preferences/types' +-import {useSession} from '#/state/session' +-import {useSetMinimalShellMode} from '#/state/shell' +-import {useLoggedOutViewControls} from '#/state/shell/logged-out' +-import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed' +-import {FeedPage} from '#/view/com/feeds/FeedPage' +-import {HomeHeader} from '#/view/com/home/HomeHeader' +-import { +- Pager, +- type PagerRef, +- type RenderTabBarFnProps, +-} from '#/view/com/pager/Pager' +-import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState' +-import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState' +-import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed' +-import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned' ++import { ActivityIndicator, StyleSheet } from 'react-native' ++import { useFocusEffect } from '@react-navigation/native' ++import { PROD_DEFAULT_FEED } from '#/lib/constants' ++import { useNonReactiveCallback } from '#/lib/hooks/useNonReactiveCallback' ++import { useOTAUpdates } from '#/lib/hooks/useOTAUpdates' ++import { useSetTitle } from '#/lib/hooks/useSetTitle' ++import { useRequestNotificationsPermission } from '#/lib/notifications/notifications' ++import { type HomeTabNavigatorParams, type NativeStackScreenProps } from '#/lib/routes/types' ++import { logEvent } from '#/lib/statsig/statsig' ++import { isWeb } from '#/platform/detection' ++import { emitSoftReset } from '#/state/events' ++import { type SavedFeedSourceInfo, usePinnedFeedsInfos } from '#/state/queries/feed' ++import { type FeedDescriptor, type FeedParams } from '#/state/queries/post-feed' ++import { usePreferencesQuery } from '#/state/queries/preferences' ++import { type UsePreferencesQueryResponse } from '#/state/queries/preferences/types' ++import { useSession } from '#/state/session' ++import { useSetMinimalShellMode } from '#/state/shell' ++import { useLoggedOutViewControls } from '#/state/shell/logged-out' ++import { useSelectedFeed, useSetSelectedFeed } from '#/state/shell/selected-feed' ++import { FeedPage } from '#/view/com/feeds/FeedPage' ++import { HomeHeader } from '#/view/com/home/HomeHeader' ++import { Pager, type PagerRef, type RenderTabBarFnProps } from '#/view/com/pager/Pager' ++import { CustomFeedEmptyState } from '#/view/com/posts/CustomFeedEmptyState' ++import { FollowingEmptyState } from '#/view/com/posts/FollowingEmptyState' ++import { FollowingEndOfFeed } from '#/view/com/posts/FollowingEndOfFeed' ++import { NoFeedsPinned } from '#/screens/Home/NoFeedsPinned' + import * as Layout from '#/components/Layout' +-import {useDemoMode} from '#/storage/hooks/demo-mode' ++import { useDemoMode } from '#/storage/hooks/demo-mode' ++ ++const DEFAULT_PINNED_FEEDS = [{ ++ feedDescriptor: 'following', ++ displayName: 'Following', ++ id: 'following', ++ type: 'feed', ++ savedFeed: undefined, ++ pinned: true, ++}] + + type Props = NativeStackScreenProps + export function HomeScreen(props: Props) { +- const {setShowLoggedOut} = useLoggedOutViewControls() +- const {data: preferences} = usePreferencesQuery() +- const {currentAccount} = useSession() +- const {data: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} = +- usePinnedFeedsInfos() ++ const { setShowLoggedOut } = useLoggedOutViewControls() ++ const { data: preferences } = usePreferencesQuery() ++ const { currentAccount } = useSession() ++ const { data: pinnedFeedInfos } = usePinnedFeedsInfos() ++ ++ const safePreferences = preferences || { feedViewPrefs: { lab_mergeFeedEnabled: false }, savedFeeds: [] } as any ++ const safePinnedFeedInfos = [{ ++ feedDescriptor: 'following', ++ displayName: 'Following', ++ id: 'following', ++ type: 'feed', ++ savedFeed: undefined, ++ pinned: true, ++ }] + + React.useEffect(() => { + if (isWeb && !currentAccount) { + const getParams = new URLSearchParams(window.location.search) + const splash = getParams.get('splash') +- if (splash === 'true') { +- setShowLoggedOut(true) +- return +- } ++ if (splash === 'true') { setShowLoggedOut(true); return } + } +- + const params = props.route.params +- if ( +- currentAccount && +- props.route.name === 'Start' && +- params?.name && +- params?.rkey +- ) { +- props.navigation.navigate('StarterPack', { +- rkey: params.rkey, +- name: params.name, +- }) ++ if (currentAccount && props.route.name === 'Start' && params?.name && params?.rkey) { ++ props.navigation.navigate('StarterPack', { rkey: params.rkey, name: params.name }) + } +- }, [ +- currentAccount, +- props.navigation, +- props.route.name, +- props.route.params, +- setShowLoggedOut, +- ]) ++ }, [currentAccount, props.navigation, props.route.name, props.route.params, setShowLoggedOut]) + +- if (preferences && pinnedFeedInfos && !isPinnedFeedsLoading) { +- return ( +- +- +- +- ) +- } else { +- return ( +- +- +- +- +- +- ) +- } ++ return ( ++ ++ ++ ++ ) + } + +-function HomeScreenReady({ +- preferences, +- pinnedFeedInfos, +-}: Props & { +- preferences: UsePreferencesQueryResponse +- pinnedFeedInfos: SavedFeedSourceInfo[] +-}) { +- const allFeeds = React.useMemo( +- () => pinnedFeedInfos.map(f => f.feedDescriptor), +- [pinnedFeedInfos], +- ) +- const maybeRawSelectedFeed: FeedDescriptor | undefined = +- useSelectedFeed() ?? allFeeds[0] ++function HomeScreenReady({ preferences, pinnedFeedInfos }: any) { ++ const allFeeds = React.useMemo(() => pinnedFeedInfos.map(f => f.feedDescriptor), [pinnedFeedInfos]) ++ const maybeRawSelectedFeed = useSelectedFeed() ?? allFeeds[0] + const setSelectedFeed = useSetSelectedFeed() + const maybeFoundIndex = allFeeds.indexOf(maybeRawSelectedFeed) + const selectedIndex = Math.max(0, maybeFoundIndex) +- const maybeSelectedFeed: FeedDescriptor | undefined = allFeeds[selectedIndex] ++ const maybeSelectedFeed = allFeeds[selectedIndex] + const requestNotificationsPermission = useRequestNotificationsPermission() + + useSetTitle(pinnedFeedInfos[selectedIndex]?.displayName) + useOTAUpdates() +- +- React.useEffect(() => { +- requestNotificationsPermission('Home') +- }, [requestNotificationsPermission]) ++ React.useEffect(() => { requestNotificationsPermission('Home') }, [requestNotificationsPermission]) + + const pagerRef = React.useRef(null) + const lastPagerReportedIndexRef = React.useRef(selectedIndex) + React.useLayoutEffect(() => { +- // Since the pager is not a controlled component, adjust it imperatively +- // if the selected index gets out of sync with what it last reported. +- // This is supposed to only happen on the web when you use the right nav. + if (selectedIndex !== lastPagerReportedIndexRef.current) { + lastPagerReportedIndexRef.current = selectedIndex + pagerRef.current?.setPage(selectedIndex) + } + }, [selectedIndex]) + +- const {hasSession} = useSession() ++ const { hasSession } = useSession() + const setMinimalShellMode = useSetMinimalShellMode() +- useFocusEffect( +- React.useCallback(() => { +- setMinimalShellMode(false) +- }, [setMinimalShellMode]), +- ) +- +- useFocusEffect( +- useNonReactiveCallback(() => { +- if (maybeSelectedFeed) { +- logEvent('home:feedDisplayed', { +- index: selectedIndex, +- feedType: maybeSelectedFeed.split('|')[0], +- feedUrl: maybeSelectedFeed, +- reason: 'focus', +- }) +- } +- }), +- ) ++ useFocusEffect(React.useCallback(() => { setMinimalShellMode(false) }, [setMinimalShellMode])) + +- const onPageSelected = React.useCallback( +- (index: number) => { +- setMinimalShellMode(false) +- const maybeFeed = allFeeds[index] +- +- // Mutate the ref before setting state to avoid the imperative syncing effect +- // above from starting a loop on Android when swiping back and forth. +- lastPagerReportedIndexRef.current = index +- setSelectedFeed(maybeFeed) +- +- if (maybeFeed) { +- logEvent('home:feedDisplayed', { +- index, +- feedType: maybeFeed.split('|')[0], +- feedUrl: maybeFeed, +- }) +- } +- }, +- [setSelectedFeed, setMinimalShellMode, allFeeds], +- ) ++ const onPageSelected = React.useCallback((index) => { ++ setMinimalShellMode(false) ++ const maybeFeed = allFeeds[index] ++ lastPagerReportedIndexRef.current = index ++ setSelectedFeed(maybeFeed) ++ }, [setSelectedFeed, setMinimalShellMode, allFeeds]) + +- const onPressSelected = React.useCallback(() => { +- emitSoftReset() +- }, []) +- +- const onPageScrollStateChanged = React.useCallback( +- (state: 'idle' | 'dragging' | 'settling') => { +- 'worklet' +- if (state === 'dragging') { +- setMinimalShellMode(false) +- } +- }, +- [setMinimalShellMode], +- ) ++ const onPressSelected = React.useCallback(() => { emitSoftReset() }, []) ++ const onPageScrollStateChanged = React.useCallback((state) => { ++ 'worklet' ++ if (state === 'dragging') setMinimalShellMode(false) ++ }, [setMinimalShellMode]) + + const [demoMode] = useDemoMode() +- +- const renderTabBar = React.useCallback( +- (props: RenderTabBarFnProps) => { +- if (demoMode) { +- return ( +- +- ) +- } +- return ( +- +- ) +- }, +- [onPressSelected, pinnedFeedInfos, demoMode], +- ) +- +- const renderFollowingEmptyState = React.useCallback(() => { +- return +- }, []) +- +- const renderCustomFeedEmptyState = React.useCallback(() => { +- return +- }, []) +- +- const homeFeedParams = React.useMemo(() => { +- return { +- mergeFeedEnabled: Boolean(preferences.feedViewPrefs.lab_mergeFeedEnabled), +- mergeFeedSources: preferences.feedViewPrefs.lab_mergeFeedEnabled +- ? preferences.savedFeeds +- .filter(f => f.type === 'feed' || f.type === 'list') +- .map(f => f.value) +- : [], +- } +- }, [preferences]) +- +- if (demoMode) { +- return ( +- +- +- +- +- ) +- } +- +- return hasSession ? ( +- +- {pinnedFeedInfos.length ? ( +- pinnedFeedInfos.map((feedInfo, index) => { +- const feed = feedInfo.feedDescriptor +- if (feed === 'following') { +- return ( +- +- ) +- } +- const savedFeedConfig = feedInfo.savedFeed +- return ( +- +- ) +- }) +- ) : ( +- +- )} +- +- ) : ( +- +- ++ const renderTabBar = React.useCallback((props) => { ++ return ++ }, [onPressSelected, pinnedFeedInfos]) ++ ++ const renderFollowingEmptyState = React.useCallback(() => , []) ++ const renderCustomFeedEmptyState = React.useCallback(() => , []) ++ ++ const homeFeedParams = React.useMemo(() => ({ ++ mergeFeedEnabled: false, mergeFeedSources: [] ++ }), [preferences]) ++ ++ return ( ++ ++ {pinnedFeedInfos.map((feedInfo, index) => { ++ const feed = feedInfo.feedDescriptor ++ if (feed === 'following') { ++ return ++ } ++ return ++ })} + + ) + } +- +-const styles = StyleSheet.create({ +- loading: { +- height: '100%', +- alignContent: 'center', +- justifyContent: 'center', +- paddingBottom: 100, +- }, +-}) ++const styles = StyleSheet.create({ loading: { height: '100%', alignContent: 'center', justifyContent: 'center', paddingBottom: 100 } }) diff --git a/task.md b/task.md new file mode 100644 index 0000000..3136801 --- /dev/null +++ b/task.md @@ -0,0 +1,4 @@ +## Rules & Constraints +- Do not reformat existing code unless explicitly requested. +- Maintain existing import styles (newlines, sorting). +- Keep patches minimal (clean deltas).