diff --git a/public/java.png b/public/java.png new file mode 100644 index 0000000..1bbe005 Binary files /dev/null and b/public/java.png differ diff --git a/public/windows.png b/public/windows.png new file mode 100644 index 0000000..de141d8 Binary files /dev/null and b/public/windows.png differ diff --git a/src/css/pages/Downloads.css b/src/css/pages/Downloads.css new file mode 100644 index 0000000..34bc9bf --- /dev/null +++ b/src/css/pages/Downloads.css @@ -0,0 +1,162 @@ +.Downloads { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; +} + +.Downloads_Container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + max-width: 1200px; + margin-top: 70px; +} + +.Downloads_Container h3 { + font-size: 18px; + font-weight: 400; + color: #c5c5c5; + text-align: center; +} + +.Downloads_Cards { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 40px; + margin: 40px; +} + +.DownloadCard { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: #000000; + padding: 20px; + width: 300px; + border-radius: 10px; + background-color: #fafafa; + box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.75); + transition: all 0.2s ease-in-out; + text-align: center; + cursor: pointer; +} + +.DownloadCard:hover { + transform: scale(1.05); + box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.75); +} + +.DownloadCard_Version { + font-size: 16px; + font-weight: 400; + color: #7a7a7a; +} + +.DownloadCard_Icon { + height: 64px; + width: 64px; + margin: 20px; +} + +.DownloadCard_Info { + margin: 20px; +} + +.DownloadCard_Info h4 { + font-size: 20px; + font-weight: 500; + margin: 0; +} + +.DownloadCard_Info p { + font-size: 16px; + font-weight: 400; + margin: 5px; + color: #7a7a7a; +} + +.DownloadCard_Download { + color: #1969c9; +} + +.Downloads_More { + color: #ffffff; + display: flex; + width: 100%; + max-width: 500px; + flex-direction: column; + align-items: center; + justify-content: center; + margin: 40px; + gap: 20px; +} + +.Downloads_More h4 { + font-size: 35px; + font-weight: 700; + margin-top: 0; +} + +.Downloads_More_Grasscutter { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + width: 100%; + gap: 20px; + background-color: #fafafa; + border-radius: 10px; + box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.75); + transition: all 0.2s ease-in-out; + text-align: center; + padding: 40px; +} + +.Downloads_More_Grasscutter h5 { + font-size: 20px; + font-weight: 500; + margin: 0; + color: #000; +} + +.Downloads_More_Cultivation { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + width: 100%; + gap: 20px; + background-color: #fafafa; + border-radius: 10px; + box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.75); + transition: all 0.2s ease-in-out; + text-align: center; + padding: 40px; +} + +.Downloads_More_Cultivation h5 { + font-size: 20px; + font-weight: 500; + margin: 0; + color: #000; +} + +@media screen and (max-width: 600px) { + .Downloads_More_Grasscutter { + flex-direction: column; + width: 300px; + } + + .Downloads_More_Cultivation { + flex-direction: column; + width: 300px; + } +} diff --git a/src/ui/App.tsx b/src/ui/App.tsx index deed6e4..90f0d4a 100644 --- a/src/ui/App.tsx +++ b/src/ui/App.tsx @@ -4,6 +4,7 @@ import { Routes, Route } from "react-router-dom"; import Header from "@components/Header"; import Home from "@pages/Home"; +import Downloads from "@pages/Downloads"; import "@css/App.css"; @@ -14,7 +15,7 @@ class App extends React.Component {
} /> - Downloads

} /> + } /> Wiki

} /> Features

} /> Config Generator

} /> diff --git a/src/ui/components/downloads/DownloadCard.tsx b/src/ui/components/downloads/DownloadCard.tsx new file mode 100644 index 0000000..e8e5157 --- /dev/null +++ b/src/ui/components/downloads/DownloadCard.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +interface IProps { + title: string; + description: string; + downloadLink: string; + downloadVersion: string; + icon: string; +} + +class DownloadCard extends React.Component { + constructor(props: IProps) { + super(props); + } + + render() { + return ( +
window.open(this.props.downloadLink)}> +

{this.props.downloadVersion}

+ {"Icon"} +
+

{this.props.title}

+

{this.props.description}

+
+
+

Download

+
+
+ ); + } +} + +export default DownloadCard; diff --git a/src/ui/pages/Downloads.tsx b/src/ui/pages/Downloads.tsx new file mode 100644 index 0000000..43de72f --- /dev/null +++ b/src/ui/pages/Downloads.tsx @@ -0,0 +1,109 @@ +import React from "react"; + +import StyledHeading from "@components/common/StyledHeading"; +import DownloadCard from "@components/downloads/DownloadCard"; +import BasicButton from "@components/common/BasicButton"; + +import { getLatestReleaseAsync } from "@app/utils"; + +import "@css/pages/Downloads.css"; + +interface IState { + grasscutterVersion: string; + grasscutterDownloadLink: string; + cultivationVersion: string; + cultivationDownloadLink: string; +} + +class Downloads extends React.Component<{}, IState> { + constructor(props: {}) { + super(props); + + this.state = { + grasscutterVersion: "", + grasscutterDownloadLink: "", + cultivationVersion: "", + cultivationDownloadLink: "" + }; + } + + setDownloads = async () => { + const data = await getLatestReleaseAsync(); + + this.setState({ + grasscutterVersion: data.grasscutter.version, + grasscutterDownloadLink: data.grasscutter.url, + cultivationVersion: data.cultivation.version, + cultivationDownloadLink: data.cultivation.url + }); + } + + async componentDidMount() { + await this.setDownloads(); + } + + render() { + return ( +
+
+ +

We recommend using Cultivation to launch your server
if you do not know what a JAR file is.

+ +
+ + + +
+ +
+

Looking for more builds?

+ +
+
Grasscutter:
+ window.open("https://nightly.link/Grasscutters/Grasscutter/workflows/build/unstable/Grasscutter.zip")} + /> + + window.open("https://github.com/Grasscutters/Grasscutter/releases")} + /> +
+ +
+
Cultivation:
+ window.open("https://nightly.link/Grasscutters/Cultivation/workflows/build/main/CultivationWin.zip")} + /> + + window.open("https://github.com/Grasscutters/Cultivation/releases")} + /> +
+
+
+
+ ); + } +} + +export default Downloads; diff --git a/src/utils.ts b/src/utils.ts index a86b581..5297845 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,10 +1,11 @@ -const repo: string = "grasscutters/grasscutter"; +const grasscutter: string = "grasscutters/grasscutter"; +const cultivation: string = "grasscutters/cultivation"; /** * Get the number of stars, forks and watchers of the repo */ export async function getStatsAsync() { - const res = await fetch(`https://api.github.com/repos/${repo}`, { + const res = await fetch(`https://api.github.com/repos/${grasscutter}`, { headers: { Accept: "application/vnd.github.v3+json" } }); @@ -16,3 +17,38 @@ export async function getStatsAsync() { watchers: data.subscribers_count } } + +/** + * Fetch the latest release of the repos + */ +export async function getLatestReleaseAsync() { + const resGC = await fetch(`https://api.github.com/repos/${grasscutter}/releases/latest`, { + headers: { Accept: "application/vnd.github.v3+json" } + }); + + const dataGC = await resGC.json(); + + const resC = await fetch(`https://api.github.com/repos/${cultivation}/releases/latest`, { + headers: { Accept: "application/vnd.github.v3+json" } + }); + + const dataC = await resC.json(); + + for (const asset of dataC.assets) { + if (asset.browser_download_url.endsWith(".msi")) { + dataC.assets[0] = asset; + break; + } + } + + return { + grasscutter: { + version: dataGC.tag_name, + url: dataGC.assets[0].browser_download_url + }, + cultivation: { + version: dataC.tag_name, + url: dataC.assets[0].browser_download_url + } + } +}