From b817fd3dc5cbac71dc12df70bf8bb1f4ca4bac9c Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Sat, 18 May 2024 18:25:18 +0200 Subject: [PATCH] Revert "refactor(webui): delete bundle reloading due to authorization issues with Metro (#56)" This reverts commit 67e785af5baf0fe937a15b03f55a909f775ab22a. --- .../src/app/--/bundles/[bundle]/reload+api.ts | 33 ++++++++++++++ webui/src/providers/bundle.tsx | 45 ++++++++++++++----- 2 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 webui/src/app/--/bundles/[bundle]/reload+api.ts diff --git a/webui/src/app/--/bundles/[bundle]/reload+api.ts b/webui/src/app/--/bundles/[bundle]/reload+api.ts new file mode 100644 index 0000000..d88efc6 --- /dev/null +++ b/webui/src/app/--/bundles/[bundle]/reload+api.ts @@ -0,0 +1,33 @@ +import { getSource } from '~/utils/atlas'; + +export async function GET(_request: Request, params: Record<'bundle', string>) { + try { + const bundle = await getSource().getBundle(params.bundle); + if (!bundle) { + return Response.json({ error: 'Bundle not found' }, { status: 404 }); + } + + if (!bundle.serializeOptions?.sourceUrl) { + return Response.json( + { error: 'Bundle has no `serializeOptions.sourceUrl`' }, + { status: 406 } + ); + } + + // Convert the source URL to localhost, and call it from within the API. + // This is necessary to avoid "unauthorized requests" in Metro. + const sourceUrl = new URL(bundle.serializeOptions.sourceUrl); + sourceUrl.hostname = 'localhost'; + const response = await fetch(sourceUrl); + if (!response.ok) { + throw new Error(`Metro responded with an error: ${response.statusText}`); + } + + // We still need to await the response body, to ensure the data is fully passed through Atlas. + await response.text(); + + return Response.json({ success: response.ok }, { status: response.status }) + } catch (error: any) { + return Response.json({ error: error.message }, { status: 406 }); + } +} diff --git a/webui/src/providers/bundle.tsx b/webui/src/providers/bundle.tsx index 9f49439..a49d3c1 100644 --- a/webui/src/providers/bundle.tsx +++ b/webui/src/providers/bundle.tsx @@ -1,11 +1,19 @@ -import { useQuery } from '@tanstack/react-query'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useLocalSearchParams } from 'expo-router'; -import { type PropsWithChildren, createContext, useContext, useMemo, useEffect } from 'react'; +import { + type PropsWithChildren, + createContext, + useContext, + useMemo, + useEffect, + useCallback, +} from 'react'; import { type BundleDeltaResponse } from '~/app/--/bundles/[bundle]/delta+api'; import { StateInfo } from '~/components/StateInfo'; +import { Button } from '~/ui/Button'; import { Spinner } from '~/ui/Spinner'; -import { type ToasterToast, useToast } from '~/ui/Toast'; +import { ToastAction, type ToasterToast, useToast } from '~/ui/Toast'; import { fetchApi, handleApiError } from '~/utils/api'; import { type PartialAtlasBundle } from '~core/data/types'; @@ -86,10 +94,20 @@ export function BundleDeltaToast({ bundle: Pick; modulePath?: string; }) { + const client = useQueryClient(); const toaster = useToast(); + const deltaResponse = useBundleDeltaData(bundle.id); const bundleDelta = deltaResponse.data?.delta; + const refetchBundleData = useCallback( + () => + fetchApi(`/bundles/${bundle.id}/reload`) + .then(handleApiError) + .then(() => client.refetchQueries({ queryKey: ['bundles', bundle.id], type: 'active' })), + [bundle.id] + ); + useEffect(() => { if (!bundleDelta) return; @@ -97,13 +115,13 @@ export function BundleDeltaToast({ if (bundleDelta.deletedPaths.includes(modulePath)) { toaster.toast(toastModuleDeleted(bundle.id)); } else if (bundleDelta.modifiedPaths.includes(modulePath)) { - toaster.toast(toastModuleModified(bundle.id)); + refetchBundleData().then(() => toaster.toast(toastModuleModified(bundle.id))); } return; } - toaster.toast(toastBundleUpdate(bundle.id)); - }, [bundle.id, bundleDelta, modulePath]); + toaster.toast(toastBundleUpdate(bundle.id, refetchBundleData)); + }, [bundle.id, bundleDelta, refetchBundleData, modulePath]); return null; } @@ -112,7 +130,7 @@ function toastModuleModified(bundleId: string): ToasterToast { return { id: `bundle-delta-${bundleId}`, title: 'Module modified', - description: 'This file has changed, open your app before reloading this page.', + description: 'This module is updated to reflect the latest changes.', }; } @@ -120,15 +138,22 @@ function toastModuleDeleted(bundleId: string): ToasterToast { return { id: `bundle-delta-${bundleId}`, title: 'Module deleted', - description: 'This file is deleted since last build, and is no longer available.', + description: 'This file is deleted since latest build, and is no longer available.', }; } -function toastBundleUpdate(bundleId: string): ToasterToast { +function toastBundleUpdate(bundleId: string, refetchBundleData: () => any): ToasterToast { return { id: `bundle-delta-${bundleId}`, title: 'Bundle outdated', - description: 'Code was changed since last build, open your app before reloading this page.', + description: 'The code was changed since last build.', + action: ( + + + + ), }; }