about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Frazee <pfrazee@gmail.com>2022-11-17 15:44:54 -0600
committerPaul Frazee <pfrazee@gmail.com>2022-11-17 15:44:54 -0600
commitb2160ae15952baf19d7375db2de77ce8d969b44d (patch)
tree52c6fcd706931cab4e247612f6a983b6f41da794
parent2b98714548d585ff14dd09252233144f48b5f4b7 (diff)
downloadvoidsky-b2160ae15952baf19d7375db2de77ce8d969b44d.tar.zst
Add new tab animation
-rw-r--r--src/state/models/navigation.ts6
-rw-r--r--src/view/shell/mobile/Composer.tsx14
-rw-r--r--src/view/shell/mobile/index.tsx45
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,
   }
 }