Better loading screens
parent
e470e3933b
commit
4ae6fbd3c8
|
@ -2,6 +2,7 @@ import {makeAutoObservable} from 'mobx'
|
|||
import * as Post from '../../third-party/api/src/client/types/app/bsky/feed/post'
|
||||
import {AtUri} from '../../third-party/uri'
|
||||
import {RootStoreModel} from './root-store'
|
||||
import {cleanError} from '../../view/lib/strings'
|
||||
|
||||
export type PostEntities = Post.Record['entities']
|
||||
export type PostReply = Post.Record['reply']
|
||||
|
@ -67,7 +68,7 @@ export class PostModel implements RemoveIndex<Post.Record> {
|
|||
private _xIdle(err: string = '') {
|
||||
this.isLoading = false
|
||||
this.hasLoaded = true
|
||||
this.error = err
|
||||
this.error = cleanError(err)
|
||||
}
|
||||
|
||||
// loader functions
|
||||
|
@ -88,7 +89,7 @@ export class PostModel implements RemoveIndex<Post.Record> {
|
|||
this._replaceAll(res.value)
|
||||
this._xIdle()
|
||||
} catch (e: any) {
|
||||
this._xIdle(`Failed to load post: ${e.toString()}`)
|
||||
this._xIdle(e.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import React, {useState, useEffect} from 'react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {ActivityIndicator, Text, View} from 'react-native'
|
||||
import {Text, View} from 'react-native'
|
||||
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
import {PostModel} from '../../../state/models/post'
|
||||
import {useStores} from '../../../state'
|
||||
|
||||
|
@ -28,7 +30,9 @@ export const PostText = observer(function PostText({
|
|||
if (!model || model.isLoading || model.uri !== uri) {
|
||||
return (
|
||||
<View>
|
||||
<ActivityIndicator />
|
||||
<LoadingPlaceholder width="100%" height={8} style={{marginTop: 6}} />
|
||||
<LoadingPlaceholder width="100%" height={8} style={{marginTop: 6}} />
|
||||
<LoadingPlaceholder width={100} height={8} style={{marginTop: 6}} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
@ -38,7 +42,7 @@ export const PostText = observer(function PostText({
|
|||
if (model.hasError) {
|
||||
return (
|
||||
<View>
|
||||
<Text style={style}>{model.error}</Text>
|
||||
<ErrorMessage style={style} message={model.error} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import React, {useEffect, useMemo} from 'react'
|
||||
import {
|
||||
Animated,
|
||||
StyleProp,
|
||||
useWindowDimensions,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {colors} from '../../lib/styles'
|
||||
|
||||
export function LoadingPlaceholder({
|
||||
width,
|
||||
height,
|
||||
style,
|
||||
}: {
|
||||
width: string | number
|
||||
height: string | number
|
||||
style?: StyleProp<ViewStyle>
|
||||
}) {
|
||||
const dim = useWindowDimensions()
|
||||
const elWidth = typeof width === 'string' ? dim.width : width
|
||||
const offset = useMemo(() => new Animated.Value(elWidth * -1), [])
|
||||
useEffect(() => {
|
||||
const anim = Animated.loop(
|
||||
Animated.sequence([
|
||||
Animated.timing(offset, {
|
||||
toValue: elWidth,
|
||||
duration: 1e3,
|
||||
useNativeDriver: true,
|
||||
isInteraction: false,
|
||||
}),
|
||||
Animated.timing(offset, {
|
||||
toValue: elWidth * -1,
|
||||
duration: 0,
|
||||
delay: 500,
|
||||
useNativeDriver: true,
|
||||
isInteraction: false,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
anim.start()
|
||||
return () => anim.stop()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
width,
|
||||
height,
|
||||
backgroundColor: colors.gray2,
|
||||
borderRadius: 6,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
style,
|
||||
]}>
|
||||
<Animated.View
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
transform: [{translateX: offset}],
|
||||
}}>
|
||||
<LinearGradient
|
||||
colors={[colors.gray2, '#d4d2d2', colors.gray2]}
|
||||
start={{x: 0, y: 0}}
|
||||
end={{x: 1, y: 0}}
|
||||
style={{width: '100%', height: '100%'}}
|
||||
/>
|
||||
</Animated.View>
|
||||
</View>
|
||||
)
|
||||
}
|
|
@ -2,6 +2,7 @@ import React, {useState, useEffect} from 'react'
|
|||
import * as GetProfile from '../../../third-party/api/src/client/types/app/bsky/actor/getProfile'
|
||||
import {StyleProp, Text, TextStyle} from 'react-native'
|
||||
import {Link} from './Link'
|
||||
import {LoadingPlaceholder} from './LoadingPlaceholder'
|
||||
import {useStores} from '../../../state'
|
||||
|
||||
export function UserInfoText({
|
||||
|
@ -48,26 +49,31 @@ export function UserInfoText({
|
|||
}
|
||||
}, [did, store.api.app.bsky])
|
||||
|
||||
let inner
|
||||
if (didFail) {
|
||||
inner = <Text style={style}>{failed}</Text>
|
||||
} else if (profile) {
|
||||
inner = <Text style={style}>{`${prefix || ''}${profile[attr]}`}</Text>
|
||||
} else {
|
||||
inner = (
|
||||
<LoadingPlaceholder
|
||||
width={80}
|
||||
height={8}
|
||||
style={{position: 'relative', top: 1, left: 2}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (asLink) {
|
||||
const title = profile?.displayName || profile?.handle || 'User'
|
||||
return (
|
||||
<Link
|
||||
href={`/profile/${profile?.handle ? profile.handle : did}`}
|
||||
title={title}>
|
||||
<Text style={style}>
|
||||
{didFail
|
||||
? failed
|
||||
: profile
|
||||
? `${prefix || ''}${profile[attr]}`
|
||||
: loading}
|
||||
</Text>
|
||||
{inner}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Text style={style}>
|
||||
{didFail ? failed : profile ? `${prefix || ''}${profile[attr]}` : loading}
|
||||
</Text>
|
||||
)
|
||||
return inner
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue