feat(replays): Improve render of archived Replay List items
diff --git a/static/app/views/replays/replayTable/tableCell.tsx b/static/app/views/replays/replayTable/tableCell.tsx
index c10176860dfdb..468f02fdf9d38 100644
--- a/static/app/views/replays/replayTable/tableCell.tsx
+++ b/static/app/views/replays/replayTable/tableCell.tsx
@@ -11,7 +11,7 @@ import {StringWalker} from 'sentry/components/replays/walker/urlWalker';
 import ScoreBar from 'sentry/components/scoreBar';
 import TimeSince from 'sentry/components/timeSince';
 import CHART_PALETTE from 'sentry/constants/chartPalette';
-import {IconCalendar, IconLocation} from 'sentry/icons';
+import {IconCalendar, IconDelete, IconLocation} from 'sentry/icons';
 import {t, tn} from 'sentry/locale';
 import {space, ValidSize} from 'sentry/styles/space';
 import type {Organization} from 'sentry/types';
@@ -28,6 +28,24 @@ type Props = {
   replay: ReplayListRecord | ReplayListRecordWithTx;
 };
 
+function getUserBadgeUser(replay: Props['replay']) {
+  return replay.is_archived
+    ? {
+        username: '',
+        email: '',
+        id: '',
+        ip_address: '',
+        name: '',
+      }
+    : {
+        username: replay.user?.display_name || '',
+        email: replay.user?.email || '',
+        id: replay.user?.id || '',
+        ip_address: replay.user?.ip || '',
+        name: replay.user?.username || '',
+      };
+}
+
 export function ReplayCell({
   eventView,
   organization,
@@ -45,6 +63,23 @@ export function ReplayCell({
     },
   };
 
+  if (replay.is_archived) {
+    return (
+      <Item isArchived={replay.is_archived}>
+        <Row gap={1}>
+          <StyledIconDelete color="gray500" size="md" />
+          <div>
+            <Row gap={0.5}>{t('Deleted Replay')}</Row>
+            <Row gap={0.5}>
+              {project ? <Avatar size={12} project={project} /> : null}
+              {getShortEventId(replay.id)}
+            </Row>
+          </div>
+        </Row>
+      </Item>
+    );
+  }
+
   const subText = replay.urls ? (
     <Cols>
       <StringWalker urls={replay.urls} />
@@ -83,17 +118,15 @@ export function ReplayCell({
       <UserBadgeFullWidth
         avatarSize={24}
         displayName={
-          <MainLink to={replayDetails}>
-            {replay.user.display_name || t('Unknown User')}
-          </MainLink>
+          replay.is_archived ? (
+            replay.user?.display_name || t('Unknown User')
+          ) : (
+            <MainLink to={replayDetails}>
+              {replay.user?.display_name || t('Unknown User')}
+            </MainLink>
+          )
         }
-        user={{
-          username: replay.user.display_name || '',
-          email: replay.user.email || '',
-          id: replay.user.id || '',
-          ip_address: replay.user.ip || '',
-          name: replay.user.username || '',
-        }}
+        user={getUserBadgeUser(replay)}
         // this is the subheading for the avatar, so displayEmail in this case is a misnomer
         displayEmail={subText}
       />
@@ -101,6 +134,10 @@ export function ReplayCell({
   );
 }
 
+const StyledIconDelete = styled(IconDelete)`
+  margin: ${space(0.25)};
+`;
+
 // Need to be full width for StringWalker to take up full width and truncate properly
 const UserBadgeFullWidth = styled(UserBadge)`
   width: 100%;
@@ -130,6 +167,9 @@ export function TransactionCell({
 }: Props & {organization: Organization}) {
   const location = useLocation();
 
+  if (replay.is_archived) {
+    return <Item isArchived />;
+  }
   const hasTxEvent = 'txEvent' in replay;
   const txDuration = hasTxEvent ? replay.txEvent?.['transaction.duration'] : undefined;
   return hasTxEvent ? (
@@ -149,6 +189,9 @@ export function OSCell({replay}: Props) {
   const theme = useTheme();
   const hasRoomForColumns = useMedia(`(min-width: ${theme.breakpoints.large})`);
 
+  if (replay.is_archived) {
+    return <Item isArchived />;
+  }
   return (
     <Item>
       <ContextIcon
@@ -164,6 +207,9 @@ export function BrowserCell({replay}: Props) {
   const theme = useTheme();
   const hasRoomForColumns = useMedia(`(min-width: ${theme.breakpoints.large})`);
 
+  if (replay.is_archived) {
+    return <Item isArchived />;
+  }
   return (
     <Item>
       <ContextIcon
@@ -175,6 +221,9 @@ export function BrowserCell({replay}: Props) {
 }
 
 export function DurationCell({replay}: Props) {
+  if (replay.is_archived) {
+    return <Item isArchived />;
+  }
   return (
     <Item>
       <Time>{formatTime(replay.duration.asMilliseconds())}</Time>
@@ -183,6 +232,9 @@ export function DurationCell({replay}: Props) {
 }
 
 export function ErrorCountCell({replay}: Props) {
+  if (replay.is_archived) {
+    return <Item isArchived />;
+  }
   return (
     <Item data-test-id="replay-table-count-errors">
       <ErrorCount countErrors={replay.count_errors} />
@@ -191,6 +243,9 @@ export function ErrorCountCell({replay}: Props) {
 }
 
 export function ActivityCell({replay}: Props) {
+  if (replay.is_archived) {
+    return <Item isArchived />;
+  }
   const scoreBarPalette = new Array(10).fill([CHART_PALETTE[0][0]]);
   return (
     <Item>
@@ -204,11 +259,12 @@ export function ActivityCell({replay}: Props) {
   );
 }
 
-const Item = styled('div')`
+const Item = styled('div')<{isArchived?: boolean}>`
   display: flex;
   align-items: center;
   gap: ${space(1)};
   padding: ${space(1.5)};
+  ${p => (p.isArchived ? 'opacity: 0.5;' : '')};
 `;
 
 const Time = styled('span')`

Give 'archived' results in the replay list special treatment. 

Some considerations addressed:
- No need to link into the details page, it would only 404
- All the fields are set to`0` or the empty string, so we can show nothing instead
- consider what the slim list view looks like as well -> not updating the slim view, it's going away in a few days since we have backend search improvements.

**Before:**
![Image](https://user-images.githubusercontent.com/187460/231650239-1d15074e-fb91-406b-b95f-7d2bd8a80e32.png)

**Regular list view with an archived item:**
<img width="1178" alt="SCR-20230414-bzdh" src="https://user-images.githubusercontent.com/187460/231834420-ee037eb7-113b-43b7-9d0c-bfd20bf9f75f.png">

The slim list is not updated, that view is going away in a few days.

Fixes https://github.com/getsentry/sentry/issues/47303
