Add new tab animation
parent
2b98714548
commit
b2160ae159
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue