about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--package.json1
-rw-r--r--src/state/lib/api.ts46
-rw-r--r--src/view/com/composer/Composer.tsx88
-rw-r--r--src/view/com/feed/FeedItem.tsx9
-rw-r--r--src/view/com/post-thread/PostThreadItem.tsx9
-rw-r--r--src/view/index.ts4
-rw-r--r--src/view/lib/styles.ts21
-rw-r--r--src/view/routes/index.tsx27
-rw-r--r--src/view/routes/types.ts1
-rw-r--r--src/view/screens/Home.tsx27
-rw-r--r--src/view/screens/stacks/Composer.tsx50
-rw-r--r--src/view/screens/stacks/PostLikedBy.tsx (renamed from src/view/screens/content/PostLikedBy.tsx)0
-rw-r--r--src/view/screens/stacks/PostRepostedBy.tsx (renamed from src/view/screens/content/PostRepostedBy.tsx)0
-rw-r--r--src/view/screens/stacks/PostThread.tsx (renamed from src/view/screens/content/PostThread.tsx)0
-rw-r--r--src/view/screens/stacks/Profile.tsx (renamed from src/view/screens/content/Profile.tsx)0
-rw-r--r--src/view/screens/tabroots/Home.tsx59
-rw-r--r--src/view/screens/tabroots/Login.tsx (renamed from src/view/screens/Login.tsx)2
-rw-r--r--src/view/screens/tabroots/Menu.tsx (renamed from src/view/screens/Menu.tsx)4
-rw-r--r--src/view/screens/tabroots/NotFound.tsx (renamed from src/view/screens/NotFound.tsx)4
-rw-r--r--src/view/screens/tabroots/Notifications.tsx (renamed from src/view/screens/Notifications.tsx)4
-rw-r--r--src/view/screens/tabroots/Search.tsx (renamed from src/view/screens/Search.tsx)4
-rw-r--r--src/view/screens/tabroots/Signup.tsx (renamed from src/view/screens/Signup.tsx)2
-rw-r--r--todos.txt5
-rw-r--r--yarn.lock7
24 files changed, 312 insertions, 62 deletions
diff --git a/package.json b/package.json
index e6c7fed05..703548e56 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
     "react-dom": "17.0.2",
     "react-native": "0.68.2",
     "react-native-inappbrowser-reborn": "^3.6.3",
+    "react-native-progress": "^5.0.0",
     "react-native-safe-area-context": "^4.3.1",
     "react-native-screens": "^3.13.1",
     "react-native-svg": "^12.4.0",
diff --git a/src/state/lib/api.ts b/src/state/lib/api.ts
index b3992544b..bb3ef5d1a 100644
--- a/src/state/lib/api.ts
+++ b/src/state/lib/api.ts
@@ -28,12 +28,46 @@ export async function setup(adx: AdxClient) {
   )
 }
 
