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:
parent
69209c988f
commit
8a93321fb1
72 changed files with 2868 additions and 2836 deletions
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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 />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue