fix: address bugs and feature requests from discussion #317

- Share link: display and copy now use the absolute URL built from the
  current origin instead of the relative path stored in events.share_link.
  Added a Copy Link button to the events list (inline + dropdown).
- Detect dev tools default: event creation now reads the global
  enable_devtools_protection app setting instead of always falling back to
  the column default; admins who disable it globally get new events with
  it disabled too.
- Require password default: added a global "Require password by default"
  setting (event_default_require_password, default true), exposed via
  Settings -> Events. Create-event form initialises from it.
- Filter bar: added gallery_show_filter_bar setting and hide the search/
  sort row in the public gallery when off, or when the gallery has zero
  photos (fixes the empty-state UX from the screenshot).
- Theme picker unclickable on Create Event: memoised availableEventTypes
  so its identity is stable. The "auto-apply event-type recommended
  preset" effect was firing on every render due to the unstable array
  reference and silently overwriting the user's preset selection ~1ms
  after each click.
- Branding logo disappearing on theme change: handlePresetChange and
  handleThemeChange no longer wipe the existing logoUrl when a preset
  config (which carries no logoUrl) is applied; handleSave falls back to
  brandingSettings.logo_url. themeMutation now invalidates the
  admin-settings and public-settings caches so saved theme changes appear
  immediately.
This commit is contained in:
Paul Nothaft
2026-04-26 22:48:59 +02:00
parent e4b0f961b7
commit 6cfff6f6a6
11 changed files with 258 additions and 48 deletions
+27
View File
@@ -132,3 +132,30 @@ export const isProductionMode = (): boolean => {
const apiBase = getApiBaseUrl();
return !ABSOLUTE_URL_REGEX.test(apiBase);
};
/**
* Build a fully-qualified gallery share URL from the value stored in
* `events.share_link`. The DB stores the relative path (e.g.
* `/gallery/<slug>/<token>`); for display and clipboard copy we need an
* absolute URL the recipient can paste into a browser. Absolute inputs are
* passed through (with the same localhost-fallback rule used elsewhere).
*
* @param link - The relative or absolute share link
* @returns A fully-qualified URL, or `'#'` if input is empty
*/
export const buildShareLinkUrl = (link: string | null | undefined): string => {
if (!link) return '#';
if (ABSOLUTE_URL_REGEX.test(link)) {
if (!shouldFallbackToRelative(link)) return link;
try {
const parsed = new URL(link);
return buildFromOrigin(`${parsed.pathname}${parsed.search}${parsed.hash}`);
} catch {
return link;
}
}
const path = link.startsWith('/') ? link : `/gallery/${link}`;
return buildFromOrigin(path);
};