diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-08-03 10:25:17 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-03 10:25:17 -0700 |
commit | 96280d5f1a24620fd12aa178027a12d81c052a34 (patch) | |
tree | dc8d4b04319383a9cddcf8a27213f29d98e878a1 /src | |
parent | 1211c353d0ef67f7a2d97e819dac488b14b73a08 (diff) | |
download | voidsky-96280d5f1a24620fd12aa178027a12d81c052a34.tar.zst |
Improve the profile preview with "swipe up to view" and local cache optimization (#1096)
* Update the ProfilePreview to use a swipe-up to navigate * Use the profile cache to optimize load performance * Hack to align the header in the profile preview against the screen view * Fix profiles cache logic to ensure cache is used * Fix dark mode on profile preview
Diffstat (limited to 'src')
-rw-r--r-- | src/Navigation.tsx | 5 | ||||
-rw-r--r-- | src/state/models/cache/profiles-view.ts | 4 | ||||
-rw-r--r-- | src/state/models/content/profile.ts | 26 | ||||
-rw-r--r-- | src/view/com/modals/Modal.tsx | 22 | ||||
-rw-r--r-- | src/view/com/modals/ProfilePreview.tsx | 91 |
5 files changed, 88 insertions, 60 deletions
diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 06cce0f00..48bab182d 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -125,7 +125,10 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { <Stack.Screen name="Profile" component={ProfileScreen} - options={({route}) => ({title: title(`@${route.params.name}`)})} + options={({route}) => ({ + title: title(`@${route.params.name}`), + animation: 'none', + })} /> <Stack.Screen name="ProfileFollowers" diff --git a/src/state/models/cache/profiles-view.ts b/src/state/models/cache/profiles-view.ts index b4bd70db5..e5a9be587 100644 --- a/src/state/models/cache/profiles-view.ts +++ b/src/state/models/cache/profiles-view.ts @@ -45,8 +45,6 @@ export class ProfilesCache { } overwrite(did: string, res: GetProfile.Response) { - if (this.cache.has(did)) { - this.cache.set(did, res) - } + this.cache.set(did, res) } } diff --git a/src/state/models/content/profile.ts b/src/state/models/content/profile.ts index c4cbe6d44..2ea4ada6e 100644 --- a/src/state/models/content/profile.ts +++ b/src/state/models/content/profile.ts @@ -103,7 +103,12 @@ export class ProfileModel { // = async setup() { - await this._load() + const precache = await this.rootStore.profiles.cache.get(this.params.actor) + if (precache) { + await this._loadWithCache(precache) + } else { + await this._load() + } } async refresh() { @@ -252,7 +257,7 @@ export class ProfileModel { this._xLoading(isRefreshing) try { const res = await this.rootStore.agent.getProfile(this.params) - this.rootStore.profiles.overwrite(this.params.actor, res) // cache invalidation + this.rootStore.profiles.overwrite(this.params.actor, res) if (res.data.handle) { this.rootStore.handleResolutions.cache.set( res.data.handle, @@ -267,6 +272,23 @@ export class ProfileModel { } } + async _loadWithCache(precache: GetProfile.Response) { + // use cached value + this._replaceAll(precache) + await this._createRichText() + this._xIdle() + + // fetch latest + try { + const res = await this.rootStore.agent.getProfile(this.params) + this.rootStore.profiles.overwrite(this.params.actor, res) // cache invalidation + this._replaceAll(res) + await this._createRichText() + } catch (e: any) { + this._xIdle(e) + } + } + _replaceAll(res: GetProfile.Response) { this.did = res.data.did this.handle = res.data.handle diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index 525df7ba1..00d061616 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -6,6 +6,8 @@ import BottomSheet from '@gorhom/bottom-sheet' import {useStores} from 'state/index' import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop' import {usePalette} from 'lib/hooks/usePalette' +import {navigate} from '../../../Navigation' +import once from 'lodash.once' import * as ConfirmModal from './Confirm' import * as EditProfileModal from './EditProfile' @@ -35,9 +37,25 @@ export const ModalsContainer = observer(function ModalsContainer() { const store = useStores() const bottomSheetRef = useRef<BottomSheet>(null) const pal = usePalette('default') + + const activeModal = + store.shell.activeModals[store.shell.activeModals.length - 1] + + const navigateOnce = once(navigate) + + const onBottomSheetAnimate = (fromIndex: number, toIndex: number) => { + if (activeModal?.name === 'profile-preview' && toIndex === 1) { + // begin loading the profile screen behind the scenes + navigateOnce('Profile', {name: activeModal.did}) + } + } const onBottomSheetChange = (snapPoint: number) => { if (snapPoint === -1) { store.shell.closeModal() + } else if (activeModal?.name === 'profile-preview' && snapPoint === 1) { + // ensure we navigate to Profile and close the modal + navigateOnce('Profile', {name: activeModal.did}) + store.shell.closeModal() } } const onClose = () => { @@ -45,9 +63,6 @@ export const ModalsContainer = observer(function ModalsContainer() { store.shell.closeModal() } - const activeModal = - store.shell.activeModals[store.shell.activeModals.length - 1] - useEffect(() => { if (store.shell.isModalActive) { bottomSheetRef.current?.expand() @@ -146,6 +161,7 @@ export const ModalsContainer = observer(function ModalsContainer() { } handleIndicatorStyle={{backgroundColor: pal.text.color}} handleStyle={[styles.handle, pal.view]} + onAnimate={onBottomSheetAnimate} onChange={onBottomSheetChange}> {element} </BottomSheet> diff --git a/src/view/com/modals/ProfilePreview.tsx b/src/view/com/modals/ProfilePreview.tsx index d3267644b..4efe81225 100644 --- a/src/view/com/modals/ProfilePreview.tsx +++ b/src/view/com/modals/ProfilePreview.tsx @@ -1,63 +1,56 @@ -import React, {useState, useEffect, useCallback} from 'react' -import {StyleSheet, View} from 'react-native' +import React, {useState, useEffect} from 'react' +import {ActivityIndicator, StyleSheet, View} from 'react-native' import {observer} from 'mobx-react-lite' -import {useNavigation, StackActions} from '@react-navigation/native' -import {Text} from '../util/text/Text' +import {ThemedText} from '../util/text/ThemedText' import {useStores} from 'state/index' import {ProfileModel} from 'state/models/content/profile' import {usePalette} from 'lib/hooks/usePalette' import {useAnalytics} from 'lib/analytics/analytics' import {ProfileHeader} from '../profile/ProfileHeader' -import {Button} from '../util/forms/Button' -import {NavigationProp} from 'lib/routes/types' +import {InfoCircleIcon} from 'lib/icons' +import {useNavigationState} from '@react-navigation/native' +import {isIOS} from 'platform/detection' +import {s} from 'lib/styles' -export const snapPoints = [560] +export const snapPoints = [520, '100%'] export const Component = observer(({did}: {did: string}) => { const store = useStores() const pal = usePalette('default') - const palInverted = usePalette('inverted') - const navigation = useNavigation<NavigationProp>() const [model] = useState(new ProfileModel(store, {actor: did})) const {screen} = useAnalytics() + // track the navigator state to detect if a page-load occurred + const navState = useNavigationState(s => s) + const [initNavState] = useState(navState) + const isLoading = initNavState !== navState + useEffect(() => { screen('Profile:Preview') model.setup() }, [model, screen]) - const onPressViewProfile = useCallback(() => { - navigation.dispatch(StackActions.push('Profile', {name: model.handle})) - store.shell.closeModal() - }, [navigation, store, model]) - return ( - <View style={pal.view}> - <View style={styles.headerWrapper}> + <View style={[pal.view, s.flex1]}> + <View + style={[ + styles.headerWrapper, + isLoading && isIOS && styles.headerPositionAdjust, + ]}> <ProfileHeader view={model} hideBackButton onRefreshAll={() => {}} /> </View> - <View style={[styles.buttonsContainer, pal.view]}> - <View style={styles.buttons}> - <Button - type="inverted" - style={[styles.button, styles.buttonWide]} - onPress={onPressViewProfile} - accessibilityLabel="View profile" - accessibilityHint=""> - <Text type="button-lg" style={palInverted.text}> - View Profile - </Text> - </Button> - <Button - type="default" - style={styles.button} - onPress={() => store.shell.closeModal()} - accessibilityLabel="Close this preview" - accessibilityHint=""> - <Text type="button-lg" style={pal.text}> - Close - </Text> - </Button> + <View style={[styles.hintWrapper, pal.view]}> + <View style={styles.hint}> + {isLoading ? ( + <ActivityIndicator /> + ) : ( + <> + <InfoCircleIcon size={21} style={pal.textLight} /> + <ThemedText type="xl" fg="light"> + Swipe up to see more + </ThemedText> + </> + )} </View> </View> </View> @@ -68,22 +61,18 @@ const styles = StyleSheet.create({ headerWrapper: { height: 440, }, - buttonsContainer: { - height: 120, + headerPositionAdjust: { + // HACK align the header for the profilescreen transition -prf + paddingTop: 23, }, - buttons: { - flexDirection: 'row', - gap: 8, - paddingHorizontal: 14, - paddingTop: 16, + hintWrapper: { + height: 80, }, - button: { - flex: 2, + hint: { flexDirection: 'row', justifyContent: 'center', - paddingVertical: 12, - }, - buttonWide: { - flex: 3, + gap: 8, + paddingHorizontal: 14, + borderRadius: 6, }, }) |