Skip to content

Commit 612aaf2

Browse files
committed
Use OTE embed for event rendering
1 parent c135a06 commit 612aaf2

3 files changed

Lines changed: 94 additions & 89 deletions

File tree

app.js

Lines changed: 77 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
var optionsContext = null;
3535
var editingCategoryId = null;
3636
var boardEventContext = null;
37+
var embedReady = false;
3738

3839
var $ = function (id) { return document.getElementById(id); };
3940

@@ -46,6 +47,7 @@
4647

4748
function applyTheme(theme) {
4849
document.documentElement.dataset.theme = theme;
50+
if ($('event-list')) $('event-list').setAttribute('theme', theme);
4951
var dark = theme === 'dark';
5052
var toggle = $('theme-toggle');
5153
if (toggle) {
@@ -1079,83 +1081,71 @@
10791081
}
10801082

10811083
function renderEvents() {
1082-
var list = $('event-list');
1084+
var widget = $('event-list');
10831085
var events = filteredEvents();
1084-
list.className = 'events view-' + state.view;
1085-
list.replaceChildren();
10861086
$('result-count').textContent = sourceLabel() + ' · ' + events.length + (events.length === 1 ? ' evento' : ' eventos');
10871087
if (!events.length) {
1088+
widget.events = [];
1089+
widget.setAttribute('empty-message', 'No hay eventos con esos filtros.');
10881090
showMessage('No hay eventos con esos filtros.', 'warn', true);
10891091
return;
10901092
}
10911093
clearMessages();
1092-
if (state.view === 'calendar') renderCalendar(events, list);
1093-
else events.forEach(function (event) { list.appendChild(renderEvent(event)); });
1094-
}
1095-
1096-
function renderCalendar(events, list) {
1097-
var groups = {};
1098-
events.forEach(function (event) {
1099-
var date = startAsDate(event);
1100-
var key = date ? date.toLocaleDateString('es', { month: 'long', year: 'numeric' }) : 'Sin fecha';
1101-
if (!groups[key]) groups[key] = [];
1102-
groups[key].push(event);
1103-
});
1104-
Object.keys(groups).forEach(function (key) {
1105-
var group = document.createElement('section');
1106-
group.className = 'calendar-group';
1107-
var title = document.createElement('h3');
1108-
title.textContent = key;
1109-
group.appendChild(title);
1110-
groups[key].forEach(function (event) {
1111-
group.appendChild(renderEvent(event));
1112-
});
1113-
list.appendChild(group);
1114-
});
1115-
}
1116-
1117-
function renderEvent(event) {
1118-
var tpl = $('event-template').content.cloneNode(true);
1119-
var card = tpl.querySelector('.event-card');
1120-
if (event.status === 'cancelled') card.classList.add('is-cancelled');
1121-
var date = startAsDate(event);
1122-
tpl.querySelector('.event-date').innerHTML = date
1123-
? '<span>' + date.toLocaleString('es', { month: 'short' }) + '</span>' + date.getDate()
1124-
: '<span>sin</span>fecha';
1125-
tpl.querySelector('.event-topline').textContent = state.view === 'feed'
1126-
? 'Actualizado ' + formatUpdated(event) + ' · ' + event._feedTitle
1127-
: [event._feedTitle, organizerText(event)].filter(Boolean).join(' · ');
1128-
tpl.querySelector('h3').textContent = event.name;
1129-
tpl.querySelector('.event-description').appendChild(markdown(event.description || 'Sin descripción en el feed.'));
1130-
tpl.querySelector('.event-meta').textContent = [
1131-
formatDate(event),
1132-
labelMode(event.attendanceMode),
1133-
locationText(event),
1134-
(event.languages || []).join(', ')
1135-
].filter(Boolean).join(' · ');
1136-
var tags = tpl.querySelector('.event-tags');
1137-
(event.tags || []).forEach(function (tag) {
1138-
var item = document.createElement('span');
1139-
item.className = 'tag';
1140-
item.textContent = '#' + tag;
1141-
tags.appendChild(item);
1142-
});
1143-
if (cfpIsOpen(event)) tags.appendChild(pill('CFP abierto', 'ok'));
1144-
if (event.status === 'cancelled') tags.appendChild(pill('Cancelado', 'warn'));
1145-
var link = tpl.querySelector('.primary-link');
1146-
link.href = event.url || event.cfp && event.cfp.url || event._feedUrl || event.id;
1147-
var save = tpl.querySelector('.save-event');
1148-
var saved = state.boards.some(function (board) { return boardHasEvent(board, event); });
1149-
save.textContent = saved ? 'En colección' : 'Favorito';
1150-
save.classList.toggle('is-active', saved);
1151-
save.setAttribute('aria-pressed', String(saved));
1152-
save.addEventListener('click', function () {
1153-
openBoardPicker(event);
1154-
});
1155-
tpl.querySelector('.calendar-event').addEventListener('click', function () {
1156-
downloadIcs(event);
1157-
});
1158-
return tpl;
1094+
if (!embedReady) {
1095+
showMessage('El visor de eventos se está cargando...', 'warn', true);
1096+
return;
1097+
}
1098+
widget.setAttribute('layout', state.view === 'feed' ? 'list' : state.view);
1099+
widget.setAttribute('theme', document.documentElement.dataset.theme || 'light');
1100+
widget.setAttribute('fields', 'image,when,location,attendance,description,price,tags,organizer');
1101+
widget.setAttribute('show-past', 'true');
1102+
widget.setAttribute('sort', 'none');
1103+
widget.setAttribute('event-actions', 'none');
1104+
widget.setAttribute('empty-message', 'No hay eventos con esos filtros.');
1105+
widget.events = events;
1106+
widget.eventActions = function (context) {
1107+
var event = context.originalEvent;
1108+
if (!event) return [];
1109+
var saved = state.boards.some(function (board) { return boardHasEvent(board, event); });
1110+
return [
1111+
{
1112+
id: 'save-to-collection',
1113+
label: saved ? 'En colección' : 'Guardar',
1114+
icon: saved ? 'bookmark' : 'star',
1115+
pressed: saved,
1116+
placement: 'both',
1117+
onClick: function (_previewEvent, actionContext) {
1118+
if (actionContext.originalEvent) openBoardPicker(actionContext.originalEvent);
1119+
}
1120+
},
1121+
{ type: 'link', placement: 'detail' },
1122+
{ type: 'ics', placement: 'detail' }
1123+
];
1124+
};
1125+
widget.eventClassName = function (context) {
1126+
var event = context.originalEvent;
1127+
return [
1128+
event && event.status === 'cancelled' ? 'reader-cancelled' : '',
1129+
event && eventIsUnread(event) ? 'reader-unread' : 'reader-read'
1130+
].filter(Boolean);
1131+
};
1132+
widget.eventBadges = function (context) {
1133+
var event = context.originalEvent;
1134+
if (!event) return [];
1135+
var badges = [];
1136+
if (event._feedTitle) badges.push({ label: event._feedTitle, icon: 'folder' });
1137+
if (cfpIsOpen(event)) badges.push({ label: 'CFP abierto', icon: 'check', tone: 'success' });
1138+
if (event.status === 'cancelled') badges.push({ label: 'Cancelado', tone: 'warning' });
1139+
return badges;
1140+
};
1141+
}
1142+
1143+
function eventIsUnread(event) {
1144+
var feed = event && findFeed(event._feedUrl);
1145+
if (!feed || feed.status !== 'ok') return false;
1146+
if (!feed.lastSeenAt) return true;
1147+
var activity = event.updatedAt ? new Date(event.updatedAt) : null;
1148+
return Boolean(activity && !Number.isNaN(activity.getTime()) && activity > new Date(feed.lastSeenAt));
11591149
}
11601150

11611151
function openBoardPicker(event) {
@@ -1638,6 +1628,7 @@
16381628
loadState();
16391629
bind();
16401630
updateInstallUi();
1631+
await waitForEmbed();
16411632
render();
16421633
applySubscribeParam();
16431634
await Promise.all(state.feeds.map(loadFeed));
@@ -1646,5 +1637,21 @@
16461637
}
16471638
}
16481639

