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/modals/ComposePost.tsx9
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx2
-rw-r--r--src/view/com/post/Post.tsx2
-rw-r--r--src/view/com/posts/Feed.tsx (renamed from src/view/com/feed/Feed.tsx)8
-rw-r--r--src/view/com/posts/FeedItem.tsx (renamed from src/view/com/feed/FeedItem.tsx)9
-rw-r--r--src/view/com/profile/ProfileHeader.tsx52
-rw-r--r--src/view/com/util/ErrorMessage.tsx66
-rw-r--r--src/view/com/util/ErrorScreen.tsx111
-rw-r--r--src/view/com/util/Selector.tsx8
9 files changed, 204 insertions, 63 deletions
diff --git a/src/view/com/modals/ComposePost.tsx b/src/view/com/modals/ComposePost.tsx
index 253db3771..22b6b14bb 100644
--- a/src/view/com/modals/ComposePost.tsx
+++ b/src/view/com/modals/ComposePost.tsx
@@ -1,12 +1,5 @@
 import React, {useState} from 'react'
-import {
-  KeyboardAvoidingView,
-  StyleSheet,
-  Text,
-  TextInput,
-  TouchableOpacity,
-  View,
-} from 'react-native'
+import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
 import {BottomSheetTextInput} from '@gorhom/bottom-sheet'
 import LinearGradient from 'react-native-linear-gradient'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index d500514ef..8752ee7f9 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -238,7 +238,7 @@ export const PostThreadItem = observer(function PostThreadItem({
 const styles = StyleSheet.create({
   outer: {
     backgroundColor: colors.white,
-    borderRadius: 10,
+    borderRadius: 6,
     margin: 2,
     marginBottom: 0,
   },
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index a6580fa5a..3dd5c0047 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -154,7 +154,7 @@ export const Post = observer(function Post({uri}: {uri: string}) {
 const styles = StyleSheet.create({
   outer: {
     marginTop: 1,
-    borderRadius: 4,
+    borderRadius: 6,
     backgroundColor: colors.white,
     padding: 10,
   },
diff --git a/src/view/com/feed/Feed.tsx b/src/view/com/posts/Feed.tsx
index 4a2ecb612..370a72e69 100644
--- a/src/view/com/feed/Feed.tsx
+++ b/src/view/com/posts/Feed.tsx
@@ -3,21 +3,17 @@ import {observer} from 'mobx-react-lite'
 import {Text, View, FlatList} from 'react-native'
 import {FeedViewModel, FeedViewItemModel} from '../../../state/models/feed-view'
 import {FeedItem} from './FeedItem'
-import {SharePostModel} from '../../../state/models/shell'
 import {useStores} from '../../../state'
 
 export const Feed = observer(function Feed({feed}: {feed: FeedViewModel}) {
   const store = useStores()
 
-  const onPressShare = (uri: string) => {
-    store.shell.openModal(new SharePostModel(uri))
-  }
   // TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf
   //   VirtualizedList: You have a large list that is slow to update - make sure your
   //   renderItem function renders components that follow React performance best practices
   //   like PureComponent, shouldComponentUpdate, etc
   const renderItem = ({item}: {item: FeedViewItemModel}) => (
-    <FeedItem item={item} onPressShare={onPressShare} />
+    <FeedItem item={item} />
   )
   const onRefresh = () => {
     feed.refresh().catch(err => console.error('Failed to refresh', err))
@@ -33,7 +29,7 @@ export const Feed = observer(function Feed({feed}: {feed: FeedViewModel}) {
       {feed.hasError && <Text>{feed.error}</Text>}
       {feed.hasContent && (
         <FlatList
-          data={feed.feed}
+          data={feed.feed.slice()}
           keyExtractor={item => item._reactKey}
           renderItem={renderItem}
           refreshing={feed.isRefreshing}
diff --git a/src/view/com/feed/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index e9cf83346..2376686df 100644
--- a/src/view/com/feed/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -4,7 +4,7 @@ import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
 import {bsky, AdxUri} from '@adxp/mock-api'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {FeedViewItemModel} from '../../../state/models/feed-view'
-import {ComposePostModel} from '../../../state/models/shell'
+import {ComposePostModel, SharePostModel} from '../../../state/models/shell'
 import {Link} from '../util/Link'
 import {PostDropdownBtn} from '../util/DropdownBtn'
 import {s, colors} from '../../lib/styles'
@@ -14,10 +14,8 @@ import {useStores} from '../../../state'
 
 export const FeedItem = observer(function FeedItem({
   item,
-  onPressShare,
 }: {
   item: FeedViewItemModel
-  onPressShare: (_uri: string) => void
 }) {
   const store = useStores()
   const record = item.record as unknown as bsky.Post.Record
@@ -41,6 +39,9 @@ export const FeedItem = observer(function FeedItem({
       .toggleLike()
       .catch(e => console.error('Failed to toggle like', record, e))
   }
+  const onPressShare = (uri: string) => {
+    store.shell.openModal(new SharePostModel(uri))
+  }
 
   return (
     <Link style={styles.outer} href={itemHref} title={itemTitle}>
@@ -151,7 +152,7 @@ export const FeedItem = observer(function FeedItem({
 
 const styles = StyleSheet.create({
   outer: {
-    borderRadius: 10,
+    borderRadius: 6,
     margin: 2,
     marginBottom: 0,
     backgroundColor: colors.white,
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index 08d895554..59af6b200 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -17,31 +17,13 @@ import {s, gradients, colors} from '../../lib/styles'
 import {AVIS, BANNER} from '../../lib/assets'
 import Toast from '../util/Toast'
 import {Link} from '../util/Link'
-import {Selector, SelectorItem} from '../util/Selector'
 
 export const ProfileHeader = observer(function ProfileHeader({
-  user,
+  view,
 }: {
-  user: string
+  view: ProfileViewModel
 }) {
   const store = useStores()
-  const [view, setView] = useState<ProfileViewModel | undefined>()
-
-  useEffect(() => {
-    if (view?.params.user === user) {
-      console.log('Profile header doing nothing')
-      return // no change needed? or trigger refresh?
-    }
-    console.log('Fetching profile', user)
-    const newView = new ProfileViewModel(store, {user: user})
-    setView(newView)
-    newView.setup().catch(err => console.error('Failed to fetch profile', err))
-  }, [user, view?.params.user, store])
-
-  const selectorItems: SelectorItem[] = [
-    {label: 'Posts', onSelect() {}},
-    {label: 'Badges', onSelect() {}},
-  ]
 
   const onPressToggleFollow = () => {
     view?.toggleFollowing().then(
@@ -66,19 +48,15 @@ export const ProfileHeader = observer(function ProfileHeader({
     // TODO
   }
   const onPressFollowers = () => {
-    store.nav.navigate(`/profile/${user}/followers`)
+    store.nav.navigate(`/profile/${view.name}/followers`)
   }
   const onPressFollows = () => {
-    store.nav.navigate(`/profile/${user}/follows`)
+    store.nav.navigate(`/profile/${view.name}/follows`)
   }
 
   // loading
   // =
-  if (
-    !view ||
-    (view.isLoading && !view.isRefreshing) ||
-    view.params.user !== user
-  ) {
+  if (!view || (view.isLoading && !view.isRefreshing)) {
     return (
       <View>
         <ActivityIndicator />
@@ -120,13 +98,13 @@ export const ProfileHeader = observer(function ProfileHeader({
             <TouchableOpacity
               onPress={onPressEditProfile}
               style={[styles.mainBtn, styles.btn]}>
-              <Text style={[s.fw600, s.f16]}>Edit Profile</Text>
+              <Text style={[s.fw400, s.f14]}>Edit Profile</Text>
             </TouchableOpacity>
           ) : view.myState.hasFollowed ? (
             <TouchableOpacity
               onPress={onPressToggleFollow}
               style={[styles.mainBtn, styles.btn]}>
-              <Text style={[s.fw600, s.f16]}>Following</Text>
+              <Text style={[s.fw400, s.f14]}>Following</Text>
             </TouchableOpacity>
           ) : (
             <TouchableOpacity onPress={onPressToggleFollow}>
@@ -146,7 +124,7 @@ export const ProfileHeader = observer(function ProfileHeader({
             <FontAwesomeIcon icon="ellipsis" style={[s.gray5]} />
           </TouchableOpacity>
         </View>
-        <View style={[s.flexRow, s.mb10]}>
+        <View style={[s.flexRow]}>
           <TouchableOpacity
             style={[s.flexRow, s.mr10]}
             onPress={onPressFollowers}>
@@ -167,10 +145,9 @@ export const ProfileHeader = observer(function ProfileHeader({
           </View>
         </View>
         {view.description && (
-          <Text style={[s.mb10, s.f15, s['lh15-1.3']]}>{view.description}</Text>
+          <Text style={[s.mt10, s.f15, s['lh15-1.3']]}>{view.description}</Text>
         )}
       </View>
-      <Selector items={selectorItems} />
     </View>
   )
 })
@@ -178,8 +155,6 @@ export const ProfileHeader = observer(function ProfileHeader({
 const styles = StyleSheet.create({
   outer: {
     backgroundColor: colors.white,
-    borderBottomWidth: 1,
-    borderColor: colors.gray2,
   },
   banner: {
     width: '100%',
@@ -222,14 +197,17 @@ const styles = StyleSheet.create({
     flexDirection: 'row',
     alignItems: 'center',
     justifyContent: 'center',
-    paddingVertical: 8,
-    paddingHorizontal: 60,
+    paddingVertical: 6,
+    paddingLeft: 55,
+    paddingRight: 60,
     borderRadius: 30,
+    borderWidth: 1,
+    borderColor: 'transparent',
   },
   btn: {
     alignItems: 'center',
     justifyContent: 'center',
-    paddingVertical: 8,
+    paddingVertical: 7,
     borderRadius: 30,
     borderWidth: 1,
     borderColor: colors.gray2,
diff --git a/src/view/com/util/ErrorMessage.tsx b/src/view/com/util/ErrorMessage.tsx
new file mode 100644
index 000000000..7c8670da3
--- /dev/null
+++ b/src/view/com/util/ErrorMessage.tsx
@@ -0,0 +1,66 @@
+import React from 'react'
+import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {colors} from '../../lib/styles'
+
+export function ErrorMessage({
+  message,
+  onPressTryAgain,
+}: {
+  message: string
+  onPressTryAgain?: () => void
+}) {
+  return (
+    <View style={styles.outer}>
+      <View style={styles.errorIcon}>
+        <FontAwesomeIcon
+          icon="exclamation"
+          style={{color: colors.white}}
+          size={16}
+        />
+      </View>
+      <Text style={styles.message}>{message}</Text>
+      {onPressTryAgain && (
+        <TouchableOpacity style={styles.btn} onPress={onPressTryAgain}>
+          <FontAwesomeIcon
+            icon="arrows-rotate"
+            style={{color: colors.red4}}
+            size={16}
+          />
+        </TouchableOpacity>
+      )}
+    </View>
+  )
+}
+
+const styles = StyleSheet.create({
+  outer: {
+    flex: 1,
+    flexDirection: 'row',
+    alignItems: 'center',
+    backgroundColor: colors.red1,
+    borderWidth: 1,
+    borderColor: colors.red3,
+    borderRadius: 6,
+    paddingVertical: 8,
+    paddingHorizontal: 8,
+  },
+  errorIcon: {
+    backgroundColor: colors.red4,
+    borderRadius: 12,
+    width: 24,
+    height: 24,
+    alignItems: 'center',
+    justifyContent: 'center',
+    marginRight: 8,
+  },
+  message: {
+    flex: 1,
+    color: colors.red4,
+    paddingRight: 10,
+  },
+  btn: {
+    paddingHorizontal: 4,
+    paddingVertical: 4,
+  },
+})
diff --git a/src/view/com/util/ErrorScreen.tsx b/src/view/com/util/ErrorScreen.tsx
new file mode 100644
index 000000000..4a3e41dc9
--- /dev/null
+++ b/src/view/com/util/ErrorScreen.tsx
@@ -0,0 +1,111 @@
+import React from 'react'
+import {StyleSheet, Text, TouchableOpacity, View} from 'react-native'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {colors} from '../../lib/styles'
+
+export function ErrorScreen({
+  title,
+  message,
+  details,
+  onPressTryAgain,
+}: {
+  title: string
+  message: string
+  details?: string
+  onPressTryAgain?: () => void
+}) {
+  return (
+    <View style={styles.outer}>
+      <View style={styles.errorIconContainer}>
+        <View style={styles.errorIcon}>
+          <FontAwesomeIcon
+            icon="exclamation"
+            style={{color: colors.white}}
+            size={24}
+          />
+        </View>
+      </View>
+      <Text style={styles.title}>{title}</Text>
+      <Text style={styles.message}>{message}</Text>
+      {details && <Text style={styles.details}>{details}</Text>}
+      {onPressTryAgain && (
+        <View style={styles.btnContainer}>
+          <TouchableOpacity style={styles.btn} onPress={onPressTryAgain}>
+            <FontAwesomeIcon
+              icon="arrows-rotate"
+              style={{color: colors.white}}
+              size={16}
+            />
+            <Text style={styles.btnText}>Try again</Text>
+          </TouchableOpacity>
+        </View>
+      )}
+    </View>
+  )
+}
+
+const styles = StyleSheet.create({
+  outer: {
+    flex: 1,
+    backgroundColor: colors.red1,
+    borderWidth: 1,
+    borderColor: colors.red3,
+    borderRadius: 6,
+    paddingVertical: 30,
+    paddingHorizontal: 14,
+    margin: 10,
+  },
+  title: {
+    textAlign: 'center',
+    color: colors.red4,
+    fontSize: 24,
+    marginBottom: 10,
+  },
+  message: {
+    textAlign: 'center',
+    color: colors.red4,
+    marginBottom: 20,
+  },
+  details: {
+    textAlign: 'center',
+    color: colors.black,
+    backgroundColor: colors.white,
+    borderWidth: 1,
+    borderColor: colors.gray5,
+    borderRadius: 6,
+    paddingVertical: 10,
+    paddingHorizontal: 14,
+    overflow: 'hidden',
+    marginBottom: 20,
+  },
+  btnContainer: {
+    alignItems: 'center',
+  },
+  btn: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    backgroundColor: colors.red4,
+    borderRadius: 6,
+    paddingHorizontal: 16,
+    paddingVertical: 10,
+  },
+  btnText: {
+    marginLeft: 5,
+    color: colors.white,
+    fontSize: 16,
+    fontWeight: 'bold',
+  },
+  errorIconContainer: {
+    alignItems: 'center',
+    marginBottom: 10,
+  },
+  errorIcon: {
+    backgroundColor: colors.red4,
+    borderRadius: 30,
+    width: 50,
+    height: 50,
+    alignItems: 'center',
+    justifyContent: 'center',
+    marginRight: 5,
+  },
+})
diff --git a/src/view/com/util/Selector.tsx b/src/view/com/util/Selector.tsx
index ef7c65d59..adc393d89 100644
--- a/src/view/com/util/Selector.tsx
+++ b/src/view/com/util/Selector.tsx
@@ -9,17 +9,13 @@ import {
 } from 'react-native'
 import {colors} from '../../lib/styles'
 
-export interface SelectorItem {
-  label: string
-}
-
 export function Selector({
   style,
   items,
   onSelect,
 }: {
   style?: StyleProp<ViewStyle>
-  items: SelectorItem[]
+  items: string[]
   onSelect?: (index: number) => void
 }) {
   const [selectedIndex, setSelectedIndex] = useState<number>(0)
@@ -36,7 +32,7 @@ export function Selector({
           <TouchableWithoutFeedback key={i} onPress={() => onPressItem(i)}>
             <View style={selected ? styles.itemSelected : styles.item}>
               <Text style={selected ? styles.labelSelected : styles.label}>
-                {item.label}
+                {item}
               </Text>
             </View>
           </TouchableWithoutFeedback>