Browse Source

feat(replays): Render the Replays Details page header as soon as data is available (#39530)

I pulled some bad images from the devtools performance inspector. I
think it's clear enough to see that the header does get some content
before the body comes in.

| Render Step  | Img |
| ------------- | ------------- |
| Placeholders | <img width="511" alt="Screen Shot 2022-09-30 at 3 37 07
PM"
src="https://user-images.githubusercontent.com/187460/193365618-01eb2f52-4523-40ae-9858-7c6749249f26.png">
|
| Header Loaded | <img width="505" alt="Screen Shot 2022-09-30 at 3 37
20 PM"
src="https://user-images.githubusercontent.com/187460/193365630-9fe2c720-b6bb-4e2e-93eb-8f3b517d4dba.png">
|
| Body Loaded | <img width="509" alt="Screen Shot 2022-09-30 at 3 37 31
PM"
src="https://user-images.githubusercontent.com/187460/193365649-b33458db-7f4b-4380-9bd4-a36496136d86.png">
|

Fixes #38521
Ryan Albrecht 2 years ago
parent
commit
2b4b88f10e

+ 6 - 0
static/app/utils/replays/hooks/useReplayData.tsx

@@ -70,6 +70,7 @@ type ReplayAttachment = {
 interface Result extends Pick<State, 'fetchError' | 'fetching'> {
   onRetry: () => void;
   replay: ReplayReader | null;
+  replayRecord: ReplayRecord | undefined;
 }
 
 export function mapRRWebAttachments(unsortedReplayAttachments): ReplayAttachment {
@@ -188,6 +189,10 @@ function useReplayData({replaySlug, orgSlug}: Options): Result {
   const fetchReplayAndErrors = useCallback(async (): Promise<[ReplayRecord, any]> => {
     const fetchedRecord = await fetchReplay();
     const mappedRecord = mapResponseToReplayRecord(fetchedRecord);
+    setState(prev => ({
+      ...prev,
+      replayRecord: mappedRecord,
+    }));
     const fetchedErrors = await fetchErrors(mappedRecord);
     return [mappedRecord, fetchedErrors];
   }, [fetchReplay, fetchErrors]);
@@ -247,6 +252,7 @@ function useReplayData({replaySlug, orgSlug}: Options): Result {
     fetching: state.fetching,
     onRetry: loadEvents,
     replay,
+    replayRecord: state.replayRecord,
   };
 }
 

+ 15 - 11
static/app/views/replays/details.tsx

@@ -16,6 +16,7 @@ import useReplayLayout from 'sentry/utils/replays/hooks/useReplayLayout';
 import useReplayPageview from 'sentry/utils/replays/hooks/useReplayPageview';
 import Layout from 'sentry/views/replays/detail/layout';
 import Page from 'sentry/views/replays/detail/page';
+import type {ReplayRecord} from 'sentry/views/replays/types';
 
 type Props = RouteComponentProps<
   {orgId: string; replaySlug: string},
@@ -33,7 +34,8 @@ function ReplayDetails({
   params: {orgId: orgSlug, replaySlug},
 }: Props) {
   useReplayPageview();
-  const {fetching, onRetry, replay, fetchError} = useReplayData({
+
+  const {fetching, onRetry, replay, replayRecord, fetchError} = useReplayData({
     replaySlug,
     orgSlug,
   });
@@ -41,7 +43,7 @@ function ReplayDetails({
   if (!fetching && !replay && fetchError) {
     if (fetchError.statusText === 'Not Found') {
       return (
-        <Page orgSlug={orgSlug}>
+        <Page orgSlug={orgSlug} replayRecord={replayRecord}>
           <PageContent>
             <NotFound />
           </PageContent>
@@ -54,7 +56,7 @@ function ReplayDetails({
       t('There is an internal systems error or active issue'),
     ];
     return (
-      <Page orgSlug={orgSlug}>
+      <Page orgSlug={orgSlug} replayRecord={replayRecord}>
         <PageContent>
           <DetailedError
             onRetry={onRetry}
@@ -78,7 +80,7 @@ function ReplayDetails({
 
   if (!fetching && replay && replay.getRRWebEvents().length < 2) {
     return (
-      <Page orgSlug={orgSlug} replayRecord={replay.getReplay()}>
+      <Page orgSlug={orgSlug} replayRecord={replayRecord}>
         <DetailedError
           hideSupportLinks
           heading={t('Expected two or more replay events')}
@@ -99,21 +101,23 @@ function ReplayDetails({
 
   return (
     <ReplayContextProvider replay={replay} initialTimeOffset={initialTimeOffset}>
-      <LoadedDetails orgSlug={orgSlug} />
+      <LoadedDetails orgSlug={orgSlug} replayRecord={replayRecord} />
     </ReplayContextProvider>
   );
 }
 
-function LoadedDetails({orgSlug}: {orgSlug: string}) {
+function LoadedDetails({
+  orgSlug,
+  replayRecord,
+}: {
+  orgSlug: string;
+  replayRecord: ReplayRecord | undefined;
+}) {
   const {getLayout} = useReplayLayout();
   const {replay} = useReplayContext();
 
   return (
-    <Page
-      orgSlug={orgSlug}
-      crumbs={replay?.getRawCrumbs()}
-      replayRecord={replay?.getReplay()}
-    >
+    <Page orgSlug={orgSlug} crumbs={replay?.getRawCrumbs()} replayRecord={replayRecord}>
       <Layout layout={getLayout()} />
     </Page>
   );