a302ac09b4
CI / test (push) Has been cancelled
Lets users opt into the new atmospheric design without affecting users on v1.
Adds a beta-flag toggle in Settings → Design. Server-side preference persists
across devices; a cookie mirrors it so unauthenticated Twig pages do correct
first-paint without an extra DB roundtrip.
Backend:
- User.designVersion column (nullable VARCHAR(10); null defaults to 'v1')
- Migration Version20260515120000
- PATCH /api/user/design endpoint accepting 'v1'|'v2', sets wevisto_design cookie
- SpaController injects data-design on <html> + refreshes the cookie on every
SPA load (keeps cross-device pref in sync)
- Twig templates (base, login, register, help, setup, token-*) read the
cookie via {{ app.request.cookies.get('wevisto_design')|default('v1') }}
so login/setup pages also respect the user's design choice
Frontend:
- design-v2.scss — opt-in overlay scoped under [data-design="v2"]. Overrides
--color-* tokens to dusk variants per theme (warm-craft → amber, ocean-dusk
stays, etc.), adds harbor photo backdrop via body::before with theme tint
via body::after. Glass-card blur on existing surfaces. v1 untouched.
- harbor.jpg shipped as a public asset (270KB, single-fetch, cached)
- User type gains designVersion ('v1' | 'v2')
- SettingsView toggle (Original / Atmospheric) calls the API, updates the
data-design attribute optimistically, reverts on failure
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
68 lines
3.4 KiB
Twig
68 lines
3.4 KiB
Twig
<!DOCTYPE html>
|
|
<html lang="en" data-design="{{ app.request.cookies.get('wevisto_design')|default('v1') }}">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link rel="icon" type="image/svg+xml" href="/build/favicon.svg">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/build/icons/favicon-32.png">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="/build/icons/favicon-16.png">
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/build/icons/apple-touch-icon.png">
|
|
<title>Add photo to your frame — WeVisto</title>
|
|
<style>
|
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
body{font-family:system-ui,sans-serif;min-height:100dvh;background:#fdf6ee;color:#3a2e22;display:flex;align-items:flex-start;justify-content:center;padding:2rem 1rem}
|
|
.card{width:100%;max-width:420px}
|
|
h1{font-size:1.4rem;font-weight:700;margin-bottom:.25rem}
|
|
.sub{font-size:.875rem;color:#8a7060;margin-bottom:1.5rem}
|
|
.thumb{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:10px;margin-bottom:1.5rem;background:#e8d9c4}
|
|
.label{display:block;font-size:.8125rem;font-weight:600;color:#8a7060;margin-bottom:.75rem}
|
|
.devices{display:flex;flex-direction:column;gap:.5rem;margin-bottom:1.5rem}
|
|
.device-row{display:flex;align-items:center;gap:.75rem;padding:.75rem;border:1.5px solid #e8d9c4;border-radius:8px;cursor:pointer}
|
|
.device-row input{width:18px;height:18px;accent-color:#c97c3a;flex-shrink:0}
|
|
.device-name{font-weight:600}
|
|
.device-orientation{font-size:.8125rem;color:#8a7060;margin-left:auto}
|
|
.btn{display:block;width:100%;padding:.875rem;border:none;border-radius:10px;font-size:1rem;font-weight:700;cursor:pointer;text-align:center;text-decoration:none;margin-bottom:.75rem}
|
|
.btn-primary{background:#c97c3a;color:#fff}
|
|
.btn-primary:disabled{opacity:.5;cursor:default}
|
|
.btn-ghost{background:transparent;border:1.5px solid #e8d9c4;color:#8a7060}
|
|
.login-cta{background:#f5ede2;border-radius:10px;padding:1.25rem;text-align:center;margin-bottom:1rem}
|
|
.login-cta p{font-size:.875rem;color:#8a7060;margin-bottom:.75rem}
|
|
.login-cta a{color:#c97c3a;font-weight:700;text-decoration:none}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="card">
|
|
<h1>Someone shared a photo with you</h1>
|
|
<p class="sub">{{ token.image.originalFilename }} — shared by {{ token.recipientEmail }}</p>
|
|
|
|
<img class="thumb" src="/api/images/{{ token.image.id }}/thumbnail" alt="Shared photo">
|
|
|
|
{% if user %}
|
|
<form method="post">
|
|
<label class="label">Add to your frames:</label>
|
|
|
|
{% if devices is empty %}
|
|
<p class="sub">You don't have any frames set up yet. <a href="/" style="color:#c97c3a">Set one up first</a>.</p>
|
|
{% else %}
|
|
<div class="devices">
|
|
{% for device in devices %}
|
|
<label class="device-row">
|
|
<input type="checkbox" name="device_ids[]" value="{{ device.id }}">
|
|
<span class="device-name">{{ device.name }}</span>
|
|
<span class="device-orientation">{{ device.orientation }}</span>
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
<button class="btn btn-primary" type="submit">Add to selected frames</button>
|
|
{% endif %}
|
|
</form>
|
|
{% else %}
|
|
<div class="login-cta">
|
|
<p>Log in to add this photo to your frame.</p>
|
|
<a href="/login?_target_path=/token/{{ token.uuid }}/approve" class="btn btn-primary" style="display:inline-block;padding:.75rem 1.5rem;border-radius:8px">Log in</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</body>
|
|
</html>
|