about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2024-08-10 00:49:11 +0100
committerGitHub <noreply@github.com>2024-08-10 00:49:11 +0100
commitc2131bb0392487f11b0c31fe68fdd3e847d62142 (patch)
tree65e926fb03c4921338343e6205a5270c3e674d13
parentab0da7c892ce7532840417b89a42302ab9db7018 (diff)
downloadvoidsky-c2131bb0392487f11b0c31fe68fdd3e847d62142.tar.zst
[Videos] Add error boundary to native (#4914)
* move error fallback to own component

* use error boundary on native

---------

Co-authored-by: Samuel Newman <10959775+mozzius@users.noreply.github.com>
-rw-r--r--src/view/com/util/post-embeds/VideoEmbed.tsx71
-rw-r--r--src/view/com/util/post-embeds/VideoEmbed.web.tsx48
-rw-r--r--src/view/com/util/post-embeds/VideoEmbedInner/VideoFallback.tsx61
3 files changed, 116 insertions, 64 deletions
diff --git a/src/view/com/util/post-embeds/VideoEmbed.tsx b/src/view/com/util/post-embeds/VideoEmbed.tsx
index 887efac1a..4e2909f40 100644
--- a/src/view/com/util/post-embeds/VideoEmbed.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbed.tsx
@@ -1,6 +1,6 @@
-import React from 'react'
+import React, {useCallback, useState} from 'react'
 import {View} from 'react-native'
-import {msg} from '@lingui/macro'
+import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
 import {VideoEmbedInnerNative} from 'view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative'
@@ -8,13 +8,23 @@ import {atoms as a, useTheme} from '#/alf'
 import {Button, ButtonIcon} from '#/components/Button'
 import {Play_Filled_Corner2_Rounded as PlayIcon} from '#/components/icons/Play'
 import {VisibilityView} from '../../../../../modules/expo-bluesky-swiss-army'
+import {ErrorBoundary} from '../ErrorBoundary'
 import {useActiveVideoView} from './ActiveVideoContext'
+import * as VideoFallback from './VideoEmbedInner/VideoFallback'
 
 export function VideoEmbed({source}: {source: string}) {
   const t = useTheme()
   const {active, setActive} = useActiveVideoView({source})
   const {_} = useLingui()
 
+  const [key, setKey] = useState(0)
+  const renderError = useCallback(
+    (error: unknown) => (
+      <VideoError error={error} retry={() => setKey(key + 1)} />
+    ),
+    [key],
+  )
+
   return (
     <View
       style={[
@@ -25,27 +35,42 @@ export function VideoEmbed({source}: {source: string}) {
         t.atoms.bg_contrast_25,
         a.my_xs,
       ]}>
-      <VisibilityView
-        enabled={true}
-        onChangeStatus={isActive => {
-          if (isActive) {
-            setActive()
-          }
-        }}>
-        {active ? (
-          <VideoEmbedInnerNative />
-        ) : (
-          <Button
-            style={[a.flex_1, t.atoms.bg_contrast_25]}
-            onPress={setActive}
-            label={_(msg`Play video`)}
-            variant="ghost"
-            color="secondary"
-            size="large">
-            <ButtonIcon icon={PlayIcon} />
-          </Button>
-        )}
-      </VisibilityView>
+      <ErrorBoundary renderError={renderError} key={key}>
+        <VisibilityView
+          enabled={true}
+          onChangeStatus={isActive => {
+            if (isActive) {
+              setActive()
+            }
+          }}>
+          {active ? (
+            <VideoEmbedInnerNative />
+          ) : (
+            <Button
+              style={[a.flex_1, t.atoms.bg_contrast_25]}
+              onPress={setActive}
+              label={_(msg`Play video`)}
+              variant="ghost"
+              color="secondary"
+              size="large">
+              <ButtonIcon icon={PlayIcon} />
+            </Button>
+          )}
+        </VisibilityView>
+      </ErrorBoundary>
     </View>
   )
 }
+
+function VideoError({retry}: {error: unknown; retry: () => void}) {
+  return (
+    <VideoFallback.Container>
+      <VideoFallback.Text>
+        <Trans>
+          An error occurred while loading the video. Please try again later.
+        </Trans>
+      </VideoFallback.Text>
+      <VideoFallback.RetryButton onPress={retry} />
+    </VideoFallback.Container>
+  )
+}
diff --git a/src/view/com/util/post-embeds/VideoEmbed.web.tsx b/src/view/com/util/post-embeds/VideoEmbed.web.tsx
index 70d887283..5803b836d 100644
--- a/src/view/com/util/post-embeds/VideoEmbed.web.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbed.web.tsx
@@ -1,17 +1,15 @@
 import React, {useCallback, useEffect, useRef, useState} from 'react'
 import {View} from 'react-native'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
+import {Trans} from '@lingui/macro'
 
 import {
   HLSUnsupportedError,
   VideoEmbedInnerWeb,
 } from 'view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb'
 import {atoms as a, useTheme} from '#/alf'
-import {Button, ButtonText} from '#/components/Button'
-import {Text} from '#/components/Typography'
 import {ErrorBoundary} from '../ErrorBoundary'
 import {useActiveVideoView} from './ActiveVideoContext'
+import * as VideoFallback from './VideoEmbedInner/VideoFallback'
 
 export function VideoEmbed({source}: {source: string}) {
   const t = useTheme()
@@ -138,32 +136,11 @@ function ViewportObserver({
 }
 
 function VideoError({error, retry}: {error: unknown; retry: () => void}) {
-  const t = useTheme()
-  const {_} = useLingui()
-
   const isHLS = error instanceof HLSUnsupportedError
 
   return (
-    <View
-      style={[
-        a.flex_1,
-        t.atoms.bg_contrast_25,
-        a.justify_center,
-        a.align_center,
-        a.px_lg,
-        a.border,
-        t.atoms.border_contrast_low,
-        a.rounded_sm,
-        a.gap_lg,
-      ]}>
-      <Text
-        style={[
-          a.text_center,
-          t.atoms.text_contrast_high,
-          a.text_md,
-          a.leading_snug,
-          {maxWidth: 300},
-        ]}>
+    <VideoFallback.Container>
+      <VideoFallback.Text>
         {isHLS ? (
           <Trans>
             Your browser does not support the video format. Please try a
@@ -174,19 +151,8 @@ function VideoError({error, retry}: {error: unknown; retry: () => void}) {
             An error occurred while loading the video. Please try again later.
           </Trans>
         )}
-      </Text>
-      {!isHLS && (
-        <Button
-          onPress={retry}
-          size="small"
-          color="secondary_inverted"
-          variant="solid"
-          label={_(msg`Retry`)}>
-          <ButtonText>
-            <Trans>Retry</Trans>
-          </ButtonText>
-        </Button>
-      )}
-    </View>
+      </VideoFallback.Text>
+      {!isHLS && <VideoFallback.RetryButton onPress={retry} />}
+    </VideoFallback.Container>
   )
 }
diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoFallback.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/VideoFallback.tsx
new file mode 100644
index 000000000..1b46163cc
--- /dev/null
+++ b/src/view/com/util/post-embeds/VideoEmbedInner/VideoFallback.tsx
@@ -0,0 +1,61 @@
+import React from 'react'
+import {View} from 'react-native'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {atoms as a, useTheme} from '#/alf'
+import {Button, ButtonText} from '#/components/Button'
+import {Text as TypoText} from '#/components/Typography'
+
+export function Container({children}: {children: React.ReactNode}) {
+  const t = useTheme()
+  return (
+    <View
+      style={[
+        a.flex_1,
+        t.atoms.bg_contrast_25,
+        a.justify_center,
+        a.align_center,
+        a.px_lg,
+        a.border,
+        t.atoms.border_contrast_low,
+        a.rounded_sm,
+        a.gap_lg,
+      ]}>
+      {children}
+    </View>
+  )
+}
+
+export function Text({children}: {children: React.ReactNode}) {
+  const t = useTheme()
+  return (
+    <TypoText
+      style={[
+        a.text_center,
+        t.atoms.text_contrast_high,
+        a.text_md,
+        a.leading_snug,
+        {maxWidth: 300},
+      ]}>
+      {children}
+    </TypoText>
+  )
+}
+
+export function RetryButton({onPress}: {onPress: () => void}) {
+  const {_} = useLingui()
+
+  return (
+    <Button
+      onPress={onPress}
+      size="small"
+      color="secondary_inverted"
+      variant="solid"
+      label={_(msg`Retry`)}>
+      <ButtonText>
+        <Trans>Retry</Trans>
+      </ButtonText>
+    </Button>
+  )
+}