about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2022-09-01 12:00:08 -0500
committerPaul Frazee <pfrazee@gmail.com>2022-09-01 12:00:08 -0500
commitbb51af5ae9c405faafad3b9685eef545c3437adb (patch)
tree0705cea86e68f223fd240aeabaf4aacb7d4ea43f
parent346385ce43b609df82a70a8cb038b6622c99c24e (diff)
downloadvoidsky-bb51af5ae9c405faafad3b9685eef545c3437adb.tar.zst
Implement working screen-state management and remove extraneous loads
-rw-r--r--src/state/index.ts1
-rw-r--r--src/state/models/root-store.ts4
-rw-r--r--src/view/lib/navigation.ts12
-rw-r--r--src/view/routes.ts1
-rw-r--r--src/view/screens/Home.tsx73
-rw-r--r--src/view/screens/Notifications.tsx79
-rw-r--r--src/view/screens/PostLikedBy.tsx25
-rw-r--r--src/view/screens/PostRepostedBy.tsx25
-rw-r--r--src/view/screens/PostThread.tsx27
-rw-r--r--src/view/screens/Profile.tsx48
-rw-r--r--src/view/screens/ProfileFollowers.tsx25
-rw-r--r--src/view/screens/ProfileFollows.tsx25
-rw-r--r--src/view/shell/mobile/index.tsx10
-rw-r--r--todos.txt6
14 files changed, 117 insertions, 244 deletions
diff --git a/src/state/index.ts b/src/state/index.ts
index 91726dc6e..9ebf321e4 100644
--- a/src/state/index.ts
+++ b/src/state/index.ts
@@ -27,7 +27,6 @@ export async function setupState() {
   // track changes & save to storage
   autorun(() => {
     const snapshot = rootStore.serialize()
-    console.log('saving', snapshot)
     storage.save(ROOT_STATE_STORAGE_KEY, snapshot)
   })
 
diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts
index d1e731328..0dab41675 100644
--- a/src/state/models/root-store.ts
+++ b/src/state/models/root-store.ts
@@ -9,15 +9,11 @@ import {isObj, hasProp} from '../lib/type-guards'
 import {SessionModel} from './session'
 import {NavigationModel} from './navigation'
 import {MeModel} from './me'
-import {FeedViewModel} from './feed-view'
-import {NotificationsViewModel} from './notifications-view'
 
 export class RootStoreModel {
   session = new SessionModel()
   nav = new NavigationModel()
   me = new MeModel(this)
-  homeFeed = new FeedViewModel(this, {})
-  notesFeed = new NotificationsViewModel(this, {})
 
   constructor(public api: AdxClient) {
     makeAutoObservable(this, {
diff --git a/src/view/lib/navigation.ts b/src/view/lib/navigation.ts
deleted file mode 100644
index 2024918e7..000000000
--- a/src/view/lib/navigation.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import {useEffect} from 'react'
-import {useStores} from '../../state'
-
-type CB = () => void
-/**
- * This custom effect hook will trigger on every "navigation"
- * Use this in screens to handle any loading behaviors needed
- */
-export function useLoadEffect(cb: CB, deps: any[] = []) {
-  const store = useStores()
-  useEffect(cb, [store.nav.tab, ...deps])
-}
diff --git a/src/view/routes.ts b/src/view/routes.ts
index 5d8776ddc..293d53e30 100644
--- a/src/view/routes.ts
+++ b/src/view/routes.ts
@@ -16,6 +16,7 @@ import {ProfileFollows} from './screens/ProfileFollows'
 
 export type ScreenParams = {
   params: Record<string, any>
+  visible: boolean
 }
 export type Route = [React.FC<ScreenParams>, IconProp, RegExp]
 export type MatchResult = {
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 1bc300a11..9d0356fbb 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -1,53 +1,31 @@
-import React, {useState, useEffect, useLayoutEffect} from 'react'
-import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import React, {useState, useEffect} from 'react'
+import {View} from 'react-native'
 import {Feed} from '../com/feed/Feed'
 import {FAB} from '../com/util/FloatingActionButton'
 import {useStores} from '../../state'
-import {useLoadEffect} from '../lib/navigation'
-import {AVIS} from '../lib/assets'
+import {FeedViewModel} from '../../state/models/feed-view'
 import {ScreenParams} from '../routes'
 
-export function Home({params}: ScreenParams) {
+export function Home({visible}: ScreenParams) {
   const [hasSetup, setHasSetup] = useState<boolean>(false)
+  const [feedView, setFeedView] = useState<FeedViewModel | undefined>()
   const store = useStores()
-  useLoadEffect(() => {
-    store.nav.setTitle('Home')
-    console.log('Fetching home feed')
-    store.homeFeed.setup().then(() => setHasSetup(true))
-  }, [store.nav, store.homeFeed])
 
-  // TODO
-  // useEffect(() => {
-  //   return navigation.addListener('focus', () => {
-  //     if (hasSetup) {
-  //       console.log('Updating home feed')
-  //       store.homeFeed.update()
-  //     }
-  //   })
-  // }, [navigation, store.homeFeed, hasSetup])
-
-  // TODO
-  // 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])
+  useEffect(() => {
+    if (!visible) {
+      return
+    }
+    if (hasSetup) {
+      console.log('Updating home feed')
+      feedView?.update()
+    } else {
+      store.nav.setTitle('Home')
+      console.log('Fetching home feed')
+      const newFeedView = new FeedViewModel(store, {})
+      setFeedView(newFeedView)
+      newFeedView.setup().then(() => setHasSetup(true))
+    }
+  }, [visible, store])
 
   const onComposePress = () => {
     store.nav.navigate('/compose')
@@ -55,17 +33,8 @@ export function Home({params}: ScreenParams) {
 
   return (
     <View>
-      <Feed feed={store.homeFeed} />
+      {feedView && <Feed feed={feedView} />}
       <FAB icon="pen-nib" onPress={onComposePress} />
     </View>
   )
 }
-
-const styles = StyleSheet.create({
-  avi: {
-    width: 20,
-    height: 20,
-    borderRadius: 10,
-    resizeMode: 'cover',
-  },
-})
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index 7ebc8a7ce..60627385f 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -1,65 +1,32 @@
-import React, {useState, useEffect, useLayoutEffect} from 'react'
-import {Image, StyleSheet, TouchableOpacity, View} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import React, {useState, useEffect} from 'react'
+import {View} from 'react-native'
 import {Feed} from '../com/notifications/Feed'
 import {useStores} from '../../state'
-import {AVIS} from '../lib/assets'
+import {NotificationsViewModel} from '../../state/models/notifications-view'
 import {ScreenParams} from '../routes'
-import {useLoadEffect} from '../lib/navigation'
 
-export const Notifications = ({params}: ScreenParams) => {
+export const Notifications = ({visible}: ScreenParams) => {
   const [hasSetup, setHasSetup] = useState<boolean>(false)
+  const [notesView, setNotesView] = useState<
+    NotificationsViewModel | undefined
+  >()
   const store = useStores()
-  useLoadEffect(() => {
-    store.nav.setTitle('Notifications')
-    console.log('Fetching notifications feed')
-    store.notesFeed.setup().then(() => setHasSetup(true))
-  }, [store.notesFeed])
 
-  // TODO
-  // useEffect(() => {
-  //   return navigation.addListener('focus', () => {
-  //     if (hasSetup) {
-  //       console.log('Updating notifications feed')
-  //       store.notesFeed.update()
-  //     }
-  //   })
-  // }, [navigation, store.notesFeed, hasSetup])
+  useEffect(() => {
+    if (!visible) {
+      return
+    }
+    if (hasSetup) {
+      console.log('Updating notifications feed')
+      notesView?.update()
+    } else {
+      store.nav.setTitle('Notifications')
+      console.log('Fetching notifications feed')
+      const newNotesView = new NotificationsViewModel(store, {})
+      setNotesView(newNotesView)
+      newNotesView.setup().then(() => setHasSetup(true))
+    }
+  }, [visible, store])
 
-  // TODO
-  // useLayoutEffect(() => {
-  //   navigation.setOptions({
-  //     headerShown: true,
-  //     headerTitle: 'Notifications',
-  //     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 (
-    <View>
-      <Feed view={store.notesFeed} />
-    </View>
-  )
+  return <View>{notesView && <Feed view={notesView} />}</View>
 }
-
-const styles = StyleSheet.create({
-  avi: {
-    width: 20,
-    height: 20,
-    borderRadius: 10,
-    resizeMode: 'cover',
-  },
-})
diff --git a/src/view/screens/PostLikedBy.tsx b/src/view/screens/PostLikedBy.tsx
index 92fae30ad..540382766 100644
--- a/src/view/screens/PostLikedBy.tsx
+++ b/src/view/screens/PostLikedBy.tsx
@@ -1,26 +1,19 @@
-import React, {useLayoutEffect} from 'react'
-import {TouchableOpacity} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import React, {useEffect} from 'react'
 import {makeRecordUri} from '../lib/strings'
 import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy'
 import {ScreenParams} from '../routes'
+import {useStores} from '../../state'
 
-export const PostLikedBy = ({params}: ScreenParams) => {
+export const PostLikedBy = ({visible, params}: ScreenParams) => {
+  const store = useStores()
   const {name, recordKey} = params
   const uri = makeRecordUri(name, 'blueskyweb.xyz:Posts', recordKey)
 
-  // TODO
-  // useLayoutEffect(() => {
-  //   navigation.setOptions({
-  //     headerShown: true,
-  //     headerTitle: 'Liked By',
-  //     headerLeft: () => (
-  //       <TouchableOpacity onPress={() => navigation.goBack()}>
-  //         <FontAwesomeIcon icon="arrow-left" />
-  //       </TouchableOpacity>
-  //     ),
-  //   })
-  // }, [navigation])
+  useEffect(() => {
+    if (visible) {
+      store.nav.setTitle('Liked by')
+    }
+  }, [store, visible])
 
   return <PostLikedByComponent uri={uri} />
 }
diff --git a/src/view/screens/PostRepostedBy.tsx b/src/view/screens/PostRepostedBy.tsx
index 81014a7c7..60950139d 100644
--- a/src/view/screens/PostRepostedBy.tsx
+++ b/src/view/screens/PostRepostedBy.tsx
@@ -1,26 +1,19 @@
-import React, {useLayoutEffect} from 'react'
-import {TouchableOpacity} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import React, {useEffect} from 'react'
 import {makeRecordUri} from '../lib/strings'
 import {PostRepostedBy as PostRepostedByComponent} from '../com/post-thread/PostRepostedBy'
 import {ScreenParams} from '../routes'
+import {useStores} from '../../state'
 
-export const PostRepostedBy = ({params}: ScreenParams) => {
+export const PostRepostedBy = ({visible, params}: ScreenParams) => {
+  const store = useStores()
   const {name, recordKey} = params
   const uri = makeRecordUri(name, 'blueskyweb.xyz:Posts', recordKey)
 
-  // TODO
-  // useLayoutEffect(() => {
-  //   navigation.setOptions({
-  //     headerShown: true,
-  //     headerTitle: 'Reposted By',
-  //     headerLeft: () => (
-  //       <TouchableOpacity onPress={() => navigation.goBack()}>
-  //         <FontAwesomeIcon icon="arrow-left" />
-  //       </TouchableOpacity>
-  //     ),
-  //   })
-  // }, [navigation])
+  useEffect(() => {
+    if (visible) {
+      store.nav.setTitle('Reposted by')
+    }
+  }, [store, visible])
 
   return <PostRepostedByComponent uri={uri} />
 }
diff --git a/src/view/screens/PostThread.tsx b/src/view/screens/PostThread.tsx
index 1003a40e1..8da40302b 100644
--- a/src/view/screens/PostThread.tsx
+++ b/src/view/screens/PostThread.tsx
@@ -1,32 +1,19 @@
-import React, {useEffect, useLayoutEffect} from 'react'
-import {TouchableOpacity} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import React, {useEffect} from 'react'
 import {makeRecordUri} from '../lib/strings'
 import {PostThread as PostThreadComponent} from '../com/post-thread/PostThread'
 import {ScreenParams} from '../routes'
 import {useStores} from '../../state'
-import {useLoadEffect} from '../lib/navigation'
 
-export const PostThread = ({params}: ScreenParams) => {
+export const PostThread = ({visible, params}: ScreenParams) => {
   const store = useStores()
   const {name, recordKey} = params
   const uri = makeRecordUri(name, 'blueskyweb.xyz:Posts', recordKey)
-  useLoadEffect(() => {
-    store.nav.setTitle(`Post by ${name}`)
-  }, [store.nav, name])
 
-  // TODO
-  // useLayoutEffect(() => {
-  //   navigation.setOptions({
-  //     headerShown: true,
-  //     headerTitle: 'Thread',
-  //     headerLeft: () => (
-  //       <TouchableOpacity onPress={() => navigation.goBack()}>
-  //         <FontAwesomeIcon icon="arrow-left" />
-  //       </TouchableOpacity>
-  //     ),
-  //   })
-  // }, [navigation])
+  useEffect(() => {
+    if (visible) {
+      store.nav.setTitle(`Post by ${name}`)
+    }
+  }, [visible, store.nav, name])
 
   return <PostThreadComponent uri={uri} />
 }
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 84ff63f5a..236f8f908 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -5,39 +5,33 @@ import {useStores} from '../../state'
 import {ProfileHeader} from '../com/profile/ProfileHeader'
 import {Feed} from '../com/feed/Feed'
 import {ScreenParams} from '../routes'
-import {useLoadEffect} from '../lib/navigation'
 
-export const Profile = ({params}: ScreenParams) => {
+export const Profile = ({visible, params}: ScreenParams) => {
   const store = useStores()
-  const [hasSetup, setHasSetup] = useState<string>('')
+  const [hasSetup, setHasSetup] = useState<boolean>(false)
   const [feedView, setFeedView] = useState<FeedViewModel | undefined>()
 
-  useLoadEffect(() => {
+  useEffect(() => {
+    if (!visible) {
+      return
+    }
     const author = params.name
-    if (feedView?.params.author === author) {
-      return // no change needed? or trigger refresh?
+    if (hasSetup) {
+      console.log('Updating profile feed for', author)
+      feedView?.update()
+    } else {
+      console.log('Fetching profile feed for', author)
+      const newFeedView = new FeedViewModel(store, {author})
+      setFeedView(newFeedView)
+      newFeedView
+        .setup()
+        .catch(err => console.error('Failed to fetch feed', err))
+        .then(() => {
+          setHasSetup(true)
+          store.nav.setTitle(author)
+        })
     }
-    console.log('Fetching profile feed', author)
-    const newFeedView = new FeedViewModel(store, {author})
-    setFeedView(newFeedView)
-    newFeedView
-      .setup()
-      .catch(err => console.error('Failed to fetch feed', err))
-      .then(() => {
-        setHasSetup(author)
-        store.nav.setTitle(author)
-      })
-  }, [params.name, feedView?.params.author, store])
-
-  // TODO
-  // useEffect(() => {
-  //   return navigation.addListener('focus', () => {
-  //     if (hasSetup === feedView?.params.author) {
-  //       console.log('Updating profile feed', hasSetup)
-  //       feedView?.update()
-  //     }
-  //   })
-  // }, [navigation, feedView, hasSetup])
+  }, [visible, params.name, store])
 
   return (
     <View style={styles.container}>
diff --git a/src/view/screens/ProfileFollowers.tsx b/src/view/screens/ProfileFollowers.tsx
index c8e752685..aabfb59a4 100644
--- a/src/view/screens/ProfileFollowers.tsx
+++ b/src/view/screens/ProfileFollowers.tsx
@@ -1,24 +1,17 @@
-import React, {useLayoutEffect} from 'react'
-import {TouchableOpacity} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import React, {useEffect} from 'react'
 import {ProfileFollowers as ProfileFollowersComponent} from '../com/profile/ProfileFollowers'
 import {ScreenParams} from '../routes'
+import {useStores} from '../../state'
 
-export const ProfileFollowers = ({params}: ScreenParams) => {
+export const ProfileFollowers = ({visible, params}: ScreenParams) => {
+  const store = useStores()
   const {name} = params
 
-  // TODO
-  // useLayoutEffect(() => {
-  //   navigation.setOptions({
-  //     headerShown: true,
-  //     headerTitle: 'Followers',
-  //     headerLeft: () => (
-  //       <TouchableOpacity onPress={() => navigation.goBack()}>
-  //         <FontAwesomeIcon icon="arrow-left" />
-  //       </TouchableOpacity>
-  //     ),
-  //   })
-  // }, [navigation])
+  useEffect(() => {
+    if (visible) {
+      store.nav.setTitle('Followers of')
+    }
+  }, [store, visible])
 
   return <ProfileFollowersComponent name={name} />
 }
diff --git a/src/view/screens/ProfileFollows.tsx b/src/view/screens/ProfileFollows.tsx
index 96ce60ddd..d020fc742 100644
--- a/src/view/screens/ProfileFollows.tsx
+++ b/src/view/screens/ProfileFollows.tsx
@@ -1,24 +1,17 @@
-import React, {useLayoutEffect} from 'react'
-import {TouchableOpacity} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import React, {useEffect} from 'react'
 import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows'
 import {ScreenParams} from '../routes'
+import {useStores} from '../../state'
 
-export const ProfileFollows = ({params}: ScreenParams) => {
+export const ProfileFollows = ({visible, params}: ScreenParams) => {
+  const store = useStores()
   const {name} = params
 
-  // TODO
-  // useLayoutEffect(() => {
-  //   navigation.setOptions({
-  //     headerShown: true,
-  //     headerTitle: 'Following',
-  //     headerLeft: () => (
-  //       <TouchableOpacity onPress={() => navigation.goBack()}>
-  //         <FontAwesomeIcon icon="arrow-left" />
-  //       </TouchableOpacity>
-  //     ),
-  //   })
-  // }, [navigation])
+  useEffect(() => {
+    if (visible) {
+      store.nav.setTitle('Followers of')
+    }
+  }, [store, visible])
 
   return <ProfileFollowsComponent name={name} />
 }
diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx
index 63edf4e69..4dd5cf349 100644
--- a/src/view/shell/mobile/index.tsx
+++ b/src/view/shell/mobile/index.tsx
@@ -113,12 +113,12 @@ export const MobileShell: React.FC = observer(() => {
       </View>
       <SafeAreaView style={styles.innerContainer}>
         <ScreenContainer style={styles.screenContainer}>
-          {screenRenderDesc.screens.map(({Com, params, key, activityState}) => (
+          {screenRenderDesc.screens.map(({Com, params, key, visible}) => (
             <Screen
               key={key}
               style={[StyleSheet.absoluteFill, styles.screen]}
-              activityState={activityState}>
-              <Com params={params} />
+              activityState={visible ? 2 : 0}>
+              <Com params={params} visible={visible} />
             </Screen>
           ))}
         </ScreenContainer>
@@ -156,7 +156,7 @@ export const MobileShell: React.FC = observer(() => {
  * This method produces the information needed by the shell to
  * render the current screens with screen-caching behaviors.
  */
-type ScreenRenderDesc = MatchResult & {key: string; activityState: 0 | 1 | 2}
+type ScreenRenderDesc = MatchResult & {key: string; visible: boolean}
 function constructScreenRenderDesc(nav: NavigationModel): {
   icon: IconProp
   screens: ScreenRenderDesc[]
@@ -176,7 +176,7 @@ function constructScreenRenderDesc(nav: NavigationModel): {
       }
       return Object.assign(matchRes, {
         key: `t${tab.id}-s${screen.index}`,
-        activityState: isCurrent ? 2 : 0,
+        visible: isCurrent,
       }) as ScreenRenderDesc
     })
     screens = screens.concat(parsedTabScreens)
diff --git a/todos.txt b/todos.txt
index 661f8082b..848a22343 100644
--- a/todos.txt
+++ b/todos.txt
@@ -12,6 +12,6 @@ Paul's todo list
   - Reposted by
   - Followers list
   - Follows list
-- Navigation
-  - Restore all functionality that was disabled during the refactor
-  - Reduce extraneous triggers of useLoadEffect
\ No newline at end of file
+- Bugs
+  - Check that sub components arent reloading too much
+  - Check that caching is choosing the right views
\ No newline at end of file