forked from partypages/party-template
Compare commits
6 Commits
dfc2d315c4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c4448a151 | ||
| 929a7080b8 | |||
| d5b0a772dd | |||
| 0b5111dfea | |||
| 6120b43b55 | |||
|
|
613a8cae1f |
@@ -1,4 +1,4 @@
|
|||||||
import React, { createContext, useEffect, useMemo, useState } from 'react';
|
import React, { createContext, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { getPartyStatusRequest, getSelfStatusRequest, parseURI } from './partyApi';
|
import { getPartyStatusRequest, getSelfStatusRequest, parseURI } from './partyApi';
|
||||||
import './PartyPage.css';
|
import './PartyPage.css';
|
||||||
|
|
||||||
@@ -20,11 +20,16 @@ export type PartyStatus = {
|
|||||||
export type SelfStatus = {
|
export type SelfStatus = {
|
||||||
token: string,
|
token: string,
|
||||||
name: string,
|
name: string,
|
||||||
coming: "yes" | "no" | "maybe",
|
coming: "yes" | "no" | "maybe" | null,
|
||||||
"grammatical_gender": "m" | "f" | "d",
|
"grammatical_gender": "m" | "f" | "d",
|
||||||
extra?: SelfStatusExtraData,
|
extra?: SelfStatusExtraData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UpdatableSelfStatus = {
|
||||||
|
coming?: "yes" | "no" | "maybe" | null,
|
||||||
|
extra?: SelfStatusExtraData,
|
||||||
|
}
|
||||||
|
|
||||||
export type APIEndPoint = { partyName: string, token: string };
|
export type APIEndPoint = { partyName: string, token: string };
|
||||||
|
|
||||||
// Adapt this type to your desires
|
// Adapt this type to your desires
|
||||||
@@ -43,18 +48,17 @@ export const PartyContextProvider: React.FC<{ children: React.ReactNode }> = (pr
|
|||||||
return p;
|
return p;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadData = async () => {
|
const loadData = useCallback(async () => {
|
||||||
if (partyContext !== undefined) return;
|
if (partyContext !== undefined) return;
|
||||||
const selfStatus = await getSelfStatusRequest(apiEndpoint);
|
const selfStatus = await getSelfStatusRequest(apiEndpoint);
|
||||||
const partyStatus = await getPartyStatusRequest(apiEndpoint);
|
const partyStatus = await getPartyStatusRequest(apiEndpoint);
|
||||||
const ctx = { party: partyStatus, self: selfStatus };
|
const ctx = { party: partyStatus, self: selfStatus };
|
||||||
setPartyContext(ctx);
|
setPartyContext(ctx);
|
||||||
};
|
}, [apiEndpoint, partyContext]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadData();
|
loadData();
|
||||||
// eslint-disable-next-line
|
}, [apiEndpoint, loadData]);
|
||||||
}, [apiEndpoint]);
|
|
||||||
|
|
||||||
return partyContext ?
|
return partyContext ?
|
||||||
<PartyContext.Provider value={partyContext}>
|
<PartyContext.Provider value={partyContext}>
|
||||||
|
|||||||
@@ -54,3 +54,45 @@ h2 {
|
|||||||
p {
|
p {
|
||||||
margin: 0.3em 0 0.3em 0;
|
margin: 0.3em 0 0.3em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.feedback {
|
||||||
|
line-height: 3em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]+label {
|
||||||
|
font-size: larger;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 1em 0 1em;
|
||||||
|
border-right: 0.1em solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]+label:hover {
|
||||||
|
text-shadow: 0 0 1em white;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]+label:last-of-type {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="radio"]:checked+label {
|
||||||
|
color: var(--selected-color);
|
||||||
|
text-shadow: 0 0 1em var(--selected-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coming-yes+label {
|
||||||
|
--selected-color: #0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coming-maybe+label {
|
||||||
|
--selected-color: #fc0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coming-no+label {
|
||||||
|
--selected-color: #f00;
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
|
|
||||||
import React, { useContext } from 'react';
|
import React, { ChangeEvent, useContext, useState } from 'react';
|
||||||
import './PartyPage.css';
|
import './PartyPage.css';
|
||||||
import { PartyContext } from './PartyContext';
|
import { APIEndPoint, PartyContext } from './PartyContext';
|
||||||
import MatrixBackground from './MatrixBackground';
|
import MatrixBackground from './MatrixBackground';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faAngleDown, faCalendarDays, faLocationDot } from '@fortawesome/free-solid-svg-icons';
|
import { faAngleDown, faCalendarDays, faLocationDot } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { modifySelfRequest, parseURI } from './partyApi';
|
||||||
|
|
||||||
const myDear = {
|
const myDear = {
|
||||||
"m": "lieber",
|
"m": "lieber",
|
||||||
@@ -18,6 +19,21 @@ export const PartyPage: React.FC = () => {
|
|||||||
const name = partyContext.self.name;
|
const name = partyContext.self.name;
|
||||||
const party = partyContext.party;
|
const party = partyContext.party;
|
||||||
|
|
||||||
|
const [comingState, setComingState] = useState(partyContext.self.coming);
|
||||||
|
|
||||||
|
// SAFETY: If this is undefined, the contextProvider already fails
|
||||||
|
// eslint-disable-next-line no-restricted-globals
|
||||||
|
const endpoint = parseURI(location.href) as APIEndPoint;
|
||||||
|
|
||||||
|
const handleSelect = async (e: ChangeEvent) => {
|
||||||
|
const value = (e.target as HTMLInputElement).value;
|
||||||
|
if (value !== "yes" && value !== "no" && value !== "maybe") {
|
||||||
|
throw new Error("received invalid value?");
|
||||||
|
}
|
||||||
|
const status = await modifySelfRequest(endpoint, { coming: value });
|
||||||
|
setComingState(status.coming);
|
||||||
|
}
|
||||||
|
|
||||||
let coming: string;
|
let coming: string;
|
||||||
if (party.maybe_coming === 0) {
|
if (party.maybe_coming === 0) {
|
||||||
// exact number
|
// exact number
|
||||||
@@ -58,9 +74,12 @@ export const PartyPage: React.FC = () => {
|
|||||||
Wir würden uns sehr freuen, wenn auch du, {dear} {name}, am Start wärst :)
|
Wir würden uns sehr freuen, wenn auch du, {dear} {name}, am Start wärst :)
|
||||||
</p>
|
</p>
|
||||||
<div className='feedback'>
|
<div className='feedback'>
|
||||||
<button>Auf gehts</button>
|
<input type="radio" id="coming-yes" name="coming" value="yes" checked={comingState === "yes"} onChange={handleSelect} />
|
||||||
<button>Hmm vielleicht</button>
|
<label htmlFor='coming-yes'>Ja</label>
|
||||||
<button>Nee sorry</button>
|
<input type="radio" id="coming-maybe" name="coming" value="maybe" checked={comingState === "maybe"} onChange={handleSelect} />
|
||||||
|
<label htmlFor='coming-maybe'>Vielleicht</label>
|
||||||
|
<input type="radio" id="coming-no" name="coming" value="no" checked={comingState === "no"} onChange={handleSelect} />
|
||||||
|
<label htmlFor='coming-no'>Nein</label>
|
||||||
</div>
|
</div>
|
||||||
<div className='hero-outer'>
|
<div className='hero-outer'>
|
||||||
Mehr Infos
|
Mehr Infos
|
||||||
@@ -73,7 +92,12 @@ export const PartyPage: React.FC = () => {
|
|||||||
<FontAwesomeIcon icon={faCalendarDays} /> <strong>29. Oktober, 19:00</strong> bis <strong>31. Oktober</strong> irgendwann.
|
<FontAwesomeIcon icon={faCalendarDays} /> <strong>29. Oktober, 19:00</strong> bis <strong>31. Oktober</strong> irgendwann.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<FontAwesomeIcon icon={faLocationDot} /> <strong>Mainzer Str. 28</strong>, 66111 Saarbrücken
|
<FontAwesomeIcon icon={faLocationDot} /> <a href="https://www.openstreetmap.org/way/213757745"><strong>Mainzer Str. 28</strong>, 66111 Saarbrücken</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Ja, der 30. ist ein Sonntag.
|
||||||
|
Am 1. 11. ist aber Allerheiligen, also bietet es sich an am 31. nen Brückentag zu machen.
|
||||||
|
So hat man dann auch genug Zeit um nach den 25+ Stunden ordentlich auszuschlafen.
|
||||||
</p>
|
</p>
|
||||||
<h2>Alter ernsthaft 25 Stunden?</h2>
|
<h2>Alter ernsthaft 25 Stunden?</h2>
|
||||||
<p>
|
<p>
|
||||||
@@ -87,6 +111,11 @@ export const PartyPage: React.FC = () => {
|
|||||||
Wir werden ein Curry, Chili o.Ä. kochen.
|
Wir werden ein Curry, Chili o.Ä. kochen.
|
||||||
Bring aber auch gerne Snacks, Getränke, Knoblauchdip oder Kuchen mit :)
|
Bring aber auch gerne Snacks, Getränke, Knoblauchdip oder Kuchen mit :)
|
||||||
</p>
|
</p>
|
||||||
|
<h2>Aber wo soll ich mein Auto hinstellen?</h2>
|
||||||
|
<p>
|
||||||
|
Am Waldhaus gibt es einen kostenlosen <a href="https://www.openstreetmap.org/way/111250120">Parkplatz</a>.
|
||||||
|
Wenn du mit dem Auto kommst sag Bescheid, wir planen uns dort zu treffen und dann mit einem Auto auf Kai's Premium-Parkplatz direkt neben der WG fahren.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { APIEndPoint, PartyStatus, SelfStatus } from "./PartyContext";
|
import { APIEndPoint, PartyStatus, SelfStatus, UpdatableSelfStatus } from "./PartyContext";
|
||||||
|
|
||||||
export const parseURI = (uri: string): APIEndPoint | undefined => {
|
export const parseURI = (uri: string): APIEndPoint | undefined => {
|
||||||
const x = uri.match(/https?:\/\/(?<partyName>\w+)\.(?<host>.+)\/(?<token>.+)/);
|
const x = uri.match(/https?:\/\/(?<partyName>\w+)\.(?<host>.+)\/(?<token>.+)/);
|
||||||
@@ -22,7 +22,14 @@ export const getSelfStatusRequest = async (apiEndpoint: APIEndPoint): Promise<Se
|
|||||||
|
|
||||||
export const getPartyStatusRequest = async (apiEndpoint: APIEndPoint): Promise<PartyStatus> => {
|
export const getPartyStatusRequest = async (apiEndpoint: APIEndPoint): Promise<PartyStatus> => {
|
||||||
const result = await fetch(`${apiUrl(apiEndpoint)}/status`);
|
const result = await fetch(`${apiUrl(apiEndpoint)}/status`);
|
||||||
if (!result.ok) throw new Error("Error sending getSelfRequest");
|
if (!result.ok) throw new Error("Error sending getPartyRequest");
|
||||||
const data = await result.json();
|
const data = await result.json();
|
||||||
return data as PartyStatus;
|
return data as PartyStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const modifySelfRequest = async (apiEndpoint: APIEndPoint, payload: UpdatableSelfStatus): Promise<SelfStatus> => {
|
||||||
|
const result = await fetch(`${apiUrl(apiEndpoint)}/me`, { method: "PATCH", body: JSON.stringify(payload), headers: { "Content-Type": "application/json" } });
|
||||||
|
if (!result.ok) throw new Error("Error sending modifySelfRequest");
|
||||||
|
const data = await result.json();
|
||||||
|
return data as SelfStatus;
|
||||||
|
};
|
||||||
|
|||||||
32
timeline/style.css
Normal file
32
timeline/style.css
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
:root {
|
||||||
|
--fg: red;
|
||||||
|
--bg: black;
|
||||||
|
--accent: white;
|
||||||
|
--mute: gray;
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
background-color: var(--bg);
|
||||||
|
}
|
||||||
|
.timeline {
|
||||||
|
stroke: var(--fg);
|
||||||
|
fill: none;
|
||||||
|
stroke-width: 1px;
|
||||||
|
stroke-dasharray: 314px 314px;
|
||||||
|
stroke-dashoffset: -314px;
|
||||||
|
}
|
||||||
|
.time {
|
||||||
|
stroke: var(--accent);
|
||||||
|
fill: none;
|
||||||
|
stroke-width: 0.1px;
|
||||||
|
}
|
||||||
|
.h0 {
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
.h3 {
|
||||||
|
stroke-width: 0.4px;
|
||||||
|
}
|
||||||
|
.timeline-mute {
|
||||||
|
stroke: var(--mute);
|
||||||
|
fill: none;
|
||||||
|
stroke-width: 0.5px;
|
||||||
|
}
|
||||||
38
timeline/timeline.html
Normal file
38
timeline/timeline.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<svg viewBox="0 0 260 35">
|
||||||
|
<path class="timeline-mute" d="M 5 25 L 245 25"/>
|
||||||
|
<path id="timeline" class="timeline" d="M 5 25 L 25 25 A 5 5 90 0 0 25 15 A 5 5 90 0 0 25 25 L 255 25" />
|
||||||
|
<path class="time h0" d="M 5 24 L 5 26"/>
|
||||||
|
<path class="time " d="M 15 24 L 15 26"/>
|
||||||
|
<path class="time " d="M 25 24 L 25 26"/>
|
||||||
|
<path class="time h3" d="M 35 24 L 35 26"/>
|
||||||
|
<path class="time " d="M 45 24 L 45 26"/>
|
||||||
|
<path class="time " d="M 55 24 L 55 26"/>
|
||||||
|
<path class="time h3" d="M 65 24 L 65 26"/>
|
||||||
|
<path class="time " d="M 75 24 L 75 26"/>
|
||||||
|
<path class="time " d="M 85 24 L 85 26"/>
|
||||||
|
<path class="time h3" d="M 95 24 L 95 26"/>
|
||||||
|
<path class="time " d="M 105 24 L 105 26"/>
|
||||||
|
<path class="time " d="M 115 24 L 115 26"/>
|
||||||
|
<path class="time h0" d="M 125 24 L 125 26"/>
|
||||||
|
<path class="time " d="M 135 24 L 135 26"/>
|
||||||
|
<path class="time " d="M 145 24 L 145 26"/>
|
||||||
|
<path class="time h3" d="M 155 24 L 155 26"/>
|
||||||
|
<path class="time " d="M 165 24 L 165 26"/>
|
||||||
|
<path class="time " d="M 175 24 L 175 26"/>
|
||||||
|
<path class="time h3" d="M 185 24 L 185 26"/>
|
||||||
|
<path class="time " d="M 195 24 L 195 26"/>
|
||||||
|
<path class="time " d="M 205 24 L 205 26"/>
|
||||||
|
<path class="time h3" d="M 215 24 L 215 26"/>
|
||||||
|
<path class="time " d="M 225 24 L 225 26"/>
|
||||||
|
<path class="time " d="M 235 24 L 235 26"/>
|
||||||
|
<path class="time h0" d="M 245 24 L 245 26"/>
|
||||||
|
</svg>
|
||||||
|
<script src="timeline.js" >
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
31
timeline/timeline.js
Normal file
31
timeline/timeline.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
let overridetime = undefined;
|
||||||
|
//overridetime = Date.now();
|
||||||
|
|
||||||
|
// Saturday at 23:00 UTC, the event starts
|
||||||
|
const startTime = overridetime ?? Date.parse('Sat, 29 Oct 2022 23:00:00');
|
||||||
|
|
||||||
|
const timeToOffset = (hours) => {
|
||||||
|
const speed1 = -10;
|
||||||
|
const speed2 = -31;
|
||||||
|
if (hours <= 0) return 0;
|
||||||
|
if (hours >= 25) return 24*speed1 + speed2;
|
||||||
|
if (hours <= 2) {
|
||||||
|
return hours * speed1;
|
||||||
|
} else if (hours > 2 && hours < 3) {
|
||||||
|
return 2*speed1 + (hours-2) * speed2;
|
||||||
|
} else {
|
||||||
|
return 2*speed1 + speed2 + (hours-3) * speed1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
const hoursPassed = (now-startTime) / 1000 / 3600;
|
||||||
|
// test speed here
|
||||||
|
const offset = timeToOffset(hoursPassed);
|
||||||
|
const timeline = document.getElementById("timeline");
|
||||||
|
timeline.style.strokeDashoffset = 314 + offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
update();
|
||||||
|
setInterval(update, 50);
|
||||||
Reference in New Issue
Block a user