diff options
author | Paul Frazee <pfrazee@gmail.com> | 2022-11-17 15:44:54 -0600 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2022-11-17 15:44:54 -0600 |
commit | b2160ae15952baf19d7375db2de77ce8d969b44d (patch) | |
tree | 52c6fcd706931cab4e247612f6a983b6f41da794 | |
parent | 2b98714548d585ff14dd09252233144f48b5f4b7 (diff) | |
download | voidsky-b2160ae15952baf19d7375db2de77ce8d969b44d.tar.zst |
Add new tab animation
-rw-r--r-- | src/state/models/navigation.ts | 6 | ||||
-rw-r--r-- | src/view/shell/mobile/Composer.tsx | 14 | ||||
-rw-r--r-- | src/view/shell/mobile/index.tsx | 45 |
3 files changed, 51 insertions, 14 deletions
diff --git a/src/state/models/navigation.ts b/src/state/models/navigation.ts index 553a897d4..533336a43 100644 --- a/src/state/models/navigation.ts +++ b/src/state/models/navigation.ts @@ -16,6 +16,7 @@ export class NavigationTabModel { id = genTabId() history: HistoryItem[] = [{url: '/', ts: Date.now()}] index = 0 + isNewTab = false constructor() { makeAutoObservable(this, { @@ -112,6 +113,10 @@ export class NavigationTabModel { this.current.title = title } + setIsNewTab(v: boolean) { + this.isNewTab = v + } + // persistence // = @@ -208,6 +213,7 @@ export class NavigationModel { newTab(url: string, title?: string) { const tab = new NavigationTabModel() tab.navigate(url, title) + tab.isNewTab = true this.tabs.push(tab) this.tabIndex = this.tabs.length - 1 } diff --git a/src/view/shell/mobile/Composer.tsx b/src/view/shell/mobile/Composer.tsx index 7a8d6681b..d31ae8949 100644 --- a/src/view/shell/mobile/Composer.tsx +++ b/src/view/shell/mobile/Composer.tsx @@ -1,12 +1,6 @@ import React, {useEffect} from 'react' import {observer} from 'mobx-react-lite' -import { - StyleSheet, - Text, - TouchableOpacity, - TouchableWithoutFeedback, - View, -} from 'react-native' +import {StyleSheet, View} from 'react-native' import Animated, { useSharedValue, useAnimatedStyle, @@ -14,13 +8,8 @@ import Animated, { interpolate, Easing, } from 'react-native-reanimated' -import {IconProp} from '@fortawesome/fontawesome-svg-core' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {HomeIcon, UserGroupIcon, BellIcon} from '../../lib/icons' import {ComposePost} from '../../com/composer/ComposePost' -import {useStores} from '../../../state' import {ComposerOpts} from '../../../state/models/shell-ui' -import {s, colors} from '../../lib/styles' export const Composer = observer( ({ @@ -36,7 +25,6 @@ export const Composer = observer( onPost?: ComposerOpts['onPost'] onClose: () => void }) => { - const store = useStores() const initInterp = useSharedValue<number>(0) useEffect(() => { diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx index 8f9b13eea..cad681bd6 100644 --- a/src/view/shell/mobile/index.tsx +++ b/src/view/shell/mobile/index.tsx @@ -17,6 +17,7 @@ import LinearGradient from 'react-native-linear-gradient' import {GestureDetector, Gesture} from 'react-native-gesture-handler' import {useSafeAreaInsets} from 'react-native-safe-area-context' import Animated, { + Easing, useSharedValue, useAnimatedStyle, withTiming, @@ -133,6 +134,8 @@ export const MobileShell: React.FC = observer(() => { const winDim = useWindowDimensions() const swipeGestureInterp = useSharedValue<number>(0) const tabMenuInterp = useSharedValue<number>(0) + const newTabInterp = useSharedValue<number>(0) + const [isRunningNewTabAnim, setIsRunningNewTabAnim] = useState(false) const colorScheme = useColorScheme() const safeAreaInsets = useSafeAreaInsets() const screenRenderDesc = constructScreenRenderDesc(store.nav) @@ -149,6 +152,8 @@ export const MobileShell: React.FC = observer(() => { const onPressNotifications = () => store.nav.navigate('/notifications') const onPressTabs = () => toggleTabsMenu(!isTabsSelectorActive) + // tab selector animation + // = const closeTabsSelector = () => setTabsSelectorActive(false) const toggleTabsMenu = (active: boolean) => { if (active) { @@ -168,6 +173,31 @@ export const MobileShell: React.FC = observer(() => { } }, [isTabsSelectorActive]) + // new tab animation + // = + useEffect(() => { + if (screenRenderDesc.hasNewTab && !isRunningNewTabAnim) { + setIsRunningNewTabAnim(true) + } + }, [screenRenderDesc.hasNewTab]) + useEffect(() => { + if (isRunningNewTabAnim) { + const reset = () => { + store.nav.tab.setIsNewTab(false) + setIsRunningNewTabAnim(false) + } + newTabInterp.value = withTiming( + 1, + {duration: 250, easing: Easing.out(Easing.exp)}, + () => runOnJS(reset)(), + ) + } else { + newTabInterp.value = 0 + } + }, [isRunningNewTabAnim]) + + // navigation swipes + // = const goBack = () => store.nav.tab.goBack() const swipeGesture = Gesture.Pan() .onUpdate(e => { @@ -201,6 +231,9 @@ export const MobileShell: React.FC = observer(() => { const tabMenuTransform = useAnimatedStyle(() => ({ transform: [{translateY: tabMenuInterp.value * -320}], })) + const newTabTransform = useAnimatedStyle(() => ({ + transform: [{scale: newTabInterp.value}], + })) if (!store.session.isAuthed) { return ( @@ -251,7 +284,11 @@ export const MobileShell: React.FC = observer(() => { s.flex1, styles.screen, current - ? [swipeTransform, tabMenuTransform] + ? [ + swipeTransform, + tabMenuTransform, + isRunningNewTabAnim ? newTabTransform : undefined, + ] : undefined, ]}> <Com @@ -326,11 +363,14 @@ type ScreenRenderDesc = MatchResult & { key: string current: boolean previous: boolean + isNewTab: boolean } function constructScreenRenderDesc(nav: NavigationModel): { icon: IconProp + hasNewTab: boolean screens: ScreenRenderDesc[] } { + let hasNewTab = false let icon: IconProp = 'magnifying-glass' let screens: ScreenRenderDesc[] = [] for (const tab of nav.tabs) { @@ -345,16 +385,19 @@ function constructScreenRenderDesc(nav: NavigationModel): { if (isCurrent) { icon = matchRes.icon } + hasNewTab = hasNewTab || tab.isNewTab return Object.assign(matchRes, { key: `t${tab.id}-s${screen.index}`, current: isCurrent, previous: isPrevious, + isNewTab: tab.isNewTab, }) as ScreenRenderDesc }) screens = screens.concat(parsedTabScreens) } return { icon, + hasNewTab, screens, } } |