Refactor animations (#2261)
* Fix fireworks not checking localStorage flag * Refactor animations * Don't import unused animations * Remove unused hook --------- Co-authored-by: k00b <k00b@stacker.news>
This commit is contained in:
		
							parent
							
								
									3a27057781
								
							
						
					
					
						commit
						18a38d8363
					
				@ -7,7 +7,7 @@ import {
 | 
			
		||||
  setRangeValue,
 | 
			
		||||
  stringToRgb
 | 
			
		||||
} from 'tsparticles-engine'
 | 
			
		||||
import useDarkMode from './dark-mode'
 | 
			
		||||
import useDarkMode from '@/components/dark-mode'
 | 
			
		||||
 | 
			
		||||
export const FireworksContext = createContext({
 | 
			
		||||
  strike: () => {}
 | 
			
		||||
							
								
								
									
										72
									
								
								components/animation/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								components/animation/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
import { useCallback, useEffect, useState } from 'react'
 | 
			
		||||
import { useMe } from '@/components/me'
 | 
			
		||||
import { randInRange } from '@/lib/rand'
 | 
			
		||||
 | 
			
		||||
// import { LightningProvider, useLightning } from './lightning'
 | 
			
		||||
import { FireworksProvider, useFireworks } from './fireworks'
 | 
			
		||||
// import { SnowProvider, useSnow } from './snow'
 | 
			
		||||
 | 
			
		||||
const [SelectedAnimationProvider, useSelectedAnimation] = [
 | 
			
		||||
  // LightningProvider, useLightning
 | 
			
		||||
  FireworksProvider, useFireworks
 | 
			
		||||
  // SnowProvider, useSnow // TODO: the snow animation doesn't seem to work anymore
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
export function AnimationProvider ({ children }) {
 | 
			
		||||
  return (
 | 
			
		||||
    <SelectedAnimationProvider>
 | 
			
		||||
      <AnimationHooks>
 | 
			
		||||
        {children}
 | 
			
		||||
      </AnimationHooks>
 | 
			
		||||
    </SelectedAnimationProvider>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useAnimation () {
 | 
			
		||||
  const animate = useSelectedAnimation()
 | 
			
		||||
 | 
			
		||||
  return useCallback(() => {
 | 
			
		||||
    const should = window.localStorage.getItem('lnAnimate') || 'yes'
 | 
			
		||||
    if (should !== 'yes') return false
 | 
			
		||||
    animate()
 | 
			
		||||
    return true
 | 
			
		||||
  }, [animate])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useAnimationEnabled () {
 | 
			
		||||
  const [enabled, setEnabled] = useState(undefined)
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const enabled = window.localStorage.getItem('lnAnimate') || 'yes'
 | 
			
		||||
    setEnabled(enabled === 'yes')
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  const toggleEnabled = useCallback(() => {
 | 
			
		||||
    setEnabled(enabled => {
 | 
			
		||||
      const newEnabled = !enabled
 | 
			
		||||
      window.localStorage.setItem('lnAnimate', newEnabled ? 'yes' : 'no')
 | 
			
		||||
      return newEnabled
 | 
			
		||||
    })
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  return [enabled, toggleEnabled]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AnimationHooks ({ children }) {
 | 
			
		||||
  const { me } = useMe()
 | 
			
		||||
  const animate = useAnimation()
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (me || window.localStorage.getItem('striked') || window.localStorage.getItem('lnAnimated')) return
 | 
			
		||||
 | 
			
		||||
    const timeout = setTimeout(() => {
 | 
			
		||||
      const animated = animate()
 | 
			
		||||
      if (animated) {
 | 
			
		||||
        window.localStorage.setItem('lnAnimated', 'yep')
 | 
			
		||||
      }
 | 
			
		||||
    }, randInRange(3000, 10000))
 | 
			
		||||
    return () => clearTimeout(timeout)
 | 
			
		||||
  }, [me?.id, animate])
 | 
			
		||||
 | 
			
		||||
  return children
 | 
			
		||||
}
 | 
			
		||||
@ -13,16 +13,11 @@ export class LightningProvider extends React.Component {
 | 
			
		||||
   * @returns boolean indicating whether the strike actually happened, based on user preferences
 | 
			
		||||
   */
 | 
			
		||||
  strike = () => {
 | 
			
		||||
    const should = window.localStorage.getItem('lnAnimate') || 'yes'
 | 
			
		||||
    if (should === 'yes') {
 | 
			
		||||
      this.setState(state => {
 | 
			
		||||
        return {
 | 
			
		||||
          bolts: [...state.bolts, <Lightning key={state.bolts.length} onDone={() => this.unstrike(state.bolts.length)} />]
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
    this.setState(state => {
 | 
			
		||||
      return {
 | 
			
		||||
        bolts: [...state.bolts, <Lightning key={state.bolts.length} onDone={() => this.unstrike(state.bolts.length)} />]
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  unstrike = (index) => {
 | 
			
		||||
@ -11,21 +11,16 @@ export const SnowProvider = ({ children }) => {
 | 
			
		||||
  const [flakes, setFlakes] = useState(Array(1024))
 | 
			
		||||
 | 
			
		||||
  const snow = useCallback(() => {
 | 
			
		||||
    const should = window.localStorage.getItem('lnAnimate') || 'yes'
 | 
			
		||||
    if (should === 'yes') {
 | 
			
		||||
      // amount of flakes to add
 | 
			
		||||
      const n = Math.floor(randInRange(5, 30))
 | 
			
		||||
      const newFlakes = [...flakes]
 | 
			
		||||
      let i
 | 
			
		||||
      for (i = startIndex; i < (startIndex + n); ++i) {
 | 
			
		||||
        const key = startIndex + i
 | 
			
		||||
        newFlakes[i % MAX_FLAKES] = <Snow key={key} />
 | 
			
		||||
      }
 | 
			
		||||
      setStartIndex(i % MAX_FLAKES)
 | 
			
		||||
      setFlakes(newFlakes)
 | 
			
		||||
      return true
 | 
			
		||||
    // amount of flakes to add
 | 
			
		||||
    const n = Math.floor(randInRange(5, 30))
 | 
			
		||||
    const newFlakes = [...flakes]
 | 
			
		||||
    let i
 | 
			
		||||
    for (i = startIndex; i < (startIndex + n); ++i) {
 | 
			
		||||
      const key = startIndex + i
 | 
			
		||||
      newFlakes[i % MAX_FLAKES] = <Snow key={key} />
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
    setStartIndex(i % MAX_FLAKES)
 | 
			
		||||
    setFlakes(newFlakes)
 | 
			
		||||
  }, [setFlakes, startIndex])
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@ -12,10 +12,10 @@ import No from '@/svgs/no.svg'
 | 
			
		||||
import Bolt from '@/svgs/bolt.svg'
 | 
			
		||||
import Amboss from '@/svgs/amboss.svg'
 | 
			
		||||
import Mempool from '@/svgs/bimi.svg'
 | 
			
		||||
import { useEffect, useState } from 'react'
 | 
			
		||||
import Rewards from './footer-rewards'
 | 
			
		||||
import useDarkMode from './dark-mode'
 | 
			
		||||
import ActionTooltip from './action-tooltip'
 | 
			
		||||
import { useAnimationEnabled } from '@/components/animation'
 | 
			
		||||
 | 
			
		||||
const RssPopover = (
 | 
			
		||||
  <Popover>
 | 
			
		||||
@ -145,24 +145,10 @@ const LegalPopover = (
 | 
			
		||||
export default function Footer ({ links = true }) {
 | 
			
		||||
  const [darkMode, darkModeToggle] = useDarkMode()
 | 
			
		||||
 | 
			
		||||
  const [lightning, setLightning] = useState(undefined)
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setLightning(window.localStorage.getItem('lnAnimate') || 'yes')
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  const toggleLightning = () => {
 | 
			
		||||
    if (lightning === 'yes') {
 | 
			
		||||
      window.localStorage.setItem('lnAnimate', 'no')
 | 
			
		||||
      setLightning('no')
 | 
			
		||||
    } else {
 | 
			
		||||
      window.localStorage.setItem('lnAnimate', 'yes')
 | 
			
		||||
      setLightning('yes')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  const [animationEnabled, toggleAnimation] = useAnimationEnabled()
 | 
			
		||||
 | 
			
		||||
  const DarkModeIcon = darkMode ? Sun : Moon
 | 
			
		||||
  const LnIcon = lightning === 'yes' ? No : Bolt
 | 
			
		||||
  const LnIcon = animationEnabled ? No : Bolt
 | 
			
		||||
 | 
			
		||||
  const version = process.env.NEXT_PUBLIC_COMMIT_HASH
 | 
			
		||||
 | 
			
		||||
@ -175,8 +161,8 @@ export default function Footer ({ links = true }) {
 | 
			
		||||
              <ActionTooltip notForm overlayText={`${darkMode ? 'disable' : 'enable'} dark mode`}>
 | 
			
		||||
                <DarkModeIcon onClick={darkModeToggle} width={20} height={20} className='fill-grey theme' suppressHydrationWarning />
 | 
			
		||||
              </ActionTooltip>
 | 
			
		||||
              <ActionTooltip notForm overlayText={`${lightning === 'yes' ? 'disable' : 'enable'} lightning animations`}>
 | 
			
		||||
                <LnIcon onClick={toggleLightning} width={20} height={20} className='ms-2 fill-grey theme' suppressHydrationWarning />
 | 
			
		||||
              <ActionTooltip notForm overlayText={`${animationEnabled ? 'disable' : 'enable'} lightning animations`}>
 | 
			
		||||
                <LnIcon onClick={toggleAnimation} width={20} height={20} className='ms-2 fill-grey theme' suppressHydrationWarning />
 | 
			
		||||
              </ActionTooltip>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className='mb-0' style={{ fontWeight: 500 }}>
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ import { ACT_MUTATION } from '@/fragments/paidAction'
 | 
			
		||||
import { meAnonSats } from '@/lib/apollo'
 | 
			
		||||
import { BoostItemInput } from './adv-post-form'
 | 
			
		||||
import { useSendWallets } from '@/wallets/index'
 | 
			
		||||
import { useFireworks } from './fireworks'
 | 
			
		||||
import { useAnimation } from '@/components/animation'
 | 
			
		||||
 | 
			
		||||
const defaultTips = [100, 1000, 10_000, 100_000]
 | 
			
		||||
 | 
			
		||||
@ -96,7 +96,7 @@ export default function ItemAct ({ onClose, item, act = 'TIP', step, children, a
 | 
			
		||||
  }, [onClose, item.id])
 | 
			
		||||
 | 
			
		||||
  const actor = useAct()
 | 
			
		||||
  const strike = useFireworks()
 | 
			
		||||
  const animate = useAnimation()
 | 
			
		||||
 | 
			
		||||
  const onSubmit = useCallback(async ({ amount }) => {
 | 
			
		||||
    if (abortSignal && zapUndoTrigger({ me, amount })) {
 | 
			
		||||
@ -111,7 +111,7 @@ export default function ItemAct ({ onClose, item, act = 'TIP', step, children, a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const onPaid = () => {
 | 
			
		||||
      strike()
 | 
			
		||||
      animate()
 | 
			
		||||
      onClose?.()
 | 
			
		||||
      if (!me) setItemMeAnonSats({ id: item.id, amount })
 | 
			
		||||
    }
 | 
			
		||||
@ -143,7 +143,7 @@ export default function ItemAct ({ onClose, item, act = 'TIP', step, children, a
 | 
			
		||||
    })
 | 
			
		||||
    if (error) throw error
 | 
			
		||||
    addCustomTip(Number(amount))
 | 
			
		||||
  }, [me, actor, wallets.length, act, item.id, onClose, abortSignal, strike])
 | 
			
		||||
  }, [me, actor, wallets.length, act, item.id, onClose, abortSignal, animate])
 | 
			
		||||
 | 
			
		||||
  return act === 'BOOST'
 | 
			
		||||
    ? <BoostForm step={step} onSubmit={onSubmit} item={item} inputRef={inputRef} act={act}>{children}</BoostForm>
 | 
			
		||||
@ -300,7 +300,7 @@ export function useAct ({ query = ACT_MUTATION, ...options } = {}) {
 | 
			
		||||
export function useZap () {
 | 
			
		||||
  const wallets = useSendWallets()
 | 
			
		||||
  const act = useAct()
 | 
			
		||||
  const strike = useFireworks()
 | 
			
		||||
  const animate = useAnimation()
 | 
			
		||||
  const toaster = useToast()
 | 
			
		||||
 | 
			
		||||
  return useCallback(async ({ item, me, abortSignal }) => {
 | 
			
		||||
@ -314,7 +314,7 @@ export function useZap () {
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      await abortSignal.pause({ me, amount: sats })
 | 
			
		||||
      strike()
 | 
			
		||||
      animate()
 | 
			
		||||
      // batch zaps if wallet is enabled or using fee credits so they can be executed serially in a single request
 | 
			
		||||
      const { error } = await act({ variables, optimisticResponse, context: { batch: wallets.length > 0 || me?.privates?.sats > sats } })
 | 
			
		||||
      if (error) throw error
 | 
			
		||||
@ -327,7 +327,7 @@ export function useZap () {
 | 
			
		||||
      // but right now this toast is noisy for optimistic zaps
 | 
			
		||||
      console.error(error)
 | 
			
		||||
    }
 | 
			
		||||
  }, [act, toaster, strike, wallets])
 | 
			
		||||
  }, [act, toaster, animate, wallets])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class ActCanceledError extends Error {
 | 
			
		||||
 | 
			
		||||
@ -14,8 +14,6 @@ import { abbrNum } from '../../lib/format'
 | 
			
		||||
import { useServiceWorker } from '../serviceworker'
 | 
			
		||||
import { signOut } from 'next-auth/react'
 | 
			
		||||
import Badges from '../badge'
 | 
			
		||||
import { randInRange } from '../../lib/rand'
 | 
			
		||||
import { useFireworks } from '../fireworks'
 | 
			
		||||
import LightningIcon from '../../svgs/bolt.svg'
 | 
			
		||||
import SearchIcon from '../../svgs/search-line.svg'
 | 
			
		||||
import classNames from 'classnames'
 | 
			
		||||
@ -400,20 +398,6 @@ export function LoginButtons ({ handleClose }) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function AnonDropdown ({ path }) {
 | 
			
		||||
  const strike = useFireworks()
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!window.localStorage.getItem('striked')) {
 | 
			
		||||
      const to = setTimeout(() => {
 | 
			
		||||
        const striked = strike()
 | 
			
		||||
        if (striked) {
 | 
			
		||||
          window.localStorage.setItem('striked', 'yep')
 | 
			
		||||
        }
 | 
			
		||||
      }, randInRange(3000, 10000))
 | 
			
		||||
      return () => clearTimeout(to)
 | 
			
		||||
    }
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className='position-relative'>
 | 
			
		||||
      <Dropdown className={styles.dropdown} align='end' autoClose>
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ import { numWithUnits } from '@/lib/format'
 | 
			
		||||
import { useShowModal } from './modal'
 | 
			
		||||
import { useRoot } from './root'
 | 
			
		||||
import { ActCanceledError, useAct } from './item-act'
 | 
			
		||||
import { useFireworks } from './fireworks'
 | 
			
		||||
import { useAnimation } from '@/components/animation'
 | 
			
		||||
import { useToast } from './toast'
 | 
			
		||||
import { useSendWallets } from '@/wallets/index'
 | 
			
		||||
import { Form, SubmitButton } from './form'
 | 
			
		||||
@ -48,7 +48,7 @@ export default function PayBounty ({ children, item }) {
 | 
			
		||||
  const { me } = useMe()
 | 
			
		||||
  const showModal = useShowModal()
 | 
			
		||||
  const root = useRoot()
 | 
			
		||||
  const strike = useFireworks()
 | 
			
		||||
  const animate = useAnimation()
 | 
			
		||||
  const toaster = useToast()
 | 
			
		||||
  const wallets = useSendWallets()
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,7 @@ export default function PayBounty ({ children, item }) {
 | 
			
		||||
 | 
			
		||||
  const handlePayBounty = async onCompleted => {
 | 
			
		||||
    try {
 | 
			
		||||
      strike()
 | 
			
		||||
      animate()
 | 
			
		||||
      const { error } = await act({ onCompleted })
 | 
			
		||||
      if (error) throw error
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ import { useRouter } from 'next/dist/client/router'
 | 
			
		||||
import { useCallback, useEffect } from 'react'
 | 
			
		||||
import { ShowModalProvider } from '@/components/modal'
 | 
			
		||||
import ErrorBoundary from '@/components/error-boundary'
 | 
			
		||||
import { FireworksProvider } from '@/components/fireworks'
 | 
			
		||||
import { AnimationProvider } from '@/components/animation'
 | 
			
		||||
import { ToastProvider } from '@/components/toast'
 | 
			
		||||
import { ServiceWorkerProvider } from '@/components/serviceworker'
 | 
			
		||||
import { SSR } from '@/lib/constants'
 | 
			
		||||
@ -116,7 +116,7 @@ export default function MyApp ({ Component, pageProps: { ...props } }) {
 | 
			
		||||
                  <WebLnProvider>
 | 
			
		||||
                    <ServiceWorkerProvider>
 | 
			
		||||
                      <PriceProvider price={price}>
 | 
			
		||||
                        <FireworksProvider>
 | 
			
		||||
                        <AnimationProvider>
 | 
			
		||||
                          <ToastProvider>
 | 
			
		||||
                            <ShowModalProvider>
 | 
			
		||||
                              <BlockHeightProvider blockHeight={blockHeight}>
 | 
			
		||||
@ -129,7 +129,7 @@ export default function MyApp ({ Component, pageProps: { ...props } }) {
 | 
			
		||||
                              </BlockHeightProvider>
 | 
			
		||||
                            </ShowModalProvider>
 | 
			
		||||
                          </ToastProvider>
 | 
			
		||||
                        </FireworksProvider>
 | 
			
		||||
                        </AnimationProvider>
 | 
			
		||||
                      </PriceProvider>
 | 
			
		||||
                    </ServiceWorkerProvider>
 | 
			
		||||
                  </WebLnProvider>
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import { getGetServerSideProps } from '@/api/ssrApollo'
 | 
			
		||||
import CCInfo from '@/components/info/cc'
 | 
			
		||||
import { Form, Input, SubmitButton } from '@/components/form'
 | 
			
		||||
import { CenterLayout } from '@/components/layout'
 | 
			
		||||
import { useFireworks } from '@/components/fireworks'
 | 
			
		||||
import { useAnimation } from '@/components/animation'
 | 
			
		||||
import { useMe } from '@/components/me'
 | 
			
		||||
import { useShowModal } from '@/components/modal'
 | 
			
		||||
import { usePaidMutation } from '@/components/use-paid-mutation'
 | 
			
		||||
@ -76,7 +76,7 @@ function WithdrawButton ({ className }) {
 | 
			
		||||
 | 
			
		||||
export function BuyCreditsButton ({ className }) {
 | 
			
		||||
  const showModal = useShowModal()
 | 
			
		||||
  const strike = useFireworks()
 | 
			
		||||
  const animate = useAnimation()
 | 
			
		||||
  const [buyCredits] = usePaidMutation(BUY_CREDITS)
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@ -94,7 +94,7 @@ export function BuyCreditsButton ({ className }) {
 | 
			
		||||
                  credits: Number(amount)
 | 
			
		||||
                },
 | 
			
		||||
                onCompleted: () => {
 | 
			
		||||
                  strike()
 | 
			
		||||
                  animate()
 | 
			
		||||
                }
 | 
			
		||||
              })
 | 
			
		||||
              onClose()
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ import { useShowModal } from '@/components/modal'
 | 
			
		||||
import dynamic from 'next/dynamic'
 | 
			
		||||
import { FAST_POLL_INTERVAL, SSR } from '@/lib/constants'
 | 
			
		||||
import { useToast } from '@/components/toast'
 | 
			
		||||
import { useFireworks } from '@/components/fireworks'
 | 
			
		||||
import { useAnimation } from '@/components/animation'
 | 
			
		||||
import { Col, Row } from 'react-bootstrap'
 | 
			
		||||
import { useData } from '@/components/use-data'
 | 
			
		||||
import { GrowthPieChartSkeleton } from '@/components/charts-skeletons'
 | 
			
		||||
@ -133,7 +133,7 @@ export default function Rewards ({ ssrData }) {
 | 
			
		||||
export function DonateButton () {
 | 
			
		||||
  const showModal = useShowModal()
 | 
			
		||||
  const toaster = useToast()
 | 
			
		||||
  const strike = useFireworks()
 | 
			
		||||
  const animate = useAnimation()
 | 
			
		||||
  const [donateToRewards] = usePaidMutation(DONATE)
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@ -151,7 +151,7 @@ export function DonateButton () {
 | 
			
		||||
                  sats: Number(amount)
 | 
			
		||||
                },
 | 
			
		||||
                onCompleted: () => {
 | 
			
		||||
                  strike()
 | 
			
		||||
                  animate()
 | 
			
		||||
                  toaster.success('donated')
 | 
			
		||||
                }
 | 
			
		||||
              })
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user