diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..a55e7a17 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..03d9549e --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index eebd0751..bbe33ea3 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,12 @@ "@types/node": "^16.11.41", "@types/react": "^18.0.14", "@types/react-dom": "^18.0.5", + "axios": "^0.27.2", + "bootstrap": "^5.1.3", "react": "^18.2.0", + "react-bootstrap": "^2.4.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.3.0", "react-scripts": "5.0.1", "typescript": "^4.7.4", "web-vitals": "^2.1.4" diff --git a/public/index.html b/public/index.html index aa069f27..5f0f39c5 100644 --- a/public/index.html +++ b/public/index.html @@ -1,20 +1,26 @@ - - - - - + + + + + - + - + + - React App - - - -
- - + To begin the development, run `npm start` or `yarn start`. + To create a production bundle, use `npm run build` or `yarn build`. +--> + diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 74b5e053..00000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.test.tsx b/src/App.test.tsx deleted file mode 100644 index 2a68616d..00000000 --- a/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/App.tsx b/src/App.tsx index a53698aa..de49fc5e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,26 +1,28 @@ import React from 'react'; -import logo from './logo.svg'; -import './App.css'; +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import HomePage from "./pages/HomePage"; +import { Button, Nav, Navbar, NavLink } from "react-bootstrap"; + +import "./css/App.css" +import DownloadPage from "./pages/DownloadPage"; function App() { - return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
- ); + return ( + + + SCISSORS + + + + + }/> + }/> + + + ); } export default App; diff --git a/src/css/App.css b/src/css/App.css new file mode 100644 index 00000000..8f57d48f --- /dev/null +++ b/src/css/App.css @@ -0,0 +1,26 @@ +@import url('https://fonts.googleapis.com/css2?family=Permanent+Marker&display=swap'); + +.navbar-brand { + font-family: 'Permanent Marker', cursive; +} + +@media only screen and (max-width: 768px) { + .download-button { + height: 7vh; + font-size: 3vw; + } + + .download-button > img { + width: 25px; + } + + .navbar-brand { + position: relative; + right: 5vw; + } + + .navbar-nav { + position: relative; + right: 5vw; + } +} \ No newline at end of file diff --git a/src/css/DownloadPage.css b/src/css/DownloadPage.css new file mode 100644 index 00000000..fb9ba51c --- /dev/null +++ b/src/css/DownloadPage.css @@ -0,0 +1,103 @@ +@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@200&display=swap'); + +body { + margin: 0; +} + +ul > li { + color: white; +} + +ul { + list-style-type: none; + flex-direction: row; + justify-content: space-around; + display: flex; +} + +.builds { + padding: 0; + margin: 0; + background-color: #363534; +} + +.builds { + flex-direction: column; +} + +.builds > li { + padding-left: 1vw; + height: 10vh; + width: 100%; + border-bottom: 2px solid black; + display: table; + flex-wrap: nowrap; +} + +.builds > li > .btn { + border-radius: 1vh !important; + position: relative; + top: 3vh; +} + +.selected { + border-bottom: aqua 1px solid; +} + +.selectable { + cursor: pointer; +} + +ul[class^="changes-"] { + justify-content: left; + flex-direction: column; + margin: 0; + flex-wrap: nowrap; + align-content: flex-start; +} + +ul[class^="changes-"] > li { + flex-wrap: nowrap; + margin: 0; + padding: 0; + font-size: 0.75vw; + position: relative; + bottom: 1vh; + left: 2vw; + width: 50%; +} + +ul[class^="changes-"] > li > a { + text-decoration: none; + color: #91ffaf; + margin: 0; +} + +.date { + display: table-cell; + float: right; + right: 10vw; + position: relative; + bottom: 2.5vh; +} + +.nochanges { + position: relative; + bottom: 0; +} + +@media only screen and (max-width: 768px) { + ul[class^="changes-"] > li { + position: relative; + left: 5vw; + font-size: 3vw; + } + + .date { + bottom: 3.75vh; + } + + .nochanges { + bottom: 1vh; + } +} \ No newline at end of file diff --git a/src/css/HomePage.css b/src/css/HomePage.css new file mode 100644 index 00000000..f0bfaa78 --- /dev/null +++ b/src/css/HomePage.css @@ -0,0 +1,35 @@ +@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@200&display=swap'); +body { + background-color: #262626; +} + +h1, h4, h5 { + color: white; + text-align: center; + position: relative; + font-family: 'IBM Plex Sans', sans-serif; +} + +h1 { + top: 10vh; + font-weight: bolder; +} + +h4 { + top: 10vh; +} + +.download-button:hover { + transition: all 0.25s ease; + background-color: #c7c7c7 !important; +} + +@media only screen and (max-width: 768px) { + h1 { + font-size: 7vw; + } + + h4 { + font-size: 5vw; + } +} \ No newline at end of file diff --git a/src/index.css b/src/index.css deleted file mode 100644 index ec2585e8..00000000 --- a/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/src/index.tsx b/src/index.tsx index 032464fb..f9133920 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,19 +1,10 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import './index.css'; import App from './App'; -import reportWebVitals from './reportWebVitals'; const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement + document.getElementById('root') as HTMLElement ); root.render( - - - + ); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/src/logo.svg b/src/logo.svg deleted file mode 100644 index 9dfc1c05..00000000 --- a/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/pages/DownloadPage.tsx b/src/pages/DownloadPage.tsx new file mode 100644 index 00000000..53a86b2d --- /dev/null +++ b/src/pages/DownloadPage.tsx @@ -0,0 +1,65 @@ +import React, { Fragment, useEffect, useState } from "react"; +import "../css/HomePage.css" +import { Build, getBuilds, getJobs, Job } from "../util/Jenkins"; + +import "../css/DownloadPage.css" +import { Button } from "react-bootstrap"; + +const DownloadPage = () => { + const [ jobs, setJobs ] = useState(new Map()) + const [ version, setVersion ] = useState("") + useEffect(() => { + doJobs().then(value => { + }) + }, []) + + function doJobs(): Promise { + return new Promise(resolve => { + getJobs().then(value => { + for (let job of value) { + getBuilds(job.name).then(value1 => { + setJobs(prevState => { + let map = new Map([ ...prevState, [ job, value1 ] ]) + if (map.size >= value.length) { + let job = Array.from(map.keys()).sort((a, b) => parseFloat(a.name) - parseFloat(b.name)).reverse()[0] + setVersion(job.name) + } + return new Map([ ...map.entries() ].sort((a, b) => parseFloat(a[0].name) - parseFloat(b[0].name)).reverse()) + }) + resolve() + }) + } + }) + }) + } + + function onClick(event: React.MouseEvent) { + setVersion(event.currentTarget.innerHTML) + } + + return ( + +
    + {Array.from(jobs.keys()).map(value => { + return
  • {value.name}
  • + })} +
+
    + {jobs.get(Array.from(jobs.keys()).filter(value => value.name === version)[0])?.map(value => { + return
  • + +
      + {value.changes?.map(value1 => { + return
    • [{value1.id}]  {value1.comment}
    • + })} +
    + 0 ? "date" : "date nochanges"}>{new Date(value.timestamp!).toISOString().split("T")[0]} +
  • + })} +
