From 5b8a022078ebeff99c6828f009454ec5d09ade7a Mon Sep 17 00:00:00 2001 From: Innei Date: Mon, 8 May 2023 18:49:01 +0800 Subject: [PATCH] fix: replace nprogress with hooks and qier-progress (#509) --- package.json | 2 +- pnpm-lock.yaml | 34 +++---------- src/app/providers.tsx | 4 +- src/css/tailwind.css | 89 +++++++++++++++++++++++++++++--- src/hooks/useNProgress.ts | 26 ++++++++++ src/hooks/useRouterEvents.ts | 98 ++++++++++++++++++++++++++++++++++++ 6 files changed, 217 insertions(+), 36 deletions(-) create mode 100644 src/hooks/useNProgress.ts create mode 100644 src/hooks/useRouterEvents.ts diff --git a/package.json b/package.json index 1b656b80..9dc4a779 100644 --- a/package.json +++ b/package.json @@ -87,9 +87,9 @@ "mdast-util-toc": "6.1.1", "medium-zoom": "1.0.8", "mermaid": "10.1.0", + "qier-progress": "1.0.4", "nanoid": "4.0.2", "next": "13.4.1", - "nextjs-toploader": "^1.3.2", "node-id3": "^0.2.6", "pangu": "^4.0.7", "pinyin": "3.0.0-alpha.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16b43eee..2d8a2b78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -187,9 +187,6 @@ dependencies: next: specifier: 13.4.1 version: 13.4.1(@babel/core@7.21.8)(react-dom@18.2.0)(react@18.2.0) - nextjs-toploader: - specifier: ^1.3.2 - version: 1.3.2(next@13.4.1)(react-dom@18.2.0)(react@18.2.0) node-id3: specifier: ^0.2.6 version: 0.2.6 @@ -202,6 +199,9 @@ dependencies: prismjs: specifier: 1.29.0 version: 1.29.0 + qier-progress: + specifier: 1.0.4 + version: 1.0.4 react: specifier: 18.2.0 version: 18.2.0 @@ -4806,10 +4806,6 @@ packages: /@types/node@20.1.0: resolution: {integrity: sha512-O+z53uwx64xY7D6roOi4+jApDGFg0qn6WHcxe5QeqjMaTezBO/mxdfFXIVAVVyNWKx84OmPB3L8kbVYOTeN34A==} - /@types/nprogress@0.2.0: - resolution: {integrity: sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==} - dev: false - /@types/pangu@4.0.0: resolution: {integrity: sha512-da3b6P7QDH41VdfF2xRE8/KgL06nX3RawfiIMAvJ/sYzLWQCQtxc+B3qWVtrIlrNlSgV/e1oG2SZYUpN6gvmhQ==} dev: true @@ -10880,21 +10876,6 @@ packages: - '@babel/core' - babel-plugin-macros - /nextjs-toploader@1.3.2(next@13.4.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-vvFObZvjMC/KNhqFG+pUgLW5/TNpz74wo9Vn/pweg7+tfwK+PCIxArTjpmD60jmUUHPmYgXy1w5qUuw8x3tSow==} - peerDependencies: - next: '>= 6.0.0' - react: '>= 16.0.0' - react-dom: '>= 16.0.0' - dependencies: - '@types/nprogress': 0.2.0 - next: 13.4.1(@babel/core@7.21.8)(react-dom@18.2.0)(react@18.2.0) - nprogress: 0.2.0 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - /node-abi@3.40.0: resolution: {integrity: sha512-zNy02qivjjRosswoYmPi8hIKJRr8MpQyeKT6qlcq/OnOgA3Rhoae+IYOqsM9V5+JnHWmxKnWOT2GxvtqdtOCXA==} engines: {node: '>=10'} @@ -11015,10 +10996,6 @@ packages: dev: false optional: true - /nprogress@0.2.0: - resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} - dev: false - /nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: @@ -11634,6 +11611,11 @@ packages: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} + /qier-progress@1.0.4: + resolution: {integrity: sha512-gCxgoM2wxW7GCo/iEpOlaunVo2cf+/AJfG6wtYIbYz18GXtUhFjgZjX2s+otWh/P8ppVgGULASkWtXfZQEd2FQ==} + engines: {node: '>=6.0.0'} + dev: false + /qrcode@1.5.3: resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} engines: {node: '>=10.13.0'} diff --git a/src/app/providers.tsx b/src/app/providers.tsx index bc071549..a11f4069 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -1,6 +1,5 @@ "use client" -import NextTopLoader from "nextjs-toploader" import { useState } from "react" import { WagmiConfig, createClient } from "wagmi" @@ -18,6 +17,7 @@ import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client // eslint-disable-next-line import/no-unresolved import { useDarkMode } from "~/hooks/useDarkMode" import { useMobileLayout } from "~/hooks/useMobileLayout" +import { useNProgress } from "~/hooks/useNProgress" import { APP_NAME } from "~/lib/env" import { toGateway } from "~/lib/ipfs-parser" import { createIDBPersister } from "~/lib/persister.client" @@ -44,6 +44,7 @@ export default function Providers({ }) { useDarkMode() useMobileLayout() + useNProgress() const [queryClient] = useState( () => @@ -81,7 +82,6 @@ export default function Providers({ signInStrategy="simple" ignoreWalletDisconnectEvent={true} > - {children} diff --git a/src/css/tailwind.css b/src/css/tailwind.css index fee07066..b51df0f6 100644 --- a/src/css/tailwind.css +++ b/src/css/tailwind.css @@ -371,17 +371,92 @@ textarea.input { background-color: var(--banner-bg-color); } -#nprogress .bar { - @apply bg-accent !important; +/* Fancy blur effect */ +#qprogress .peg { + display: block; + position: absolute; + right: 0px; + width: 100px; + height: 100%; + opacity: 1; + + -webkit-transform: rotate(3deg) translate(0px, -4px); + -ms-transform: rotate(3deg) translate(0px, -4px); + transform: rotate(3deg) translate(0px, -4px); } -#nprogress .peg { - @apply shadow-none !important; +.qprogress-custom-parent { + overflow: hidden; + position: relative; } -#nprogress .spinner-icon { - @apply border-t-accent !important; - @apply border-l-accent !important; +.qprogress-custom-parent #qprogress .bar { + position: absolute; +} + +/* Colorful effect */ +#qprogress .colorful-bar { + background: -webkit-gradient( + linear, + left top, + right top, + from(#1890ff), + color-stop(#13c2c2), + color-stop(#52c41a), + color-stop(#fa8c16), + color-stop(#fa541c), + color-stop(#f5222d), + to(#1890ff) + ); + background: -webkit-linear-gradient( + left, + #1890ff, + #13c2c2, + #52c41a, + #fa8c16, + #fa541c, + #f5222d, + #1890ff + ); + background: linear-gradient( + to right, + #1890ff, + #13c2c2, + #52c41a, + #fa8c16, + #fa541c, + #f5222d, + #1890ff + ); + + -webkit-animation: 2s linear infinite ins; + animation: 2s linear infinite ins; + + -webkit-transition: opacity 0.2s ease-in-out; + transition: opacity 0.2s ease-in-out; + + -webkit-transform-origin: left; + transform-origin: left; + + background-size: 500%; +} + +@-webkit-keyframes ins { + 0% { + background-position: 0% 0; + } + to { + background-position: 125% 0; + } +} + +@keyframes ins { + 0% { + background-position: 0% 0; + } + to { + background-position: 125% 0; + } } .loading:before { diff --git a/src/hooks/useNProgress.ts b/src/hooks/useNProgress.ts new file mode 100644 index 00000000..4bdb16a9 --- /dev/null +++ b/src/hooks/useNProgress.ts @@ -0,0 +1,26 @@ +"use client" + +import Progress from "qier-progress" +import { useEffect, useRef } from "react" + +import { useAppRouterEventerListener } from "./useRouterEvents" + +export const useNProgress = () => { + const events = useAppRouterEventerListener() + + const instance = useRef( + new Progress({ color: "var(--theme-color)", colorful: false }), + ) + useEffect(() => { + const disposers = [] as any[] + + disposers.push( + events.onStart(() => { + console.log("start") + instance.current.start() + }), + ) + disposers.push(events.onComplete(() => instance.current.finish())) + return () => disposers.forEach((disposer) => disposer()) + }, []) +} diff --git a/src/hooks/useRouterEvents.ts b/src/hooks/useRouterEvents.ts new file mode 100644 index 00000000..93027b7f --- /dev/null +++ b/src/hooks/useRouterEvents.ts @@ -0,0 +1,98 @@ +import { usePathname, useRouter, useSearchParams } from "next/navigation" +import { useEffect, useRef, useState } from "react" + +import { pick } from "~/lib/utils" + +interface RouterNavigationEvent {} + +type RouterEventFunction = (e: RouterNavigationEvent) => void + +// TODO detect error event +export const useAppRouterEventerListener = () => { + const [isRouterComplete, setIsRouterComplete] = useState(false) + + const startChangeCallback = () => { + setIsRouterComplete(false) + eventsRegisters.current.onStartQ.forEach(($) => $(buildEvent())) + } + const router = useRouter() + useEffect(() => { + const rawPush = router.push + const rawReplace = router.replace + + const popstateHandler = () => { + startChangeCallback() + } + + window.addEventListener("popstate", popstateHandler) + + router.push = (...rest) => { + startChangeCallback() + + // eslint-disable-next-line prefer-spread + rawPush.apply(null, rest) + } + + router.replace = (...rest) => { + startChangeCallback() + + // eslint-disable-next-line prefer-spread + rawReplace.apply(null, rest) + } + + return () => { + router.push = rawPush + router.replace = rawReplace + + window.removeEventListener("popstate", popstateHandler) + } + }, []) + + const eventsRegisters = useRef({ + onStartQ: [] as RouterEventFunction[], + // onErrorQ: [] as RouterEventFunction[], + onCompleteQ: [] as RouterEventFunction[], + onStart(cb: RouterEventFunction) { + eventsRegisters.current.onStartQ.push(cb) + return () => { + eventsRegisters.current.onStartQ = + eventsRegisters.current.onStartQ.filter(($) => $ !== cb) + } + }, + + onComplete(cb: RouterEventFunction) { + eventsRegisters.current.onCompleteQ.push(cb) + return () => { + eventsRegisters.current.onCompleteQ = + eventsRegisters.current.onCompleteQ.filter(($) => $ !== cb) + } + }, + }) + + const buildEvent = (): RouterNavigationEvent => { + return { + url: location.pathname + location.search, + } + } + + useEffect(() => { + if (!isRouterComplete) return + + eventsRegisters.current.onCompleteQ.forEach(($) => $(buildEvent())) + }, [isRouterComplete]) + + const currentPathname = usePathname() + const searchParams = useSearchParams() + + useEffect(() => { + if ( + currentPathname === location.pathname && + searchParams?.toString() === + new URLSearchParams(location.search).toString() + ) { + setIsRouterComplete(true) + } + }, [currentPathname, searchParams]) + + return pick(eventsRegisters.current, ["onStart", "onComplete"]) +}