From 96fd27157336ef1461f425c516d08e752f6a952c Mon Sep 17 00:00:00 2001
From: Keyan <34140557+huumn@users.noreply.github.com>
Date: Fri, 4 Jul 2025 01:14:45 -0500
Subject: [PATCH] add fireworks (#2258)
* add fireworks
* fix weird search background color due to canvas overlay
* prevent going off small screens
---
components/fireworks.js | 273 ++++++++++++++++++++++++++++++++
components/fireworks.module.css | 8 +
components/item-act.js | 6 +-
components/layout.module.css | 1 +
components/nav/common.js | 10 +-
components/pay-bounty.js | 4 +-
components/search.module.css | 1 -
package-lock.json | 228 ++++++++++++++++++++++++++
package.json | 3 +
pages/_app.js | 6 +-
pages/credits.js | 4 +-
pages/rewards/index.js | 4 +-
styles/globals.scss | 10 ++
13 files changed, 541 insertions(+), 17 deletions(-)
create mode 100644 components/fireworks.js
create mode 100644 components/fireworks.module.css
diff --git a/components/fireworks.js b/components/fireworks.js
new file mode 100644
index 00000000..0a4080ec
--- /dev/null
+++ b/components/fireworks.js
@@ -0,0 +1,273 @@
+import { useCallback, createContext, useContext, useState, useEffect } from 'react'
+import Particles from 'react-particles'
+import { loadFireworksPreset } from 'tsparticles-preset-fireworks'
+import styles from './fireworks.module.css'
+import {
+ rgbToHsl,
+ setRangeValue,
+ stringToRgb
+} from 'tsparticles-engine'
+import useDarkMode from './dark-mode'
+
+export const FireworksContext = createContext({
+ strike: () => {}
+})
+
+export const FireworksConsumer = FireworksContext.Consumer
+export function useFireworks () {
+ const { strike } = useContext(FireworksContext)
+ return strike
+}
+
+export function FireworksProvider ({ children }) {
+ const [cont, setCont] = useState()
+ const [context, setContext] = useState({ strike: () => {} })
+ const [darkMode] = useDarkMode()
+
+ useEffect(() => {
+ setContext({
+ strike: () => {
+ cont?.addEmitter(
+ {
+ direction: 'top',
+ life: {
+ count: 1,
+ duration: 0.1,
+ delay: 0.1
+ },
+ rate: {
+ delay: 0,
+ quantity: 1
+ },
+ size: {
+ width: 10,
+ height: 0
+ },
+ position: {
+ y: 100,
+ x: 50
+ }
+ })
+ return true
+ }
+ })
+ }, [cont])
+
+ const particlesLoaded = useCallback((container) => {
+ setCont(container)
+ }, [])
+
+ const particlesInit = useCallback(async engine => {
+ // you can initiate the tsParticles instance (engine) here, adding custom shapes or presets
+ // this loads the tsparticles package bundle, it's the easiest method for getting everything ready
+ // starting from v2 you can add only the features you need reducing the bundle size
+ await loadFireworksPreset(engine)
+ }, [])
+
+ return (
+
+
+ {children}
+
+ )
+}
+
+const fixRange = (value, min, max) => {
+ const diffSMax = value.max > max ? value.max - max : 0
+ let res = setRangeValue(value)
+
+ if (diffSMax) {
+ res = setRangeValue(value.min - diffSMax, max)
+ }
+
+ const diffSMin = value.min < min ? value.min : 0
+
+ if (diffSMin) {
+ res = setRangeValue(0, value.max + diffSMin)
+ }
+
+ return res
+}
+
+const fireworksOptions = ['#ff595e', '#ffca3a', '#8ac926', '#1982c4', '#6a4c93']
+ .map((color) => {
+ const rgb = stringToRgb(color)
+
+ if (!rgb) {
+ return undefined
+ }
+
+ const hsl = rgbToHsl(rgb)
+ const sRange = fixRange({ min: hsl.s - 30, max: hsl.s + 30 }, 0, 100)
+ const lRange = fixRange({ min: hsl.l - 30, max: hsl.l + 30 }, 0, 100)
+
+ return {
+ color: {
+ value: {
+ h: hsl.h,
+ s: sRange,
+ l: lRange
+ }
+ },
+ stroke: {
+ width: 0
+ },
+ number: {
+ value: 0
+ },
+ opacity: {
+ value: {
+ min: 0.1,
+ max: 1
+ },
+ animation: {
+ enable: true,
+ speed: 0.7,
+ sync: false,
+ startValue: 'max',
+ destroy: 'min'
+ }
+ },
+ shape: {
+ type: 'circle'
+ },
+ size: {
+ value: { min: 1, max: 2 },
+ animation: {
+ enable: true,
+ speed: 5,
+ count: 1,
+ sync: false,
+ startValue: 'min',
+ destroy: 'none'
+ }
+ },
+ life: {
+ count: 1,
+ duration: {
+ value: {
+ min: 1,
+ max: 2
+ }
+ }
+ },
+ move: {
+ decay: { min: 0.075, max: 0.1 },
+ enable: true,
+ gravity: {
+ enable: true,
+ inverse: false,
+ acceleration: 5
+ },
+ speed: { min: 5, max: 15 },
+ direction: 'none',
+ outMode: {
+ top: 'destroy',
+ default: 'bounce'
+ }
+ }
+ }
+ })
+ .filter((t) => t !== undefined)
+
+const particlesOptions = (theme) => ({
+ number: {
+ value: 0
+ },
+ destroy: {
+ mode: 'split',
+ bounds: {
+ top: { min: 5, max: 40 }
+ },
+ split: {
+ sizeOffset: false,
+ count: 1,
+ factor: {
+ value: 0.333333
+ },
+ rate: {
+ value: { min: 75, max: 150 }
+ },
+ particles: fireworksOptions
+ }
+ },
+ life: {
+ count: 1
+ },
+ shape: {
+ type: 'line'
+ },
+ size: {
+ value: {
+ min: 0.1,
+ max: 50
+ },
+ animation: {
+ enable: true,
+ sync: true,
+ speed: 90,
+ startValue: 'max',
+ destroy: 'min'
+ }
+ },
+ rotate: {
+ path: true
+ },
+ stroke: {
+ color: {
+ value: theme === 'dark' ? '#fff' : '#aaa'
+ },
+ width: 1
+ },
+ move: {
+ enable: true,
+ gravity: {
+ acceleration: 15,
+ enable: true,
+ inverse: true,
+ maxSpeed: 100
+ },
+ speed: {
+ min: 10,
+ max: 20
+ },
+ outModes: {
+ default: 'split',
+ top: 'none'
+ },
+ trail: {
+ fillColor: theme === 'dark' ? '#000' : '#f5f5f7',
+ enable: true,
+ length: 10
+ }
+ }
+})
+
+const darkOptions = {
+ fullScreen: { enable: true, zIndex: -1 },
+ detectRetina: true,
+ background: {
+ color: '#000',
+ opacity: 0
+ },
+ fpsLimit: 120,
+ emitters: [],
+ particles: particlesOptions('dark')
+}
+
+const lightOptions = {
+ fullScreen: { enable: true, zIndex: -1 },
+ detectRetina: true,
+ background: {
+ color: '#fff',
+ opacity: 0
+ },
+ fpsLimit: 120,
+ emitters: [],
+ particles: particlesOptions('light')
+}
diff --git a/components/fireworks.module.css b/components/fireworks.module.css
new file mode 100644
index 00000000..b3bdb354
--- /dev/null
+++ b/components/fireworks.module.css
@@ -0,0 +1,8 @@
+.fireworks {
+ z-index: 0;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+}
\ No newline at end of file
diff --git a/components/item-act.js b/components/item-act.js
index 3cc23a57..7c03e9db 100644
--- a/components/item-act.js
+++ b/components/item-act.js
@@ -6,7 +6,6 @@ import { useMe } from './me'
import UpBolt from '@/svgs/bolt.svg'
import { amountSchema, boostSchema } from '@/lib/validate'
import { useToast } from './toast'
-import { useLightning } from './lightning'
import { nextTip, defaultTipIncludingRandom } from './upvote'
import { ZAP_UNDO_DELAY_MS } from '@/lib/constants'
import { usePaidMutation } from './use-paid-mutation'
@@ -14,6 +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'
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 = useLightning()
+ const strike = useFireworks()
const onSubmit = useCallback(async ({ amount }) => {
if (abortSignal && zapUndoTrigger({ me, amount })) {
@@ -300,7 +300,7 @@ export function useAct ({ query = ACT_MUTATION, ...options } = {}) {
export function useZap () {
const wallets = useSendWallets()
const act = useAct()
- const strike = useLightning()
+ const strike = useFireworks()
const toaster = useToast()
return useCallback(async ({ item, me, abortSignal }) => {
diff --git a/components/layout.module.css b/components/layout.module.css
index 798a085d..50ae67ed 100644
--- a/components/layout.module.css
+++ b/components/layout.module.css
@@ -5,6 +5,7 @@
height: 100%;
align-items: center;
margin: auto;
+ z-index: 0;
}
.content {
diff --git a/components/nav/common.js b/components/nav/common.js
index 89df6c7e..0c6143fc 100644
--- a/components/nav/common.js
+++ b/components/nav/common.js
@@ -15,7 +15,7 @@ import { useServiceWorker } from '../serviceworker'
import { signOut } from 'next-auth/react'
import Badges from '../badge'
import { randInRange } from '../../lib/rand'
-import { useLightning } from '../lightning'
+import { useFireworks } from '../fireworks'
import LightningIcon from '../../svgs/bolt.svg'
import SearchIcon from '../../svgs/search-line.svg'
import classNames from 'classnames'
@@ -400,13 +400,15 @@ export function LoginButtons ({ handleClose }) {
}
export function AnonDropdown ({ path }) {
- const strike = useLightning()
+ const strike = useFireworks()
useEffect(() => {
if (!window.localStorage.getItem('striked')) {
const to = setTimeout(() => {
- strike()
- window.localStorage.setItem('striked', 'yep')
+ const striked = strike()
+ if (striked) {
+ window.localStorage.setItem('striked', 'yep')
+ }
}, randInRange(3000, 10000))
return () => clearTimeout(to)
}
diff --git a/components/pay-bounty.js b/components/pay-bounty.js
index 9619d43b..e376c898 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 { useLightning } from './lightning'
+import { useFireworks } from './fireworks'
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 = useLightning()
+ const strike = useFireworks()
const toaster = useToast()
const wallets = useSendWallets()
diff --git a/components/search.module.css b/components/search.module.css
index 89b45237..f42979bf 100644
--- a/components/search.module.css
+++ b/components/search.module.css
@@ -1,5 +1,4 @@
.searchSection {
- background-color: var(--bs-body-bg);
z-index: 1;
}
diff --git a/package-lock.json b/package-lock.json
index 22c5ae19..10912eb4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -77,6 +77,7 @@
"react-dom": "^18.3.1",
"react-ios-pwa-prompt": "^1.8.4",
"react-markdown": "^9.0.1",
+ "react-particles": "^2.12.2",
"react-string-replace": "^1.1.1",
"react-syntax-highlighter": "^15.5.0",
"react-textarea-autosize": "^8.5.4",
@@ -92,6 +93,8 @@
"source-map": "^0.8.0-beta.0",
"textarea-caret": "^3.1.0",
"tldts": "^6.1.51",
+ "tsparticles-engine": "^2.12.0",
+ "tsparticles-preset-fireworks": "^2.12.0",
"tsx": "^4.19.1",
"twitter-api-v2": "^1.22.0",
"unist-util-visit": "^5.0.0",
@@ -17561,6 +17564,33 @@
"@types/unist": "*"
}
},
+ "node_modules/react-particles": {
+ "version": "2.12.2",
+ "resolved": "https://registry.npmjs.org/react-particles/-/react-particles-2.12.2.tgz",
+ "integrity": "sha512-Bo9DdrBRPFy8uiT7BA8P36Rdmz6GhB/RG9kkWUyZGIsS8AxWyuUjpVxw9Lr23K0LVE2aenAJ0vnqEbbLDpBgQw==",
+ "deprecated": "@tsparticles/react is the new version, please use that",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/matteobruni"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/tsparticles"
+ },
+ {
+ "type": "buymeacoffee",
+ "url": "https://www.buymeacoffee.com/matteobruni"
+ }
+ ],
+ "hasInstallScript": true,
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ },
+ "peerDependencies": {
+ "react": ">=16"
+ }
+ },
"node_modules/react-smooth": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz",
@@ -19784,6 +19814,204 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA=="
},
+ "node_modules/tsparticles-basic": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-basic/-/tsparticles-basic-2.12.0.tgz",
+ "integrity": "sha512-pN6FBpL0UsIUXjYbiui5+IVsbIItbQGOlwyGV55g6IYJBgdTNXgFX0HRYZGE9ZZ9psEXqzqwLM37zvWnb5AG9g==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/matteobruni"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/tsparticles"
+ },
+ {
+ "type": "buymeacoffee",
+ "url": "https://www.buymeacoffee.com/matteobruni"
+ }
+ ],
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0",
+ "tsparticles-move-base": "^2.12.0",
+ "tsparticles-shape-circle": "^2.12.0",
+ "tsparticles-updater-color": "^2.12.0",
+ "tsparticles-updater-opacity": "^2.12.0",
+ "tsparticles-updater-out-modes": "^2.12.0",
+ "tsparticles-updater-size": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-engine": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-engine/-/tsparticles-engine-2.12.0.tgz",
+ "integrity": "sha512-ZjDIYex6jBJ4iMc9+z0uPe7SgBnmb6l+EJm83MPIsOny9lPpetMsnw/8YJ3xdxn8hV+S3myTpTN1CkOVmFv0QQ==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/matteobruni"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/tsparticles"
+ },
+ {
+ "type": "buymeacoffee",
+ "url": "https://www.buymeacoffee.com/matteobruni"
+ }
+ ],
+ "hasInstallScript": true
+ },
+ "node_modules/tsparticles-move-base": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-move-base/-/tsparticles-move-base-2.12.0.tgz",
+ "integrity": "sha512-oSogCDougIImq+iRtIFJD0YFArlorSi8IW3HD2gO3USkH+aNn3ZqZNTqp321uB08K34HpS263DTbhLHa/D6BWw==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-plugin-emitters": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-plugin-emitters/-/tsparticles-plugin-emitters-2.12.0.tgz",
+ "integrity": "sha512-fbskYnaXWXivBh9KFReVCfqHdhbNQSK2T+fq2qcGEWpwtDdgujcaS1k2Q/xjZnWNMfVesik4IrqspcL51gNdSA==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-plugin-sounds": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-plugin-sounds/-/tsparticles-plugin-sounds-2.12.0.tgz",
+ "integrity": "sha512-Jm/Mdddq9E9VdHtAbYmv3pgtHQ3nBSbXN+n2axqm6vACgrE/JA4tgLZyuknns0scxibvqh/7zFxkOSa+4ucYuQ==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-preset-fireworks": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-preset-fireworks/-/tsparticles-preset-fireworks-2.12.0.tgz",
+ "integrity": "sha512-8Y1F2YZH/LkqPVniEmfArHe2qyuJwzTiWmX1of9bClyA+Xz7p219JRednNp0QaAiZeJ+GS9X7hlBa4r3m1kPiw==",
+ "deprecated": "@tsparticles/preset-fireworks is the new package, please use that",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/matteobruni"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/tsparticles"
+ },
+ {
+ "type": "buymeacoffee",
+ "url": "https://www.buymeacoffee.com/matteobruni"
+ }
+ ],
+ "dependencies": {
+ "tsparticles-basic": "^2.12.0",
+ "tsparticles-engine": "^2.12.0",
+ "tsparticles-plugin-emitters": "^2.12.0",
+ "tsparticles-plugin-sounds": "^2.12.0",
+ "tsparticles-shape-line": "^2.12.0",
+ "tsparticles-updater-destroy": "^2.12.0",
+ "tsparticles-updater-life": "^2.12.0",
+ "tsparticles-updater-rotate": "^2.12.0",
+ "tsparticles-updater-stroke-color": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-shape-circle": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-shape-circle/-/tsparticles-shape-circle-2.12.0.tgz",
+ "integrity": "sha512-L6OngbAlbadG7b783x16ns3+SZ7i0SSB66M8xGa5/k+YcY7zm8zG0uPt1Hd+xQDR2aNA3RngVM10O23/Lwk65Q==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-shape-line": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-shape-line/-/tsparticles-shape-line-2.12.0.tgz",
+ "integrity": "sha512-RcpKmmpKlk+R8mM5wA2v64Lv1jvXtU4SrBDv3vbdRodKbKaWGGzymzav1Q0hYyDyUZgplEK/a5ZwrfrOwmgYGA==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-updater-color": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-updater-color/-/tsparticles-updater-color-2.12.0.tgz",
+ "integrity": "sha512-KcG3a8zd0f8CTiOrylXGChBrjhKcchvDJjx9sp5qpwQK61JlNojNCU35xoaSk2eEHeOvFjh0o3CXWUmYPUcBTQ==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-updater-destroy": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-updater-destroy/-/tsparticles-updater-destroy-2.12.0.tgz",
+ "integrity": "sha512-6NN3dJhxACvzbIGL4dADbYQSZJmdHfwjujj1uvnxdMbb2x8C/AZzGxiN33smo4jkrZ5VLEWZWCJPJ8aOKjQ2Sg==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-updater-life": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-updater-life/-/tsparticles-updater-life-2.12.0.tgz",
+ "integrity": "sha512-J7RWGHAZkowBHpcLpmjKsxwnZZJ94oGEL2w+wvW1/+ZLmAiFFF6UgU0rHMC5CbHJT4IPx9cbkYMEHsBkcRJ0Bw==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-updater-opacity": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-updater-opacity/-/tsparticles-updater-opacity-2.12.0.tgz",
+ "integrity": "sha512-YUjMsgHdaYi4HN89LLogboYcCi1o9VGo21upoqxq19yRy0hRCtx2NhH22iHF/i5WrX6jqshN0iuiiNefC53CsA==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-updater-out-modes": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-updater-out-modes/-/tsparticles-updater-out-modes-2.12.0.tgz",
+ "integrity": "sha512-owBp4Gk0JNlSrmp12XVEeBroDhLZU+Uq3szbWlHGSfcR88W4c/0bt0FiH5bHUqORIkw+m8O56hCjbqwj69kpOQ==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-updater-rotate": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-updater-rotate/-/tsparticles-updater-rotate-2.12.0.tgz",
+ "integrity": "sha512-waOFlGFmEZOzsQg4C4VSejNVXGf4dMf3fsnQrEROASGf1FCd8B6WcZau7JtXSTFw0OUGuk8UGz36ETWN72DkCw==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-updater-size": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-updater-size/-/tsparticles-updater-size-2.12.0.tgz",
+ "integrity": "sha512-B0yRdEDd/qZXCGDL/ussHfx5YJ9UhTqNvmS5X2rR2hiZhBAE2fmsXLeWkdtF2QusjPeEqFDxrkGiLOsh6poqRA==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
+ "node_modules/tsparticles-updater-stroke-color": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/tsparticles-updater-stroke-color/-/tsparticles-updater-stroke-color-2.12.0.tgz",
+ "integrity": "sha512-MPou1ZDxsuVq6SN1fbX+aI5yrs6FyP2iPCqqttpNbWyL+R6fik1rL0ab/x02B57liDXqGKYomIbBQVP3zUTW1A==",
+ "deprecated": "starting from tsparticles v3 the packages are now moved to @tsparticles/package-name instead of tsparticles-package-name",
+ "dependencies": {
+ "tsparticles-engine": "^2.12.0"
+ }
+ },
"node_modules/tstl": {
"version": "2.5.16",
"resolved": "https://registry.npmjs.org/tstl/-/tstl-2.5.16.tgz",
diff --git a/package.json b/package.json
index d27c39a5..3472faa9 100644
--- a/package.json
+++ b/package.json
@@ -82,6 +82,7 @@
"react-dom": "^18.3.1",
"react-ios-pwa-prompt": "^1.8.4",
"react-markdown": "^9.0.1",
+ "react-particles": "^2.12.2",
"react-string-replace": "^1.1.1",
"react-syntax-highlighter": "^15.5.0",
"react-textarea-autosize": "^8.5.4",
@@ -97,6 +98,8 @@
"source-map": "^0.8.0-beta.0",
"textarea-caret": "^3.1.0",
"tldts": "^6.1.51",
+ "tsparticles-engine": "^2.12.0",
+ "tsparticles-preset-fireworks": "^2.12.0",
"tsx": "^4.19.1",
"twitter-api-v2": "^1.22.0",
"unist-util-visit": "^5.0.0",
diff --git a/pages/_app.js b/pages/_app.js
index 1c2ee9b3..34d9fc86 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 { LightningProvider } from '@/components/lightning'
+import { FireworksProvider } from '@/components/fireworks'
import { ToastProvider } from '@/components/toast'
import { ServiceWorkerProvider } from '@/components/serviceworker'
import { SSR } from '@/lib/constants'
@@ -118,7 +118,7 @@ export default function MyApp ({ Component, pageProps: { ...props } }) {
-
+
@@ -131,7 +131,7 @@ export default function MyApp ({ Component, pageProps: { ...props } }) {
-
+
diff --git a/pages/credits.js b/pages/credits.js
index 5f68da04..c0b05983 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 { useLightning } from '@/components/lightning'
+import { useFireworks } from '@/components/fireworks'
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 = useLightning()
+ const strike = useFireworks()
const [buyCredits] = usePaidMutation(BUY_CREDITS)
return (
diff --git a/pages/rewards/index.js b/pages/rewards/index.js
index 0d36401c..b005f960 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 { useLightning } from '@/components/lightning'
+import { useFireworks } from '@/components/fireworks'
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 = useLightning()
+ const strike = useFireworks()
const [donateToRewards] = usePaidMutation(DONATE)
return (
diff --git a/styles/globals.scss b/styles/globals.scss
index b7923c82..30513261 100644
--- a/styles/globals.scss
+++ b/styles/globals.scss
@@ -220,6 +220,8 @@ $zindex-sticky: 900;
scroll-margin-top: 60px;
}
+
+
.ms-xs {
margin-left: 0.125rem;
}
@@ -596,6 +598,14 @@ div[contenteditable]:disabled,
min-height: 100svh;
}
+#__next > main {
+ z-index: 0;
+}
+
+#__next > footer {
+ z-index: 0;
+}
+
footer {
margin-top: auto;
display: flex;