Skip to content

Commit d249fa5

Browse files
committed
fix: allow offline usage of app picker
Don't show the list of apps only if we failed to fetch it, and not whenever we're offline. This also fixes an annoying bug where the app picker would show "offline" if you opened it during "updating" network status, which notably also happens when you just focused the window (due to a `rpc.maybeNetwork()` call). And this has been causing E2E test failures recently. `getHttpResponse` is able to cache data since not so long ago, so let's utilize that. Note that the fact that we have cached the list of apps doesn't mean that all the apps themselved are cached, so users might still encounter an error. However, if the list of apps and the app file is cached, this allows you, even when offline, to send an app to the chat and open it. This change also makes sense even if `getHttpResponse` didn't do any caching. We do not rely on this.
1 parent 77181e8 commit d249fa5

File tree

2 files changed

+35
-16
lines changed

2 files changed

+35
-16
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- update `@deltachat/stdio-rpc-server` and `deltachat/jsonrpc-client` to `2.12.0`
1010

1111
### Fixed
12+
- fix app picker sometimes incorrectly showing "Offline"
13+
- allow using the app picker even when offline, as long as the app store data is cached
1214
- if adding an app from the app picker fails, show an error dialog
1315
- fix "Recent 3 apps" in the chat header showing apps from another chat sometimes #5265
1416
- accessibility: improve screen-reader accessibility of the general structure of the app by using landmarks #5067

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

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import {
2121
} from '../Dialog'
2222
import SearchInputButton from '../SearchInput/SearchInputButton'
2323
import { ClickableLink } from '../helpers/ClickableLink'
24-
import { useFetch } from '../../hooks/useFetch'
24+
import { useFetch, useRpcFetch } from '../../hooks/useFetch'
25+
import { unknownErrorToString } from '../helpers/unknownErrorToString'
2526

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

@@ -72,7 +73,6 @@ type Props = {
7273
export function AppPicker({ onAppSelected }: Props) {
7374
const tx = useTranslationFunction()
7475
const [searchQuery, setSearchQuery] = useState('')
75-
const [isOffline, setIsOffline] = useState(false)
7676
const [selectedCategory, setSelectedCategory] = useState(AppCategoryEnum.home)
7777
const [selectedAppInfo, setSelectedAppInfo] = useState<AppInfo | null>(null)
7878
const [icons, setIcons] = useState<{ [key: string]: string }>({})
@@ -107,19 +107,26 @@ export function AppPicker({ onAppSelected }: Props) {
107107
return apps
108108
}, [])
109109
const appsFetch = useFetch(fetchApps, [])
110-
if (appsFetch.result?.ok === false) {
111-
log.error('Failed to fetch apps:', appsFetch.result.err)
112-
}
113110
const apps = appsFetch.result?.ok ? appsFetch.result.value : null
114111

112+
const appsFetchFailed = appsFetch.result?.ok === false
113+
const connectivityFetch = useRpcFetch(BackendRemote.rpc.getConnectivity, [
114+
selectedAccountId(),
115+
])
116+
const connectivityFetchRefresh = connectivityFetch?.refresh
117+
useEffect(() => {
118+
if (!appsFetchFailed) {
119+
return
120+
}
121+
connectivityFetchRefresh()
122+
}, [appsFetchFailed, connectivityFetchRefresh])
123+
const isOffline =
124+
connectivityFetch.result?.ok === true &&
125+
connectivityFetch.result.value !== C.DC_CONNECTIVITY_CONNECTED
126+
115127
useEffect(() => {
116128
const loadIcons = async () => {
117129
if (apps == null) {
118-
const connectivity =
119-
await BackendRemote.rpc.getConnectivity(selectedAccountId())
120-
if (connectivity !== C.DC_CONNECTIVITY_CONNECTED) {
121-
setIsOffline(true)
122-
}
123130
return
124131
}
125132
const newIcons: { [key: string]: string } = {}
@@ -307,7 +314,19 @@ export function AppPicker({ onAppSelected }: Props) {
307314
/>
308315
)}
309316
<div className={styles.appPickerList}>
310-
{!isOffline && filteredApps != null ? (
317+
{appsFetch.loading ? (
318+
<div className={styles.offlineMessage}>{tx('loading')}</div>
319+
) : appsFetch.result.ok !== true ? (
320+
<div className={styles.offlineMessage}>
321+
{isOffline
322+
? tx('offline')
323+
: tx(
324+
'error_x',
325+
'Failed to fetch apps:\n' +
326+
unknownErrorToString(appsFetch.result.err)
327+
)}
328+
</div>
329+
) : (
311330
<>
312331
{selectedAppInfo && (
313332
<AppInfoOverlay
@@ -316,7 +335,9 @@ export function AppPicker({ onAppSelected }: Props) {
316335
onSelect={onAppSelected}
317336
/>
318337
)}
319-
{filteredApps.map(app => (
338+
{/* `appsFetch.result.ok === true` implies that
339+
this is not null. */}
340+
{filteredApps!.map(app => (
320341
<button
321342
key={app.app_id}
322343
className={styles.appListItem}
@@ -326,10 +347,6 @@ export function AppPicker({ onAppSelected }: Props) {
326347
</button>
327348
))}
328349
</>
329-
) : (
330-
<div className={styles.offlineMessage}>
331-
{tx(isOffline ? 'offline' : 'loading')}
332-
</div>
333350
)}
334351
</div>
335352
<div className={styles.tabBar}>

0 commit comments

Comments
 (0)