From 91867f285aa68ebf32cc5f162407145c640f4ec4 Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 10 Dec 2023 17:50:24 -0800 Subject: [PATCH 01/12] Add GET /staticContent/:id endpoint --- server/api/staticContent.js | 42 +++++++++++++++++++++++++ server/index.js | 2 ++ static_content/voting_instructions.html | 6 ++++ 3 files changed, 50 insertions(+) create mode 100644 server/api/staticContent.js create mode 100644 static_content/voting_instructions.html diff --git a/server/api/staticContent.js b/server/api/staticContent.js new file mode 100644 index 00000000..cd6cacd9 --- /dev/null +++ b/server/api/staticContent.js @@ -0,0 +1,42 @@ +const axios = require('axios'); + +const db = require('../db'); +const { createLogger } = require('../logger'); +const memcache = require('../memcache'); + +const logger = createLogger('API/STATIC_CONTENT'); + +/** + * Get static content from Firebase. + * + * @param {Object} req - Express request + * @param {Object} req.params - Parameters on request + * @param {string} req.params.id - DB id of static content to fetch + * @param {*} res - Express response + */ +exports.get = async ({ params: { id } }, res) => { + try { + let markdown = false; + const content = await memcache.get(`sc/${id}`, async () => { + const [result = {}] = await db.select('SELECT url, markdown FROM static_content WHERE id = $1', [id]); + + const { url } = result; + if (!url) { + return null; + } + markdown = result.markdown; + + const { data } = await axios.get(url); + return data; + }); + if (!content) { + logger.info(`Static content with id='${id}' not found`); + res.status(404).send(); + } + + res.send({ content, markdown }); + } catch (err) { + logger.warn(`Error getting /${id}: ${err}`); + res.status(500).send(); + } +}; diff --git a/server/index.js b/server/index.js index 151d3bae..ae137c89 100644 --- a/server/index.js +++ b/server/index.js @@ -17,6 +17,7 @@ const init = require('./api/init'); const reviewSubmissions = require('./api/reviewSubmissions'); const revokeToken = require('./api/revokeToken'); const settings = require('./api/settings'); +const staticContent = require('./api/staticContent'); const submission = require('./api/submission'); const { checkRequiredFields } = require('./api/validation'); const votes = require('./api/votes'); @@ -110,6 +111,7 @@ if (!IS_DEV && cluster.isMaster) { apiRouter.get('/init', processUser(true), init.get); apiRouter.get('/revokeToken/:refreshToken', revokeToken.get); apiRouter.route('/settings').all(requireAuthentication).get(settings.get).put(settings.put); + apiRouter.get('/staticContent/:id', staticContent.get); apiRouter .route('/submission') .get(processUser(false), submission.get) diff --git a/static_content/voting_instructions.html b/static_content/voting_instructions.html new file mode 100644 index 00000000..c24d39ee --- /dev/null +++ b/static_content/voting_instructions.html @@ -0,0 +1,6 @@ + \ No newline at end of file From cbe58bd1b158f81efdc08e68b72681748ec2609e Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 10 Dec 2023 18:17:39 -0800 Subject: [PATCH 02/12] Create static content component --- react-ui/package-lock.json | 12 +++---- react-ui/src/components/HtmlWrapper.jsx | 3 -- react-ui/src/components/StaticContent.jsx | 38 +++++++++++++++++++++++ react-ui/src/components/index.js | 1 + react-ui/src/data/useSwrAuth.jsx | 29 +++++++++++++++++ react-ui/src/data/useSwrStaticContent.jsx | 5 +++ 6 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 react-ui/src/components/StaticContent.jsx create mode 100644 react-ui/src/data/useSwrAuth.jsx create mode 100644 react-ui/src/data/useSwrStaticContent.jsx diff --git a/react-ui/package-lock.json b/react-ui/package-lock.json index c22ffbc6..1e91c68c 100644 --- a/react-ui/package-lock.json +++ b/react-ui/package-lock.json @@ -6453,9 +6453,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001481", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz", - "integrity": "sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==", + "version": "1.0.30001568", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001568.tgz", + "integrity": "sha512-vSUkH84HontZJ88MiNrOau1EBrCqEQYgkC5gIySiDlpsm8sGVrhU7Kx4V6h0tnqaHzIHZv08HlJIwPbL4XL9+A==", "funding": [ { "type": "opencollective", @@ -23628,9 +23628,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001481", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001481.tgz", - "integrity": "sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==" + "version": "1.0.30001568", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001568.tgz", + "integrity": "sha512-vSUkH84HontZJ88MiNrOau1EBrCqEQYgkC5gIySiDlpsm8sGVrhU7Kx4V6h0tnqaHzIHZv08HlJIwPbL4XL9+A==" }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", diff --git a/react-ui/src/components/HtmlWrapper.jsx b/react-ui/src/components/HtmlWrapper.jsx index 1a332372..42bb0c77 100644 --- a/react-ui/src/components/HtmlWrapper.jsx +++ b/react-ui/src/components/HtmlWrapper.jsx @@ -45,9 +45,6 @@ HtmlWrapper.propTypes = { HtmlWrapper.defaultProps = { className: undefined, -}; - -HtmlWrapper.defaultProps = { html: '', }; diff --git a/react-ui/src/components/StaticContent.jsx b/react-ui/src/components/StaticContent.jsx new file mode 100644 index 00000000..9e5bf1c8 --- /dev/null +++ b/react-ui/src/components/StaticContent.jsx @@ -0,0 +1,38 @@ +import PropTypes from 'prop-types'; + +import useSwrContest from '../data/useSwrStaticContent'; + +import HtmlWrapper from './HtmlWrapper'; +import RedditMarkdown from './RedditMarkdown'; + +/** + * Renders static content from Firebase. + * @param props + * @param {string} [props.className] - Additional classes to apply. + * @param {string} props.id - DB ID of static content. + */ +function StaticContent({ className, id }) { + const { data } = useSwrContest(id); + if (!data) { + return null; + } + + const { content, markdown } = data; + + return markdown ? ( + + ) : ( + + ); +} + +StaticContent.propTypes = { + className: PropTypes.string, + id: PropTypes.string.isRequired, +}; + +StaticContent.defaultProps = { + className: undefined, +}; + +export default StaticContent; diff --git a/react-ui/src/components/index.js b/react-ui/src/components/index.js index 8caf0b2f..c07f543a 100644 --- a/react-ui/src/components/index.js +++ b/react-ui/src/components/index.js @@ -32,6 +32,7 @@ export { default as RedditLogInDialog } from './RedditLogInDialog'; export { default as RedditMarkdown } from './RedditMarkdown'; export { default as RedditUserAttribution } from './RedditUserAttribution'; export { default as SpinnerButton } from './SpinnerButton'; +export { default as StaticContent } from './StaticContent'; export { default as SubmissionsTable } from './SubmissionsTable'; export { default as TabPanel } from './TabPanel'; export { default as VotingSlider } from './VotingSlider'; diff --git a/react-ui/src/data/useSwrAuth.jsx b/react-ui/src/data/useSwrAuth.jsx new file mode 100644 index 00000000..160083fc --- /dev/null +++ b/react-ui/src/data/useSwrAuth.jsx @@ -0,0 +1,29 @@ +/** + * Fetch data + */ + +import useSWR from 'swr'; + +import { useAuthState } from '../common'; + +const useSwrAuth = (path, options = {}) => { + const [{ authTokens }] = useAuthState(); + + // https://swr.vercel.app/docs/arguments + const uniqueKey = [path, authTokens]; + // fetcher is set as provider in App.jsx and calls getData in index.js + const { + data, error, isLoading, isValidating, mutate, + } = useSWR(uniqueKey, options); + + // isValidating vs isLoading: https://swr.vercel.app/docs/advanced/understanding + return { + data: data || {}, + error, + isLoading, + isValidating, + mutate, + }; +}; + +export default useSwrAuth; diff --git a/react-ui/src/data/useSwrStaticContent.jsx b/react-ui/src/data/useSwrStaticContent.jsx new file mode 100644 index 00000000..d4ec18a1 --- /dev/null +++ b/react-ui/src/data/useSwrStaticContent.jsx @@ -0,0 +1,5 @@ +import useSwrAuth from './useSwrAuth'; + +const useSwrContest = (id) => useSwrAuth(`/staticContent/${id}`); + +export default useSwrContest; From 72a8447009a4d57b476dea9b0ea3cb95117b9755 Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 10 Dec 2023 18:18:30 -0800 Subject: [PATCH 03/12] Add voting instructions static content --- react-ui/src/pages/contest/Contest.jsx | 33 +++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/react-ui/src/pages/contest/Contest.jsx b/react-ui/src/pages/contest/Contest.jsx index ca92f08d..0b11ca05 100644 --- a/react-ui/src/pages/contest/Contest.jsx +++ b/react-ui/src/pages/contest/Contest.jsx @@ -13,16 +13,13 @@ import { } from 'react-router-dom'; import { animateScroll } from 'react-scroll'; -import { - useCache, - useSwrData, -} from '../../common'; +import { useCache, useSwrData } from '../../common'; import { EntryDescriptionDrawer, - HtmlWrapper, PageContainer, PageWithDrawer, RedditLogInDialog, + StaticContent, } from '../../components'; import ContestAppBarMain from './ContestAppBarMain'; @@ -75,9 +72,12 @@ function Contest() { // Scroll to top when unmounted // For site-wide solution see https://reactrouter.com/en/main/components/scroll-restoration - useEffect(() => () => { - scrollInstantlyTo(0); - }, []); + useEffect( + () => () => { + scrollInstantlyTo(0); + }, + [], + ); // Check for elements in viewport when isLoaded changes useEffect(() => { @@ -196,12 +196,7 @@ function Contest() { const { headingVariant } = useContestSizing(); const { - categories, - isContestMode, - name, - subtext, - votingWindowOpen, - winners, + categories, isContestMode, name, votingWindowOpen, winners, } = contest; return ( @@ -214,7 +209,11 @@ function Contest() { right: , children: , }} - drawer={drawerEntryId ? { heading: 'Info', children: } : { heading: 'Settings', children: }} + drawer={ + drawerEntryId + ? { heading: 'Info', children: } + : { heading: 'Settings', children: } + } > {name && ( @@ -223,10 +222,10 @@ function Contest() { {name} {votingWindowOpen === false && } - {isContestMode && subtext && ( + {isContestMode && ( - + )} From faba487436204a82ddb00a1bf32c24ddf311deba Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 10 Dec 2023 21:32:08 -0800 Subject: [PATCH 04/12] Add README to static_content directory --- static_content/README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 static_content/README.md diff --git a/static_content/README.md b/static_content/README.md new file mode 100644 index 00000000..95bf1970 --- /dev/null +++ b/static_content/README.md @@ -0,0 +1,2 @@ +The files in this directory are copies of the files that are stored in Firebase, placed here for +reference. Best effort should be made to keep them in sync, but Firebase is the source of truth. \ No newline at end of file From c943b71361a1091561de6f9b4a426ff93006ca0d Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 10 Dec 2023 21:41:15 -0800 Subject: [PATCH 05/12] Correct hook name --- react-ui/src/components/StaticContent.jsx | 4 ++-- react-ui/src/data/useSwrStaticContent.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/react-ui/src/components/StaticContent.jsx b/react-ui/src/components/StaticContent.jsx index 9e5bf1c8..7ed44e88 100644 --- a/react-ui/src/components/StaticContent.jsx +++ b/react-ui/src/components/StaticContent.jsx @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; -import useSwrContest from '../data/useSwrStaticContent'; +import useSwrStaticContent from '../data/useSwrStaticContent'; import HtmlWrapper from './HtmlWrapper'; import RedditMarkdown from './RedditMarkdown'; @@ -12,7 +12,7 @@ import RedditMarkdown from './RedditMarkdown'; * @param {string} props.id - DB ID of static content. */ function StaticContent({ className, id }) { - const { data } = useSwrContest(id); + const { data } = useSwrStaticContent(id); if (!data) { return null; } diff --git a/react-ui/src/data/useSwrStaticContent.jsx b/react-ui/src/data/useSwrStaticContent.jsx index d4ec18a1..8fe9d5a7 100644 --- a/react-ui/src/data/useSwrStaticContent.jsx +++ b/react-ui/src/data/useSwrStaticContent.jsx @@ -1,5 +1,5 @@ import useSwrAuth from './useSwrAuth'; -const useSwrContest = (id) => useSwrAuth(`/staticContent/${id}`); +const useSwrStaticContent = (id) => useSwrAuth(`/staticContent/${id}`); -export default useSwrContest; +export default useSwrStaticContent; From e1f73fa421f8f3df7e266e1cc30ea87b2336d26c Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Mon, 25 Dec 2023 17:41:31 -0800 Subject: [PATCH 06/12] Stop using firebase for static content --- react-ui/package-lock.json | 12 ++++++------ .../src/pages/contest/ContestAppBarMain.jsx | 16 +++++----------- .../src/pages/contest/ContestAppBarRight.jsx | 15 +++++---------- server/api/staticContent.js | 18 +++++------------- 4 files changed, 21 insertions(+), 40 deletions(-) diff --git a/react-ui/package-lock.json b/react-ui/package-lock.json index 1e91c68c..28a4832e 100644 --- a/react-ui/package-lock.json +++ b/react-ui/package-lock.json @@ -6453,9 +6453,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001568", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001568.tgz", - "integrity": "sha512-vSUkH84HontZJ88MiNrOau1EBrCqEQYgkC5gIySiDlpsm8sGVrhU7Kx4V6h0tnqaHzIHZv08HlJIwPbL4XL9+A==", + "version": "1.0.30001571", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz", + "integrity": "sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==", "funding": [ { "type": "opencollective", @@ -23628,9 +23628,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001568", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001568.tgz", - "integrity": "sha512-vSUkH84HontZJ88MiNrOau1EBrCqEQYgkC5gIySiDlpsm8sGVrhU7Kx4V6h0tnqaHzIHZv08HlJIwPbL4XL9+A==" + "version": "1.0.30001571", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz", + "integrity": "sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==" }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", diff --git a/react-ui/src/pages/contest/ContestAppBarMain.jsx b/react-ui/src/pages/contest/ContestAppBarMain.jsx index 00e44324..1f2faade 100644 --- a/react-ui/src/pages/contest/ContestAppBarMain.jsx +++ b/react-ui/src/pages/contest/ContestAppBarMain.jsx @@ -6,10 +6,7 @@ import Box from '@material-ui/core/Box'; import PropTypes from 'prop-types'; import { useLocation } from 'react-router-dom'; -import { - RouterLinkIconButton, - Countdown, -} from '../../components'; +import { RouterLinkIconButton, Countdown } from '../../components'; export default function ContestAppBarMain({ handleVotingExpired, handleReload, contest }) { const { state = {} } = useLocation(); @@ -17,10 +14,7 @@ export default function ContestAppBarMain({ handleVotingExpired, handleReload, c const backLink = (state || {}).back || '/contests'; const { - date, - isContestMode, - name, - voteEnd, + date, isContestMode, name, voteEnd, } = contest; if (!name) { @@ -49,12 +43,12 @@ export default function ContestAppBarMain({ handleVotingExpired, handleReload, c ContestAppBarMain.propTypes = { handleReload: PropTypes.func.isRequired, handleVotingExpired: PropTypes.func.isRequired, - contest: { + contest: PropTypes.shape({ date: PropTypes.string.isRequired, isContestMode: PropTypes.bool, name: PropTypes.string, - voteEnd: PropTypes.instanceOf(Date), - }, + voteEnd: PropTypes.string, + }), }; ContestAppBarMain.defaultProps = { diff --git a/react-ui/src/pages/contest/ContestAppBarRight.jsx b/react-ui/src/pages/contest/ContestAppBarRight.jsx index 0e36456a..37fe02f9 100644 --- a/react-ui/src/pages/contest/ContestAppBarRight.jsx +++ b/react-ui/src/pages/contest/ContestAppBarRight.jsx @@ -8,18 +8,13 @@ import ThumbsUpDownOutlinedIcon from '@material-ui/icons/ThumbsUpDownOutlined'; import PropTypes from 'prop-types'; import { useParams } from 'react-router-dom'; -import { - CustomIconButton, -} from '../../components'; +import { CustomIconButton } from '../../components'; export default function ContestAppBarRight({ toggleDrawerOpen, contest }) { const { contestId } = useParams(); const { - localVoting, - name, - validRedditId, - winnersThreadId, + localVoting, name, validRedditId, winnersThreadId, } = contest; if (!name) { @@ -53,16 +48,16 @@ export default function ContestAppBarRight({ toggleDrawerOpen, contest }) { ContestAppBarRight.propTypes = { toggleDrawerOpen: PropTypes.func, - contest: { + contest: PropTypes.shape({ localVoting: PropTypes.bool, name: PropTypes.string, validRedditId: PropTypes.bool, winnersThreadId: PropTypes.string, - }, + }), }; ContestAppBarRight.defaultProps = { - toggleDrawerOpen: () => { }, + toggleDrawerOpen: () => {}, contest: { localVoting: true, name: undefined, diff --git a/server/api/staticContent.js b/server/api/staticContent.js index cd6cacd9..8d237ccf 100644 --- a/server/api/staticContent.js +++ b/server/api/staticContent.js @@ -16,19 +16,11 @@ const logger = createLogger('API/STATIC_CONTENT'); */ exports.get = async ({ params: { id } }, res) => { try { - let markdown = false; - const content = await memcache.get(`sc/${id}`, async () => { - const [result = {}] = await db.select('SELECT url, markdown FROM static_content WHERE id = $1', [id]); - - const { url } = result; - if (!url) { - return null; - } - markdown = result.markdown; - - const { data } = await axios.get(url); - return data; - }); + const [result = {}] = await db.select( + 'SELECT content, markdown FROM static_content WHERE id = $1', + [id], + ); + const { content, markdown } = result; if (!content) { logger.info(`Static content with id='${id}' not found`); res.status(404).send(); From d78df30eb5711bdec6d9c83d8591e854c6786071 Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 21 Jan 2024 18:16:08 -0800 Subject: [PATCH 07/12] Add Contest Rules modal dialog --- react-ui/src/App.jsx | 7 +- .../src/pages/submission/ContestRules.jsx | 30 ++++++++ react-ui/src/pages/submission/Submission.jsx | 10 ++- static_content/contest_rules.html | 69 +++++++++++++++++++ 4 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 react-ui/src/pages/submission/ContestRules.jsx create mode 100644 static_content/contest_rules.html diff --git a/react-ui/src/App.jsx b/react-ui/src/App.jsx index cce0f04d..ebbbd073 100644 --- a/react-ui/src/App.jsx +++ b/react-ui/src/App.jsx @@ -22,6 +22,9 @@ import { Settings, Submission, } from './pages'; +// TODO: Remove when removing pages index file +// eslint-disable-next-line no-restricted-imports +import ContestRules from './pages/submission/ContestRules'; function App() { return ( @@ -61,7 +64,9 @@ function ModalSwitch() { } /> } /> - } /> + }> + } /> + } diff --git a/react-ui/src/pages/submission/ContestRules.jsx b/react-ui/src/pages/submission/ContestRules.jsx new file mode 100644 index 00000000..8645b367 --- /dev/null +++ b/react-ui/src/pages/submission/ContestRules.jsx @@ -0,0 +1,30 @@ +// TODO: Remove when deleting index files +/* eslint-disable no-restricted-imports */ +import Dialog from '@material-ui/core/Dialog'; +import { makeStyles } from '@material-ui/core/styles'; + +import Header from '../../components/Header'; +import PageContainer from '../../components/PageContainer'; +import StaticContent from '../../components/StaticContent'; + +const useStyles = makeStyles((theme) => ({ + container: { + marginTop: theme.spacing(3), + }, +})); + +function ContestRules() { + const classes = useStyles(); + return ( + +
+ Contest Rules +
+ + + +
+ ); +} + +export default ContestRules; diff --git a/react-ui/src/pages/submission/Submission.jsx b/react-ui/src/pages/submission/Submission.jsx index e1bf7d74..712630b9 100644 --- a/react-ui/src/pages/submission/Submission.jsx +++ b/react-ui/src/pages/submission/Submission.jsx @@ -23,7 +23,7 @@ import parseISO from 'date-fns/parseISO'; import debounce from 'lodash/debounce'; import pluralize from 'pluralize'; import { useRef, useState } from 'react'; -import { useLocation } from 'react-router-dom'; +import { Outlet, useLocation } from 'react-router-dom'; import { postData } from '../../api'; import { @@ -443,7 +443,12 @@ function Submission() { - {contestId === 'may23' ? : } + <> + {contestId === 'may23' ? : } + + Please read the contest rules in full before submitting. + + )} + ); } diff --git a/static_content/contest_rules.html b/static_content/contest_rules.html new file mode 100644 index 00000000..e465ebe2 --- /dev/null +++ b/static_content/contest_rules.html @@ -0,0 +1,69 @@ +
+ You will be asked to confirm you followed each rule upon submission. Repeated rule + violators will be banned from the contest for the rest of the year. +
    +
  • + + Rule 1: Submission limit + + - You may submit up to + TWO + entries to each contest. + In the event + you submit more than two designs, moderators will reach out via Reddit Direct + Messages for clarification. + In the event + clarification is not received, the two most recent entries will be accepted, while + all others will be rejected. +
  • +
  • + + Rule 2: Technical limit + + - Flags should be at most + 3000 pixels wide + . They should also be flat and not textured. +
  • +
  • + + Rule 3: Sharing prohibition + + - + DO NOT + post your flag designs elsewhere on the r/vexillology subreddit before the winners + are announced. Don't post it as another thread. Don't post it as a response to the + contest announcement thread. The contest aims to keep the submissions anonymous + until results are announced, so as to avoid a popularity contest. +
  • +
  • + + Rule 4: Attribution requirement + + - If you take components from public domain, or other sources that are not your own + work, please attribute them in the description portion of your submission. +
  • +
  • + + Rule 5: Sincerity requirement + + - Your flag should be a sincere attempt to respond to the prompt. It should not be + designed to troll. +
  • +
  • + + Rule 6: Deadline + + - Entries are generally due by the + 18th + of the month, unless otherwise specified in the contest prompt. +
  • +
  • + + Rule 7: Contest specific rules + + - Each contest will have specific rules outlined in the announcement thread. Please + read them and follow them accordingly. +
  • +
+ Best of luck! +
\ No newline at end of file From 4f6a7c2afef4cfbd68df7c83edab0a32d74c81e0 Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 21 Jan 2024 18:19:35 -0800 Subject: [PATCH 08/12] Format HTML file --- static_content/contest_rules.html | 128 +++++++++++++++--------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/static_content/contest_rules.html b/static_content/contest_rules.html index e465ebe2..1d92661a 100644 --- a/static_content/contest_rules.html +++ b/static_content/contest_rules.html @@ -1,69 +1,69 @@
- You will be asked to confirm you followed each rule upon submission. Repeated rule - violators will be banned from the contest for the rest of the year. + You will be asked to confirm you followed each rule upon submission. Repeated rule violators will + be banned from the contest for the rest of the year.
    -
  • - - Rule 1: Submission limit - - - You may submit up to - TWO - entries to each contest. - In the event - you submit more than two designs, moderators will reach out via Reddit Direct - Messages for clarification. - In the event - clarification is not received, the two most recent entries will be accepted, while - all others will be rejected. -
  • -
  • - - Rule 2: Technical limit - - - Flags should be at most - 3000 pixels wide - . They should also be flat and not textured. -
  • -
  • - - Rule 3: Sharing prohibition - - - - DO NOT - post your flag designs elsewhere on the r/vexillology subreddit before the winners - are announced. Don't post it as another thread. Don't post it as a response to the - contest announcement thread. The contest aims to keep the submissions anonymous - until results are announced, so as to avoid a popularity contest. -
  • -
  • - - Rule 4: Attribution requirement - - - If you take components from public domain, or other sources that are not your own - work, please attribute them in the description portion of your submission. -
  • -
  • - - Rule 5: Sincerity requirement - - - Your flag should be a sincere attempt to respond to the prompt. It should not be - designed to troll. -
  • -
  • - - Rule 6: Deadline - - - Entries are generally due by the - 18th - of the month, unless otherwise specified in the contest prompt. -
  • -
  • - - Rule 7: Contest specific rules - - - Each contest will have specific rules outlined in the announcement thread. Please - read them and follow them accordingly. -
  • +
  • + + Rule 1: Submission limit + + - You may submit up to + TWO + entries to each contest. + In the event + you submit more than two designs, moderators will reach out via Reddit Direct Messages for + clarification. + In the event + clarification is not received, the two most recent entries will be accepted, while all others + will be rejected. +
  • +
  • + + Rule 2: Technical limit + + - Flags should be at most + 3000 pixels wide + . They should also be flat and not textured. +
  • +
  • + + Rule 3: Sharing prohibition + + - + DO NOT + post your flag designs elsewhere on the r/vexillology subreddit before the winners are + announced. Don't post it as another thread. Don't post it as a response to the contest + announcement thread. The contest aims to keep the submissions anonymous until results are + announced, so as to avoid a popularity contest. +
  • +
  • + + Rule 4: Attribution requirement + + - If you take components from public domain, or other sources that are not your own work, + please attribute them in the description portion of your submission. +
  • +
  • + + Rule 5: Sincerity requirement + + - Your flag should be a sincere attempt to respond to the prompt. It should not be designed to + troll. +
  • +
  • + + Rule 6: Deadline + + - Entries are generally due by the + 18th + of the month, unless otherwise specified in the contest prompt. +
  • +
  • + + Rule 7: Contest specific rules + + - Each contest will have specific rules outlined in the announcement thread. Please read them + and follow them accordingly. +
Best of luck!
\ No newline at end of file From a48ce78b2b6d9531e46e5edd761ca491b63a329b Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 21 Jan 2024 18:19:56 -0800 Subject: [PATCH 09/12] Add trailing whitespace --- static_content/contest_rules.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static_content/contest_rules.html b/static_content/contest_rules.html index 1d92661a..96653957 100644 --- a/static_content/contest_rules.html +++ b/static_content/contest_rules.html @@ -66,4 +66,4 @@ Best of luck! - \ No newline at end of file + From 9decbc47747ae3eff0553843bc67d420aa5c28bf Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 15 Sep 2024 15:17:43 -0700 Subject: [PATCH 10/12] Fix merge issues --- react-ui/src/pages/contest/Contest.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/react-ui/src/pages/contest/Contest.jsx b/react-ui/src/pages/contest/Contest.jsx index 74564e7c..68a72323 100644 --- a/react-ui/src/pages/contest/Contest.jsx +++ b/react-ui/src/pages/contest/Contest.jsx @@ -12,7 +12,6 @@ import { forceCheck } from 'react-lazyload'; import { Outlet, useLocation, useNavigate } from 'react-router-dom'; import { animateScroll } from 'react-scroll'; -import { useCache, useSwrData } from '../../common'; import { EntryDescriptionDrawer, PageContainer, From d3171276e63a57574f689aa9b40bb6cd45ec8560 Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 15 Sep 2024 15:25:25 -0700 Subject: [PATCH 11/12] Fix lint warnings --- react-ui/src/components/MenuItemLink.jsx | 2 +- react-ui/src/pages/hallOfFame/HallOfFameCard.jsx | 1 - react-ui/src/pages/submission/Submission.jsx | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/react-ui/src/components/MenuItemLink.jsx b/react-ui/src/components/MenuItemLink.jsx index 20e3f2ea..4371aec3 100644 --- a/react-ui/src/components/MenuItemLink.jsx +++ b/react-ui/src/components/MenuItemLink.jsx @@ -12,8 +12,8 @@ import { Link } from 'react-router-dom'; import types from '../common/types'; function menuItemComponent({ state, to }) { - // eslint-disable-next-line react/jsx-props-no-spreading return forwardRef((itemProps, ref) => ( + // eslint-disable-next-line react/jsx-props-no-spreading )); } diff --git a/react-ui/src/pages/hallOfFame/HallOfFameCard.jsx b/react-ui/src/pages/hallOfFame/HallOfFameCard.jsx index 0cb5f303..1a706f1c 100644 --- a/react-ui/src/pages/hallOfFame/HallOfFameCard.jsx +++ b/react-ui/src/pages/hallOfFame/HallOfFameCard.jsx @@ -20,7 +20,6 @@ import { ExternalLink, FormattedContent, LazyLoadCardImage, - RedditMarkdown, RedditUserAttribution, } from '../../components'; import flair from '../../images/flair.png'; diff --git a/react-ui/src/pages/submission/Submission.jsx b/react-ui/src/pages/submission/Submission.jsx index 8fcf05a1..db80e0b1 100644 --- a/react-ui/src/pages/submission/Submission.jsx +++ b/react-ui/src/pages/submission/Submission.jsx @@ -9,7 +9,7 @@ import { makeStyles } from '@material-ui/core/styles'; import isFuture from 'date-fns/isFuture'; import parseISO from 'date-fns/parseISO'; import { useState } from 'react'; -import { useLocation } from 'react-router-dom'; +import { Outlet, useLocation } from 'react-router-dom'; import countdownTypes from '../../common/countdownTypes'; import { From d3798feefac0b555f3f7700bc7c8f8ed34ce2890 Mon Sep 17 00:00:00 2001 From: Hesham Mourad Date: Sun, 15 Sep 2024 15:49:10 -0700 Subject: [PATCH 12/12] Show contest rules link at the end of the submission prompt --- react-ui/src/pages/submission/Submission.jsx | 3 +-- .../src/pages/submission/SubmissionPrompt.jsx | 23 +++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/react-ui/src/pages/submission/Submission.jsx b/react-ui/src/pages/submission/Submission.jsx index db80e0b1..8f431818 100644 --- a/react-ui/src/pages/submission/Submission.jsx +++ b/react-ui/src/pages/submission/Submission.jsx @@ -9,7 +9,7 @@ import { makeStyles } from '@material-ui/core/styles'; import isFuture from 'date-fns/isFuture'; import parseISO from 'date-fns/parseISO'; import { useState } from 'react'; -import { Outlet, useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import countdownTypes from '../../common/countdownTypes'; import { @@ -134,7 +134,6 @@ function Submission() { noData )} - ); } diff --git a/react-ui/src/pages/submission/SubmissionPrompt.jsx b/react-ui/src/pages/submission/SubmissionPrompt.jsx index cf23c16c..9599706f 100644 --- a/react-ui/src/pages/submission/SubmissionPrompt.jsx +++ b/react-ui/src/pages/submission/SubmissionPrompt.jsx @@ -1,20 +1,33 @@ /** * Prompt for contest submission */ +import Typography from '@material-ui/core/Typography'; import PropTypes from 'prop-types'; +import { Outlet } from 'react-router-dom'; -import { FormattedContent } from '../../components'; +import { FormattedContent, InternalLink } from '../../components'; import May23 from './content/May23'; function SubmissionPrompt({ contestId, prompt }) { - if (contestId === 'may23') { - return ; - } if (!prompt) { return null; } - return ; + let content = ; + if (contestId === 'may23') { + content = ; + } + return ( + <> + {content} + + + Please read the contest rules in full before submitting. + + + + + ); } export default SubmissionPrompt;