about summary refs log tree commit diff
path: root/src/view/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com')
-rw-r--r--src/view/com/post/PostText.tsx10
-rw-r--r--src/view/com/util/LoadingPlaceholder.tsx73
-rw-r--r--src/view/com/util/UserInfoText.tsx30
3 files changed, 98 insertions, 15 deletions
diff --git a/src/view/com/post/PostText.tsx b/src/view/com/post/PostText.tsx
index 541f2fc16..5d6c45113 100644
--- a/src/view/com/post/PostText.tsx
+++ b/src/view/com/post/PostText.tsx
@@ -1,6 +1,8 @@
 import React, {useState, useEffect} from 'react'
 import {observer} from 'mobx-react-lite'
-import {ActivityIndicator, Text, View} from 'react-native'
+import {Text, View} from 'react-native'
+import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
+import {ErrorMessage} from '../util/ErrorMessage'
 import {PostModel} from '../../../state/models/post'
 import {useStores} from '../../../state'
 
@@ -28,7 +30,9 @@ export const PostText = observer(function PostText({
   if (!model || model.isLoading || model.uri !== uri) {
     return (
       <View>
-        <ActivityIndicator />
+        <LoadingPlaceholder width="100%" height={8} style={{marginTop: 6}} />
+        <LoadingPlaceholder width="100%" height={8} style={{marginTop: 6}} />
+        <LoadingPlaceholder width={100} height={8} style={{marginTop: 6}} />
       </View>
     )
   }
@@ -38,7 +42,7 @@ export const PostText = observer(function PostText({
   if (model.hasError) {
     return (
       <View>
-        <Text style={style}>{model.error}</Text>
+        <ErrorMessage style={style} message={model.error} />
       </View>
     )
   }
diff --git a/src/view/com/util/LoadingPlaceholder.tsx b/src/view/com/util/LoadingPlaceholder.tsx
new file mode 100644
index 000000000..55b6ad1b3
--- /dev/null
+++ b/src/view/com/util/LoadingPlaceholder.tsx
@@ -0,0 +1,73 @@
+import React, {useEffect, useMemo} from 'react'
+import {
+  Animated,
+  StyleProp,
+  useWindowDimensions,
+  View,
+  ViewStyle,
+} from 'react-native'
+import LinearGradient from 'react-native-linear-gradient'
+import {colors} from '../../lib/styles'
+
+export function LoadingPlaceholder({
+  width,
+  height,
+  style,
+}: {
+  width: string | number
+  height: string | number
+  style?: StyleProp<ViewStyle>
+}) {
+  const dim = useWindowDimensions()
+  const elWidth = typeof width === 'string' ? dim.width : width
+  const offset = useMemo(() => new Animated.Value(elWidth * -1), [])
+  useEffect(() => {
+    const anim = Animated.loop(
+      Animated.sequence([
+        Animated.timing(offset, {
+          toValue: elWidth,
+          duration: 1e3,
+          useNativeDriver: true,
+          isInteraction: false,
+        }),
+        Animated.timing(offset, {
+          toValue: elWidth * -1,
+          duration: 0,
+          delay: 500,
+          useNativeDriver: true,
+          isInteraction: false,
+        }),
+      ]),
+    )
+    anim.start()
+    return () => anim.stop()
+  }, [])
+
+  return (
+    <View
+      style={[
+        {
+          width,
+          height,
+          backgroundColor: colors.gray2,
+          borderRadius: 6,
+          overflow: 'hidden',
+        },
+        style,
+      ]}>
+      <Animated.View
+        style={{
+          width,
+          height,
+          transform: [{translateX: offset}],
+        }}>
+        <LinearGradient
+          colors={[colors.gray2, '#d4d2d2', colors.gray2]}
+          start={{x: 0, y: 0}}
+          end={{x: 1, y: 0}}
+          style={{width: '100%', height: '100%'}}
+        />
+      </Animated.View>
+    </View>
+  )
+}
diff --git a/src/view/com/util/UserInfoText.tsx b/src/view/com/util/UserInfoText.tsx
index f4dbd1fa4..d1292cc70 100644
--- a/src/view/com/util/UserInfoText.tsx
+++ b/src/view/com/util/UserInfoText.tsx
@@ -2,6 +2,7 @@ import React, {useState, useEffect} from 'react'
 import * as GetProfile from '../../../third-party/api/src/client/types/app/bsky/actor/getProfile'
 import {StyleProp, Text, TextStyle} from 'react-native'
 import {Link} from './Link'
+import {LoadingPlaceholder} from './LoadingPlaceholder'
 import {useStores} from '../../../state'
 
 export function UserInfoText({
@@ -48,26 +49,31 @@ export function UserInfoText({
     }
   }, [did, store.api.app.bsky])
 
+  let inner
+  if (didFail) {
+    inner = <Text style={style}>{failed}</Text>
+  } else if (profile) {
+    inner = <Text style={style}>{`${prefix || ''}${profile[attr]}`}</Text>
+  } else {
+    inner = (
+      <LoadingPlaceholder
+        width={80}
+        height={8}
+        style={{position: 'relative', top: 1, left: 2}}
+      />
+    )
+  }
+
   if (asLink) {
     const title = profile?.displayName || profile?.handle || 'User'
     return (
       <Link
         href={`/profile/${profile?.handle ? profile.handle : did}`}
         title={title}>
-        <Text style={style}>
-          {didFail
-            ? failed
-            : profile
-            ? `${prefix || ''}${profile[attr]}`
-            : loading}
-        </Text>
+        {inner}
       </Link>
     )
   }
 
-  return (
-    <Text style={style}>
-      {didFail ? failed : profile ? `${prefix || ''}${profile[attr]}` : loading}
-    </Text>
-  )
+  return inner
 }