Improve error messages
parent
6e93301542
commit
fb3a43c216
|
@ -3,6 +3,7 @@ import * as GetTimeline from '../../third-party/api/src/client/types/app/bsky/fe
|
|||
import * as GetAuthorFeed from '../../third-party/api/src/client/types/app/bsky/feed/getAuthorFeed'
|
||||
import {RootStoreModel} from './root-store'
|
||||
import * as apilib from '../lib/api'
|
||||
import {cleanError} from '../../view/lib/strings'
|
||||
|
||||
export class FeedItemMyStateModel {
|
||||
repost?: string
|
||||
|
@ -254,7 +255,7 @@ export class FeedModel {
|
|||
this.isLoading = false
|
||||
this.isRefreshing = false
|
||||
this.hasLoaded = true
|
||||
this.error = err
|
||||
this.error = cleanError(err)
|
||||
}
|
||||
|
||||
// loader functions
|
||||
|
@ -282,7 +283,7 @@ export class FeedModel {
|
|||
this._replaceAll(res)
|
||||
this._xIdle()
|
||||
} catch (e: any) {
|
||||
this._xIdle(`Failed to load feed: ${e.toString()}`)
|
||||
this._xIdle(e.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,7 +294,7 @@ export class FeedModel {
|
|||
this._prependAll(res)
|
||||
this._xIdle()
|
||||
} catch (e: any) {
|
||||
this._xIdle(`Failed to load feed: ${e.toString()}`)
|
||||
this._xIdle(e.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import {RootStoreModel} from './root-store'
|
|||
import {Declaration} from './_common'
|
||||
import {hasProp} from '../lib/type-guards'
|
||||
import {APP_BSKY_GRAPH} from '../../third-party/api'
|
||||
import {cleanError} from '../../view/lib/strings'
|
||||
|
||||
const UNGROUPABLE_REASONS = ['trend', 'assertion']
|
||||
|
||||
|
@ -215,7 +216,7 @@ export class NotificationsViewModel {
|
|||
this.isLoading = false
|
||||
this.isRefreshing = false
|
||||
this.hasLoaded = true
|
||||
this.error = err
|
||||
this.error = cleanError(err)
|
||||
}
|
||||
|
||||
// loader functions
|
||||
|
|
|
@ -6,11 +6,14 @@ import {
|
|||
NotificationsViewItemModel,
|
||||
} from '../../../state/models/notifications-view'
|
||||
import {FeedItem} from './FeedItem'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
|
||||
export const Feed = observer(function Feed({
|
||||
view,
|
||||
onPressTryAgain,
|
||||
}: {
|
||||
view: NotificationsViewModel
|
||||
onPressTryAgain?: () => void
|
||||
}) {
|
||||
// TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf
|
||||
// VirtualizedList: You have a large list that is slow to update - make sure your
|
||||
|
@ -30,7 +33,14 @@ export const Feed = observer(function Feed({
|
|||
{view.isLoading && !view.isRefreshing && !view.hasContent && (
|
||||
<Text>Loading...</Text>
|
||||
)}
|
||||
{view.hasError && <Text>{view.error}</Text>}
|
||||
{view.hasError && (
|
||||
<ErrorMessage
|
||||
dark
|
||||
message={view.error}
|
||||
style={{margin: 6}}
|
||||
onPressTryAgain={onPressTryAgain}
|
||||
/>
|
||||
)}
|
||||
{view.hasContent && (
|
||||
<FlatList
|
||||
data={view.notifications}
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
import React, {useState, useEffect} from 'react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
FlatList,
|
||||
Image,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {ActivityIndicator, FlatList, StyleSheet, Text, View} from 'react-native'
|
||||
import {
|
||||
RepostedByViewModel,
|
||||
RepostedByViewItemModel,
|
||||
} from '../../../state/models/reposted-by-view'
|
||||
import {UserAvatar} from '../util/UserAvatar'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
import {Link} from '../util/Link'
|
||||
import {useStores} from '../../../state'
|
||||
import {s, colors} from '../../lib/styles'
|
||||
|
@ -38,6 +32,10 @@ export const PostRepostedBy = observer(function PostRepostedBy({
|
|||
.catch(err => console.error('Failed to fetch reposted by', err))
|
||||
}, [uri, view?.params.uri, store])
|
||||
|
||||
const onRefresh = () => {
|
||||
view?.refresh()
|
||||
}
|
||||
|
||||
// loading
|
||||
// =
|
||||
if (
|
||||
|
@ -57,7 +55,12 @@ export const PostRepostedBy = observer(function PostRepostedBy({
|
|||
if (view.hasError) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{view.error}</Text>
|
||||
<ErrorMessage
|
||||
dark
|
||||
message={view.error}
|
||||
style={{margin: 6}}
|
||||
onPressTryAgain={onRefresh}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
import {useStores} from '../../../state'
|
||||
import {SharePostModel} from '../../../state/models/shell-ui'
|
||||
import {PostThreadItem} from './PostThreadItem'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
|
||||
export const PostThread = observer(function PostThread({uri}: {uri: string}) {
|
||||
const store = useStores()
|
||||
|
@ -50,7 +51,12 @@ export const PostThread = observer(function PostThread({uri}: {uri: string}) {
|
|||
if (view.hasError) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{view.error}</Text>
|
||||
<ErrorMessage
|
||||
dark
|
||||
message={view.error}
|
||||
style={{margin: 6}}
|
||||
onPressTryAgain={onRefresh}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
VotesViewItemModel,
|
||||
} from '../../../state/models/votes-view'
|
||||
import {Link} from '../util/Link'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
import {UserAvatar} from '../util/UserAvatar'
|
||||
import {useStores} from '../../../state'
|
||||
import {s, colors} from '../../lib/styles'
|
||||
|
@ -31,6 +32,10 @@ export const PostVotedBy = observer(function PostVotedBy({
|
|||
newView.setup().catch(err => console.error('Failed to fetch voted by', err))
|
||||
}, [uri, view?.params.uri, store])
|
||||
|
||||
const onRefresh = () => {
|
||||
view?.refresh()
|
||||
}
|
||||
|
||||
// loading
|
||||
// =
|
||||
if (
|
||||
|
@ -50,7 +55,12 @@ export const PostVotedBy = observer(function PostVotedBy({
|
|||
if (view.hasError) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{view.error}</Text>
|
||||
<ErrorMessage
|
||||
dark
|
||||
message={view.error}
|
||||
style={{margin: 6}}
|
||||
onPressTryAgain={onRefresh}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, {MutableRefObject} from 'react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {Text, View, FlatList, StyleProp, ViewStyle} from 'react-native'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
import {FeedModel, FeedItemModel} from '../../../state/models/feed-view'
|
||||
import {FeedItem} from './FeedItem'
|
||||
|
||||
|
@ -8,10 +9,12 @@ export const Feed = observer(function Feed({
|
|||
feed,
|
||||
style,
|
||||
scrollElRef,
|
||||
onPressTryAgain,
|
||||
}: {
|
||||
feed: FeedModel
|
||||
style?: StyleProp<ViewStyle>
|
||||
scrollElRef?: MutableRefObject<FlatList<any> | null>
|
||||
onPressTryAgain?: () => void
|
||||
}) {
|
||||
// TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf
|
||||
// VirtualizedList: You have a large list that is slow to update - make sure your
|
||||
|
@ -29,7 +32,14 @@ export const Feed = observer(function Feed({
|
|||
{feed.isLoading && !feed.isRefreshing && !feed.hasContent && (
|
||||
<Text>Loading...</Text>
|
||||
)}
|
||||
{feed.hasError && <Text>{feed.error}</Text>}
|
||||
{feed.hasError && (
|
||||
<ErrorMessage
|
||||
dark
|
||||
message={feed.error}
|
||||
style={{margin: 6}}
|
||||
onPressTryAgain={onPressTryAgain}
|
||||
/>
|
||||
)}
|
||||
{feed.hasContent && (
|
||||
<FlatList
|
||||
ref={scrollElRef}
|
||||
|
@ -41,7 +51,11 @@ export const Feed = observer(function Feed({
|
|||
onEndReached={onEndReached}
|
||||
/>
|
||||
)}
|
||||
{feed.isEmpty && <Text>This feed is empty!</Text>}
|
||||
{feed.isEmpty && !feed.hasError && (
|
||||
<View>
|
||||
<Text>This feed is empty!</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
FollowerItem,
|
||||
} from '../../../state/models/user-followers-view'
|
||||
import {Link} from '../util/Link'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
import {UserAvatar} from '../util/UserAvatar'
|
||||
import {useStores} from '../../../state'
|
||||
import {s, colors} from '../../lib/styles'
|
||||
|
@ -31,6 +32,10 @@ export const ProfileFollowers = observer(function ProfileFollowers({
|
|||
.catch(err => console.error('Failed to fetch user followers', err))
|
||||
}, [name, view?.params.user, store])
|
||||
|
||||
const onRefresh = () => {
|
||||
view?.refresh()
|
||||
}
|
||||
|
||||
// loading
|
||||
// =
|
||||
if (
|
||||
|
@ -50,7 +55,12 @@ export const ProfileFollowers = observer(function ProfileFollowers({
|
|||
if (view.hasError) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{view.error}</Text>
|
||||
<ErrorMessage
|
||||
dark
|
||||
message={view.error}
|
||||
style={{margin: 6}}
|
||||
onPressTryAgain={onRefresh}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
} from '../../../state/models/user-follows-view'
|
||||
import {useStores} from '../../../state'
|
||||
import {Link} from '../util/Link'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
import {UserAvatar} from '../util/UserAvatar'
|
||||
import {s, colors} from '../../lib/styles'
|
||||
|
||||
|
@ -31,6 +32,10 @@ export const ProfileFollows = observer(function ProfileFollows({
|
|||
.catch(err => console.error('Failed to fetch user follows', err))
|
||||
}, [name, view?.params.user, store])
|
||||
|
||||
const onRefresh = () => {
|
||||
view?.refresh()
|
||||
}
|
||||
|
||||
// loading
|
||||
// =
|
||||
if (
|
||||
|
@ -50,7 +55,12 @@ export const ProfileFollows = observer(function ProfileFollows({
|
|||
if (view.hasError) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{view.error}</Text>
|
||||
<ErrorMessage
|
||||
dark
|
||||
message={view.error}
|
||||
style={{margin: 6}}
|
||||
onPressTryAgain={onRefresh}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import React, {useState, useEffect} from 'react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {ActivityIndicator, FlatList, Text, View} from 'react-native'
|
||||
import {ActivityIndicator, FlatList, View} from 'react-native'
|
||||
import {MembersViewModel, MemberItem} from '../../../state/models/members-view'
|
||||
import {ProfileCard} from './ProfileCard'
|
||||
import {ErrorMessage} from '../util/ErrorMessage'
|
||||
import {useStores} from '../../../state'
|
||||
|
||||
export const ProfileMembers = observer(function ProfileMembers({
|
||||
|
@ -24,6 +25,10 @@ export const ProfileMembers = observer(function ProfileMembers({
|
|||
newView.setup().catch(err => console.error('Failed to fetch members', err))
|
||||
}, [name, view?.params.actor, store])
|
||||
|
||||
const onRefresh = () => {
|
||||
view?.refresh()
|
||||
}
|
||||
|
||||
// loading
|
||||
// =
|
||||
if (
|
||||
|
@ -43,7 +48,12 @@ export const ProfileMembers = observer(function ProfileMembers({
|
|||
if (view.hasError) {
|
||||
return (
|
||||
<View>
|
||||
<Text>{view.error}</Text>
|
||||
<ErrorMessage
|
||||
dark
|
||||
message={view.error}
|
||||
style={{margin: 6}}
|
||||
onPressTryAgain={onRefresh}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,40 +1,66 @@
|
|||
import React from 'react'
|
||||
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
StyleProp,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {colors} from '../../lib/styles'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {colors, gradients} from '../../lib/styles'
|
||||
|
||||
export function ErrorMessage({
|
||||
message,
|
||||
numberOfLines,
|
||||
dark,
|
||||
style,
|
||||
onPressTryAgain,
|
||||
}: {
|
||||
message: string
|
||||
numberOfLines?: number
|
||||
dark?: boolean
|
||||
style?: StyleProp<ViewStyle>
|
||||
onPressTryAgain?: () => void
|
||||
}) {
|
||||
return (
|
||||
<View style={styles.outer}>
|
||||
<View style={styles.errorIcon}>
|
||||
const inner = (
|
||||
<>
|
||||
<View style={[styles.errorIcon, dark ? styles.darkErrorIcon : undefined]}>
|
||||
<FontAwesomeIcon
|
||||
icon="exclamation"
|
||||
style={{color: colors.white}}
|
||||
style={{color: dark ? colors.red3 : colors.white}}
|
||||
size={16}
|
||||
/>
|
||||
</View>
|
||||
<Text style={styles.message} numberOfLines={numberOfLines}>
|
||||
<Text
|
||||
style={[styles.message, dark ? styles.darkMessage : undefined]}
|
||||
numberOfLines={numberOfLines}>
|
||||
{message}
|
||||
</Text>
|
||||
{onPressTryAgain && (
|
||||
<TouchableOpacity style={styles.btn} onPress={onPressTryAgain}>
|
||||
<FontAwesomeIcon
|
||||
icon="arrows-rotate"
|
||||
style={{color: colors.red4}}
|
||||
style={{color: dark ? colors.white : colors.red4}}
|
||||
size={16}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
if (dark) {
|
||||
return (
|
||||
<LinearGradient
|
||||
colors={[gradients.error.start, gradients.error.end]}
|
||||
start={{x: 0.5, y: 0}}
|
||||
end={{x: 1, y: 1}}
|
||||
style={[styles.outer, style]}>
|
||||
{inner}
|
||||
</LinearGradient>
|
||||
)
|
||||
}
|
||||
return <View style={[styles.outer, style]}>{inner}</View>
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -57,11 +83,18 @@ const styles = StyleSheet.create({
|
|||
justifyContent: 'center',
|
||||
marginRight: 8,
|
||||
},
|
||||
darkErrorIcon: {
|
||||
backgroundColor: colors.white,
|
||||
},
|
||||
message: {
|
||||
flex: 1,
|
||||
color: colors.red4,
|
||||
paddingRight: 10,
|
||||
},
|
||||
darkMessage: {
|
||||
color: colors.white,
|
||||
fontWeight: '600',
|
||||
},
|
||||
btn: {
|
||||
paddingHorizontal: 4,
|
||||
paddingVertical: 4,
|
||||
|
|
|
@ -96,3 +96,10 @@ export function enforceLen(str: string, len: number): string {
|
|||
}
|
||||
return str
|
||||
}
|
||||
|
||||
export function cleanError(str: string): string {
|
||||
if (str.startsWith('Error: ')) {
|
||||
return str.slice('Error: '.length)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ export const colors = {
|
|||
|
||||
export const gradients = {
|
||||
primary: {start: '#db00ff', end: '#ff007a'},
|
||||
error: {start: '#ff007a', end: '#ed0d78'},
|
||||
purple: {start: colors.pink3, end: colors.purple3},
|
||||
blue: {start: colors.purple3, end: colors.blue3},
|
||||
green: {start: colors.blue3, end: colors.green3},
|
||||
|
|
|
@ -51,6 +51,9 @@ export const Home = observer(function Home({
|
|||
const onCreatePost = () => {
|
||||
defaultFeedView.loadLatest()
|
||||
}
|
||||
const onPressTryAgain = () => {
|
||||
defaultFeedView.refresh()
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={s.flex1}>
|
||||
|
@ -63,6 +66,7 @@ export const Home = observer(function Home({
|
|||
feed={defaultFeedView}
|
||||
scrollElRef={scrollElRef}
|
||||
style={{flex: 1}}
|
||||
onPressTryAgain={onPressTryAgain}
|
||||
/>
|
||||
<FAB icon="pen-nib" onPress={onComposePress} />
|
||||
</View>
|
||||
|
|
|
@ -36,10 +36,14 @@ export const Notifications = ({visible}: ScreenParams) => {
|
|||
}
|
||||
}, [visible, store])
|
||||
|
||||
const onPressTryAgain = () => {
|
||||
notesView?.refresh()
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<ViewHeader title="Notifications" />
|
||||
{notesView && <Feed view={notesView} />}
|
||||
{notesView && <Feed view={notesView} onPressTryAgain={onPressTryAgain} />}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue