From fb3a43c216a884f7185c53cba6e210d6659448d1 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Tue, 15 Nov 2022 10:46:12 -0600 Subject: [PATCH] Improve error messages --- src/state/models/feed-view.ts | 7 +-- src/state/models/notifications-view.ts | 3 +- src/view/com/notifications/Feed.tsx | 12 ++++- src/view/com/post-thread/PostRepostedBy.tsx | 21 +++++---- src/view/com/post-thread/PostThread.tsx | 8 +++- src/view/com/post-thread/PostVotedBy.tsx | 12 ++++- src/view/com/posts/Feed.tsx | 18 +++++++- src/view/com/profile/ProfileFollowers.tsx | 12 ++++- src/view/com/profile/ProfileFollows.tsx | 12 ++++- src/view/com/profile/ProfileMembers.tsx | 14 +++++- src/view/com/util/ErrorMessage.tsx | 51 +++++++++++++++++---- src/view/lib/strings.ts | 7 +++ src/view/lib/styles.ts | 1 + src/view/screens/Home.tsx | 4 ++ src/view/screens/Notifications.tsx | 6 ++- 15 files changed, 156 insertions(+), 32 deletions(-) diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts index 905f7af7..1c58a44d 100644 --- a/src/state/models/feed-view.ts +++ b/src/state/models/feed-view.ts @@ -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()) } } diff --git a/src/state/models/notifications-view.ts b/src/state/models/notifications-view.ts index 72cc17f6..3a91eb32 100644 --- a/src/state/models/notifications-view.ts +++ b/src/state/models/notifications-view.ts @@ -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 diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx index a3cac0cd..2b7bb878 100644 --- a/src/view/com/notifications/Feed.tsx +++ b/src/view/com/notifications/Feed.tsx @@ -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 && ( Loading... )} - {view.hasError && {view.error}} + {view.hasError && ( + + )} {view.hasContent && ( 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.error} + ) } diff --git a/src/view/com/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index 0349d342..ee87a6bf 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -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.error} + ) } diff --git a/src/view/com/post-thread/PostVotedBy.tsx b/src/view/com/post-thread/PostVotedBy.tsx index 596a6a1d..ad85d077 100644 --- a/src/view/com/post-thread/PostVotedBy.tsx +++ b/src/view/com/post-thread/PostVotedBy.tsx @@ -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.error} + ) } diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index dc341ddd..75b48fad 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -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 scrollElRef?: MutableRefObject | 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 && ( Loading... )} - {feed.hasError && {feed.error}} + {feed.hasError && ( + + )} {feed.hasContent && ( )} - {feed.isEmpty && This feed is empty!} + {feed.isEmpty && !feed.hasError && ( + + This feed is empty! + + )} ) }) diff --git a/src/view/com/profile/ProfileFollowers.tsx b/src/view/com/profile/ProfileFollowers.tsx index 65297ccc..c4e9435d 100644 --- a/src/view/com/profile/ProfileFollowers.tsx +++ b/src/view/com/profile/ProfileFollowers.tsx @@ -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.error} + ) } diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx index 1bdd18f6..9efd58f8 100644 --- a/src/view/com/profile/ProfileFollows.tsx +++ b/src/view/com/profile/ProfileFollows.tsx @@ -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.error} + ) } diff --git a/src/view/com/profile/ProfileMembers.tsx b/src/view/com/profile/ProfileMembers.tsx index 11db0205..75ef18ca 100644 --- a/src/view/com/profile/ProfileMembers.tsx +++ b/src/view/com/profile/ProfileMembers.tsx @@ -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.error} + ) } diff --git a/src/view/com/util/ErrorMessage.tsx b/src/view/com/util/ErrorMessage.tsx index 834cd598..3f6522b8 100644 --- a/src/view/com/util/ErrorMessage.tsx +++ b/src/view/com/util/ErrorMessage.tsx @@ -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 onPressTryAgain?: () => void }) { - return ( - - + const inner = ( + <> + - + {message} {onPressTryAgain && ( )} - + ) + if (dark) { + return ( + + {inner} + + ) + } + return {inner} } 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, diff --git a/src/view/lib/strings.ts b/src/view/lib/strings.ts index 19bd5c47..e47863d5 100644 --- a/src/view/lib/strings.ts +++ b/src/view/lib/strings.ts @@ -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 +} diff --git a/src/view/lib/styles.ts b/src/view/lib/styles.ts index dfcaeef9..4bea6443 100644 --- a/src/view/lib/styles.ts +++ b/src/view/lib/styles.ts @@ -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}, diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index c16eff80..036f7d14 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -51,6 +51,9 @@ export const Home = observer(function Home({ const onCreatePost = () => { defaultFeedView.loadLatest() } + const onPressTryAgain = () => { + defaultFeedView.refresh() + } return ( @@ -63,6 +66,7 @@ export const Home = observer(function Home({ feed={defaultFeedView} scrollElRef={scrollElRef} style={{flex: 1}} + onPressTryAgain={onPressTryAgain} /> diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index c82e7cc7..f1084540 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -36,10 +36,14 @@ export const Notifications = ({visible}: ScreenParams) => { } }, [visible, store]) + const onPressTryAgain = () => { + notesView?.refresh() + } + return ( - {notesView && } + {notesView && } ) }