Add new tab animation

zio/stable
Paul Frazee 2022-11-17 15:44:54 -06:00
parent 2b98714548
commit b2160ae159
3 changed files with 51 additions and 14 deletions

View File

@ -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
}

View File

@ -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(() => {

View File

@ -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,
}
}