diff --git a/config/config.example.toml b/config/config.example.toml index 4e00171..5502038 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -4,7 +4,7 @@ domain = "localhost:3000" port = "3000" email = "contact@example.com" -site_name = "gathio" +site_name = "Gathio" is_federated = true # Events will be deleted this many days after they have ended. Set to 0 to # disable automatic deletion (old events will never be deleted). diff --git a/package.json b/package.json index faa6a59..8f830db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gathio", - "version": "1.4.1", + "version": "1.5.0", "description": "A simple, federated, privacy-first event hosting platform", "main": "index.js", "type": "module", diff --git a/public/css/style.css b/public/css/style.css index 938e6a5..9a81967 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,6 +1,10 @@ /* TYPOGRAPHY */ @import url("https://fonts.googleapis.com/css2?family=Fredoka:wght@300..700&display=swap"); +body { + color: var(--color--black); +} + h1, h2, h3 { @@ -18,6 +22,187 @@ h3 { font-style: normal; } +:root { + --color-purple: hsl(273, 44%, 58%); + --color-purple-dark: hsl(273, 44%, 38%); + --color-purple-lighter: hsl(273, 44%, 86%); + --color-purple-light: hsl(273, 44%, 96%); + --color-red: hsl(359, 100%, 65%); + --color-red-dark: hsl(359, 100%, 45%); + --color-grey-99: hsl(0, 0%, 99%); + --color-grey-97: hsl(0, 0%, 97%); + --color-grey-95: hsl(0, 0%, 95%); + --color-grey-90: hsl(0, 0%, 90%); + --color-grey-85: hsl(0, 0%, 85%); + --color-grey-80: hsl(0, 0%, 80%); + --color-grey-75: hsl(0, 0%, 75%); + --color-grey-70: hsl(0, 0%, 70%); + --color-grey-60: hsl(0, 0%, 60%); + --color-grey-50: hsl(0, 0%, 50%); + --color-grey-40: hsl(0, 0%, 40%); + --color-grey-30: hsl(0, 0%, 30%); + --color-grey-20: hsl(0, 0%, 20%); + --color--black: hsl(0, 0%, 10%); + --transition: 0.15s ease-in; +} + +.flex-gap { + display: flex; + gap: 1rem; +} + +.flex-gap--small { + display: flex; + gap: 0.5rem; +} + +.button { + display: inline-block; + height: 2.25rem; + line-height: 2.25rem; + border-radius: 2.25rem; + font-weight: 500; + letter-spacing: 0.35px; + transition: background var(--transition); + text-decoration: none; + border: none; + font-size: 0.95rem; + padding: 0 1rem; + white-space: nowrap; + text-align: center; +} + +.button:hover { + text-decoration: none; + color: inherit; +} + +.button--primary { + background: var(--color-purple); + color: #fff; +} + +.button--primary:hover { + background: var(--color-purple-dark); + color: #fff; +} + +.button--secondary { + background: var(--color-grey-85); + color: var(--color-black); +} + +.button--secondary:hover { + background: var(--color-grey-70); +} + +.button--outline-primary { + background: transparent; + border: 1px solid var(--color-purple); + color: var(--color-purple); +} + +.button--outline-primary:hover { + border: 1px solid var(--color-purple-dark); + background: var(--color-purple-light); + color: var(--color-purple-dark); +} + +.button--outline-secondary { + background: transparent; + border: 1px solid var(--color-grey-75); + color: var(--color-grey-30); +} + +.button--outline-secondary:hover { + border: 1px solid var(--color-grey-70); + background: var(--color-purple-light); +} + +.button--sm { + height: 1.75rem; + line-height: 1.65rem; + border-radius: 1.75rem; + font-size: 0.85rem; + padding: 0 0.75rem; +} + +.button--lg { + height: 2.75rem; + line-height: 2.75rem; + border-radius: 2.75rem; + font-size: 1.05rem; + padding: 0 1.25rem; +} + +.button--danger { + background: var(--color-red); + color: #fff; +} + +.button--danger:hover { + background: var(--color-red-dark); + color: #fff; +} + +.button--primary:disabled { + background: var(--color-purple-lighter); + color: var(--color-grey-50); +} + +.button--secondary:disabled { + background: var(--color-grey-97); + color: var(--color-grey-75); +} + +.button--danger:disabled { + background: #f9d4d4; + color: #ff4d4f; +} + +.button--outline-primary:disabled { + background: transparent; + border: 1px solid var(--color-purple-lighter); + color: var(--color-purple-lighter); +} + +.button--outline-secondary:disabled { + background: transparent; + border: 1px solid var(--color-grey-85); + color: var(--color-grey-85); +} + +.button-stack { + display: flex; + flex-direction: column; +} + +.button-stack > .button:not(:first-child):not(:last-child) { + border-radius: 0; +} + +.button-stack > .button:not(:last-child) { + border-bottom: none; +} + +.button-stack > .button:first-child { + border-radius: 1rem 1rem 0 0; +} + +.button-stack > .button:last-child { + border-radius: 0 0 1rem 1rem; +} + +.button-stack > .button.button--sm { + height: 2rem; + line-height: 2rem; +} + +.button-stack > .button.button--sm { + height: 2rem; + line-height: 2rem; +} + /* LAYOUT */ html { @@ -25,11 +210,7 @@ html { } body { - background: #f8f8f8; - display: flex; - flex-direction: column; - min-height: 100vh; - align-items: center; + background: var(--color-grey-97); } body > #container { @@ -37,14 +218,16 @@ body > #container { width: 100%; max-width: 75rem; display: grid; + margin: 0 auto; grid-template-columns: 1fr; + grid-template-rows: min-content auto; padding: 0; } #container > #content { overflow: hidden; - border: 1px solid #eaeaea; - background: #ffffff; + border: 1px solid var(--color-grey-90); + background: #fff; display: flex; flex-direction: column; } @@ -66,7 +249,7 @@ body > #container { border-top: 1px solid #e0e0e0; text-align: center; padding: 0.25rem 0; - background: #fdfdfd; + background: var(--color-grey-99); } #container > #content > footer p { @@ -77,6 +260,7 @@ body > #container { body > #container { padding: 1rem; grid-template-columns: 1fr 4fr; + grid-template-rows: auto; gap: 1rem; } #container > #content { @@ -117,7 +301,12 @@ body > #container { #sidebar h1 a:hover { text-decoration: none; - background: linear-gradient(to right, #27aa45, #7fe0c8, #5d26c1); + background: linear-gradient( + to right, + rgb(1, 76, 173), + rgb(128, 224, 200), + var(--color-purple) + ); background-size: 100% 100%; background-clip: text; -webkit-background-clip: text; @@ -157,7 +346,7 @@ ul#sidebar__nav a { width: 100%; padding: 0 0 0.5rem 0; } - ul#sidebar__nav li:has(a:not(.btn)):not(:last-child) { + ul#sidebar__nav li:has(a:not(.button)):not(:last-child) { border-bottom: 1px solid #e0e0e0; } @@ -252,6 +441,11 @@ ul#sidebar__nav a { flex-wrap: wrap; } +#event__actions #editEvent { + width: 100%; + margin-top: 16px; +} + .attendeesList > li { border: 4px solid #0ea130; border-radius: 2em; @@ -355,7 +549,7 @@ li.hidden-attendee .attendee-name { flex-direction: column; align-items: flex-start; } -#eventAttendees h5 .btn-group { +#eventAttendees h5 .button--group { margin-top: 0.5rem; } @@ -365,7 +559,7 @@ li.hidden-attendee .attendee-name { justify-content: space-between; align-items: center; } - #eventAttendees h5 .btn-group { + #eventAttendees h5 .button--group { margin-top: 0; } } @@ -428,12 +622,6 @@ li.hidden-attendee .attendee-name { /* FORMS */ -#newEventFormContainer, -#importEventFormContainer, -#newEventGroupFormContainer { - display: none; -} - #icsImportLabel { overflow: hidden; text-overflow: ellipsis; @@ -524,10 +712,10 @@ img.group-preview__image { } } -.btn--loading { +.button--loading { position: relative; } -.btn--loading::after { +.button--loading::after { content: ""; position: absolute; left: -45%; @@ -573,7 +761,7 @@ img.group-preview__image { /* EVENT AND GROUP LISTS */ .list-group-item-action:hover { - background-color: #f2f8ff; + background-color: var(--color-purple-light); } /* STATIC PAGES */ diff --git a/public/js/modules/event-edit.js b/public/js/modules/event-edit.js index 736547f..6d2c216 100644 --- a/public/js/modules/event-edit.js +++ b/public/js/modules/event-edit.js @@ -7,7 +7,6 @@ $(document).ready(function () { label_selected: "Change file", no_label: false, }); - autosize($("textarea")); if (window.eventData.image) { $("#event-image-preview").css( "background-image", @@ -18,6 +17,16 @@ $(document).ready(function () { } }); +$('#editModal').on('shown.bs.modal', function (e) { + console.log('hii'); + const ta = document.querySelector("#editModal textarea"); + ta.style.display = 'none'; + autosize(ta); + ta.style.display = ''; + // Call the update method to recalculate the size: + autosize.update(ta); +}); + function editEventForm() { return { data: { diff --git a/public/js/modules/group-edit.js b/public/js/modules/group-edit.js index 2d55346..db2d411 100644 --- a/public/js/modules/group-edit.js +++ b/public/js/modules/group-edit.js @@ -7,7 +7,6 @@ $(document).ready(function () { label_selected: "Change file", no_label: false, }); - autosize($("textarea")); if (window.groupData.image) { $("#group-image-preview").css( "background-image", @@ -19,6 +18,14 @@ $(document).ready(function () { $("#timezone").val(window.groupData.timezone).trigger("change"); }); +$('#editModal').on('shown.bs.modal', function (e) { + const ta = document.querySelector("#editModal textarea"); + ta.style.display = 'none'; + autosize(ta); + ta.style.display = ''; + autosize.update(ta); +}); + function editEventGroupForm() { return { data: { diff --git a/public/js/modules/new.js b/public/js/modules/new.js index f7c3e34..7915b59 100644 --- a/public/js/modules/new.js +++ b/public/js/modules/new.js @@ -5,48 +5,6 @@ $(document).ready(function () { .next("label") .html(' ' + file); } - $("#showNewEventFormButton").click(function () { - $("button").removeClass("active"); - $( - "#showImportEventFormButton #showNewEventGroupFormButton", - ).removeClass("active"); - if ($("#newEventFormContainer").is(":visible")) { - $("#newEventFormContainer").slideUp("fast"); - } else { - $("#newEventFormContainer").slideDown("fast"); - $("#importEventFormContainer").slideUp("fast"); - $("#newEventGroupFormContainer").slideUp("fast"); - $(this).addClass("active"); - } - }); - $("#showImportEventFormButton").click(function () { - $("button").removeClass("active"); - $("#showNewEventFormButton #showNewEventGroupFormButton").removeClass( - "active", - ); - if ($("#importEventFormContainer").is(":visible")) { - $("#importEventFormContainer").slideUp("fast"); - } else { - $("#importEventFormContainer").slideDown("fast"); - $("#newEventFormContainer").slideUp("fast"); - $("#newEventGroupFormContainer").slideUp("fast"); - $(this).addClass("active"); - } - }); - $("#showNewEventGroupFormButton").click(function () { - $("button").removeClass("active"); - $("#showNewEventFormButton #showImportEventFormButton").removeClass( - "active", - ); - if ($("#newEventGroupFormContainer").is(":visible")) { - $("#newEventGroupFormContainer").slideUp("fast"); - } else { - $("#newEventGroupFormContainer").slideDown("fast"); - $("#newEventFormContainer").slideUp("fast"); - $("#importEventFormContainer").slideUp("fast"); - $(this).addClass("active"); - } - }); $("#icsImportControl").change(function () { var file = $("#icsImportControl")[0].files[0].name; $(this) diff --git a/src/lib/config.ts b/src/lib/config.ts index 4bc43bd..b4086d8 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -2,6 +2,7 @@ import fs from "fs"; import toml from "toml"; import { exitWithError } from "./process.js"; import { Response } from "express"; +import { markdownToSanitizedHTML } from "../util/markdown.js"; interface StaticPage { title: string; @@ -152,6 +153,31 @@ export const instanceRules = (): InstanceRule[] => { return rules; }; +export const instanceDescription = (): string => { + const config = getConfig(); + const defaultInstanceDescription = + "**{{ siteName }}** is running on Gathio — a simple, federated, privacy-first event hosting platform."; + let instanceDescription = defaultInstanceDescription; + try { + if (fs.existsSync("./static/instance-description.md")) { + const fileBody = fs.readFileSync( + "./static/instance-description.md", + "utf-8", + ); + instanceDescription = markdownToSanitizedHTML(fileBody); + } + // Replace {{siteName}} with the instance name + instanceDescription = instanceDescription.replace( + /\{\{ ?siteName ?\}\}/g, + config?.general.site_name, + ); + return instanceDescription; + } catch (err) { + console.log(err); + return defaultInstanceDescription; + } +}; + // Attempt to load our global config. Will stop the app if the config file // cannot be read (there's no point trying to continue!) export const getConfig = (): GathioConfig => { diff --git a/src/routes/frontend.ts b/src/routes/frontend.ts index 4d977d7..86ad69c 100644 --- a/src/routes/frontend.ts +++ b/src/routes/frontend.ts @@ -1,8 +1,13 @@ import { Router, Request, Response } from "express"; +import fs from "fs"; import moment from "moment-timezone"; import { marked } from "marked"; import { markdownToSanitizedHTML, renderPlain } from "../util/markdown.js"; -import { frontendConfig, instanceRules } from "../lib/config.js"; +import { + frontendConfig, + instanceDescription, + instanceRules, +} from "../lib/config.js"; import { addToLog, exportICal } from "../helpers.js"; import Event from "../models/Event.js"; import EventGroup, { IEventGroup } from "../models/EventGroup.js"; @@ -26,6 +31,7 @@ router.get("/", (_: Request, res: Response) => { return res.render("home", { ...frontendConfig(res), instanceRules: instanceRules(), + instanceDescription: instanceDescription(), }); }); @@ -33,6 +39,7 @@ router.get("/about", (_: Request, res: Response) => { return res.render("home", { ...frontendConfig(res), instanceRules: instanceRules(), + instanceDescription: instanceDescription(), }); }); @@ -126,6 +133,8 @@ router.get("/events", async (_: Request, res: Response) => { upcomingEvents: upcomingEvents, pastEvents: pastEvents, eventGroups: updatedEventGroups, + instanceDescription: instanceDescription(), + instanceRules: instanceRules(), ...frontendConfig(res), }); }); diff --git a/src/routes/static.ts b/src/routes/static.ts index 6fab98d..6670214 100644 --- a/src/routes/static.ts +++ b/src/routes/static.ts @@ -2,10 +2,13 @@ import { Router, Request, Response } from "express"; import fs from "fs"; import getConfig, { frontendConfig } from "../lib/config.js"; import { markdownToSanitizedHTML } from "../util/markdown.js"; +import { getConfigMiddleware } from "../lib/middleware.js"; const config = getConfig(); const router = Router(); +router.use(getConfigMiddleware); + if (config.static_pages?.length) { config.static_pages .filter((page) => page.path?.startsWith("/") && page.filename) diff --git a/static/instance-description.md b/static/instance-description.md new file mode 100644 index 0000000..747850d --- /dev/null +++ b/static/instance-description.md @@ -0,0 +1 @@ +**{{ siteName }}** is running on Gathio — a simple, federated, privacy-first event hosting platform. diff --git a/views/createEventMagicLink.handlebars b/views/createEventMagicLink.handlebars index 7329801..d0a0a49 100644 --- a/views/createEventMagicLink.handlebars +++ b/views/createEventMagicLink.handlebars @@ -23,7 +23,7 @@
- +
diff --git a/views/event.handlebars b/views/event.handlebars index 3ccbc08..cd1645a 100755 --- a/views/event.handlebars +++ b/views/event.handlebars @@ -11,9 +11,7 @@
{{#if editingEnabled}} - - {{else}} - + {{/if}}
@@ -75,7 +73,7 @@ https://{{domain}}/{{eventData.id}} - @@ -85,7 +83,7 @@ @{{eventData.id}}@{{domain}} - @@ -94,20 +92,24 @@ @@ -136,11 +138,11 @@ {{#if eventData.usersCanAttend}}
Attendees {{#if numberOfAttendees}}({{numberOfAttendees}}){{/if}} -
+
{{#unless noMoreSpots}} - + {{/unless}} - +
@@ -218,8 +220,8 @@
@@ -245,8 +247,8 @@ @@ -268,8 +270,8 @@

Are you sure you want to remove this attendee from the event? This action cannot be undone.

@@ -286,14 +288,14 @@
- +
-
- +
+
- +
@@ -322,12 +324,12 @@ {{/if}}
- {{#if ../editingEnabled}} - @@ -338,13 +340,13 @@
- +
-
- +
+
- +
@@ -381,8 +383,8 @@
@@ -408,8 +410,8 @@

Are you sure you want to delete this event? This action cannot be undone.

diff --git a/views/eventgroup.handlebars b/views/eventgroup.handlebars index 9fb2276..8fbedbc 100755 --- a/views/eventgroup.handlebars +++ b/views/eventgroup.handlebars @@ -10,9 +10,9 @@
{{#if editingEnabled}} - + {{else}} - + {{/if}}
@@ -52,7 +52,7 @@ https://{{domain}}/group/{{eventGroupData.id}} - @@ -63,7 +63,7 @@ https://{{domain}}/group/{{eventGroupData.id}}/feed.ics  @@ -74,14 +74,14 @@