diff options
author | Paul Frazee <pfrazee@gmail.com> | 2022-09-01 12:00:08 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2022-09-01 12:00:08 -0500 |
commit | bb51af5ae9c405faafad3b9685eef545c3437adb (patch) | |
tree | 0705cea86e68f223fd240aeabaf4aacb7d4ea43f | |
parent | 346385ce43b609df82a70a8cb038b6622c99c24e (diff) | |
download | voidsky-bb51af5ae9c405faafad3b9685eef545c3437adb.tar.zst |
Implement working screen-state management and remove extraneous loads
-rw-r--r-- | src/state/index.ts | 1 | ||||
-rw-r--r-- | src/state/models/root-store.ts | 4 | ||||
-rw-r--r-- | src/view/lib/navigation.ts | 12 | ||||
-rw-r--r-- | src/view/routes.ts | 1 | ||||
-rw-r--r-- | src/view/screens/Home.tsx | 73 | ||||
-rw-r--r-- | src/view/screens/Notifications.tsx | 79 | ||||
-rw-r--r-- | src/view/screens/PostLikedBy.tsx | 25 | ||||
-rw-r--r-- | src/view/screens/PostRepostedBy.tsx | 25 | ||||
-rw-r--r-- | src/view/screens/PostThread.tsx | 27 | ||||
-rw-r--r-- | src/view/screens/Profile.tsx | 48 | ||||
-rw-r--r-- | src/view/screens/ProfileFollowers.tsx | 25 | ||||
-rw-r--r-- | src/view/screens/ProfileFollows.tsx | 25 | ||||
-rw-r--r-- | src/view/shell/mobile/index.tsx | 10 | ||||
-rw-r--r-- | todos.txt | 6 |
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 |