+
+ ); +} + +export default DownloadPage; \ No newline at end of file diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx new file mode 100644 index 00000000..a019fd14 --- /dev/null +++ b/src/pages/HomePage.tsx @@ -0,0 +1,15 @@ +import { Fragment } from "react"; +import { Button, Nav, Navbar, NavLink } from "react-bootstrap"; +import "../css/HomePage.css" + +const HomePage = () => { + return ( + +

Scissors

+

Minecraft server software oriented towards patching Creative Mode exploits.

+

Versions: 1.17.1, 1.18.2, 1.19

+
+ ); +} + +export default HomePage; \ No newline at end of file diff --git a/src/reportWebVitals.ts b/src/reportWebVitals.ts deleted file mode 100644 index 49a2a16e..00000000 --- a/src/reportWebVitals.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ReportHandler } from 'web-vitals'; - -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/src/setupTests.ts b/src/setupTests.ts deleted file mode 100644 index 8f2609b7..00000000 --- a/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/src/util/Jenkins.ts b/src/util/Jenkins.ts new file mode 100644 index 00000000..b4eb2d49 --- /dev/null +++ b/src/util/Jenkins.ts @@ -0,0 +1,62 @@ +import axios from "axios"; + +const JENKINS_URL: string = "https://ci.scissors.gg/job/" +const ARTIFACT_NAME: string = "Scissors" + +export type Job = { + name: string, + url: string +} + +export type Build = { + number: number, + url: string, + changes?: BuildChange[], + timestamp?: number +} + +export type BuildChange = { + comment: string, + id: string, +} +export function getJobs(): Promise { + let jobs: Job[] = [] + let request = axios.get(`${JENKINS_URL}/${ARTIFACT_NAME}/api/json?pretty=true`) + + return new Promise((resolve, reject) => { + request.then(value => { + jobs = value.data.jobs as Job[] + resolve(jobs) + }).catch(() => reject) + }) +} + +export function getBuilds(version: string): Promise { + let builds: Build[] = [] + let request = axios.get(`${JENKINS_URL}/${ARTIFACT_NAME}/job/${version}/api/json?pretty=true`) + + return new Promise((resolve, reject) => { + request.then(value => { + builds = value.data.builds as Build[] + let count = 0; + for (let build of builds) { + axios.get(`${JENKINS_URL}/${ARTIFACT_NAME}/job/${version}/${build.number}/api/json?pretty=true`).then(value1 => { + build.timestamp = value1.data.timestamp + let changeSet: any[] = value1.data.changeSets + if (changeSet.length > 0) { + let changes = changeSet[0].items as BuildChange[] + for (let change of changes) { + change.id = change.id.substring(0, 7) + } + build.changes = changes + count++; + if (count === builds.length - 1) { + resolve(builds) + } + } + }) + } + }).catch(() => reject) + }) +} + diff --git a/tsconfig.json b/tsconfig.json index a273b0cf..d48d3f4d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2015", "lib": [ "dom", "dom.iterable",