+export async function post(
+  adx: AdxClient,
+  user: string,
+  text: string,
+  replyToUri?: string,
+) {
+  let reply
+  if (replyToUri) {
+    const replyToUrip = new AdxUri(replyToUri)
+    const parentPost = await adx
+      .repo(replyToUrip.host, false)
+      .collection(replyToUrip.collection)
+      .get('Post', replyToUrip.recordKey)
+    if (parentPost) {
+      reply = {
+        root: parentPost.value.reply?.root || parentPost.uri,
+        parent: parentPost.uri,
+      }
+    }
+  }
+  return await adx
+    .repo(user, true)
+    .collection('blueskyweb.xyz:Posts')
+    .create('Post', {
+      $type: 'blueskyweb.xyz:Post',
+      text,
+      reply,
+      createdAt: new Date().toISOString(),
+    })
+}
+
 export async function like(adx: AdxClient, user: string, uri: string) {
-  await adx.repo(user, true).collection('blueskyweb.xyz:Likes').create('Like', {
-    $type: 'blueskyweb.xyz:Like',
-    subject: uri,
-    createdAt: new Date().toISOString(),
-  })
+  return await adx
+    .repo(user, true)
+    .collection('blueskyweb.xyz:Likes')
+    .create('Like', {
+      $type: 'blueskyweb.xyz:Like',
+      subject: uri,
+      createdAt: new Date().toISOString(),
+    })
 }
 
 export async function unlike(adx: AdxClient, user: string, uri: string) {
@@ -45,7 +79,7 @@ export async function unlike(adx: AdxClient, user: string, uri: string) {
 }
 
 export async function repost(adx: AdxClient, user: string, uri: string) {
-  await adx
+  return await adx
     .repo(user, true)
     .collection('blueskyweb.xyz:Posts')
     .create('Repost', {
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
new file mode 100644
index 000000000..5a6ad5215
--- /dev/null
+++ b/src/view/com/composer/Composer.tsx
@@ -0,0 +1,88 @@
+import React, {useState, forwardRef, useImperativeHandle} from 'react'
+import {observer} from 'mobx-react-lite'
+import {KeyboardAvoidingView, StyleSheet, TextInput, View} from 'react-native'
+// @ts-ignore no type definition -prf
+import ProgressCircle from 'react-native-progress/Circle'
+import {useStores} from '../../../state'
+import {s} from '../../lib/styles'
+import * as apilib from '../../../state/lib/api'
+
+const MAX_TEXT_LENGTH = 256
+const WARNING_TEXT_LENGTH = 200
+const DANGER_TEXT_LENGTH = 255
+
+export const Composer = observer(
+  forwardRef(function Composer(
+    {
+      replyTo,
+    }: {
+      replyTo: string | undefined
+    },
+    ref,
+  ) {
+    const store = useStores()
+    const [text, setText] = useState('')
+
+    const onChangeText = (newText: string) => {
+      if (newText.length > MAX_TEXT_LENGTH) {
+        setText(newText.slice(0, MAX_TEXT_LENGTH))
+      } else {
+        setText(newText)
+      }
+    }
+
+    useImperativeHandle(ref, () => ({
+      async publish() {
+        if (text.trim().length === 0) {
+          return false
+        }
+        await apilib.post(store.api, 'alice.com', text, replyTo)
+        return true
+      },
+    }))
+
+    const progressColor =
+      text.length > DANGER_TEXT_LENGTH
+        ? '#e60000'
+        : text.length > WARNING_TEXT_LENGTH
+        ? '#f7c600'
+        : undefined
+
+    return (
+      <KeyboardAvoidingView style={styles.outer} behavior="padding">
+        <TextInput
+          multiline
+          scrollEnabled
+          onChangeText={text => onChangeText(text)}
+          value={text}
+          placeholder={
+            replyTo ? 'Write your reply' : "What's new in the scene?"
+          }
+          style={styles.textInput}
+        />
+        <View style={[s.flexRow, s.pt10, s.pb10, s.pr5]}>
+          <View style={s.flex1} />
+          <View>
+            <ProgressCircle
+              color={progressColor}
+              progress={text.length / MAX_TEXT_LENGTH}
+            />
+          </View>
+        </View>
+      </KeyboardAvoidingView>
+    )
+  }),
+)
+
+const styles = StyleSheet.create({
+  outer: {
+    flexDirection: 'column',
+    backgroundColor: '#fff',
+    padding: 10,
+    height: '100%',
+  },
+  textInput: {
+    flex: 1,
+    padding: 10,
+  },
+})
diff --git a/src/view/com/feed/FeedItem.tsx b/src/view/com/feed/FeedItem.tsx
index 6ba1401c9..9f3ec7c56 100644
--- a/src/view/com/feed/FeedItem.tsx
+++ b/src/view/com/feed/FeedItem.tsx
@@ -30,6 +30,11 @@ export const FeedItem = observer(function FeedItem({
       name: item.author.name,
     })
   }
+  const onPressReply = () => {
+    onNavigateContent('Composer', {
+      replyTo: item.uri,
+    })
+  }
   const onPressToggleRepost = () => {
     item
       .toggleRepost()
@@ -78,13 +83,13 @@ export const FeedItem = observer(function FeedItem({
             {record.text}
           </Text>
           <View style={styles.ctrls}>
-            <View style={styles.ctrl}>
+            <TouchableOpacity style={styles.ctrl} onPress={onPressReply}>
               <FontAwesomeIcon
                 style={styles.ctrlIcon}
                 icon={['far', 'comment']}
               />
               <Text>{item.replyCount}</Text>
-            </View>
+            </TouchableOpacity>
             <TouchableOpacity style={styles.ctrl} onPress={onPressToggleRepost}>
               <FontAwesomeIcon
                 style={
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 7263c61b3..bd22ecf9a 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -54,6 +54,11 @@ export const PostThreadItem = observer(function PostThreadItem({
       recordKey: urip.recordKey,
     })
   }
+  const onPressReply = () => {
+    onNavigateContent('Composer', {
+      replyTo: item.uri,
+    })
+  }
   const onPressToggleRepost = () => {
     item
       .toggleRepost()
@@ -129,13 +134,13 @@ export const PostThreadItem = observer(function PostThreadItem({
             <></>
           )}
           <View style={styles.ctrls}>
-            <View style={styles.ctrl}>
+            <TouchableOpacity style={styles.ctrl} onPress={onPressReply}>
               <FontAwesomeIcon
                 style={styles.ctrlIcon}
                 icon={['far', 'comment']}
               />
               <Text>{item.replyCount}</Text>
-            </View>
+            </TouchableOpacity>
             <TouchableOpacity style={styles.ctrl} onPress={onPressToggleRepost}>
               <FontAwesomeIcon
                 style={
diff --git a/src/view/index.ts b/src/view/index.ts
index c80e929c1..4a469d263 100644
--- a/src/view/index.ts
+++ b/src/view/index.ts
@@ -9,8 +9,10 @@ import {faHeart} from '@fortawesome/free-regular-svg-icons/faHeart'
 import {faHeart as fasHeart} from '@fortawesome/free-solid-svg-icons/faHeart'
 import {faHouse} from '@fortawesome/free-solid-svg-icons/faHouse'
 import {faMagnifyingGlass} from '@fortawesome/free-solid-svg-icons/faMagnifyingGlass'
+import {faPlus} from '@fortawesome/free-solid-svg-icons/faPlus'
 import {faShareFromSquare} from '@fortawesome/free-solid-svg-icons/faShareFromSquare'
 import {faRetweet} from '@fortawesome/free-solid-svg-icons/faRetweet'
+import {faX} from '@fortawesome/free-solid-svg-icons/faX'
 
 export function setup() {
   moment.updateLocale('en', {
@@ -41,8 +43,10 @@ export function setup() {
     faHeart,
     fasHeart,
     faHouse,
+    faPlus,
     faMagnifyingGlass,
     faRetweet,
     faShareFromSquare,
+    faX,
   )
 }
diff --git a/src/view/lib/styles.ts b/src/view/lib/styles.ts
index f0796723c..2ae928119 100644
--- a/src/view/lib/styles.ts
+++ b/src/view/lib/styles.ts
@@ -34,7 +34,7 @@ export const s = StyleSheet.create({
   // colors
   black: {color: 'black'},
   gray: {color: 'gray'},
-  blue: {color: 'blue'},
+  blue: {color: '#006bf7'},
   green: {color: 'green'},
   red: {color: 'red'},
 
@@ -52,6 +52,25 @@ export const s = StyleSheet.create({
   mb5: {marginBottom: 5},
   mb10: {marginBottom: 10},
 
+  // paddings
+  p2: {padding: 2},
+  p5: {padding: 5},
+  p10: {padding: 10},
+  pr2: {paddingRight: 2},
+  pr5: {paddingRight: 5},
+  pr10: {paddingRight: 10},
+  pl2: {paddingLeft: 2},
+  pl5: {paddingLeft: 5},
+  pl10: {paddingLeft: 10},
+  pt2: {paddingTop: 2},
+  pt5: {paddingTop: 5},
+  pt10: {paddingTop: 10},
+  pb2: {paddingBottom: 2},
+  pb5: {paddingBottom: 5},
+  pb10: {paddingBottom: 10},
+
   // flex
   flexRow: {flexDirection: 'row'},
+  flexCol: {flexDirection: 'column'},
+  flex1: {flex: 1},
 })
diff --git a/src/view/routes/index.tsx b/src/view/routes/index.tsx
index 989fda470..23340a7fe 100644
--- a/src/view/routes/index.tsx
+++ b/src/view/routes/index.tsx
@@ -13,17 +13,18 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import type {RootTabsParamList} from './types'
 import {useStores} from '../../state'
 import * as platform from '../../platform/detection'
-import {Home} from '../screens/Home'
-import {Search} from '../screens/Search'
-import {Notifications} from '../screens/Notifications'
-import {Menu} from '../screens/Menu'
-import {Profile} from '../screens/content/Profile'
-import {PostThread} from '../screens/content/PostThread'
-import {PostLikedBy} from '../screens/content/PostLikedBy'
-import {PostRepostedBy} from '../screens/content/PostRepostedBy'
-import {Login} from '../screens/Login'
-import {Signup} from '../screens/Signup'
-import {NotFound} from '../screens/NotFound'
+import {Home} from '../screens/tabroots/Home'
+import {Search} from '../screens/tabroots/Search'
+import {Notifications} from '../screens/tabroots/Notifications'
+import {Menu} from '../screens/tabroots/Menu'
+import {Login} from '../screens/tabroots/Login'
+import {Signup} from '../screens/tabroots/Signup'
+import {NotFound} from '../screens/tabroots/NotFound'
+import {Composer} from '../screens/stacks/Composer'
+import {PostThread} from '../screens/stacks/PostThread'
+import {PostLikedBy} from '../screens/stacks/PostLikedBy'
+import {PostRepostedBy} from '../screens/stacks/PostRepostedBy'
+import {Profile} from '../screens/stacks/Profile'
 
 const linking: LinkingOptions<RootTabsParamList> = {
   prefixes: [
@@ -42,6 +43,7 @@ const linking: LinkingOptions<RootTabsParamList> = {
       PostThread: 'profile/:name/post/:recordKey',
       PostLikedBy: 'profile/:name/post/:recordKey/liked-by',
       PostRepostedBy: 'profile/:name/post/:recordKey/reposted-by',
+      Composer: 'compose',
       Login: 'login',
       Signup: 'signup',
       NotFound: '*',
@@ -88,7 +90,8 @@ const HIDE_TAB = {tabBarButton: () => null}
 function HomeStackCom() {
   return (
     <HomeTabStack.Navigator>
-      <HomeTabStack.Screen name="Home" component={Home} options={HIDE_HEADER} />
+      <HomeTabStack.Screen name="Home" component={Home} />
+      <HomeTabStack.Screen name="Composer" component={Composer} />
       <HomeTabStack.Screen name="Profile" component={Profile} />
       <HomeTabStack.Screen name="PostThread" component={PostThread} />
       <HomeTabStack.Screen name="PostLikedBy" component={PostLikedBy} />
diff --git a/src/view/routes/types.ts b/src/view/routes/types.ts
index fd58a7666..2a9e1046a 100644
--- a/src/view/routes/types.ts
+++ b/src/view/routes/types.ts
@@ -9,6 +9,7 @@ export type RootTabsParamList = {
   PostThread: {name: string; recordKey: string}
   PostLikedBy: {name: string; recordKey: string}
   PostRepostedBy: {name: string; recordKey: string}
+  Composer: {replyTo?: string}
   Login: undefined
   Signup: undefined
   NotFound: undefined
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
deleted file mode 100644
index 1b41b2d35..000000000
--- a/src/view/screens/Home.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React, {useEffect} from 'react'
-import {View} from 'react-native'
-import {Shell} from '../shell'
-import {Feed} from '../com/feed/Feed'
-import type {RootTabsScreenProps} from '../routes/types'
-import {useStores} from '../../state'
-
-export function Home({navigation}: RootTabsScreenProps<'HomeTab'>) {
-  const store = useStores()
-  useEffect(() => {
-    console.log('Fetching home feed')
-    store.homeFeed.setup()
-  }, [store.homeFeed])
-
-  const onNavigateContent = (screen: string, props: Record<string, string>) => {
-    // @ts-ignore it's up to the callers to supply correct params -prf
-    navigation.navigate(screen, props)
-  }
-
-  return (
-    <Shell>
-      <View>
-        <Feed feed={store.homeFeed} onNavigateContent={onNavigateContent} />
-      </View>
-    </Shell>
-  )
-}
diff --git a/src/view/screens/stacks/Composer.tsx b/src/view/screens/stacks/Composer.tsx
new file mode 100644
index 000000000..e1b36567a
--- /dev/null
+++ b/src/view/screens/stacks/Composer.tsx
@@ -0,0 +1,50 @@
+import React, {useLayoutEffect, useRef} from 'react'
+import {Text, TouchableOpacity} from 'react-native'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {Shell} from '../../shell'
+import type {RootTabsScreenProps} from '../../routes/types'
+import {Composer as ComposerComponent} from '../../com/composer/Composer'
+
+export const Composer = ({
+  navigation,
+  route,
+}: RootTabsScreenProps<'Composer'>) => {
+  const {replyTo} = route.params
+  const ref = useRef<{publish: () => Promise<boolean>}>()
+
+  useLayoutEffect(() => {
+    navigation.setOptions({
+      headerShown: true,
+      headerTitle: replyTo ? 'Reply' : 'New Post',
+      headerLeft: () => (
+        <TouchableOpacity onPress={() => navigation.goBack()}>
+          <FontAwesomeIcon icon="x" />
+        </TouchableOpacity>
+      ),
+      headerRight: () => (
+        <TouchableOpacity
+          onPress={() => {
+            if (!ref.current) {
+              return
+            }
+            ref.current.publish().then(
+              posted => {
+                if (posted) {
+                  navigation.goBack()
+                }
+              },
+              err => console.error('Failed to create post', err),
+            )
+          }}>
+          <Text>Post</Text>
+        </TouchableOpacity>
+      ),
+    })
+  }, [navigation, replyTo, ref])
+
+  return (
+    <Shell>
+      <ComposerComponent ref={ref} replyTo={replyTo} />
+    </Shell>
+  )
+}
diff --git a/src/view/screens/content/PostLikedBy.tsx b/src/view/screens/stacks/PostLikedBy.tsx
index f12990141..f12990141 100644
--- a/src/view/screens/content/PostLikedBy.tsx
+++ b/src/view/screens/stacks/PostLikedBy.tsx
diff --git a/src/view/screens/content/PostRepostedBy.tsx b/src/view/screens/stacks/PostRepostedBy.tsx
index 000c1a7fc..000c1a7fc 100644
--- a/src/view/screens/content/PostRepostedBy.tsx
+++ b/src/view/screens/stacks/PostRepostedBy.tsx
diff --git a/src/view/screens/content/PostThread.tsx b/src/view/screens/stacks/PostThread.tsx
index 485a2e49a..485a2e49a 100644
--- a/src/view/screens/content/PostThread.tsx
+++ b/src/view/screens/stacks/PostThread.tsx
diff --git a/src/view/screens/content/Profile.tsx b/src/view/screens/stacks/Profile.tsx
index ccdaed4a4..ccdaed4a4 100644
--- a/src/view/screens/content/Profile.tsx
+++ b/src/view/screens/stacks/Profile.tsx
diff --git a/src/view/screens/tabroots/Home.tsx b/src/view/screens/tabroots/Home.tsx
new file mode 100644
index 000000000..446a5a7e9
--- /dev/null
+++ b/src/view/screens/tabroots/Home.tsx
@@ -0,0 +1,59 @@
+import React, {useEffect, useLayoutEffect} from 'react'
+import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {Shell} from '../../shell'
+import {Feed} from '../../com/feed/Feed'
+import type {RootTabsScreenProps} from '../../routes/types'
+import {useStores} from '../../../state'
+import {AVIS} from '../../lib/assets'
+
+export function Home({navigation}: RootTabsScreenProps<'HomeTab'>) {
+  const store = useStores()
+  useEffect(() => {
+    console.log('Fetching home feed')
+    store.homeFeed.setup()
+  }, [store.homeFeed])
+
+  const onNavigateContent = (screen: string, props: Record<string, string>) => {
+    // @ts-ignore it's up to the callers to supply correct params -prf
+    navigation.navigate(screen, props)
+  }
+
+  useLayoutEffect(() => {
+    navigation.setOptions({
+      headerShown: true,
+      headerTitle: 'V I B E',
+      headerLeft: () => (
+        <TouchableOpacity
+          onPress={() => navigation.push('Profile', {name: 'alice.com'})}>
+          <Image source={AVIS['alice.com']} style={styles.avi} />
+        </TouchableOpacity>
+      ),
+      headerRight: () => (
+        <TouchableOpacity
+          onPress={() => {
+            navigation.push('Composer', {})
+          }}>
+          <FontAwesomeIcon icon="plus" style={{color: '#006bf7'}} />
+        </TouchableOpacity>
+      ),
+    })
+  }, [navigation])
+
+  return (
+    <Shell>
+      <View>
+        <Feed feed={store.homeFeed} onNavigateContent={onNavigateContent} />
+      </View>
+    </Shell>
+  )
+}
+
+const styles = StyleSheet.create({
+  avi: {
+    width: 20,
+    height: 20,
+    borderRadius: 10,
+    resizeMode: 'cover',
+  },
+})
diff --git a/src/view/screens/Login.tsx b/src/view/screens/tabroots/Login.tsx
index d08a5a256..a5f670bdd 100644
--- a/src/view/screens/Login.tsx
+++ b/src/view/screens/tabroots/Login.tsx
@@ -1,7 +1,7 @@
 import React from 'react'
 import {Text, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
-import {Shell} from '../shell'
+import {Shell} from '../../shell'
 // import type {RootTabsScreenProps} from '../routes/types'
 // import {useStores} from '../../state'
 
diff --git a/src/view/screens/Menu.tsx b/src/view/screens/tabroots/Menu.tsx
index d0cc0826f..dca5ad33b 100644
--- a/src/view/screens/Menu.tsx
+++ b/src/view/screens/tabroots/Menu.tsx
@@ -1,7 +1,7 @@
 import React from 'react'
-import {Shell} from '../shell'
+import {Shell} from '../../shell'
 import {ScrollView, Text, View} from 'react-native'
-import type {RootTabsScreenProps} from '../routes/types'
+import type {RootTabsScreenProps} from '../../routes/types'
 
 export const Menu = (_props: RootTabsScreenProps<'MenuTab'>) => {
   return (
diff --git a/src/view/screens/NotFound.tsx b/src/view/screens/tabroots/NotFound.tsx
index 5357a428a..a35808cbc 100644
--- a/src/view/screens/NotFound.tsx
+++ b/src/view/screens/tabroots/NotFound.tsx
@@ -1,7 +1,7 @@
 import React from 'react'
-import {Shell} from '../shell'
+import {Shell} from '../../shell'
 import {Text, Button, View} from 'react-native'
-import type {RootTabsScreenProps} from '../routes/types'
+import type {RootTabsScreenProps} from '../../routes/types'
 
 export const NotFound = ({navigation}: RootTabsScreenProps<'NotFound'>) => {
   return (
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/tabroots/Notifications.tsx
index a7918f177..091410ad8 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/tabroots/Notifications.tsx
@@ -1,7 +1,7 @@
 import React from 'react'
-import {Shell} from '../shell'
+import {Shell} from '../../shell'
 import {Text, View} from 'react-native'
-import type {RootTabsScreenProps} from '../routes/types'
+import type {RootTabsScreenProps} from '../../routes/types'
 
 export const Notifications = (
   _props: RootTabsScreenProps<'NotificationsTab'>,
diff --git a/src/view/screens/Search.tsx b/src/view/screens/tabroots/Search.tsx
index 26df2954c..044ca749c 100644
--- a/src/view/screens/Search.tsx
+++ b/src/view/screens/tabroots/Search.tsx
@@ -1,7 +1,7 @@
 import React from 'react'
-import {Shell} from '../shell'
+import {Shell} from '../../shell'
 import {Text, View} from 'react-native'
-import type {RootTabsScreenProps} from '../routes/types'
+import type {RootTabsScreenProps} from '../../routes/types'
 
 export const Search = (_props: RootTabsScreenProps<'SearchTab'>) => {
   return (
diff --git a/src/view/screens/Signup.tsx b/src/view/screens/tabroots/Signup.tsx
index 4a8c5df2d..dc2af2b1e 100644
--- a/src/view/screens/Signup.tsx
+++ b/src/view/screens/tabroots/Signup.tsx
@@ -1,7 +1,7 @@
 import React from 'react'
 import {Text, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
-import {Shell} from '../shell'
+import {Shell} from '../../shell'
 // import type {RootTabsScreenProps} from '../routes/types'
 // import {useStores} from '../../state'
 
diff --git a/todos.txt b/todos.txt
index 7e38d5302..e170e38e9 100644
--- a/todos.txt
+++ b/todos.txt
@@ -6,12 +6,13 @@ Paul's todo list
 - Thread view
   - Refresh
   - Share btn
-  - Reply control
 - Profile view
   - Refresh
   - Follow / Unfollow
   - Badges
-- Compose post control
+- Composer
+  - Refresh original view after reply
+  - Check on navigation stack during a bunch of replies
 - Search view
   - *
 - Notifications view
diff --git a/yarn.lock b/yarn.lock
index 581f59f69..b15d85f35 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11146,6 +11146,13 @@ react-native-inappbrowser-reborn@^3.6.3:
     invariant "^2.2.4"
     opencollective-postinstall "^2.0.2"
 
+react-native-progress@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/react-native-progress/-/react-native-progress-5.0.0.tgz#f5ac6ceaeee27f184c660b00f29419e82a9d0ab0"
+  integrity sha512-KjnGIt3r9i5Kn2biOD9fXLJocf0bwxPRxOyAgXEnZTJQU2O+HyzgGFRCbM5h3izm9kKIkSc1txh8aGmMafCD9A==
+  dependencies:
+    prop-types "^15.7.2"
+
 react-native-safe-area-context@^4.3.1:
   version "4.3.1"
   resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.3.1.tgz#5cf97b25b395e0d09bc1f828920cd7da0d792ade"