mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-14 10:09:09 +03:00
Quick Save
This commit is contained in:
+4
-5
@@ -1,15 +1,14 @@
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
.env
|
||||
|
||||
/reference
|
||||
|
||||
/_site
|
||||
/build
|
||||
|
||||
/public/media/svg
|
||||
/src/react-app-env.d.ts
|
||||
|
||||
.vscode
|
||||
|
||||
/src/react-app-env.d.ts
|
||||
/src/react-app-env.d.ts
|
||||
|
||||
.pnpm-debug.log
|
||||
+4
-1
@@ -30,6 +30,9 @@
|
||||
"@material-ui/lab": "4.0.0-alpha.53",
|
||||
"@material-ui/pickers": "3.2.10",
|
||||
"@material-ui/styles": "4.9.14",
|
||||
"@nivo/calendar": "^0.73.0",
|
||||
"@nivo/core": "^0.73.0",
|
||||
"@nivo/line": "^0.73.0",
|
||||
"@svgr/webpack": "4.3.2",
|
||||
"@tanem/svg-injector": "8.0.50",
|
||||
"apexcharts": "3.19.2",
|
||||
@@ -58,7 +61,7 @@
|
||||
"postcss-normalize": "7.0.1",
|
||||
"postcss-preset-env": "6.7.0",
|
||||
"postcss-safe-parser": "4.0.1",
|
||||
"react": "16.12.0",
|
||||
"react": "17",
|
||||
"react-async": "10.0.1",
|
||||
"react-bootstrap": "1.0.1",
|
||||
"react-dom": "16.12.0",
|
||||
|
||||
Generated
+474
-118
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,11 @@ import { UserContext } from "context/UserContext";
|
||||
import { PublicUserDocument } from "tachi-common";
|
||||
import toast from "react-hot-toast";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import useSetSubheader from "components/layout/header/useSetSubheader";
|
||||
|
||||
export default function LoginPage() {
|
||||
useSetSubheader("Login");
|
||||
|
||||
const [err, setErr] = useState("");
|
||||
const { setUser } = useContext(UserContext);
|
||||
const history = useHistory();
|
||||
|
||||
@@ -11,11 +11,14 @@ import toast from "react-hot-toast";
|
||||
import { UseFormik } from "types/react";
|
||||
import Divider from "components/util/Divider";
|
||||
import { TachiConfig } from "lib/config";
|
||||
import useSetSubheader from "components/layout/header/useSetSubheader";
|
||||
|
||||
// seconds it takes for a user to actually read the rules.
|
||||
const RULES_READ_TIME = 30;
|
||||
|
||||
export default function RegisterPage() {
|
||||
useSetSubheader("Register");
|
||||
|
||||
const [err, setErr] = useState("");
|
||||
// not opened: user has not clicked the rules link
|
||||
// opened: user has clicked the rules link
|
||||
|
||||
@@ -25,6 +25,7 @@ import { GamePT } from "types/react";
|
||||
import { APIFetchV1 } from "util/api";
|
||||
import AsyncLoader from "components/util/AsyncLoader";
|
||||
import { Playtype } from "types/tachi";
|
||||
import TimelineChart from "components/charts/TimelineChart";
|
||||
|
||||
export default function OverviewPage({
|
||||
reqUser,
|
||||
@@ -59,12 +60,34 @@ function RankingInfo({ reqUser, game, playtype }: { reqUser: PublicUserDocument
|
||||
throw new Error(res.description);
|
||||
}
|
||||
|
||||
return res.body;
|
||||
const data: number[] = [];
|
||||
|
||||
for (let i = 0; i <= 90; i++) {
|
||||
data.push(
|
||||
(data[i - 1] ?? 50) +
|
||||
Math.floor(Math.random() > 0.5 ? -Math.random() * 5 : Math.random() * 5)
|
||||
);
|
||||
}
|
||||
|
||||
return data.map((e, i) => ({
|
||||
ranking: e,
|
||||
timestamp: Date.now() - i * 1000 * 60 * 60 * 24,
|
||||
}));
|
||||
}}
|
||||
>
|
||||
{data => (
|
||||
<Card className="mt-4" header="Ranking">
|
||||
<div>{data.map(e => e.ranking)}</div>
|
||||
<TimelineChart
|
||||
height="30rem"
|
||||
width="100%"
|
||||
data={[
|
||||
{
|
||||
id: "ranking",
|
||||
data: data.map(d => ({ x: d.timestamp, y: -d.ranking })),
|
||||
},
|
||||
]}
|
||||
yAxisFormat={y => `#${-y}`}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
</AsyncLoader>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
import { PointTooltipProps } from "@nivo/line";
|
||||
|
||||
const defaultRenderFn = (point: PointTooltipProps["point"]) => (
|
||||
<div>
|
||||
{point.data.xFormatted} {point.data.yFormatted}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default function ChartTooltip({
|
||||
renderFn = defaultRenderFn,
|
||||
point,
|
||||
}: {
|
||||
point: PointTooltipProps["point"];
|
||||
renderFn?: (p: PointTooltipProps["point"]) => JSX.Element;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className="chart-tooltip d-flex align-items-center justify-content-center"
|
||||
style={{
|
||||
width: "10rem",
|
||||
height: "5rem",
|
||||
backgroundColor: "#131313",
|
||||
borderRadius: "1 1 1 1",
|
||||
boxShadow: "0px 0px 2px 0px black",
|
||||
}}
|
||||
>
|
||||
{renderFn(point)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import React from "react";
|
||||
import { FormatDate, FormatDuration, FormatTime, FormatTimeSmall, MillisToSince } from "util/time";
|
||||
import { ResponsiveLine, Serie } from "@nivo/line";
|
||||
import { DateTime } from "luxon";
|
||||
import { TACHI_CHART_THEME } from "util/constants/chart-theme";
|
||||
import ChartTooltip from "./ChartTooltip";
|
||||
|
||||
export default function TimelineChart<T>({
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
yAxisFormat,
|
||||
}: {
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
data: Serie[];
|
||||
yAxisFormat?: (y: number) => string;
|
||||
}) {
|
||||
return (
|
||||
<div style={{ height, width }}>
|
||||
<ResponsiveLine
|
||||
data={data}
|
||||
margin={{ right: 110, top: 30, bottom: 50, left: 60 }}
|
||||
xScale={{ type: "time", format: "%Q" }}
|
||||
xFormat="time:%Q"
|
||||
axisBottom={{
|
||||
format: x => FormatTimeSmall(Number(x)),
|
||||
}}
|
||||
yScale={{ type: "linear", min: "auto", max: "auto", stacked: true, reverse: false }}
|
||||
axisLeft={{
|
||||
tickSize: 5,
|
||||
tickPadding: 5,
|
||||
tickRotation: 0,
|
||||
format: y => (Number.isInteger(y) ? `#${-y}` : ""),
|
||||
}}
|
||||
enablePoints={false}
|
||||
enableArea={true}
|
||||
colors={["#cc527a"]}
|
||||
useMesh={true}
|
||||
theme={TACHI_CHART_THEME}
|
||||
tooltip={d => (
|
||||
<ChartTooltip
|
||||
point={d.point}
|
||||
renderFn={p => (
|
||||
<div>
|
||||
{MillisToSince(+p.data.xFormatted)}: #{-p.data.yFormatted}
|
||||
<br />
|
||||
<small className="text-muted">
|
||||
({FormatDate(+p.data.xFormatted)})
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
legends={[
|
||||
{
|
||||
anchor: "bottom-right",
|
||||
direction: "column",
|
||||
justify: false,
|
||||
translateX: 100,
|
||||
translateY: 0,
|
||||
itemsSpacing: 0,
|
||||
itemDirection: "left-to-right",
|
||||
itemWidth: 80,
|
||||
itemHeight: 20,
|
||||
itemOpacity: 0.75,
|
||||
symbolSize: 12,
|
||||
symbolShape: "circle",
|
||||
symbolBorderColor: "rgba(0, 0, 0, .5)",
|
||||
effects: [
|
||||
{
|
||||
on: "hover",
|
||||
style: {
|
||||
itemBackground: "rgba(0, 0, 0, .03)",
|
||||
itemOpacity: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -17,9 +17,5 @@ export default function useSetSubheader(
|
||||
setBreadcrumbs,
|
||||
overrideTitle
|
||||
);
|
||||
|
||||
return () => {
|
||||
UpdateSubheader([], setTitle, setBreadcrumbs);
|
||||
};
|
||||
}, deps);
|
||||
}
|
||||
|
||||
@@ -14,4 +14,12 @@ import "./_metronic/_assets/plugins/flaticon2/flaticon.css";
|
||||
|
||||
const { PUBLIC_URL } = process.env;
|
||||
|
||||
ReactDOM.render(<App basename={PUBLIC_URL} />, document.getElementById("root"));
|
||||
console.log(process.env);
|
||||
|
||||
// if (!PUBLIC_URL) {
|
||||
// throw new Error(
|
||||
// `No PUBLIC_URL was defined in environment. Have you forgot to set it in a .env file?`
|
||||
// );
|
||||
// }
|
||||
|
||||
ReactDOM.render(<App basename={PUBLIC_URL!} />, document.getElementById("root"));
|
||||
|
||||
@@ -7,15 +7,26 @@ if (!mode) {
|
||||
}
|
||||
|
||||
let conf;
|
||||
let colourConf;
|
||||
|
||||
if (mode === "ktchi") {
|
||||
conf = StaticConfig.KTCHI_CONFIG;
|
||||
colourConf = {
|
||||
primary: "#e61c6e",
|
||||
};
|
||||
} else if (mode === "btchi") {
|
||||
conf = StaticConfig.BTCHI_CONFIG;
|
||||
colourConf = {
|
||||
primary: "#4974a5",
|
||||
};
|
||||
} else if (mode === "omni") {
|
||||
conf = StaticConfig.OMNI_CONFIG;
|
||||
colourConf = {
|
||||
primary: "#e61c6e",
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Invalid TCHIC_MODE of ${mode}. Cannot boot.`);
|
||||
}
|
||||
|
||||
export const TachiConfig = conf;
|
||||
export const ColourConfig = colourConf;
|
||||
|
||||
@@ -6,9 +6,9 @@ export function ChangeOpacity(str: string, opacity: number) {
|
||||
if (str.startsWith("#")) {
|
||||
if (str.length === 7) {
|
||||
return str + Math.floor(opacity * 255).toString(16);
|
||||
} else {
|
||||
return str.slice(0, 7) + Math.floor(opacity * 255).toString(16);
|
||||
}
|
||||
|
||||
return str.slice(0, 7) + Math.floor(opacity * 255).toString(16);
|
||||
} else if (str.startsWith("rgba(")) {
|
||||
return str.replace(/, ?[0-9.]+\)$/u, `,${opacity})`);
|
||||
} else if (str.startsWith("rgb(")) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
export const TACHI_CHART_THEME = {
|
||||
background: "#2b292b",
|
||||
textColor: "#ffffff",
|
||||
fontSize: 11,
|
||||
axis: {
|
||||
domain: {
|
||||
line: {
|
||||
stroke: "#777777",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
line: {
|
||||
stroke: "#777777",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
line: {
|
||||
stroke: "#131313",
|
||||
strokeWidth: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -7,7 +7,7 @@ export function UpdateSubheader(
|
||||
setBreadcrumbs: SetState<string[]>,
|
||||
customTitle?: string
|
||||
) {
|
||||
const title = customTitle ?? breadcrumbs[breadcrumbs.length - 1];
|
||||
const title = customTitle ?? breadcrumbs[breadcrumbs.length - 1] ?? "Somewhere...";
|
||||
|
||||
document.title = `${title} | ${TachiConfig.name}`;
|
||||
setTitle(title);
|
||||
|
||||
@@ -9,6 +9,14 @@ export function FormatTime(ms: number) {
|
||||
return DateTime.fromMillis(ms).toLocaleString(DateTime.DATETIME_MED);
|
||||
}
|
||||
|
||||
export function FormatDate(ms: number) {
|
||||
return DateTime.fromMillis(ms).toLocaleString(DateTime.DATE_HUGE);
|
||||
}
|
||||
|
||||
export function FormatDuration(ms: number) {
|
||||
return humaniseDuration(ms);
|
||||
}
|
||||
|
||||
export function FormatTimeSmall(ms: number) {
|
||||
return DateTime.fromMillis(ms).toLocaleString(DateTime.DATE_SHORT);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user