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

@ -16,7 +16,7 @@ enum ScreenState {
S_CreateAccount,
}
export const LoggedOut = observer(() => {
export const LoggedOut = observer(function LoggedOutImpl() {
const pal = usePalette('default')
const store = useStores()
const {screen} = useAnalytics()

View file

@ -8,7 +8,7 @@ import {useStores} from 'state/index'
import {Welcome} from './onboarding/Welcome'
import {RecommendedFeeds} from './onboarding/RecommendedFeeds'
export const Onboarding = observer(() => {
export const Onboarding = observer(function OnboardingImpl() {
const pal = usePalette('default')
const store = useStores()

View file

@ -20,114 +20,116 @@ import {Step1} from './Step1'
import {Step2} from './Step2'
import {Step3} from './Step3'
export const CreateAccount = observer(
({onPressBack}: {onPressBack: () => void}) => {
const {track, screen} = useAnalytics()
const pal = usePalette('default')
const store = useStores()
const model = React.useMemo(() => new CreateAccountModel(store), [store])
export const CreateAccount = observer(function CreateAccountImpl({
onPressBack,
}: {
onPressBack: () => void
}) {
const {track, screen} = useAnalytics()
const pal = usePalette('default')
const store = useStores()
const model = React.useMemo(() => new CreateAccountModel(store), [store])
React.useEffect(() => {
screen('CreateAccount')
}, [screen])
React.useEffect(() => {
screen('CreateAccount')
}, [screen])
React.useEffect(() => {
model.fetchServiceDescription()
}, [model])
React.useEffect(() => {
model.fetchServiceDescription()
}, [model])
const onPressRetryConnect = React.useCallback(
() => model.fetchServiceDescription(),
[model],
)
const onPressRetryConnect = React.useCallback(
() => model.fetchServiceDescription(),
[model],
)
const onPressBackInner = React.useCallback(() => {
if (model.canBack) {
model.back()
} else {
onPressBack()
const onPressBackInner = React.useCallback(() => {
if (model.canBack) {
model.back()
} else {
onPressBack()
}
}, [model, onPressBack])
const onPressNext = React.useCallback(async () => {
if (!model.canNext) {
return
}
if (model.step < 3) {
model.next()
} else {
try {
await model.submit()
} catch {
// dont need to handle here
} finally {
track('Try Create Account')
}
}, [model, onPressBack])
}
}, [model, track])
const onPressNext = React.useCallback(async () => {
if (!model.canNext) {
return
}
if (model.step < 3) {
model.next()
} else {
try {
await model.submit()
} catch {
// dont need to handle here
} finally {
track('Try Create Account')
}
}
}, [model, track])
return (
<LoggedOutLayout
leadin={`Step ${model.step}`}
title="Create Account"
description="We're so excited to have you join us!">
<ScrollView testID="createAccount" style={pal.view}>
<KeyboardAvoidingView behavior="padding">
<View style={styles.stepContainer}>
{model.step === 1 && <Step1 model={model} />}
{model.step === 2 && <Step2 model={model} />}
{model.step === 3 && <Step3 model={model} />}
</View>
<View style={[s.flexRow, s.pl20, s.pr20]}>
return (
<LoggedOutLayout
leadin={`Step ${model.step}`}
title="Create Account"
description="We're so excited to have you join us!">
<ScrollView testID="createAccount" style={pal.view}>
<KeyboardAvoidingView behavior="padding">
<View style={styles.stepContainer}>
{model.step === 1 && <Step1 model={model} />}
{model.step === 2 && <Step2 model={model} />}
{model.step === 3 && <Step3 model={model} />}
</View>
<View style={[s.flexRow, s.pl20, s.pr20]}>
<TouchableOpacity
onPress={onPressBackInner}
testID="backBtn"
accessibilityRole="button">
<Text type="xl" style={pal.link}>
Back
</Text>
</TouchableOpacity>
<View style={s.flex1} />
{model.canNext ? (
<TouchableOpacity
onPress={onPressBackInner}
testID="backBtn"
testID="nextBtn"
onPress={onPressNext}
accessibilityRole="button">
<Text type="xl" style={pal.link}>
Back
{model.isProcessing ? (
<ActivityIndicator />
) : (
<Text type="xl-bold" style={[pal.link, s.pr5]}>
Next
</Text>
)}
</TouchableOpacity>
) : model.didServiceDescriptionFetchFail ? (
<TouchableOpacity
testID="retryConnectBtn"
onPress={onPressRetryConnect}
accessibilityRole="button"
accessibilityLabel="Retry"
accessibilityHint="Retries account creation"
accessibilityLiveRegion="polite">
<Text type="xl-bold" style={[pal.link, s.pr5]}>
Retry
</Text>
</TouchableOpacity>
<View style={s.flex1} />
{model.canNext ? (
<TouchableOpacity
testID="nextBtn"
onPress={onPressNext}
accessibilityRole="button">
{model.isProcessing ? (
<ActivityIndicator />
) : (
<Text type="xl-bold" style={[pal.link, s.pr5]}>
Next
</Text>
)}
</TouchableOpacity>
) : model.didServiceDescriptionFetchFail ? (
<TouchableOpacity
testID="retryConnectBtn"
onPress={onPressRetryConnect}
accessibilityRole="button"
accessibilityLabel="Retry"
accessibilityHint="Retries account creation"
accessibilityLiveRegion="polite">
<Text type="xl-bold" style={[pal.link, s.pr5]}>
Retry
</Text>
</TouchableOpacity>
) : model.isFetchingServiceDescription ? (
<>
<ActivityIndicator color="#fff" />
<Text type="xl" style={[pal.text, s.pr5]}>
Connecting...
</Text>
</>
) : undefined}
</View>
<View style={s.footerSpacer} />
</KeyboardAvoidingView>
</ScrollView>
</LoggedOutLayout>
)
},
)
) : model.isFetchingServiceDescription ? (
<>
<ActivityIndicator color="#fff" />
<Text type="xl" style={[pal.text, s.pr5]}>
Connecting...
</Text>
</>
) : undefined}
</View>
<View style={s.footerSpacer} />
</KeyboardAvoidingView>
</ScrollView>
</LoggedOutLayout>
)
})
const styles = StyleSheet.create({
stepContainer: {

View file

@ -20,7 +20,11 @@ import {LOGIN_INCLUDE_DEV_SERVERS} from 'lib/build-flags'
* @field Bluesky (default)
* @field Other (staging, local dev, your own PDS, etc.)
*/
export const Step1 = observer(({model}: {model: CreateAccountModel}) => {
export const Step1 = observer(function Step1Impl({
model,
}: {
model: CreateAccountModel
}) {
const pal = usePalette('default')
const [isDefaultSelected, setIsDefaultSelected] = React.useState(true)

View file

@ -21,7 +21,11 @@ import {useStores} from 'state/index'
* @field Birth date
* @readonly Terms of service & privacy policy
*/
export const Step2 = observer(({model}: {model: CreateAccountModel}) => {
export const Step2 = observer(function Step2Impl({
model,
}: {
model: CreateAccountModel
}) {
const pal = usePalette('default')
const store = useStores()

View file

@ -13,7 +13,11 @@ import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
/** STEP 3: Your user handle
* @field User handle
*/
export const Step3 = observer(({model}: {model: CreateAccountModel}) => {
export const Step3 = observer(function Step3Impl({
model,
}: {
model: CreateAccountModel
}) {
const pal = usePalette('default')
return (
<View>

View file

@ -15,7 +15,9 @@ import {RECOMMENDED_FEEDS} from 'lib/constants'
type Props = {
next: () => void
}
export const RecommendedFeeds = observer(({next}: Props) => {
export const RecommendedFeeds = observer(function RecommendedFeedsImpl({
next,
}: Props) {
const pal = usePalette('default')
const {isTabletOrMobile} = useWebMediaQueries()

View file

@ -13,130 +13,134 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {makeRecordUri} from 'lib/strings/url-helpers'
import {sanitizeHandle} from 'lib/strings/handles'
export const RecommendedFeedsItem = observer(
({did, rkey}: {did: string; rkey: string}) => {
const {isMobile} = useWebMediaQueries()
const pal = usePalette('default')
const uri = makeRecordUri(did, 'app.bsky.feed.generator', rkey)
const item = useCustomFeed(uri)
if (!item) return null
const onToggle = async () => {
if (item.isSaved) {
try {
await item.unsave()
} catch (e) {
Toast.show('There was an issue contacting your server')
console.error('Failed to unsave feed', {e})
}
} else {
try {
await item.save()
await item.pin()
} catch (e) {
Toast.show('There was an issue contacting your server')
console.error('Failed to pin feed', {e})
}
export const RecommendedFeedsItem = observer(function RecommendedFeedsItemImpl({
did,
rkey,
}: {
did: string
rkey: string
}) {
const {isMobile} = useWebMediaQueries()
const pal = usePalette('default')
const uri = makeRecordUri(did, 'app.bsky.feed.generator', rkey)
const item = useCustomFeed(uri)
if (!item) return null
const onToggle = async () => {
if (item.isSaved) {
try {
await item.unsave()
} catch (e) {
Toast.show('There was an issue contacting your server')
console.error('Failed to unsave feed', {e})
}
} else {
try {
await item.save()
await item.pin()
} catch (e) {
Toast.show('There was an issue contacting your server')
console.error('Failed to pin feed', {e})
}
}
return (
<View testID={`feed-${item.displayName}`}>
<View
style={[
pal.border,
{
flex: isMobile ? 1 : undefined,
flexDirection: 'row',
gap: 18,
maxWidth: isMobile ? undefined : 670,
borderRightWidth: isMobile ? undefined : 1,
paddingHorizontal: 24,
paddingVertical: isMobile ? 12 : 24,
borderTopWidth: 1,
},
]}>
<View style={{marginTop: 2}}>
<UserAvatar type="algo" size={42} avatar={item.data.avatar} />
</View>
<View style={{flex: isMobile ? 1 : undefined}}>
}
return (
<View testID={`feed-${item.displayName}`}>
<View
style={[
pal.border,
{
flex: isMobile ? 1 : undefined,
flexDirection: 'row',
gap: 18,
maxWidth: isMobile ? undefined : 670,
borderRightWidth: isMobile ? undefined : 1,
paddingHorizontal: 24,
paddingVertical: isMobile ? 12 : 24,
borderTopWidth: 1,
},
]}>
<View style={{marginTop: 2}}>
<UserAvatar type="algo" size={42} avatar={item.data.avatar} />
</View>
<View style={{flex: isMobile ? 1 : undefined}}>
<Text
type="2xl-bold"
numberOfLines={1}
style={[pal.text, {fontSize: 19}]}>
{item.displayName}
</Text>
<Text style={[pal.textLight, {marginBottom: 8}]} numberOfLines={1}>
by {sanitizeHandle(item.data.creator.handle, '@')}
</Text>
{item.data.description ? (
<Text
type="2xl-bold"
numberOfLines={1}
style={[pal.text, {fontSize: 19}]}>
{item.displayName}
type="xl"
style={[
pal.text,
{
flex: isMobile ? 1 : undefined,
maxWidth: 550,
marginBottom: 18,
},
]}
numberOfLines={6}>
{item.data.description}
</Text>
) : null}
<Text style={[pal.textLight, {marginBottom: 8}]} numberOfLines={1}>
by {sanitizeHandle(item.data.creator.handle, '@')}
</Text>
{item.data.description ? (
<Text
type="xl"
style={[
pal.text,
{
flex: isMobile ? 1 : undefined,
maxWidth: 550,
marginBottom: 18,
},
]}
numberOfLines={6}>
{item.data.description}
</Text>
) : null}
<View style={{flexDirection: 'row', alignItems: 'center', gap: 12}}>
<Button
type="inverted"
style={{paddingVertical: 6}}
onPress={onToggle}>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
paddingRight: 2,
gap: 6,
}}>
{item.isSaved ? (
<>
<FontAwesomeIcon
icon="check"
size={16}
color={pal.colors.textInverted}
/>
<Text type="lg-medium" style={pal.textInverted}>
Added
</Text>
</>
) : (
<>
<FontAwesomeIcon
icon="plus"
size={16}
color={pal.colors.textInverted}
/>
<Text type="lg-medium" style={pal.textInverted}>
Add
</Text>
</>
)}
</View>
</Button>
<View style={{flexDirection: 'row', gap: 4}}>
<HeartIcon
size={16}
strokeWidth={2.5}
style={[pal.textLight, {position: 'relative', top: 2}]}
/>
<Text type="lg-medium" style={[pal.text, pal.textLight]}>
{item.data.likeCount || 0}
</Text>
<View style={{flexDirection: 'row', alignItems: 'center', gap: 12}}>
<Button
type="inverted"
style={{paddingVertical: 6}}
onPress={onToggle}>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
paddingRight: 2,
gap: 6,
}}>
{item.isSaved ? (
<>
<FontAwesomeIcon
icon="check"
size={16}
color={pal.colors.textInverted}
/>
<Text type="lg-medium" style={pal.textInverted}>
Added
</Text>
</>
) : (
<>
<FontAwesomeIcon
icon="plus"
size={16}
color={pal.colors.textInverted}
/>
<Text type="lg-medium" style={pal.textInverted}>
Add
</Text>
</>
)}
</View>
</Button>
<View style={{flexDirection: 'row', gap: 4}}>
<HeartIcon
size={16}
strokeWidth={2.5}
style={[pal.textLight, {position: 'relative', top: 2}]}
/>
<Text type="lg-medium" style={[pal.text, pal.textLight]}>
{item.data.likeCount || 0}
</Text>
</View>
</View>
</View>
</View>
)
},
)
</View>
)
})

View file

@ -14,7 +14,9 @@ type Props = {
skip: () => void
}
export const WelcomeDesktop = observer(({next}: Props) => {
export const WelcomeDesktop = observer(function WelcomeDesktopImpl({
next,
}: Props) {
const pal = usePalette('default')
const horizontal = useMediaQuery({minWidth: 1300})
const title = (

View file

@ -13,7 +13,10 @@ type Props = {
skip: () => void
}
export const WelcomeMobile = observer(({next, skip}: Props) => {
export const WelcomeMobile = observer(function WelcomeMobileImpl({
next,
skip,
}: Props) {
const pal = usePalette('default')
return (

View file

@ -17,7 +17,7 @@ import {STATUS_PAGE_URL} from 'lib/constants'
export const withAuthRequired = <P extends object>(
Component: React.ComponentType<P>,
): React.FC<P> =>
observer((props: P) => {
observer(function AuthRequired(props: P) {
const store = useStores()
if (store.session.isResumingSession) {
return <Loading />