Repository: microsoft/playwright
File: packages/trace-viewer/src/ui/workbenchLoader.tsx (TypeScript)

Code before:

      const swListener = (event: any) => {
        if (event.data.method === 'progress')
          setProgress(event.data.params);
      };
      try {
        navigator.serviceWorker.addEventListener('message', swListener);
        setProgress({ done: 0, total: 1 });

        async function tryFetch(traceURL: string): Promise<string | undefined> {
          const params = new URLSearchParams();
          params.set('trace', traceURL);
          const response = await fetch(`contexts?${params.toString()}`);
          if (!response.ok) {
            const { error } = await response.json();
            setProcessingErrorMessage(error);
            return error;
          }
          const contextEntries = await response.json();
          const model = new MultiTraceModel(traceURL, contextEntries);
          setProgress({ done: 0, total: 0 });
          setProcessingErrorMessage(null);
          setModel(model);
        }

        let error = await tryFetch(traceURL);
        if (error?.includes('please grant permission for Local Network Access')) {
          // fetching the asset opens the permission prompt. but only from window, not from SW (https://issues.chromium.org/issues/460180743)
          await fetch(traceURL, { headers: { 'x-pw-serviceworker': 'skip' } });
          error = await tryFetch(traceURL);
        }
        if (error) {
          if (!isServer)
            setTraceURL(undefined);
        }
      } finally {
        navigator.serviceWorker.removeEventListener('message', swListener);
      }
    })();
  }, [isServer, traceURL, uploadedTraceName]);

  const showLoading = progress.done !== progress.total && progress.total !== 0 && !processingErrorMessage;

  React.useEffect(() => {
    if (showLoading) {
      const timeout = setTimeout(() => {
        setShowProgressDialog(true);
      }, 200);

      return () => clearTimeout(timeout);
    } else {

Review comment (suggestion):
We control the type, let's return a dedicated status. `error: { message: string, lanError?: boolean }`.

Code after:
      setProcessingErrorMessage(error);
      return error;
    }
    const contextEntries = await response.json();
    const model = new TraceModel(traceURL, contextEntries);
    setProgress({ done: 0, total: 0 });
    setProcessingErrorMessage(null);
    setModel(model);
  }, []);

  React.useEffect(() => {
    (async () => {
      if (!traceURL) {
        setModel(emptyModel);
        return;
      }

      const swListener = (event: any) => {
        if (event.data.method === 'progress')
          setProgress(event.data.params);
      };
      try {
        navigator.serviceWorker.addEventListener('message', swListener);
        setProgress({ done: 0, total: 1 });
        let error = await fetchTrace(traceURL);
        if (error?.includes('please grant permission for Local Network Access')) {
          // fetching the asset opens the permission prompt. but only from window, not from SW (https://issues.chromium.org/issues/460180743)
          await fetch(traceURL, { method: 'HEAD', headers: { 'x-pw-serviceworker': 'skip' } });
          error = await fetchTrace(traceURL);
        }
        if (error) {
          if (!isServer)
            setTraceURL(undefined);
        }
      } finally {
        navigator.serviceWorker.removeEventListener('message', swListener);
      }
    })();
  }, [isServer, traceURL, uploadedTraceName, fetchTrace]);

  const showLoading = progress.done !== progress.total && progress.total !== 0 && !processingErrorMessage;

  React.useEffect(() => {
    if (showLoading) {
      const timeout = setTimeout(() => {
        setShowProgressDialog(true);
      }, 200);

      return () => clearTimeout(timeout);
    } else {
      setShowProgressDialog(false);