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"])
+}