feat(slideshow): db columns for live slideshow
- 137: events.show_share_token + show_interval_ms/transition/transition_ms - 138: per-event watermark (tri-state, nullable=inherit global), source/ position/opacity/style + color filter columns, and event_types.slideshow_preset JSON so new events inherit a per-type default. Opt-in, no backfill.
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
const { addColumnIfNotExists, createIndexIfNotExists } = require('../helpers');
|
||||||
|
|
||||||
|
// Live Slideshow ("Diashow") link for live events. A SECOND, token-only
|
||||||
|
// share surface that mirrors the client-access pattern (migration 074):
|
||||||
|
// a dedicated fullscreen kiosk URL `/gallery/:slug/show/:token` that
|
||||||
|
// auto-picks-up newly uploaded photos while it runs.
|
||||||
|
//
|
||||||
|
// Opt-in by design: `show_share_token` stays NULL until the admin clicks
|
||||||
|
// "Generate slideshow link", and the public `/show/` route 404s while it
|
||||||
|
// is null. The three settings columns drive the running projector and can
|
||||||
|
// be changed LIVE from the admin panel (a short settings poll on the show
|
||||||
|
// page picks them up within a few seconds) — they carry sensible defaults
|
||||||
|
// so an existing event is fully configured the moment a token is minted.
|
||||||
|
exports.up = async function up(knex) {
|
||||||
|
// Token IS the secret (no gallery password). Unique so a stray collision
|
||||||
|
// can never point two events at one link.
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_share_token', (table) => {
|
||||||
|
table.string('show_share_token', 64).nullable().unique();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Per-slide display time in ms (how long each photo stays on screen).
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_interval_ms', (table) => {
|
||||||
|
table.integer('show_interval_ms').defaultTo(5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Transition style between slides: crossfade | cut | slide | kenburns.
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_transition', (table) => {
|
||||||
|
table.string('show_transition', 20).defaultTo('crossfade');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Transition animation duration in ms (how fast the transition plays).
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_transition_ms', (table) => {
|
||||||
|
table.integer('show_transition_ms').defaultTo(800);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Lookup is always by token; index it for the public /show/ route.
|
||||||
|
await createIndexIfNotExists(knex, 'events', ['show_share_token'], 'idx_events_show_share_token');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async function down() {
|
||||||
|
// Safe rollback - intentionally no-op to avoid data loss (mirrors 074).
|
||||||
|
};
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
const { addColumnIfNotExists } = require('../helpers');
|
||||||
|
|
||||||
|
// Live Slideshow styling (migration 137 follow-up):
|
||||||
|
// - a ZDF/ARD-ident-style watermark: a white, semi-transparent logo in a
|
||||||
|
// corner of the projected slideshow (sourced from the site branding logo
|
||||||
|
// or the event's own logo).
|
||||||
|
// - a color filter applied to every slide (none / b&w / sepia / warm / cool
|
||||||
|
// / vignette).
|
||||||
|
// - per-EVENT-TYPE slideshow presets: a JSON blob on event_types that new
|
||||||
|
// events of that type seed their slideshow settings from, so an admin sets
|
||||||
|
// "weddings fade slowly with our white logo, sepia" once.
|
||||||
|
//
|
||||||
|
// All opt-in: watermark defaults OFF, colorfilter defaults 'none', and the
|
||||||
|
// type preset is NULL until configured — existing events/types are unchanged.
|
||||||
|
exports.up = async function up(knex) {
|
||||||
|
// --- per-event live styling (seeded from the type preset on create) ---
|
||||||
|
// Tri-state: NULL = inherit the global default (app_settings
|
||||||
|
// slideshow_watermark_*), true/false = explicit per-event override.
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_watermark', (table) => {
|
||||||
|
table.boolean('show_watermark').nullable();
|
||||||
|
});
|
||||||
|
// Which logo to overlay: 'logo' (light branding logo) | 'logo_dark' (dark-mode
|
||||||
|
// branding logo) | 'favicon' | 'event' (event hero logo).
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_watermark_source', (table) => {
|
||||||
|
table.string('show_watermark_source', 20).defaultTo('logo');
|
||||||
|
});
|
||||||
|
// Corner: top-left | top-right | bottom-left | bottom-right.
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_watermark_position', (table) => {
|
||||||
|
table.string('show_watermark_position', 20).defaultTo('bottom-right');
|
||||||
|
});
|
||||||
|
// 0-100; rendered semi-transparent like a TV station ident.
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_watermark_opacity', (table) => {
|
||||||
|
table.integer('show_watermark_opacity').defaultTo(60);
|
||||||
|
});
|
||||||
|
// 'white' = recolor the logo white (for dark/transparent marks, the TV-ident
|
||||||
|
// look); 'original' = render as-is (for logos with their own filled box /
|
||||||
|
// colors, e.g. a boxed badge that would otherwise become a white blob).
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_watermark_style', (table) => {
|
||||||
|
table.string('show_watermark_style', 20).defaultTo('white');
|
||||||
|
});
|
||||||
|
// none | bw | sepia | warm | cool | vignette.
|
||||||
|
await addColumnIfNotExists(knex, 'events', 'show_colorfilter', (table) => {
|
||||||
|
table.string('show_colorfilter', 20).defaultTo('none');
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- per-event-type slideshow preset (JSON; null = no preset) ---
|
||||||
|
// Shape: { interval_ms, transition, transition_ms, watermark, watermark_source,
|
||||||
|
// watermark_position, watermark_opacity, colorfilter }. Single column
|
||||||
|
// keeps the type table tidy; the create-event path reads it and seeds the new
|
||||||
|
// event's show_* columns.
|
||||||
|
await addColumnIfNotExists(knex, 'event_types', 'slideshow_preset', (table) => {
|
||||||
|
table.text('slideshow_preset').nullable();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = async function down() {
|
||||||
|
// Safe rollback - intentionally no-op to avoid data loss (mirrors 074/137).
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user