mirror of
https://github.com/zhigang1992/devhub.git
synced 2026-06-17 02:51:16 +08:00
Show error message when column fetch fails
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
import React from 'react'
|
||||
import { ActivityIndicator, Text, View } from 'react-native'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Text,
|
||||
TextStyle,
|
||||
View,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
|
||||
import { LoadState } from '@devhub/core/src/types'
|
||||
import { contentPadding } from '../../styles/variables'
|
||||
@@ -33,14 +39,64 @@ const clearMessage = getRandomClearMessage()
|
||||
const emoji = getRandomEmoji()
|
||||
|
||||
export interface EmptyCardsProps {
|
||||
errorMessage?: string
|
||||
fetchNextPage: ((params?: { perPage?: number }) => void) | undefined
|
||||
loadState: LoadState
|
||||
refresh: (() => void | Promise<void>) | undefined
|
||||
}
|
||||
|
||||
export function EmptyCards(props: EmptyCardsProps) {
|
||||
const theme = useTheme()
|
||||
|
||||
const { fetchNextPage, loadState } = props
|
||||
const { errorMessage, fetchNextPage, loadState, refresh } = props
|
||||
|
||||
const hasError = errorMessage || loadState === 'error'
|
||||
|
||||
const renderContent = () => {
|
||||
if (loadState === 'loading_first') {
|
||||
return <ActivityIndicator color={theme.foregroundColor} />
|
||||
}
|
||||
|
||||
const containerStyle: ViewStyle = { width: '100%', padding: contentPadding }
|
||||
const textStyle: TextStyle = {
|
||||
lineHeight: 20,
|
||||
fontSize: 14,
|
||||
color: theme.foregroundColorMuted50,
|
||||
textAlign: 'center',
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return (
|
||||
<View style={containerStyle}>
|
||||
<Text style={textStyle}>
|
||||
{`⚠️\nSomething went wrong`}
|
||||
{!!errorMessage && (
|
||||
<Text style={{ fontSize: 13 }}>{`\nError: ${errorMessage}`}</Text>
|
||||
)}
|
||||
</Text>
|
||||
|
||||
{!!refresh && (
|
||||
<View style={{ padding: contentPadding }}>
|
||||
<Button
|
||||
children="Try again"
|
||||
disabled={loadState !== 'error'}
|
||||
loading={loadState === 'loading'}
|
||||
onPress={() => refresh()}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={containerStyle}>
|
||||
<Text style={textStyle}>
|
||||
{clearMessage} {emoji}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<TransparentTextOverlay
|
||||
@@ -58,17 +114,11 @@ export function EmptyCards(props: EmptyCardsProps) {
|
||||
padding: contentPadding,
|
||||
}}
|
||||
>
|
||||
{loadState === 'loading_first' ? (
|
||||
<ActivityIndicator color={theme.foregroundColor} />
|
||||
) : (
|
||||
<Text style={{ color: theme.foregroundColorMuted50 }}>
|
||||
{clearMessage} {emoji}
|
||||
</Text>
|
||||
)}
|
||||
{renderContent()}
|
||||
</View>
|
||||
|
||||
<View style={{ minHeight: 40 + 2 * contentPadding }}>
|
||||
{!!fetchNextPage && loadState !== 'loading_first' && (
|
||||
{!!fetchNextPage && !hasError && loadState !== 'loading_first' && (
|
||||
<View style={{ padding: contentPadding }}>
|
||||
<Button
|
||||
children="Load more"
|
||||
|
||||
@@ -7,15 +7,17 @@ import { contentPadding } from '../../styles/variables'
|
||||
import { Button } from '../common/Button'
|
||||
import { TransparentTextOverlay } from '../common/TransparentTextOverlay'
|
||||
import { useTheme } from '../context/ThemeContext'
|
||||
import { EmptyCards } from './EmptyCards'
|
||||
import { EmptyCards, EmptyCardsProps } from './EmptyCards'
|
||||
import { EventCard } from './EventCard'
|
||||
import { CardItemSeparator } from './partials/CardItemSeparator'
|
||||
import { SwipeableEventCard } from './SwipeableEventCard'
|
||||
|
||||
export interface EventCardsProps {
|
||||
errorMessage: EmptyCardsProps['errorMessage']
|
||||
events: EnhancedGitHubEvent[]
|
||||
fetchNextPage: ((params?: { perPage?: number }) => void) | undefined
|
||||
loadState: LoadState
|
||||
refresh: EmptyCardsProps['refresh']
|
||||
repoIsKnown?: boolean
|
||||
swipeable?: boolean
|
||||
}
|
||||
@@ -23,10 +25,17 @@ export interface EventCardsProps {
|
||||
export const EventCards = React.memo((props: EventCardsProps) => {
|
||||
const theme = useTheme()
|
||||
|
||||
const { events, fetchNextPage, loadState } = props
|
||||
const { errorMessage, events, fetchNextPage, loadState, refresh } = props
|
||||
|
||||
if (!(events && events.length))
|
||||
return <EmptyCards fetchNextPage={fetchNextPage} loadState={loadState} />
|
||||
return (
|
||||
<EmptyCards
|
||||
errorMessage={errorMessage}
|
||||
fetchNextPage={fetchNextPage}
|
||||
loadState={loadState}
|
||||
refresh={refresh}
|
||||
/>
|
||||
)
|
||||
|
||||
function keyExtractor(event: EnhancedGitHubEvent) {
|
||||
return `event-card-${event.id}`
|
||||
@@ -54,10 +63,10 @@ export const EventCards = React.memo((props: EventCardsProps) => {
|
||||
<CardItemSeparator />
|
||||
<View style={{ padding: contentPadding }}>
|
||||
<Button
|
||||
children={loadState === 'error' ? 'Oops. Try again' : 'Load more'}
|
||||
disabled={loadState !== 'loaded'}
|
||||
loading={loadState === 'loading_more'}
|
||||
onPress={() => fetchNextPage()}
|
||||
children="Load more"
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
|
||||
@@ -7,26 +7,41 @@ import { contentPadding } from '../../styles/variables'
|
||||
import { Button } from '../common/Button'
|
||||
import { TransparentTextOverlay } from '../common/TransparentTextOverlay'
|
||||
import { useTheme } from '../context/ThemeContext'
|
||||
import { EmptyCards } from './EmptyCards'
|
||||
import { EmptyCards, EmptyCardsProps } from './EmptyCards'
|
||||
import { NotificationCard } from './NotificationCard'
|
||||
import { CardItemSeparator } from './partials/CardItemSeparator'
|
||||
import { SwipeableNotificationCard } from './SwipeableNotificationCard'
|
||||
|
||||
export interface NotificationCardsProps {
|
||||
errorMessage: EmptyCardsProps['errorMessage']
|
||||
fetchNextPage: ((params?: { perPage?: number }) => void) | undefined
|
||||
notifications: EnhancedGitHubNotification[]
|
||||
repoIsKnown?: boolean
|
||||
loadState: LoadState
|
||||
notifications: EnhancedGitHubNotification[]
|
||||
refresh: EmptyCardsProps['refresh']
|
||||
repoIsKnown?: boolean
|
||||
swipeable?: boolean
|
||||
}
|
||||
|
||||
export function NotificationCards(props: NotificationCardsProps) {
|
||||
const theme = useTheme()
|
||||
|
||||
const { fetchNextPage, loadState, notifications } = props
|
||||
const {
|
||||
errorMessage,
|
||||
fetchNextPage,
|
||||
loadState,
|
||||
notifications,
|
||||
refresh,
|
||||
} = props
|
||||
|
||||
if (!(notifications && notifications.length))
|
||||
return <EmptyCards fetchNextPage={fetchNextPage} loadState={loadState} />
|
||||
return (
|
||||
<EmptyCards
|
||||
errorMessage={errorMessage}
|
||||
fetchNextPage={fetchNextPage}
|
||||
loadState={loadState}
|
||||
refresh={refresh}
|
||||
/>
|
||||
)
|
||||
|
||||
function keyExtractor(notification: EnhancedGitHubNotification) {
|
||||
return `notification-card-${notification.id}`
|
||||
@@ -64,7 +79,7 @@ export function NotificationCards(props: NotificationCardsProps) {
|
||||
<CardItemSeparator />
|
||||
<View style={{ padding: contentPadding }}>
|
||||
<Button
|
||||
children="Load more"
|
||||
children={loadState === 'error' ? 'Oops. Try again' : 'Load more'}
|
||||
disabled={loadState !== 'loaded'}
|
||||
loading={loadState === 'loading_more'}
|
||||
onPress={() => fetchNextPage()}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { getFilteredEvents } from '../utils/helpers/filters'
|
||||
|
||||
export type EventCardsContainerProps = Omit<
|
||||
EventCardsProps,
|
||||
'events' | 'fetchNextPage' | 'loadState'
|
||||
'errorMessage' | 'events' | 'fetchNextPage' | 'loadState' | 'refresh'
|
||||
> & {
|
||||
column: Column
|
||||
subscriptions: ColumnSubscription[]
|
||||
@@ -38,6 +38,11 @@ export const EventCardsContainer = React.memo(
|
||||
const [loadState, setLoadState] = useState<LoadState>('loading_first')
|
||||
const [pagination, setPagination] = useState({ page: 1, perPage: 10 })
|
||||
|
||||
const [error, setError] = useState<{
|
||||
[key: string]: any
|
||||
message: string
|
||||
} | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [])
|
||||
@@ -135,9 +140,11 @@ export const EventCardsContainer = React.memo(
|
||||
setPagination(prevPagination => ({ ...prevPagination, page }))
|
||||
setCanFetchMore(false)
|
||||
}
|
||||
setError(null)
|
||||
} catch (error) {
|
||||
console.error('Failed to load GitHub activity', error)
|
||||
setLoadState('loaded')
|
||||
setLoadState('error')
|
||||
setError(error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,9 +157,11 @@ export const EventCardsContainer = React.memo(
|
||||
<EventCards
|
||||
{...props}
|
||||
key={`event-cards-${column.id}`}
|
||||
errorMessage={(error && error.message) || ''}
|
||||
events={filteredEvents}
|
||||
fetchNextPage={canFetchMore ? fetchNextPage : undefined}
|
||||
loadState={loadState}
|
||||
refresh={() => fetchData()}
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ import { getFilteredNotifications } from '../utils/helpers/filters'
|
||||
|
||||
export type NotificationCardsContainerProps = Omit<
|
||||
NotificationCardsProps,
|
||||
'notifications' | 'fetchNextPage' | 'loadState'
|
||||
'errorMessage' | 'notifications' | 'fetchNextPage' | 'loadState' | 'refresh'
|
||||
> & {
|
||||
column: Column
|
||||
subscriptions: ColumnSubscription[]
|
||||
@@ -49,6 +49,11 @@ export const NotificationCardsContainer = React.memo(
|
||||
const [pagination, setPagination] = useState({ page: 1, perPage: 10 })
|
||||
const githubToken = useReduxState(selectors.githubTokenSelector)!
|
||||
|
||||
const [error, setError] = useState<{
|
||||
[key: string]: any
|
||||
message: string
|
||||
} | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
fetchData()
|
||||
}, [])
|
||||
@@ -169,9 +174,11 @@ export const NotificationCardsContainer = React.memo(
|
||||
setPagination(prevPagination => ({ ...prevPagination, page }))
|
||||
setCanFetchMore(false)
|
||||
}
|
||||
setError(null)
|
||||
} catch (error) {
|
||||
console.error('Failed to load GitHub notifications', error)
|
||||
setLoadState('loaded')
|
||||
setLoadState('error')
|
||||
setError(error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,9 +191,11 @@ export const NotificationCardsContainer = React.memo(
|
||||
<NotificationCards
|
||||
{...props}
|
||||
key={`notification-cards-${column.id}`}
|
||||
notifications={filteredNotifications}
|
||||
errorMessage={(error && error.message) || ''}
|
||||
fetchNextPage={canFetchMore ? fetchNextPage : undefined}
|
||||
loadState={loadState}
|
||||
notifications={filteredNotifications}
|
||||
refresh={() => fetchData()}
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -178,6 +178,7 @@ export type ModalPayload =
|
||||
}
|
||||
|
||||
export type LoadState =
|
||||
| 'error'
|
||||
| 'loaded'
|
||||
| 'loading'
|
||||
| 'loading_first'
|
||||
|
||||
Reference in New Issue
Block a user