Give explicit names to MobX observer components (#1413)

* Consider observer(...) as components

* Add display names to MobX observers

* Temporarily suppress nested components

* Suppress new false positives for react/prop-types
This commit is contained in:
dan 2023-09-08 01:36:08 +01:00 committed by GitHub
parent 69209c988f
commit 8a93321fb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 2868 additions and 2836 deletions

View file

@ -19,7 +19,7 @@ import {CenteredView} from 'view/com/util/Views'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppPasswords'>
export const AppPasswords = withAuthRequired(
observer(({}: Props) => {
observer(function AppPasswordsImpl({}: Props) {
const pal = usePalette('default')
const store = useStores()
const {screen} = useAnalytics()

View file

@ -42,7 +42,7 @@ import {NavigationProp} from 'lib/routes/types'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'CustomFeed'>
export const CustomFeedScreen = withAuthRequired(
observer((props: Props) => {
observer(function CustomFeedScreenImpl(props: Props) {
const pal = usePalette('default')
const store = useStores()
const navigation = useNavigation<NavigationProp>()
@ -119,7 +119,10 @@ export const CustomFeedScreen = withAuthRequired(
)
export const CustomFeedScreenInner = observer(
({route, feedOwnerDid}: Props & {feedOwnerDid: string}) => {
function CustomFeedScreenInnerImpl({
route,
feedOwnerDid,
}: Props & {feedOwnerDid: string}) {
const store = useStores()
const pal = usePalette('default')
const {isTabletOrDesktop} = useWebMediaQueries()

View file

@ -19,7 +19,7 @@ import debounce from 'lodash.debounce'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'DiscoverFeeds'>
export const DiscoverFeedsScreen = withAuthRequired(
observer(({}: Props) => {
observer(function DiscoverFeedsScreenImpl({}: Props) {
const store = useStores()
const pal = usePalette('default')
const feeds = React.useMemo(() => new FeedsDiscoveryModel(store), [store])

View file

@ -25,7 +25,7 @@ const MOBILE_HEADER_OFFSET = 40
type Props = NativeStackScreenProps<FeedsTabNavigatorParams, 'Feeds'>
export const FeedsScreen = withAuthRequired(
observer<Props>(({}: Props) => {
observer<Props>(function FeedsScreenImpl({}: Props) {
const pal = usePalette('default')
const store = useStores()
const {isMobile} = useWebMediaQueries()

View file

@ -28,7 +28,7 @@ const POLL_FREQ = 30e3 // 30sec
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
export const HomeScreen = withAuthRequired(
observer(({}: Props) => {
observer(function HomeScreenImpl({}: Props) {
const store = useStores()
const pagerRef = React.useRef<PagerRef>(null)
const [selectedPage, setSelectedPage] = React.useState(0)
@ -142,152 +142,141 @@ export const HomeScreen = withAuthRequired(
}),
)
const FeedPage = observer(
({
testID,
isPageFocused,
feed,
renderEmptyState,
}: {
testID?: string
feed: PostsFeedModel
isPageFocused: boolean
renderEmptyState?: () => JSX.Element
}) => {
const store = useStores()
const {isMobile} = useWebMediaQueries()
const [onMainScroll, isScrolledDown, resetMainScroll] =
useOnMainScroll(store)
const {screen, track} = useAnalytics()
const [headerOffset, setHeaderOffset] = React.useState(
isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP,
)
const scrollElRef = React.useRef<FlatList>(null)
const {appState} = useAppState({
onForeground: () => doPoll(true),
})
const isScreenFocused = useIsFocused()
const FeedPage = observer(function FeedPageImpl({
testID,
isPageFocused,
feed,
renderEmptyState,
}: {
testID?: string
feed: PostsFeedModel
isPageFocused: boolean
renderEmptyState?: () => JSX.Element
}) {
const store = useStores()
const {isMobile} = useWebMediaQueries()
const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll(store)
const {screen, track} = useAnalytics()
const [headerOffset, setHeaderOffset] = React.useState(
isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP,
)
const scrollElRef = React.useRef<FlatList>(null)
const {appState} = useAppState({
onForeground: () => doPoll(true),
})
const isScreenFocused = useIsFocused()
React.useEffect(() => {
// called on first load
if (!feed.hasLoaded && isPageFocused) {
feed.setup()
}
}, [isPageFocused, feed])
React.useEffect(() => {
// called on first load
if (!feed.hasLoaded && isPageFocused) {
feed.setup()
}
}, [isPageFocused, feed])
const doPoll = React.useCallback(
(knownActive = false) => {
if (
(!knownActive && appState !== 'active') ||
!isScreenFocused ||
!isPageFocused
) {
return
}
if (feed.isLoading) {
return
}
store.log.debug('HomeScreen: Polling for new posts')
feed.checkForLatest()
},
[appState, isScreenFocused, isPageFocused, store, feed],
)
const scrollToTop = React.useCallback(() => {
scrollElRef.current?.scrollToOffset({offset: -headerOffset})
resetMainScroll()
}, [headerOffset, resetMainScroll])
const onSoftReset = React.useCallback(() => {
if (isPageFocused) {
scrollToTop()
feed.refresh()
}
}, [isPageFocused, scrollToTop, feed])
// listens for resize events
React.useEffect(() => {
setHeaderOffset(isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP)
}, [isMobile])
// fires when page within screen is activated/deactivated
// - check for latest
React.useEffect(() => {
if (!isPageFocused || !isScreenFocused) {
const doPoll = React.useCallback(
(knownActive = false) => {
if (
(!knownActive && appState !== 'active') ||
!isScreenFocused ||
!isPageFocused
) {
return
}
const softResetSub = store.onScreenSoftReset(onSoftReset)
const feedCleanup = feed.registerListeners()
const pollInterval = setInterval(doPoll, POLL_FREQ)
screen('Feed')
store.log.debug('HomeScreen: Updating feed')
if (feed.isLoading) {
return
}
store.log.debug('HomeScreen: Polling for new posts')
feed.checkForLatest()
if (feed.hasContent) {
feed.update()
}
},
[appState, isScreenFocused, isPageFocused, store, feed],
)
return () => {
clearInterval(pollInterval)
softResetSub.remove()
feedCleanup()
}
}, [
store,
doPoll,
onSoftReset,
screen,
feed,
isPageFocused,
isScreenFocused,
])
const scrollToTop = React.useCallback(() => {
scrollElRef.current?.scrollToOffset({offset: -headerOffset})
resetMainScroll()
}, [headerOffset, resetMainScroll])
const onPressCompose = React.useCallback(() => {
track('HomeScreen:PressCompose')
store.shell.openComposer({})
}, [store, track])
const onPressTryAgain = React.useCallback(() => {
feed.refresh()
}, [feed])
const onPressLoadLatest = React.useCallback(() => {
const onSoftReset = React.useCallback(() => {
if (isPageFocused) {
scrollToTop()
feed.refresh()
}, [feed, scrollToTop])
}
}, [isPageFocused, scrollToTop, feed])
const hasNew = feed.hasNewLatest && !feed.isRefreshing
return (
<View testID={testID} style={s.h100pct}>
<Feed
testID={testID ? `${testID}-feed` : undefined}
key="default"
feed={feed}
scrollElRef={scrollElRef}
onPressTryAgain={onPressTryAgain}
onScroll={onMainScroll}
scrollEventThrottle={100}
renderEmptyState={renderEmptyState}
headerOffset={headerOffset}
// listens for resize events
React.useEffect(() => {
setHeaderOffset(isMobile ? HEADER_OFFSET_MOBILE : HEADER_OFFSET_DESKTOP)
}, [isMobile])
// fires when page within screen is activated/deactivated
// - check for latest
React.useEffect(() => {
if (!isPageFocused || !isScreenFocused) {
return
}
const softResetSub = store.onScreenSoftReset(onSoftReset)
const feedCleanup = feed.registerListeners()
const pollInterval = setInterval(doPoll, POLL_FREQ)
screen('Feed')
store.log.debug('HomeScreen: Updating feed')
feed.checkForLatest()
if (feed.hasContent) {
feed.update()
}
return () => {
clearInterval(pollInterval)
softResetSub.remove()
feedCleanup()
}
}, [store, doPoll, onSoftReset, screen, feed, isPageFocused, isScreenFocused])
const onPressCompose = React.useCallback(() => {
track('HomeScreen:PressCompose')
store.shell.openComposer({})
}, [store, track])
const onPressTryAgain = React.useCallback(() => {
feed.refresh()
}, [feed])
const onPressLoadLatest = React.useCallback(() => {
scrollToTop()
feed.refresh()
}, [feed, scrollToTop])
const hasNew = feed.hasNewLatest && !feed.isRefreshing
return (
<View testID={testID} style={s.h100pct}>
<Feed
testID={testID ? `${testID}-feed` : undefined}
key="default"
feed={feed}
scrollElRef={scrollElRef}
onPressTryAgain={onPressTryAgain}
onScroll={onMainScroll}
scrollEventThrottle={100}
renderEmptyState={renderEmptyState}
headerOffset={headerOffset}
/>
{(isScrolledDown || hasNew) && (
<LoadLatestBtn
onPress={onPressLoadLatest}
label="Load new posts"
showIndicator={hasNew}
minimalShellMode={store.shell.minimalShellMode}
/>
{(isScrolledDown || hasNew) && (
<LoadLatestBtn
onPress={onPressLoadLatest}
label="Load new posts"
showIndicator={hasNew}
minimalShellMode={store.shell.minimalShellMode}
/>
)}
<FAB
testID="composeFAB"
onPress={onPressCompose}
icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
accessibilityRole="button"
accessibilityLabel="New post"
accessibilityHint=""
/>
</View>
)
},
)
)}
<FAB
testID="composeFAB"
onPress={onPressCompose}
icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
accessibilityRole="button"
accessibilityLabel="New post"
accessibilityHint=""
/>
</View>
)
})

View file

@ -27,7 +27,7 @@ type Props = NativeStackScreenProps<
'ModerationBlockedAccounts'
>
export const ModerationBlockedAccounts = withAuthRequired(
observer(({}: Props) => {
observer(function ModerationBlockedAccountsImpl({}: Props) {
const pal = usePalette('default')
const store = useStores()
const {isTabletOrDesktop} = useWebMediaQueries()
@ -116,6 +116,8 @@ export const ModerationBlockedAccounts = withAuthRequired(
onEndReached={onEndReached}
renderItem={renderItem}
initialNumToRender={15}
// FIXME(dan)
// eslint-disable-next-line react/no-unstable-nested-components
ListFooterComponent={() => (
<View style={styles.footer}>
{blockedAccounts.isLoading && <ActivityIndicator />}

View file

@ -27,7 +27,7 @@ type Props = NativeStackScreenProps<
'ModerationMutedAccounts'
>
export const ModerationMutedAccounts = withAuthRequired(
observer(({}: Props) => {
observer(function ModerationMutedAccountsImpl({}: Props) {
const pal = usePalette('default')
const store = useStores()
const {isTabletOrDesktop} = useWebMediaQueries()
@ -112,6 +112,8 @@ export const ModerationMutedAccounts = withAuthRequired(
onEndReached={onEndReached}
renderItem={renderItem}
initialNumToRender={15}
// FIXME(dan)
// eslint-disable-next-line react/no-unstable-nested-components
ListFooterComponent={() => (
<View style={styles.footer}>
{mutedAccounts.isLoading && <ActivityIndicator />}

View file

@ -24,7 +24,7 @@ type Props = NativeStackScreenProps<
'Notifications'
>
export const NotificationsScreen = withAuthRequired(
observer(({}: Props) => {
observer(function NotificationsScreenImpl({}: Props) {
const store = useStores()
const [onMainScroll, isScrolledDown, resetMainScroll] =
useOnMainScroll(store)

View file

@ -48,7 +48,9 @@ type Props = NativeStackScreenProps<
CommonNavigatorParams,
'PreferencesHomeFeed'
>
export const PreferencesHomeFeed = observer(({navigation}: Props) => {
export const PreferencesHomeFeed = observer(function PreferencesHomeFeedImpl({
navigation,
}: Props) {
const pal = usePalette('default')
const store = useStores()
const {isTabletOrDesktop} = useWebMediaQueries()

View file

@ -32,7 +32,7 @@ import {combinedDisplayName} from 'lib/strings/display-names'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Profile'>
export const ProfileScreen = withAuthRequired(
observer(({route}: Props) => {
observer(function ProfileScreenImpl({route}: Props) {
const store = useStores()
const {screen, track} = useAnalytics()
const viewSelectorRef = React.useRef<ViewSelectorHandle>(null)

View file

@ -23,7 +23,7 @@ import {s} from 'lib/styles'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileList'>
export const ProfileListScreen = withAuthRequired(
observer(({route}: Props) => {
observer(function ProfileListScreenImpl({route}: Props) {
const store = useStores()
const navigation = useNavigation<NavigationProp>()
const {isTabletOrDesktop} = useWebMediaQueries()

View file

@ -35,7 +35,7 @@ import {Link, TextLink} from 'view/com/util/Link'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'SavedFeeds'>
export const SavedFeeds = withAuthRequired(
observer(({}: Props) => {
observer(function SavedFeedsImpl({}: Props) {
const pal = usePalette('default')
const store = useStores()
const {isMobile, isTabletOrDesktop} = useWebMediaQueries()
@ -151,96 +151,98 @@ export const SavedFeeds = withAuthRequired(
}),
)
const ListItem = observer(
({item, drag}: {item: CustomFeedModel; drag: () => void}) => {
const pal = usePalette('default')
const store = useStores()
const savedFeeds = useMemo(() => store.me.savedFeeds, [store])
const isPinned = savedFeeds.isPinned(item)
const ListItem = observer(function ListItemImpl({
item,
drag,
}: {
item: CustomFeedModel
drag: () => void
}) {
const pal = usePalette('default')
const store = useStores()
const savedFeeds = useMemo(() => store.me.savedFeeds, [store])
const isPinned = savedFeeds.isPinned(item)
const onTogglePinned = useCallback(() => {
Haptics.default()
savedFeeds.togglePinnedFeed(item).catch(e => {
const onTogglePinned = useCallback(() => {
Haptics.default()
savedFeeds.togglePinnedFeed(item).catch(e => {
Toast.show('There was an issue contacting the server')
store.log.error('Failed to toggle pinned feed', {e})
})
}, [savedFeeds, item, store])
const onPressUp = useCallback(
() =>
savedFeeds.movePinnedFeed(item, 'up').catch(e => {
Toast.show('There was an issue contacting the server')
store.log.error('Failed to toggle pinned feed', {e})
})
}, [savedFeeds, item, store])
const onPressUp = useCallback(
() =>
savedFeeds.movePinnedFeed(item, 'up').catch(e => {
Toast.show('There was an issue contacting the server')
store.log.error('Failed to set pinned feed order', {e})
}),
[store, savedFeeds, item],
)
const onPressDown = useCallback(
() =>
savedFeeds.movePinnedFeed(item, 'down').catch(e => {
Toast.show('There was an issue contacting the server')
store.log.error('Failed to set pinned feed order', {e})
}),
[store, savedFeeds, item],
)
store.log.error('Failed to set pinned feed order', {e})
}),
[store, savedFeeds, item],
)
const onPressDown = useCallback(
() =>
savedFeeds.movePinnedFeed(item, 'down').catch(e => {
Toast.show('There was an issue contacting the server')
store.log.error('Failed to set pinned feed order', {e})
}),
[store, savedFeeds, item],
)
return (
<ScaleDecorator>
<ShadowDecorator>
<Pressable
accessibilityRole="button"
onLongPress={isPinned ? drag : undefined}
delayLongPress={200}
style={[styles.itemContainer, pal.border]}>
{isPinned && isWeb ? (
<View style={styles.webArrowButtonsContainer}>
<TouchableOpacity
accessibilityRole="button"
onPress={onPressUp}>
<FontAwesomeIcon
icon="arrow-up"
size={12}
style={[pal.text, styles.webArrowUpButton]}
/>
</TouchableOpacity>
<TouchableOpacity
accessibilityRole="button"
onPress={onPressDown}>
<FontAwesomeIcon
icon="arrow-down"
size={12}
style={[pal.text]}
/>
</TouchableOpacity>
</View>
) : isPinned ? (
<FontAwesomeIcon
icon="bars"
size={20}
color={pal.colors.text}
style={s.ml20}
/>
) : null}
<CustomFeed
key={item.data.uri}
item={item}
showSaveBtn
style={styles.noBorder}
return (
<ScaleDecorator>
<ShadowDecorator>
<Pressable
accessibilityRole="button"
onLongPress={isPinned ? drag : undefined}
delayLongPress={200}
style={[styles.itemContainer, pal.border]}>
{isPinned && isWeb ? (
<View style={styles.webArrowButtonsContainer}>
<TouchableOpacity accessibilityRole="button" onPress={onPressUp}>
<FontAwesomeIcon
icon="arrow-up"
size={12}
style={[pal.text, styles.webArrowUpButton]}
/>
</TouchableOpacity>
<TouchableOpacity
accessibilityRole="button"
onPress={onPressDown}>
<FontAwesomeIcon
icon="arrow-down"
size={12}
style={[pal.text]}
/>
</TouchableOpacity>
</View>
) : isPinned ? (
<FontAwesomeIcon
icon="bars"
size={20}
color={pal.colors.text}
style={s.ml20}
/>
<TouchableOpacity
accessibilityRole="button"
hitSlop={10}
onPress={onTogglePinned}>
<FontAwesomeIcon
icon="thumb-tack"
size={20}
color={isPinned ? colors.blue3 : pal.colors.icon}
/>
</TouchableOpacity>
</Pressable>
</ShadowDecorator>
</ScaleDecorator>
)
},
)
) : null}
<CustomFeed
key={item.data.uri}
item={item}
showSaveBtn
style={styles.noBorder}
/>
<TouchableOpacity
accessibilityRole="button"
hitSlop={10}
onPress={onTogglePinned}>
<FontAwesomeIcon
icon="thumb-tack"
size={20}
color={isPinned ? colors.blue3 : pal.colors.icon}
/>
</TouchableOpacity>
</Pressable>
</ShadowDecorator>
</ScaleDecorator>
)
})
const styles = StyleSheet.create({
desktopContainer: {

View file

@ -18,7 +18,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'>
export const SearchScreen = withAuthRequired(
observer(({navigation, route}: Props) => {
observer(function SearchScreenImpl({navigation, route}: Props) {
const store = useStores()
const params = route.params || {}
const foafs = React.useMemo<FoafsModel>(

View file

@ -30,7 +30,7 @@ import {isAndroid, isIOS} from 'platform/detection'
type Props = NativeStackScreenProps<SearchTabNavigatorParams, 'Search'>
export const SearchScreen = withAuthRequired(
observer<Props>(({}: Props) => {
observer<Props>(function SearchScreenImpl({}: Props) {
const pal = usePalette('default')
const store = useStores()
const scrollViewRef = React.useRef<ScrollView>(null)