Skip to content

Commit 77181e8

Browse files
committed
refactor: app picker: make apps nullable
And use `useFetch`. This should affect behavior, at least not significantly. But hopefully makes things clearer.
1 parent 44d6c69 commit 77181e8

File tree

1 file changed

+37
-32
lines changed
  • packages/frontend/src/components/AppPicker

1 file changed

+37
-32
lines changed

packages/frontend/src/components/AppPicker/index.tsx

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useMemo, useState } from 'react'
1+
import React, { useCallback, useEffect, useMemo, useState } from 'react'
22
import classNames from 'classnames'
33
import { filesize } from 'filesize'
44
import moment from 'moment'
@@ -21,6 +21,7 @@ import {
2121
} from '../Dialog'
2222
import SearchInputButton from '../SearchInput/SearchInputButton'
2323
import { ClickableLink } from '../helpers/ClickableLink'
24+
import { useFetch } from '../../hooks/useFetch'
2425

2526
const log = getLogger('renderer/components/AppPicker')
2627

@@ -70,7 +71,6 @@ type Props = {
7071

7172
export function AppPicker({ onAppSelected }: Props) {
7273
const tx = useTranslationFunction()
73-
const [apps, setApps] = useState<AppInfo[]>([])
7474
const [searchQuery, setSearchQuery] = useState('')
7575
const [isOffline, setIsOffline] = useState(false)
7676
const [selectedCategory, setSelectedCategory] = useState(AppCategoryEnum.home)
@@ -82,44 +82,45 @@ export function AppPicker({ onAppSelected }: Props) {
8282
AppCategoryEnum.game,
8383
]
8484

85-
useEffect(() => {
86-
const fetchApps = async () => {
87-
try {
88-
const response = await BackendRemote.rpc.getHttpResponse(
89-
selectedAccountId(),
90-
AppStoreUrl + 'xdcget-lock.json'
91-
)
92-
const apps = getJsonFromBase64(response.blob) as AppInfo[]
93-
if (apps === null) return
94-
apps.sort((a: AppInfo, b: AppInfo) => {
95-
const dateA = new Date(a.date)
96-
const dateB = new Date(b.date)
97-
return dateB.getTime() - dateA.getTime() // Show newest first
98-
})
99-
for (const app of apps) {
100-
app.short_description = app.description.split('\n')[0]
101-
app.description = app.description.split('\n').slice(1).join('\n')
102-
const url = new URL(app.source_code_url)
103-
app.author = url.pathname.split('/')[1]
104-
app.date = moment(app.date).format('LL')
105-
}
106-
setApps(apps)
107-
} catch (error) {
108-
log.error('Failed to fetch apps:', error)
109-
}
85+
const fetchApps = useCallback(async () => {
86+
// This may throw, e.g. on network error.
87+
const response = await BackendRemote.rpc.getHttpResponse(
88+
selectedAccountId(),
89+
AppStoreUrl + 'xdcget-lock.json'
90+
)
91+
const apps = getJsonFromBase64(response.blob) as AppInfo[]
92+
if (apps == null) {
93+
throw new Error(`Received \`null\` response from ${AppStoreUrl}`)
94+
}
95+
apps.sort((a: AppInfo, b: AppInfo) => {
96+
const dateA = new Date(a.date)
97+
const dateB = new Date(b.date)
98+
return dateB.getTime() - dateA.getTime() // Show newest first
99+
})
100+
for (const app of apps) {
101+
app.short_description = app.description.split('\n')[0]
102+
app.description = app.description.split('\n').slice(1).join('\n')
103+
const url = new URL(app.source_code_url)
104+
app.author = url.pathname.split('/')[1]
105+
app.date = moment(app.date).format('LL')
110106
}
111-
fetchApps()
112-
}, [setApps])
107+
return apps
108+
}, [])
109+
const appsFetch = useFetch(fetchApps, [])
110+
if (appsFetch.result?.ok === false) {
111+
log.error('Failed to fetch apps:', appsFetch.result.err)
112+
}
113+
const apps = appsFetch.result?.ok ? appsFetch.result.value : null
113114

114115
useEffect(() => {
115116
const loadIcons = async () => {
116-
if (!apps.length) {
117+
if (apps == null) {
117118
const connectivity =
118119
await BackendRemote.rpc.getConnectivity(selectedAccountId())
119120
if (connectivity !== C.DC_CONNECTIVITY_CONNECTED) {
120121
setIsOffline(true)
121-
return
122122
}
123+
return
123124
}
124125
const newIcons: { [key: string]: string } = {}
125126
setIcons(newIcons)
@@ -144,6 +145,10 @@ export function AppPicker({ onAppSelected }: Props) {
144145
}, [apps, isOffline])
145146

146147
const filteredApps = useMemo(() => {
148+
if (apps == null) {
149+
return null
150+
}
151+
147152
const lowerCaseQuery = searchQuery.toLowerCase()
148153
const findByRelevance = (apps: AppInfo[]) => {
149154
const queryEqualsAuthor = apps.filter(
@@ -302,7 +307,7 @@ export function AppPicker({ onAppSelected }: Props) {
302307
/>
303308
)}
304309
<div className={styles.appPickerList}>
305-
{!isOffline && Object.keys(apps).length > 0 ? (
310+
{!isOffline && filteredApps != null ? (
306311
<>
307312
{selectedAppInfo && (
308313
<AppInfoOverlay

0 commit comments

Comments
 (0)