Better Oneboxes for Events

At present, the Oneboxes for events are a bit meh. Basically, they are no different to regular topic Oneboxes, and don’t include the date, time, description, etc etc (unless put into the Topic Title as a workaround).

Here is an example event from our site:

Would it be possible to hook into the new (and brilliant) cards that appear on click in the calendar for events, and serve these up as oneboxes? For example:

That might not be feasible at all, but I do wonder if the metadata could be modified for event posts so that they Onebox in a way that highlights the critical event info (especially internally).

7 Likes

I have 0 votes left but you have my vote anyway :wink: , this would make the event promotion shine more indeed.

2 Likes

@chapoi / @j.jaffeux I have been thinking about this.

I think a fundamental change we need here is to introduce proper event URLs so you can use them to onebox and instead of oneboxing:

https://SITE/t/slug/123

We would onebox

https://SITE/event/slug/1234

It does open a bit of a pandoras box (in a good way though)

  1. How do people find the link to the event? (I guess we add to context menu)
  2. What happens if they visit the event directly from the URL? (I guess we redirect to post event for now)
2 Likes

Agree with the idea – can definitely do something nicer here

If you mean this:

That seems the right place to me too.

2 Likes

While messing around with that context menu, I do wonder if the Add to calendar would be better placed directly on the card.

All the other things in that menu are quite admin-ey, but Add to calendar is an all-users thing that would do well to be more prominent in the UI.

1 Like

In events posts I place the “add to calendar” option in an obvious spot; you may add this to the theme javascript header reachable from the admin appearance menu :

<script>
(() => {
    const eventInfoSelector = ".event-header .event-info";
    const submenuButtonSelector = "button.fk-d-menu__trigger.discourse-post-event-more-menu-trigger";
    const menuContentSelector = ".fk-d-menu__inner-content";
    const addToCalendarSelector = "li.add-to-calendar > button";

    // Add inline CSS to keep text and icon black on hover
    const style = document.createElement("style");
    style.innerHTML = `
    .btn.btn-icon-text.custom-add-to-calendar-btn,
    .btn.btn-icon-text.custom-add-to-calendar-btn:hover {
        color: black !important;
    }
    .btn.btn-icon-text.custom-add-to-calendar-btn svg,
    .btn.btn-icon-text.custom-add-to-calendar-btn:hover svg {
        fill: black !important;
    }
    `;
    document.head.appendChild(style);

    // Helper to wait for an element in the DOM
    const waitForElement = (selector, timeout = 3000) => {
        return new Promise((resolve, reject) => {
            const el = document.querySelector(selector);
            if (el) return resolve(el);

            const observer = new MutationObserver(() => {
                const found = document.querySelector(selector);
                if (found) {
                    observer.disconnect();
                    resolve(found);
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
            setTimeout(() => {
                observer.disconnect();
                reject();
            }, timeout);
        });
    };

    const createAddToCalendarButton = async () => {
        const eventInfo = document.querySelector(eventInfoSelector);
        if (!eventInfo || eventInfo.querySelector(".custom-add-to-calendar-btn")) return;

        const button = document.createElement("button");
        button.className = "btn btn-icon-text custom-add-to-calendar-btn";
        button.title = "Add to calendar";
        button.innerHTML = `
            <svg class="fa d-icon d-icon-file svg-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
                <use href="#file"></use>
            </svg>
            <span class="d-button-label">Add to calendar</span>
        `;

        // Inline styles for left alignment
        button.style.display = "inline-flex";
        button.style.justifyContent = "flex-start";
        button.style.marginLeft = "0";
        button.style.marginBottom = "4px";
        button.style.backgroundColor = "#f0f0f0";
        button.style.borderRadius = "6px";
        button.style.transition = "transform 0.2s";
        button.style.cursor = "pointer";

        // Click logic: open menu and trigger original Add to Calendar
        button.addEventListener("click", async () => {
            const submenuButton = document.querySelector(submenuButtonSelector);
            if (!submenuButton) return;
            submenuButton.click(); // open menu

            let menuContent;
            try {
                menuContent = await waitForElement(menuContentSelector, 2000);
            } catch {
                return;
            }

            const originalButton = menuContent.querySelector(addToCalendarSelector);
            originalButton?.click();
        });

        // Insert into event-info
        if (eventInfo.firstElementChild) {
            eventInfo.insertBefore(button, eventInfo.firstElementChild.nextSibling);
        } else {
            eventInfo.appendChild(button);
        }
    };

    // Observe DOM changes (Ember-safe)
    const observer = new MutationObserver(() => createAddToCalendarButton());
    observer.observe(document.body, { childList: true, subtree: true });

    // Initial attempt
    createAddToCalendarButton();
})();
</script>