1640+
async function waitForEmbed() {
1641+
if (!window.customElements || !customElements.whenDefined) return;
1642+
try {
1643+
await Promise.race([
1644+
customElements.whenDefined('ote-events'),
1645+
new Promise(function (_, reject) {
1646+
setTimeout(function () { reject(new Error('timeout')); }, 6000);
1647+
})
1648+
]);
1649+
embedReady = true;
1650+
} catch (e) {
1651+
embedReady = false;
1652+
showMessage('No se pudo cargar el componente de visualización OTE.', 'warn', true);
1653+
}
1654+
}
1655+
16491656
boot();
16501657
}());

index.html

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ <h2 id="result-count">Eventos</h2>
129129
</div>
130130
</div>
131131
<div id="messages"></div>
132-
<div class="events" id="event-list"></div>
132+
<ote-events class="reader-events" id="event-list" lang="es" sort="none" event-actions="none"></ote-events>
133133
</section>
134134
</section>
135135
<div class="scrim" id="sidebar-scrim" hidden></div>
@@ -231,24 +231,7 @@ <h3>Instalación</h3>
231231
</section>
232232
</dialog>
233233

234-
<template id="event-template">
235-
<article class="event-card">
236-
<div class="event-date"></div>
237-
<div class="event-body">
238-
<div class="event-topline"></div>
239-
<h3></h3>
240-
<div class="event-description"></div>
241-
<div class="event-meta"></div>
242-
<div class="event-tags"></div>
243-
<div class="event-actions">
244-
<a class="primary-link" target="_blank" rel="noopener">Abrir origen</a>
245-
<button type="button" class="save-event">Favorito</button>
246-
<button type="button" class="calendar-event">.ics</button>
247-
</div>
248-
</div>
249-
</article>
250-
</template>
251-
234+
<script type="module" src="https://tools.opentechevents.org/embed/v0.3.0/ote-events.js"></script>
252235
<script src="app.js"></script>
253236
</body>
254237
</html>

styles.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,21 @@ label {
778778
display: grid;
779779
gap: 12px;
780780
}
781+
.reader-events {
782+
display: block;
783+
--ote-font-family: var(--sans);
784+
--ote-font-size: .94rem;
785+
--ote-surface: var(--panel);
786+
--ote-border: var(--line);
787+
--ote-text: var(--ink);
788+
--ote-muted: var(--muted);
789+
--ote-accent: var(--accent);
790+
--ote-accent-hover: var(--accent-strong);
791+
--ote-accent-soft: var(--accent-soft);
792+
--ote-error: var(--danger);
793+
--ote-error-bg: color-mix(in srgb, var(--danger) 12%, var(--panel));
794+
--ote-radius: 8px;
795+
}
781796
.view-cards {
782797
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
783798
}

0 commit comments

Comments
 (0)