diff --git a/.gitignore b/.gitignore
index 63c739d..bbe04ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,6 @@ next-env.d.ts
# Sentry Config File
.sentryclirc
+
+# Webpack bundle analyzer
+analyze
diff --git a/next.config.js b/next.config.js
index ff32472..d59a8a8 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,4 +1,7 @@
const nextBuildId = require("next-build-id");
+const withBundleAnalyzer = require("@next/bundle-analyzer")({
+ enabled: false,
+});
/** @type {import('next').NextConfig} */
const nextConfig = {
@@ -26,10 +29,9 @@ const nextConfig = {
},
};
-module.exports = nextConfig;
+module.exports = withBundleAnalyzer(nextConfig);
-
-// Injected content via Sentry wizard below
+// // Injected content via Sentry wizard below
const { withSentryConfig } = require("@sentry/nextjs");
@@ -43,17 +45,17 @@ module.exports = withSentryConfig(
silent: true,
org: "sentry",
project: "scoresaber-reloaded",
- url: "https://sentry.fascinated.cc/"
+ url: "https://sentry.fascinated.cc/",
},
{
// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
// Upload a larger set of source maps for prettier stack traces (increases build time)
- widenClientFileUpload: true,
+ widenClientFileUpload: false,
// Transpiles SDK to be compatible with IE11 (increases bundle size)
- transpileClientSDK: true,
+ transpileClientSDK: false,
// Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers (increases server load)
tunnelRoute: "/monitoring",
@@ -63,5 +65,5 @@ module.exports = withSentryConfig(
// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: true,
- }
+ },
);
diff --git a/package-lock.json b/package-lock.json
index 92b6dfd..25b1298 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,8 +13,8 @@
"bluebird": "^3.7.2",
"chart.js": "^4.4.0",
"clsx": "^2.0.0",
+ "date-fns": "^2.30.0",
"encoding": "^0.1.13",
- "moment": "^2.29.4",
"next": "13.5.6",
"next-build-id": "^3.0.0",
"node-fetch-cache": "^3.1.3",
@@ -27,11 +27,13 @@
"zustand": "^4.4.3"
},
"devDependencies": {
+ "@next/bundle-analyzer": "^13.5.6",
"@types/node": "^20",
"@types/node-fetch-cache": "^3.0.3",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.4.16",
+ "cross-env": "^7.0.3",
"eslint": "^8",
"eslint-config-next": "13.5.6",
"postcss": "^8.4.31",
@@ -66,7 +68,6 @@
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
"integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
- "dev": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@@ -228,6 +229,15 @@
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
},
+ "node_modules/@next/bundle-analyzer": {
+ "version": "13.5.6",
+ "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-13.5.6.tgz",
+ "integrity": "sha512-4P5YVpR3N/B5+p0TQ/rPAr+9fsjkdfCVTGzJhKwE7XHqS+QME4gYxAYeGKkfkHEkP2A3GKXs8QSp0LjIvWLI3g==",
+ "dev": true,
+ "dependencies": {
+ "webpack-bundle-analyzer": "4.7.0"
+ }
+ },
"node_modules/@next/env": {
"version": "13.5.6",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.6.tgz",
@@ -434,6 +444,12 @@
"node": ">=10"
}
},
+ "node_modules/@polka/url": {
+ "version": "1.0.0-next.23",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.23.tgz",
+ "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==",
+ "dev": true
+ },
"node_modules/@rollup/plugin-commonjs": {
"version": "24.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.0.tgz",
@@ -960,6 +976,15 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
+ "node_modules/acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -1657,6 +1682,24 @@
"node": ">= 0.6"
}
},
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1695,6 +1738,21 @@
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
"dev": true
},
+ "node_modules/date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=0.11"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/date-fns"
+ }
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -1832,6 +1890,12 @@
"node": ">=6.0.0"
}
},
+ "node_modules/duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true
+ },
"node_modules/electron-to-chromium": {
"version": "1.4.557",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.557.tgz",
@@ -2824,6 +2888,21 @@
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true
},
+ "node_modules/gzip-size": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
+ "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
+ "dev": true,
+ "dependencies": {
+ "duplexer": "^0.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/has": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz",
@@ -3555,6 +3634,12 @@
"resolved": "https://registry.npmjs.org/locko/-/locko-1.0.0.tgz",
"integrity": "sha512-eK3TW5bJs6BrjmtzEb+RSyKt48etEitQviC6A6qMMSYl0dDFl0tIOD23QV/i0/rTuuXUgSYWX1yssbtop97Gog=="
},
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -3744,12 +3829,13 @@
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
- "node_modules/moment": {
- "version": "2.29.4",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
- "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "node_modules/mrmime": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",
+ "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==",
+ "dev": true,
"engines": {
- "node": "*"
+ "node": ">=10"
}
},
"node_modules/ms": {
@@ -4072,6 +4158,15 @@
"wrappy": "1"
}
},
+ "node_modules/opener": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
+ "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+ "dev": true,
+ "bin": {
+ "opener": "bin/opener-bin.js"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
@@ -4720,8 +4815,7 @@
"node_modules/regenerator-runtime": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
- "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==",
- "dev": true
+ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
},
"node_modules/regexp.prototype.flags": {
"version": "1.5.1",
@@ -5047,6 +5141,20 @@
"is-arrayish": "^0.3.1"
}
},
+ "node_modules/sirv": {
+ "version": "1.0.19",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz",
+ "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==",
+ "dev": true,
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.20",
+ "mrmime": "^1.0.0",
+ "totalist": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -5432,6 +5540,15 @@
"node": ">=8.0"
}
},
+ "node_modules/totalist": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz",
+ "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
@@ -5681,6 +5798,38 @@
"node": ">=10.13.0"
}
},
+ "node_modules/webpack-bundle-analyzer": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz",
+ "integrity": "sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.0.4",
+ "acorn-walk": "^8.0.0",
+ "chalk": "^4.1.0",
+ "commander": "^7.2.0",
+ "gzip-size": "^6.0.0",
+ "lodash": "^4.17.20",
+ "opener": "^1.5.2",
+ "sirv": "^1.0.7",
+ "ws": "^7.3.1"
+ },
+ "bin": {
+ "webpack-bundle-analyzer": "lib/bin/analyzer.js"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/webpack-bundle-analyzer/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
@@ -5784,6 +5933,27 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
+ "node_modules/ws": {
+ "version": "7.5.9",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
+ "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
diff --git a/package.json b/package.json
index 70fa296..5666679 100644
--- a/package.json
+++ b/package.json
@@ -14,8 +14,8 @@
"bluebird": "^3.7.2",
"chart.js": "^4.4.0",
"clsx": "^2.0.0",
+ "date-fns": "^2.30.0",
"encoding": "^0.1.13",
- "moment": "^2.29.4",
"next": "13.5.6",
"next-build-id": "^3.0.0",
"node-fetch-cache": "^3.1.3",
@@ -28,11 +28,13 @@
"zustand": "^4.4.3"
},
"devDependencies": {
+ "@next/bundle-analyzer": "^13.5.6",
"@types/node": "^20",
"@types/node-fetch-cache": "^3.0.3",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.4.16",
+ "cross-env": "^7.0.3",
"eslint": "^8",
"eslint-config-next": "13.5.6",
"postcss": "^8.4.31",
diff --git a/src/components/player/PlayerInfo.tsx b/src/components/player/PlayerInfo.tsx
index 4c2337c..88bc9c1 100644
--- a/src/components/player/PlayerInfo.tsx
+++ b/src/components/player/PlayerInfo.tsx
@@ -19,8 +19,8 @@ import { useStore } from "zustand";
import Avatar from "../Avatar";
import Card from "../Card";
import Label from "../Label";
-import PlayerChart from "./PlayerChart";
+const PlayerChart = dynamic(() => import("./PlayerChart"));
const ReactCountryFlag = dynamic(() => import("react-country-flag"));
type PlayerInfoProps = {
diff --git a/src/components/player/Score.tsx b/src/components/player/Score.tsx
index 532e825..ab66560 100644
--- a/src/components/player/Score.tsx
+++ b/src/components/player/Score.tsx
@@ -2,13 +2,13 @@ import { ScoresaberLeaderboardInfo } from "@/schemas/scoresaber/leaderboard";
import { ScoresaberPlayer } from "@/schemas/scoresaber/player";
import { ScoresaberScore } from "@/schemas/scoresaber/score";
import { formatNumber } from "@/utils/number";
+import { formatDate, formatTimeAgo } from "@/utils/timeUtils";
import {
CheckIcon,
GlobeAsiaAustraliaIcon,
XMarkIcon,
} from "@heroicons/react/20/solid";
import clsx from "clsx";
-import moment from "moment";
import Image from "next/image";
import ScoreStatLabel from "./ScoreStatLabel";
@@ -28,8 +28,11 @@ export default function Score({ score, player, leaderboard }: ScoreProps) {
#{score.rank}
-- {moment(score.timeSet).fromNow()} +
+ {formatTimeAgo(score.timeSet)}
{/* Song Image */} @@ -60,9 +63,11 @@ export default function Score({ score, player, leaderboard }: ScoreProps) { {/* Time Set (Mobile) */}- {moment(score.timeSet).fromNow()} +
+ {formatTimeAgo(score.timeSet)}