From 73c5cffabbb8b36d877811c4d1b56abf6939bda5 Mon Sep 17 00:00:00 2001 From: quietreading Date: Mon, 7 Jul 2025 17:00:11 -0400 Subject: edits --- locales/en.json | 10 +- public/css/style.css | 74 ++++++++---- public/fonts/charter_bold.woff2 | Bin 0 -> 15028 bytes public/fonts/charter_bold_italic.woff2 | Bin 0 -> 16108 bytes public/fonts/charter_italic.woff2 | Bin 0 -> 15376 bytes public/fonts/charter_regular.woff2 | Bin 0 -> 14648 bytes public/hex.ico | Bin 0 -> 224674 bytes public/js/modules/new.js | 49 ++++---- public/logo2-min.png | Bin 0 -> 65648 bytes public/pictures/bookclub.jpg | Bin 0 -> 325685 bytes public/pictures/jefferson.jpg | Bin 0 -> 183898 bytes public/pictures/marthas.jpg | Bin 0 -> 232435 bytes public/pictures/mccarren.jpg | Bin 0 -> 406425 bytes src/routes/event.ts | 2 + src/routes/frontend.ts | 130 +++++++++++---------- src/util/validation.ts | 6 + static/instance-description-en-US.md | 5 + static/instance-description-en.md | 1 - .../emails/createEvent/createEventHtml.handlebars | 1 + .../emails/createEvent/createEventText.handlebars | 2 + views/layouts/main.handlebars | 25 +--- views/newevent.handlebars | 92 ++++++++++----- views/partials/eventForm.handlebars | 14 +-- views/partials/eventList.handlebars | 7 +- views/partials/sidebar.handlebars | 3 + views/publicEventList.handlebars | 52 +++------ 26 files changed, 266 insertions(+), 207 deletions(-) create mode 100644 public/fonts/charter_bold.woff2 create mode 100644 public/fonts/charter_bold_italic.woff2 create mode 100644 public/fonts/charter_italic.woff2 create mode 100644 public/fonts/charter_regular.woff2 create mode 100644 public/hex.ico create mode 100644 public/logo2-min.png create mode 100644 public/pictures/bookclub.jpg create mode 100644 public/pictures/jefferson.jpg create mode 100644 public/pictures/marthas.jpg create mode 100644 public/pictures/mccarren.jpg create mode 100644 static/instance-description-en-US.md delete mode 100644 static/instance-description-en.md diff --git a/locales/en.json b/locales/en.json index fc7eba0..c663bef 100644 --- a/locales/en.json +++ b/locales/en.json @@ -11,13 +11,13 @@ "common.eventgroupid": "Event group ID", "common.eventgroupname": "Event group name", "common.eventgroups": "Event groups", - "common.eventlocation": "Location", - "common.eventname": "Event name", + "common.eventlocation": "Location getails", + "common.eventname": "Quiet Reading location (include neighborhood)", "common.eventstart": "Starts", "common.timezone": "Timezone", "common.year-month-format": "MMMM YYYY", "common.youremail": "Your email", - "config.defaultinstancedesc": "**{{ siteName }}** is running on Gathio — a simple, federated, privacy-first event hosting platform.", + "config.defaultinstancedesc": "**{{ siteName }}** is running on yyGathio — a simple, federated, privacy-first event hosting platform.", "config.instancerule.creatoremail-false": "Anyone can create events and groups", "config.instancerule.creatoremail-true": "Only specific people can create events and groups", "config.instancerule.deleteafterdays-false": "Events are permanent, and are never automatically deleted", @@ -267,7 +267,7 @@ "views.partials.eventform.eventgroupid": "You can find this short string of characters in the event group's link, in your confirmation email, or on the event group's page.", "views.partials.eventform.eventgrouplinker": "Choose a group you've edited before", "views.partials.eventform.eventurl": "Link", - "views.partials.eventform.eventurldesc": "For tickets or another event page (optional).", + "views.partials.eventform.eventurldesc": "Link", "views.partials.eventform.groupbutton": "Enter group details manually", "views.partials.eventform.hostname": "Host name", "views.partials.eventform.hostnamedesc": "Will be shown on the event page (optional).", @@ -298,7 +298,7 @@ "views.partials.sidebar.createevent": "Create an event", "views.partials.sidebar.events": "View events", "views.partials.snappy": "Make it snappy.", - "views.partials.wontshow": "Will not be shown anywhere (optional).", + "views.partials.wontshow": "Will not be shown anywhere.", "views.publiceventlist.events": "Events", "views.publiceventlist.groups": "Groups", "views.publiceventlist.nogroups": "No groups!", diff --git a/public/css/style.css b/public/css/style.css index 3060098..70c6657 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,4 +1,29 @@ /* TYPOGRAPHY */ +@font-face { + font-family: Charter; + src: url(/fonts/charter_regular.woff2) format('woff2-variations'); + font-style: normal; +} + +@font-face { + font-family: Charter; + src: url(/fonts/charter_italic.woff2) format('woff2-variations'); + font-style: italic; +} + +@font-face { + font-family: Charter; + src: url(/fonts/charter_bold.woff2) format('woff2-variations'); + font-weight: bold; +} + +@font-face { + font-family: Charter; + src: url(/fonts/charter_bold_italic.woff2) format('woff2-variations'); + font-style: italic; + font-weight: bold; +} + @font-face { font-family: "Fredoka"; font-style: normal; @@ -12,13 +37,14 @@ } body { + font-family: "", sans-serif; color: var(--color--black); } h1, h2, h3 { - font-family: "Fredoka", sans-serif; + font-family: "Charter", sans-serif; font-weight: 400; font-optical-sizing: auto; font-weight: 400; @@ -26,7 +52,7 @@ h3 { } .lead { - font-family: "Fredoka", sans-serif; + font-family: "Charter", sans-serif; font-weight: 300; font-optical-sizing: auto; font-style: normal; @@ -225,26 +251,25 @@ html { } body { - background: var(--color-grey-97); + background: #fdfdfd; } body > #container { min-height: 100vh; width: 100%; - max-width: 75rem; + max-width: 55em; display: grid; margin: 0 auto; - grid-template-columns: 1fr; - grid-template-rows: min-content auto; padding: 0; } #container > #content { overflow: hidden; border: 1px solid var(--color-grey-90); - background: #fff; + background: #fdfdfd; display: flex; flex-direction: column; + padding: 1em; } #container > #content > main { @@ -252,7 +277,7 @@ body > #container { } #container > #content > main.page { - padding: 1rem; + /* padding: 1rem; */ } #container > #content > main.event > *:not(.event-header-image,.event__editing-banner) { @@ -274,29 +299,22 @@ body > #container { @media (min-width: 768px) { body > #container { padding: 1rem; - grid-template-columns: 1fr 4fr; - grid-template-rows: auto; + margin-top: 6%; gap: 1rem; } #container > #content { - border-radius: 1rem; - box-shadow: 0 0 6px rgba(0, 0, 0, 0.1); - } - #container > #content > main.page { - padding: 2rem; + border-radius: 4px; + border: 1px DarkSlateGrey dotted; + box-shadow: 4px 4px 0px rgba(0, 0, 0, .5); } + #container > #content > main.page {} } -@media (min-width: 992px) { - body > #container { - grid-template-columns: 1fr 5fr; - } -} /* SIDEBAR */ #sidebar h1 { - font-family: "Fredoka", sans-serif; + font-family: "Charter", sans-serif; font-weight: 700; font-optical-sizing: auto; font-style: normal; @@ -799,3 +817,17 @@ article.static-page header { margin-bottom: 1rem; border-bottom: 1px solid #e0e0e0; } + +.navi { + text-align: center; +} + +.instance-description { + text-align: center; +} + +.logo { + width: 250px; + max-width: 60%; + margin: 3em; +3 diff --git a/public/fonts/charter_bold.woff2 b/public/fonts/charter_bold.woff2 new file mode 100644 index 0000000..008c4f5 Binary files /dev/null and b/public/fonts/charter_bold.woff2 differ diff --git a/public/fonts/charter_bold_italic.woff2 b/public/fonts/charter_bold_italic.woff2 new file mode 100644 index 0000000..8a2cacc Binary files /dev/null and b/public/fonts/charter_bold_italic.woff2 differ diff --git a/public/fonts/charter_italic.woff2 b/public/fonts/charter_italic.woff2 new file mode 100644 index 0000000..ea15e1a Binary files /dev/null and b/public/fonts/charter_italic.woff2 differ diff --git a/public/fonts/charter_regular.woff2 b/public/fonts/charter_regular.woff2 new file mode 100644 index 0000000..d4bc9e0 Binary files /dev/null and b/public/fonts/charter_regular.woff2 differ diff --git a/public/hex.ico b/public/hex.ico new file mode 100644 index 0000000..304edfb Binary files /dev/null and b/public/hex.ico differ diff --git a/public/js/modules/new.js b/public/js/modules/new.js index 70df641..1dd8843 100644 --- a/public/js/modules/new.js +++ b/public/js/modules/new.js @@ -32,25 +32,34 @@ $(document).ready(function () { }); function newEventForm() { + data = { + eventName: "", + eventLocation: "", + eventStart: "", + eventEnd: "", + timezone: "", + eventDescription: "", + eventURL: "", + hostName: "", + creatorEmail: "", + eventGroupID: "", + eventGroupEditToken: "", + publicCheckbox: false, + interactionCheckbox: false, + joinCheckbox: false, + maxAttendeesCheckbox: false, + maxAttendees: "", + } + const urlParams = new URLSearchParams(window.location.search); + var els = ['eventName', 'eventLocation', 'eventStart', 'eventEnd', 'timezone', 'eventDescription', 'eventURL', 'hostName', 'creatorEmail', 'interactionCheckbox', 'joinCheckbox', 'maxAttendeesCheckbox', 'maxAttendees'] + for (var el of els) { + const attr = urlParams.get(el); + if (attr) { + data[el] = attr + } + } return { - data: { - eventName: "", - eventLocation: "", - eventStart: "", - eventEnd: "", - timezone: "", - eventDescription: "", - eventURL: "", - hostName: "", - creatorEmail: "", - eventGroupID: "", - eventGroupEditToken: "", - publicCheckbox: false, - interactionCheckbox: false, - joinCheckbox: false, - maxAttendeesCheckbox: false, - maxAttendees: "", - }, + data: data, errors: [], submitting: false, init() { @@ -66,7 +75,7 @@ function newEventForm() { this.data.interactionCheckbox = false; this.data.joinCheckbox = false; this.data.maxAttendeesCheckbox = false; - this.data.publicCheckbox = false; + this.data.publicCheckbox = true; }, updateEventEnd() { if (this.data.eventEnd === "" || this.data.eventEnd < this.data.eventStart) { @@ -129,7 +138,7 @@ function newEventGroupForm() { }, init() { // Reset checkboxes - this.data.publicCheckbox = false; + this.data.publicCheckbox = true; }, errors: [], submitting: false, diff --git a/public/logo2-min.png b/public/logo2-min.png new file mode 100644 index 0000000..96579e0 Binary files /dev/null and b/public/logo2-min.png differ diff --git a/public/pictures/bookclub.jpg b/public/pictures/bookclub.jpg new file mode 100644 index 0000000..b323e4b Binary files /dev/null and b/public/pictures/bookclub.jpg differ diff --git a/public/pictures/jefferson.jpg b/public/pictures/jefferson.jpg new file mode 100644 index 0000000..d8dd448 Binary files /dev/null and b/public/pictures/jefferson.jpg differ diff --git a/public/pictures/marthas.jpg b/public/pictures/marthas.jpg new file mode 100644 index 0000000..f7cee87 Binary files /dev/null and b/public/pictures/marthas.jpg differ diff --git a/public/pictures/mccarren.jpg b/public/pictures/mccarren.jpg new file mode 100644 index 0000000..6a580f3 Binary files /dev/null and b/public/pictures/mccarren.jpg differ diff --git a/src/routes/event.ts b/src/routes/event.ts index 84a7c6b..5b12374 100644 --- a/src/routes/event.ts +++ b/src/routes/event.ts @@ -193,6 +193,7 @@ router.post( const savedEvent = await event.save(); addToLog("createEvent", "success", "Event " + eventID + "created"); // Send email with edit link + var duplicateParams = new URLSearchParams(eventData).toString(); if (eventData.creatorEmail) { req.emailService.sendEmailFromTemplate({ to: eventData.creatorEmail, @@ -201,6 +202,7 @@ router.post( templateData: { eventID, editToken, + duplicateParams } }); } diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index fca14c6..0f46098 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -25,64 +25,7 @@ const router = Router(); // Add config middleware to all routes router.use(getConfigMiddleware); -router.get("/", (_, res) => { - if (res.locals.config?.general.show_public_event_list) { - return res.redirect("/events"); - } - return res.render("home", { - ...frontendConfig(res), - instanceRules: instanceRules(), - instanceDescription: instanceDescription(), - }); -}); - -router.get("/about", (_: Request, res: Response) => { - return res.render("home", { - ...frontendConfig(res), - instanceRules: instanceRules(), - instanceDescription: instanceDescription(), - }); -}); - -router.get("/new", (_: Request, res: Response) => { - if (res.locals.config?.general.creator_email_addresses?.length) { - return res.render("createEventMagicLink", frontendConfig(res)); - } - return res.render("newevent", { - title: i18next.t("frontend.newevent"), - ...frontendConfig(res), - }); -}); - -router.get("/new/:magicLinkToken", async (req: Request, res: Response) => { - // If we don't have any creator email addresses, we don't need to check the magic link - // so we can just redirect to the new event page - if (!res.locals.config?.general.creator_email_addresses?.length) { - return res.redirect("/new"); - } - const magicLink = await MagicLink.findOne({ - token: req.params.magicLinkToken, - expiryTime: { $gt: new Date() }, - permittedActions: "createEvent", - }); - if (!magicLink) { - return res.render("createEventMagicLink", { - ...frontendConfig(res), - message: { - type: "danger", - text: i18next.t("routes.magiclink-invalid"), - }, - }); - } - res.render("newevent", { - title: i18next.t("frontend.newevent"), - ...frontendConfig(res), - magicLinkToken: req.params.magicLinkToken, - creatorEmail: magicLink.email, - }); -}); - -router.get("/events", async (_: Request, res: Response) => { +const evtrun = async (_: Request, res: Response) => { if (!res.locals.config?.general.show_public_event_list) { return res.status(404).render("404", frontendConfig(res)); } @@ -93,21 +36,26 @@ router.get("/events", async (_: Request, res: Response) => { const updatedEvents: EventListEvent[] = events.map((event) => { const startMoment = moment.tz(event.start, event.timezone); const endMoment = moment.tz(event.end, event.timezone); + let eventStartISO = moment.tz(event.start, "Etc/UTC").toISOString(); + let eventEndISO = moment.tz(event.end, "Etc/UTC").toISOString(); const isSameDay = startMoment.isSame(endMoment, "day"); + const calfmt = 'dddd, MMMM D · h:mma' return { id: event.id, name: event.name, location: event.location, displayDate: isSameDay - ? startMoment.format("LL") - : `${startMoment.format("LL")} - ${endMoment.format( - "LL", + ? startMoment.format(calfmt) + : `${startMoment.format(calfmt)} - ${endMoment.format( + calfmt, )}`, eventHasConcluded: endMoment.isBefore(moment.tz(event.timezone)), eventGroup: event.eventGroup as any as IEventGroup, startMoment, endMoment, + eventStartISO, + eventEndISO, }; }); const upcomingEventsInMonthBuckets = updatedEvents @@ -140,8 +88,68 @@ router.get("/events", async (_: Request, res: Response) => { instanceRules: instanceRules(), ...frontendConfig(res), }); +} + +router.get("/", evtrun); +// router.get("/", (_, res) => { +// if (res.locals.config?.general.show_public_event_list) { +// return evt(); +// } +// return res.render("home", { +// ...frontendConfig(res), +// instanceRules: instanceRules(), +// instanceDescription: instanceDescription(), +// }); +// }); + +router.get("/about", (_: Request, res: Response) => { + return res.render("home", { + ...frontendConfig(res), + instanceRules: instanceRules(), + instanceDescription: instanceDescription(), + }); +}); + +router.get("/new", (_: Request, res: Response) => { + if (res.locals.config?.general.creator_email_addresses?.length) { + return res.render("createEventMagicLink", frontendConfig(res)); + } + return res.render("newevent", { + title: i18next.t("frontend.newevent"), + ...frontendConfig(res), + }); }); +router.get("/new/:magicLinkToken", async (req: Request, res: Response) => { + // If we don't have any creator email addresses, we don't need to check the magic link + // so we can just redirect to the new event page + if (!res.locals.config?.general.creator_email_addresses?.length) { + return res.redirect("/new"); + } + const magicLink = await MagicLink.findOne({ + token: req.params.magicLinkToken, + expiryTime: { $gt: new Date() }, + permittedActions: "createEvent", + }); + if (!magicLink) { + return res.render("createEventMagicLink", { + ...frontendConfig(res), + message: { + type: "danger", + text: i18next.t("routes.magiclink-invalid"), + }, + }); + } + res.render("newevent", { + title: i18next.t("frontend.newevent"), + ...frontendConfig(res), + magicLinkToken: req.params.magicLinkToken, + creatorEmail: magicLink.email, + }); +}); + +// router.get("/events", evt); + router.get("/:eventID", async (req: Request, res: Response) => { try { const event = await Event.findOne({ diff --git a/src/util/validation.ts b/src/util/validation.ts index 42b524a..640baf3 100644 --- a/src/util/validation.ts +++ b/src/util/validation.ts @@ -202,6 +202,12 @@ export const validateEventData = ( }); } } + if (!validatedData.creatorEmail) { + errors.push({ + message: i18next.t("util.validation.eventdata.creatoremail"), + field: "creatorEmail", + }); + } if (validatedData.creatorEmail) { if (!validateEmail(validatedData.creatorEmail)) { errors.push({ diff --git a/static/instance-description-en-US.md b/static/instance-description-en-US.md new file mode 100644 index 0000000..a79363d --- /dev/null +++ b/static/instance-description-en-US.md @@ -0,0 +1,5 @@ +*Free weekly events in +libraries, parks, and cafes.* + +One reading hour. One social hour.\ +**Bring your own book, make a new friend!** diff --git a/static/instance-description-en.md b/static/instance-description-en.md deleted file mode 100644 index 747850d..0000000 --- a/static/instance-description-en.md +++ /dev/null @@ -1 +0,0 @@ -**{{ siteName }}** is running on Gathio — a simple, federated, privacy-first event hosting platform. diff --git a/views/emails/createEvent/createEventHtml.handlebars b/views/emails/createEvent/createEventHtml.handlebars index 9310c2a..b7d71ed 100644 --- a/views/emails/createEvent/createEventHtml.handlebars +++ b/views/emails/createEvent/createEventHtml.handlebars @@ -1,6 +1,7 @@

{{t "views.emails.createevent.preface" }}

{{t "views.emails.createevent.sharelink" }}: https://{{domain}}/{{eventID}}

{{{t "views.emails.createeventhtml.desc" }}}

+

Create a new edition of this event with the same information

diff --git a/views/emails/createEvent/createEventText.handlebars b/views/emails/createEvent/createEventText.handlebars index 7c85d5c..d88f12c 100644 --- a/views/emails/createEvent/createEventText.handlebars +++ b/views/emails/createEvent/createEventText.handlebars @@ -4,3 +4,5 @@ {{t "views.emails.createevent.desc" }} https://{{domain}}/{{eventID}}?e={{editToken}} + +Duplicate this event: https://{{domain}}/new{{duplicateParams}} diff --git a/views/layouts/main.handlebars b/views/layouts/main.handlebars index d2eeeac..925114d 100755 --- a/views/layouts/main.handlebars +++ b/views/layouts/main.handlebars @@ -54,32 +54,9 @@
-
+
{{{body}}} -
- {{#if showInstanceInformation}} -

- {{siteName}} - {{#each staticPages}} - {{#if @first}} - · - {{/if}} - - {{this.title}} - - {{#unless @last}} - · - {{/unless}} - {{/each}} -

- {{/if}} -

- {{{t "views.layouts.main.footnote" }}} -

-
diff --git a/views/newevent.handlebars b/views/newevent.handlebars index 9de2efd..57ee4d1 100755 --- a/views/newevent.handlebars +++ b/views/newevent.handlebars @@ -1,16 +1,72 @@ -
-

{{t "views.newevent.pagetitle" }}

+
+
+
+ + Quiet reading belongs to you. Learn more about hosting your own events.
+
+
+
+ At quiet reading events, readers bring their own books, read for one hour, + and then chat for one hour about books, literature, and arts. Start + your own independently-operated quiet reading today! +
+
+
+
+
+ 1. Audience
+ If you’re an individual, invite your friends, family, or classmates. +
+
+ If you run an event space, invite your regulars and local community. +
+
+ Quiet reading events generally do not require advance registration. +
+
+
+
+
+ 2. Location
+ For example: a park, library discussion room, community garden, apartment, cafe, or bar. +
+
+ A quiet environment is nice but silence is not required; readers can bring noise-cancelling headphones if they wish. +
+
+
+
+
+ 3. Time
+ Quiet reading can happen at mornings, afternoons, evenings, or nights. Weekly events are perfect for building community. +
+
+
+
+
+ 4. Promotion
+ Text, social media, flyers, venue website, local subreddit, and so on. +

+ If you run a public space like a library, park, cafe, or bar, we can help advertise your independent event: contact team@quietreading.org. +
+
+
+
+
+ 5. Host
+ Quiet reading is designed to be a casual community meetup. There’s no need for a host: readers arrive, read, and then chat by themselves. +
+
+ Happy reading! +
+
+
+
-
- -
-
- -
@@ -49,26 +105,6 @@ {{>importeventform}} - -
diff --git a/views/partials/eventForm.handlebars b/views/partials/eventForm.handlebars index 8714058..53f7683 100755 --- a/views/partials/eventForm.handlebars +++ b/views/partials/eventForm.handlebars @@ -1,24 +1,24 @@
- +
- +
- +
- +
@@ -30,7 +30,7 @@
- +
{{{t "views.partials.mdsupport" }}} @@ -39,7 +39,7 @@
- + {{t "views.partials.eventform.eventurldesc" }}
@@ -65,7 +65,7 @@
- +
{{t "views.partials.creatoremaildesc" }} diff --git a/views/partials/eventList.handlebars b/views/partials/eventList.handlebars index b694fa3..e8226fb 100644 --- a/views/partials/eventList.handlebars +++ b/views/partials/eventList.handlebars @@ -5,11 +5,10 @@
{{this.title}}
{{#each this.events}} - - + + {{this.displayDate}} {{this.name}} - {{#if this.location}} {{this.location}}{{/if}} - {{this.displayDate}} + {{#if this.location}}
{{this.location}}{{/if}} {{#if this.eventGroup}} {{this.eventGroup.name}} {{/if}} diff --git a/views/partials/sidebar.handlebars b/views/partials/sidebar.handlebars index ae4929d..b771681 100755 --- a/views/partials/sidebar.handlebars +++ b/views/partials/sidebar.handlebars @@ -1,6 +1,8 @@
diff --git a/views/publicEventList.handlebars b/views/publicEventList.handlebars index a16735b..70376b7 100644 --- a/views/publicEventList.handlebars +++ b/views/publicEventList.handlebars @@ -1,51 +1,31 @@
-

{{siteName}}

- {{#if instanceDescription}}
{{{instanceDescription}}}
{{/if}} -{{> instanceRules }} - - +{{!> instanceRules }} -
-
-
{{t "views.publiceventlist.upcomingevents" }}
- {{> eventList upcomingEvents }} -
+ -
-
{{t "views.publiceventlist.pastevents" }}
- {{> eventList pastEvents }} -
+
+
{{t "views.publiceventlist.upcomingevents" }}
+ {{> eventList upcomingEvents }}
-
-
-
{{t "common.eventgroups" }}
-
- {{#if eventGroups}} - {{#each eventGroups}} - - - {{this.name}} - {{plural "views.publiceventlist.numoevents" this.numberOfEvents }} - - {{/each}} - {{else}} -
{{t "views.publiceventlist.nogroups" }}
- {{/if}} +
+
{{t "views.publiceventlist.pastevents" }}
+ {{> eventList pastEvents }}
-- cgit v1.2.3