Skip to content

Commit e9b0433

Browse files
hhkaosclaude
andcommitted
Gate demo feeds behind demo mode, add empty state and demo hello bar
Previously demo-feed.json seeded automatically for every first-time visitor. Demo feeds now only load via ?demo=1 or /demo/, first-time visitors see an empty state with actions to add/discover feeds, and demo mode shows a dismissible hello bar with an exit action that breaks out of the /demo/ iframe wrapper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 2ab9895 commit e9b0433

5 files changed

Lines changed: 130 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,15 @@
88
modal with the feed URL pre-filled instead of subscribing immediately,
99
so the user confirms before a feed is added. No dialog is shown if the
1010
feed is already subscribed. ([#1](https://github.com/OpenTechEvents/ote-reader/issues/1))
11+
- Demo feeds (`demo-feed.json` and the directory samples) now only load in
12+
demo mode (`?demo=1` or `/demo/`), instead of seeding automatically for
13+
every first-time visitor with an empty state.
14+
15+
### Added
16+
17+
- An empty state ("Empieza con tus propias fuentes OTE") with actions to
18+
add a feed, discover feeds, or open the demo, shown when there are no
19+
subscribed feeds.
20+
- A dismissible "Estás viendo la demo" hello bar with a "Salir de la demo"
21+
action when running in demo mode, including breaking out of the `/demo/`
22+
iframe wrapper back to the top-level reader.

app.js

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
(function () {
22
'use strict';
33

4-
var STORAGE = 'ote-reader-state-v1';
4+
var DEMO_MODE = new URLSearchParams(location.search).get('demo') === '1' || location.pathname.replace(/\/+$/, '').endsWith('/demo');
5+
var STORAGE = DEMO_MODE ? 'ote-reader-demo-state-v1' : 'ote-reader-state-v1';
56
var INSTALL_DISMISSED = 'ote-reader-install-dismissed-v1';
67
var THEME_STORAGE = 'ote-reader-theme-v1';
78
var WIDTH_STORAGE = 'ote-reader-width-v1';
89
var ADOPTERS_URL = 'https://opentechevents.org/data/adopters.json';
9-
var DEFAULT_FEEDS = [
10-
{ url: new URL('demo-feed.json', location.href).href, title: 'Feed demo' }
10+
var DEMO_FEEDS = [
11+
{ url: new URL('demo-feed.json', document.baseURI).href, title: 'Feed demo' }
1112
];
1213
var DIRECTORY_DEMO_FEEDS = [
13-
{ url: new URL('eventos-wiki-demo-feed.json', location.href).href, title: 'Eventos Wiki demo' },
14-
{ url: new URL('techconf-demo-feed.json', location.href).href, title: 'TechConf España demo' }
14+
{ url: new URL('eventos-wiki-demo-feed.json', document.baseURI).href, title: 'Eventos Wiki demo' },
15+
{ url: new URL('techconf-demo-feed.json', document.baseURI).href, title: 'TechConf España demo' }
1516
];
1617
var DIRECTORY_FOLDER = 'Directorios OTE';
1718
var DEFAULT_FOLDERS = ['Conferencias', 'Eventos Almería', 'CFP para charlas'];
@@ -96,13 +97,13 @@
9697
} catch (e) {
9798
showMessage('No se pudo leer el estado guardado. Se usará una sesión limpia.', 'warn');
9899
}
99-
if (!state.feeds.length) {
100-
DEFAULT_FEEDS.forEach(function (feed) {
100+
if (DEMO_MODE && !state.feeds.length) {
101+
DEMO_FEEDS.forEach(function (feed) {
101102
state.feeds.push({ url: feed.url, title: feed.title, status: 'pending' });
102103
});
103104
}
104105
migrateCategories();
105-
seedDirectoryDemoFeeds();
106+
if (DEMO_MODE) seedDirectoryDemoFeeds();
106107
migrateBoards();
107108
persist();
108109
}
@@ -120,15 +121,16 @@
120121
}
121122

122123
function migrateCategories() {
123-
if (!state.categories.length) {
124+
if (!state.categories.length && state.feeds.length) {
124125
state.categories.push({
125126
id: 'community',
126127
name: 'Community feeds',
127128
open: true,
128129
feedUrls: state.feeds.map(function (feed) { return feed.url; })
129130
});
130131
}
131-
seedDefaultFolders();
132+
if (DEMO_MODE) seedDefaultFolders();
133+
if (!state.categories.length) return;
132134
var known = new Set(state.categories.flatMap(function (category) { return category.feedUrls || []; }));
133135
var missing = state.feeds.map(function (feed) { return feed.url; }).filter(function (url) { return !known.has(url); });
134136
if (missing.length) state.categories[0].feedUrls = unique((state.categories[0].feedUrls || []).concat(missing));
@@ -1087,7 +1089,8 @@
10871089
if (!events.length) {
10881090
widget.events = [];
10891091
widget.setAttribute('empty-message', 'No hay eventos con esos filtros.');
1090-
showMessage('No hay eventos con esos filtros.', 'warn', true);
1092+
if (!state.feeds.length) showEmptyState();
1093+
else showMessage('No hay eventos con esos filtros.', 'warn', true);
10911094
return;
10921095
}
10931096
clearMessages();
@@ -1342,6 +1345,40 @@
13421345
$('messages').replaceChildren();
13431346
}
13441347

1348+
function showEmptyState() {
1349+
var box = $('messages');
1350+
box.replaceChildren();
1351+
var empty = document.createElement('section');
1352+
empty.className = 'empty-state';
1353+
var title = document.createElement('h3');
1354+
title.textContent = DEMO_MODE ? 'Demo sin eventos cargados' : 'Empieza con tus propias fuentes OTE';
1355+
var copy = document.createElement('p');
1356+
copy.textContent = DEMO_MODE
1357+
? 'La demo usa feeds de ejemplo para enseñar folders, vistas y colecciones.'
1358+
: 'Suscríbete a feeds OTE, descubre fuentes publicadas o abre una demo con datos de ejemplo.';
1359+
var actions = document.createElement('div');
1360+
actions.className = 'empty-actions';
1361+
var manual = document.createElement('button');
1362+
manual.type = 'button';
1363+
manual.textContent = 'Añadir feed';
1364+
manual.addEventListener('click', function () { openModal('subscribe-modal'); });
1365+
var discover = document.createElement('button');
1366+
discover.type = 'button';
1367+
discover.className = 'ghost';
1368+
discover.textContent = 'Discover new feeds';
1369+
discover.addEventListener('click', openSources);
1370+
actions.append(manual, discover);
1371+
if (!DEMO_MODE) {
1372+
var demo = document.createElement('a');
1373+
demo.className = 'ghost-link';
1374+
demo.href = 'demo/';
1375+
demo.textContent = 'Ver demo';
1376+
actions.appendChild(demo);
1377+
}
1378+
empty.append(title, copy, actions);
1379+
box.appendChild(empty);
1380+
}
1381+
13451382
function subscribe(rawUrl) {
13461383
var url;
13471384
try { url = normaliseUrl(rawUrl); } catch (e) {
@@ -1405,6 +1442,7 @@
14051442
}
14061443

14071444
function bind() {
1445+
$('demo-exit').addEventListener('click', exitDemo);
14081446
$('subscribe-open-side').addEventListener('click', function () { openModal('subscribe-modal'); });
14091447
$('find-sources-open').addEventListener('click', openSources);
14101448
$('sidebar-toggle').addEventListener('click', toggleSidebar);
@@ -1578,6 +1616,18 @@
15781616
$('help-install').hidden = !canInstall;
15791617
}
15801618

1619+
function updateDemoUi() {
1620+
$('demo-bar').hidden = !DEMO_MODE;
1621+
}
1622+
1623+
function exitDemo(event) {
1624+
var target = location.origin + '/';
1625+
if (window.top !== window.self) {
1626+
event.preventDefault();
1627+
window.top.location.href = target;
1628+
}
1629+
}
1630+
15811631
async function promptInstall() {
15821632
if (!deferredInstall) return;
15831633
deferredInstall.prompt();
@@ -1633,6 +1683,7 @@
16331683
loadState();
16341684
bind();
16351685
updateInstallUi();
1686+
updateDemoUi();
16361687
await waitForEmbed();
16371688
render();
16381689
applySubscribeParam();

demo/index.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<html lang="es">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>OTE Reader Demo</title>
7+
<style>
8+
html, body {
9+
width: 100%;
10+
height: 100%;
11+
margin: 0;
12+
}
13+
iframe {
14+
display: block;
15+
width: 100%;
16+
height: 100%;
17+
border: 0;
18+
}
19+
</style>
20+
</head>
21+
<body>
22+
<iframe title="OTE Reader demo" src="../?demo=1"></iframe>
23+
</body>
24+
</html>

index.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
<body>
1515
<a class="skip" href="#events">Saltar a eventos</a>
1616

17+
<section class="hello-bar" id="demo-bar" hidden>
18+
<p>Estás viendo la <strong>demo</strong> de OTE Reader con feeds de ejemplo.</p>
19+
<div>
20+
<a class="primary-link" id="demo-exit" href="./">Salir de la demo</a>
21+
</div>
22+
</section>
23+
1724
<section class="hello-bar" id="install-bar" hidden>
1825
<p>Instala OTE Reader para tener tus eventos siempre a mano.</p>
1926
<div>

styles.css

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,11 @@ button:hover, .primary-link:hover {
160160
background: var(--field);
161161
color: var(--ink);
162162
}
163-
.primary-action, .install-button, .hello-bar button:not(.icon-button), .help-install button {
163+
.primary-action, .install-button, .hello-bar button:not(.icon-button), .hello-bar .primary-link, .help-install button {
164164
background: var(--accent);
165165
border-color: var(--accent);
166166
}
167-
.hello-bar button:not(.icon-button) {
167+
.hello-bar button:not(.icon-button), .hello-bar .primary-link {
168168
min-height: 28px;
169169
padding: 4px 9px;
170170
border-radius: 6px;
@@ -743,6 +743,29 @@ label {
743743
color: var(--muted);
744744
}
745745
.message.warn { border-color: color-mix(in srgb, var(--warn) 45%, var(--line)); background: color-mix(in srgb, var(--warn) 10%, var(--panel)); color: var(--warn); }
746+
.empty-state {
747+
display: grid;
748+
gap: 10px;
749+
max-width: 620px;
750+
padding: 22px;
751+
border: 1px solid var(--line);
752+
border-radius: 8px;
753+
background: var(--panel);
754+
}
755+
.empty-state h3 {
756+
margin: 0;
757+
font-size: 1.1rem;
758+
}
759+
.empty-state p {
760+
margin: 0;
761+
color: var(--muted);
762+
}
763+
.empty-actions {
764+
display: flex;
765+
flex-wrap: wrap;
766+
gap: 8px;
767+
margin-top: 4px;
768+
}
746769

747770
.chips {
748771
display: flex;

0 commit comments

Comments
 (0)