diff --git a/crabfit-backend/package.json b/crabfit-backend/package.json index 885330d..ca93155 100644 --- a/crabfit-backend/package.json +++ b/crabfit-backend/package.json @@ -18,6 +18,7 @@ "cors": "^2.8.5", "dayjs": "^1.10.4", "dotenv": "^8.2.0", - "express": "^4.17.1" + "express": "^4.17.1", + "punycode": "^2.1.1" } } diff --git a/crabfit-backend/routes/createEvent.js b/crabfit-backend/routes/createEvent.js index 08562f3..9fa0fe1 100644 --- a/crabfit-backend/routes/createEvent.js +++ b/crabfit-backend/routes/createEvent.js @@ -1,29 +1,49 @@ const dayjs = require('dayjs'); +const punycode = require('punycode/'); const adjectives = require('../res/adjectives.json'); const crabs = require('../res/crabs.json'); -const capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1); - -const generateId = (name) => { - const id = name.trim().toLowerCase().replace(/[^A-Za-z0-9 ]/g, '').replace(/\s+/g, '-'); - const number = Math.floor(100000 + Math.random() * 900000); - return `${id}-${number}`; -}; +const capitalize = string => string.charAt(0).toUpperCase() + string.slice(1); +// Generate a random name based on an adjective and a crab species const generateName = () => { return `${capitalize(adjectives[Math.floor(Math.random() * adjectives.length)])} ${crabs[Math.floor(Math.random() * crabs.length)]} Crab`; }; +// Generate a slug for the crab fit +const generateId = name => { + let id = punycode.encode(name.trim().toLowerCase()).trim().replace(/[^A-Za-z0-9 ]/g, '').replace(/\s+/g, '-'); + if (id.replace(/-/g, '') === '') { + id = generateName().trim().toLowerCase().replace(/[^A-Za-z0-9 ]/g, '').replace(/\s+/g, '-'); + } + const number = Math.floor(100000 + Math.random() * 900000); + return `${id}-${number}`; +}; + module.exports = async (req, res) => { const { event } = req.body; try { const name = event.name.trim() === '' ? generateName() : event.name.trim(); - const eventId = generateId(name); + let eventId = generateId(name); const currentTime = dayjs().unix(); - const entity = { + // Check if the event ID already exists, and if so generate a new one + let eventResult; + do { + const query = req.datastore.createQuery(req.types.event) + .select('__key__') + .filter('__key__', req.datastore.key([req.types.event, eventId])); + + eventResult = (await req.datastore.runQuery(query))[0][0]; + + if (eventResult !== undefined) { + eventId = generateId(name); + } + } while (eventResult !== undefined); + + const entity = { key: req.datastore.key([req.types.event, eventId]), data: { name: name, diff --git a/crabfit-backend/yarn.lock b/crabfit-backend/yarn.lock index 4c35f38..04adf65 100644 --- a/crabfit-backend/yarn.lock +++ b/crabfit-backend/yarn.lock @@ -942,6 +942,11 @@ pumpify@^2.0.1: inherits "^2.0.3" pump "^3.0.0" +punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" diff --git a/crabfit-frontend/src/pages/Event/Event.tsx b/crabfit-frontend/src/pages/Event/Event.tsx index a0184e1..52ea2f1 100644 --- a/crabfit-frontend/src/pages/Event/Event.tsx +++ b/crabfit-frontend/src/pages/Event/Event.tsx @@ -6,6 +6,7 @@ import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; import customParseFormat from 'dayjs/plugin/customParseFormat'; +import relativeTime from 'dayjs/plugin/relativeTime'; import { Center, @@ -23,6 +24,7 @@ import { Logo, Title, EventName, + EventDate, LoginForm, LoginSection, Info, @@ -40,6 +42,7 @@ import timezones from 'res/timezones.json'; dayjs.extend(utc); dayjs.extend(timezone); dayjs.extend(customParseFormat); +dayjs.extend(relativeTime); const Event = (props) => { const timeFormat = useSettingsStore(state => state.timeFormat); @@ -283,6 +286,7 @@ const Event = (props) => { {(!!event || isLoading) ? ( <> {event?.name} + {event?.created && `Created ${dayjs.unix(event?.created).fromNow()}`} navigator.clipboard?.writeText(`https://crab.fit/${id}`) .then(() => { @@ -366,6 +370,22 @@ const Event = (props) => { onChange={event => setTimezone(event.currentTarget.value)} options={timezones} /> + {event?.timezone && event.timezone !== timezone &&

This event was created in the timezone {event.timezone}. { + e.preventDefault(); + setTimezone(event.timezone); + }}>Click here to use it.

} + {(( + Intl.DateTimeFormat().resolvedOptions().timeZone !== timezone + && (event?.timezone && event.timezone !== Intl.DateTimeFormat().resolvedOptions().timeZone) + ) || ( + event?.timezone === undefined + && Intl.DateTimeFormat().resolvedOptions().timeZone !== timezone + )) && ( +

Your local timezone is detected to be {Intl.DateTimeFormat().resolvedOptions().timeZone}. { + e.preventDefault(); + setTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone); + }}>Click here to use it.

+ )} diff --git a/crabfit-frontend/src/pages/Event/eventStyle.ts b/crabfit-frontend/src/pages/Event/eventStyle.ts index f5f3354..2727b99 100644 --- a/crabfit-frontend/src/pages/Event/eventStyle.ts +++ b/crabfit-frontend/src/pages/Event/eventStyle.ts @@ -24,7 +24,7 @@ export const Title = styled.span` export const EventName = styled.h1` text-align: center; font-weight: 800; - margin: 20px 0 14px; + margin: 20px 0 5px; ${props => props.isLoading && ` &:after { @@ -39,6 +39,28 @@ export const EventName = styled.h1` `} `; +export const EventDate = styled.span` + display: block; + text-align: center; + font-size: 14px; + opacity: .8; + margin: 0 0 10px; + font-weight: 500; + letter-spacing: .01em; + + ${props => props.isLoading && ` + &:after { + content: ''; + display: inline-block; + height: 1em; + width: 200px; + max-width: 100%; + background-color: ${props.theme.loading}; + border-radius: 3px; + } + `} +`; + export const LoginForm = styled.form` display: grid; grid-template-columns: 1fr 1fr 100px;