From 18a38d83637eb7de58097dbfe50c5d5430d14b55 Mon Sep 17 00:00:00 2001 From: ekzyis Date: Mon, 7 Jul 2025 21:34:37 +0200 Subject: [PATCH] Refactor animations (#2261) * Fix fireworks not checking localStorage flag * Refactor animations * Don't import unused animations * Remove unused hook --------- Co-authored-by: k00b --- components/{ => animation}/fireworks.js | 2 +- .../{ => animation}/fireworks.module.css | 0 components/animation/index.js | 72 +++++++++++++++++++ components/{ => animation}/lightning.js | 15 ++-- components/{ => animation}/snow.js | 23 +++--- components/footer.js | 24 ++----- components/item-act.js | 14 ++-- components/nav/common.js | 16 ----- components/pay-bounty.js | 6 +- pages/_app.js | 6 +- pages/credits.js | 6 +- pages/rewards/index.js | 6 +- 12 files changed, 111 insertions(+), 79 deletions(-) rename components/{ => animation}/fireworks.js (99%) rename components/{ => animation}/fireworks.module.css (100%) create mode 100644 components/animation/index.js rename components/{ => animation}/lightning.js (92%) rename components/{ => animation}/snow.js (61%) diff --git a/components/fireworks.js b/components/animation/fireworks.js similarity index 99% rename from components/fireworks.js rename to components/animation/fireworks.js index db9b6b7b..fc378fb9 100644 --- a/components/fireworks.js +++ b/components/animation/fireworks.js @@ -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: () => {} diff --git a/components/fireworks.module.css b/components/animation/fireworks.module.css similarity index 100% rename from components/fireworks.module.css rename to components/animation/fireworks.module.css diff --git a/components/animation/index.js b/components/animation/index.js new file mode 100644 index 00000000..37c3fe4e --- /dev/null +++ b/components/animation/index.js @@ -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 ( + + + {children} + + + ) +} + +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 +} diff --git a/components/lightning.js b/components/animation/lightning.js similarity index 92% rename from components/lightning.js rename to components/animation/lightning.js index a9594ba8..a05403a0 100644 --- a/components/lightning.js +++ b/components/animation/lightning.js @@ -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, this.unstrike(state.bolts.length)} />] - } - }) - return true - } - return false + this.setState(state => { + return { + bolts: [...state.bolts, this.unstrike(state.bolts.length)} />] + } + }) } unstrike = (index) => { diff --git a/components/snow.js b/components/animation/snow.js similarity index 61% rename from components/snow.js rename to components/animation/snow.js index d05f16b6..a2cf4b0d 100644 --- a/components/snow.js +++ b/components/animation/snow.js @@ -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] = - } - 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] = } - return false + setStartIndex(i % MAX_FLAKES) + setFlakes(newFlakes) }, [setFlakes, startIndex]) return ( diff --git a/components/footer.js b/components/footer.js index dba99f5f..7d1b0fa0 100644 --- a/components/footer.js +++ b/components/footer.js @@ -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 = ( @@ -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 }) { - - + +
diff --git a/components/item-act.js b/components/item-act.js index 7c03e9db..27172d3b 100644 --- a/components/item-act.js +++ b/components/item-act.js @@ -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' ? {children} @@ -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 { diff --git a/components/nav/common.js b/components/nav/common.js index 0c6143fc..3f9bbb61 100644 --- a/components/nav/common.js +++ b/components/nav/common.js @@ -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 (
diff --git a/components/pay-bounty.js b/components/pay-bounty.js index e376c898..d960c316 100644 --- a/components/pay-bounty.js +++ b/components/pay-bounty.js @@ -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) { diff --git a/pages/_app.js b/pages/_app.js index efc15347..f9ede40c 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -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 } }) { - + @@ -129,7 +129,7 @@ export default function MyApp ({ Component, pageProps: { ...props } }) { - + diff --git a/pages/credits.js b/pages/credits.js index c0b05983..27d85d90 100644 --- a/pages/credits.js +++ b/pages/credits.js @@ -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() diff --git a/pages/rewards/index.js b/pages/rewards/index.js index b005f960..a8b654c1 100644 --- a/pages/rewards/index.js +++ b/pages/rewards/index.js @@ -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') } })