From 83418eb2dd6b9250e50b9d7f41daf8bca9e11f1c Mon Sep 17 00:00:00 2001 From: Ned Zimmerman Date: Thu, 6 Aug 2020 20:43:51 -0300 Subject: [PATCH] feat: add filters, remove times and dates (#29) * feat: add filters (WIP) * tweaks to card sizes on mobile * fix: mobile css for filters * feat: remove schedule Co-authored-by: Nealevf --- _data/streams.js | 32 +++++--- _includes/layouts/base.njk | 3 + _includes/layouts/home.njk | 20 +---- _includes/partials/filters.njk | 21 +++++ _includes/partials/stream.njk | 18 +++++ app.js | 143 ++++++--------------------------- style.css | 41 ++++++++-- 7 files changed, 119 insertions(+), 159 deletions(-) create mode 100644 _includes/partials/filters.njk create mode 100644 _includes/partials/stream.njk diff --git a/_data/streams.js b/_data/streams.js index 2c5fcef..ec6ad9c 100644 --- a/_data/streams.js +++ b/_data/streams.js @@ -5,15 +5,12 @@ const airtable = require('airtable'); const Bottleneck = require('bottleneck'); const base = new airtable({apiKey}).base('appIDPnCgGApCGUtF'); -const compareStreams = (a, b) => { - const streamA = new Date(a.startTime); - const streamB = new Date(b.startTime); - +const compareStreams = (a, b) => { let comparison = 0; - if (streamA.getTime() > streamB.getTime()) { + if (a.name > b.name) { comparison = 1; - } else if (streamA.getTime() < streamB.getTime()) { + } else if (a.name < b.name) { comparison = -1; } @@ -25,21 +22,17 @@ module.exports = async function() { const limiter = new Bottleneck({minTime: 1000 / 5}); const streams = []; - limiter.wrap(base('Streams').select({ - filterByFormula: "IS_AFTER({End Time}, NOW())" - }).eachPage(function page(records, fetchNextPage) { + limiter.wrap(base('Streams').select().eachPage(function page(records, fetchNextPage) { records.forEach(function(record) { const name = record.get('Name'); const link = record.get('Link'); const photo = record.get('Photo'); - const startTime = record.get('Start Time'); - const endTime = record.get('End Time'); const creatorName = record.get('Creator Name'); const creatorLink = record.get('Creator Link'); const platform = record.get('Platform') || false; const subject = record.get('Subject'); const ageRange = record.get('Age Range'); - streams.push({name, link, photo, startTime, endTime, creatorName, creatorLink, platform, subject, ageRange}); + streams.push({name, link, photo, creatorName, creatorLink, platform, subject, ageRange}); }); fetchNextPage(); }, function done(err) { @@ -47,7 +40,20 @@ module.exports = async function() { console.error(err); reject(error); } - resolve({items: streams.sort(compareStreams)}); + + const subjects = streams.reduce((accumulator, currentStream) => { + if (!accumulator.includes(currentStream.subject)) { + accumulator.push(currentStream.subject); + } + + return accumulator; + }, []); + + resolve({ + items: streams.sort(compareStreams), + ageRanges: ["3+", "6+", "9+", "12+", "Teens+", "All Ages"], + subjects: subjects.sort() + }); })); }); } diff --git a/_includes/layouts/base.njk b/_includes/layouts/base.njk index e5acd06..6ae4167 100644 --- a/_includes/layouts/base.njk +++ b/_includes/layouts/base.njk @@ -33,6 +33,9 @@
{% include 'partials/navigation.njk' %}
+ {% if page.url === '/' %} + {% include 'partials/filters.njk' %} + {% endif %}
{% block content %} diff --git a/_includes/layouts/home.njk b/_includes/layouts/home.njk index 56bb08f..792db67 100644 --- a/_includes/layouts/home.njk +++ b/_includes/layouts/home.njk @@ -2,24 +2,6 @@ {% block content %} {% for item in streams.items %} - + {% include 'partials/stream.njk' %} {% endfor %} {% endblock %} diff --git a/_includes/partials/filters.njk b/_includes/partials/filters.njk new file mode 100644 index 0000000..f1ecbb5 --- /dev/null +++ b/_includes/partials/filters.njk @@ -0,0 +1,21 @@ +
+

Filter Streams

+

+ + +

+

+ + +

+
diff --git a/_includes/partials/stream.njk b/_includes/partials/stream.njk new file mode 100644 index 0000000..e24bdd1 --- /dev/null +++ b/_includes/partials/stream.njk @@ -0,0 +1,18 @@ + diff --git a/app.js b/app.js index 9a1d714..9a82545 100644 --- a/app.js +++ b/app.js @@ -1,129 +1,32 @@ -import { format, isPast, isAfter, isBefore, isToday, isTomorrow, addHours, addDays, endOfTomorrow } from "/web_modules/date-fns.js"; - -function insertHeading(stream, label) { - const heading = document.createElement('h2'); - heading.className = 'clearfix date-title'; - heading.textContent = label; - stream.parentNode.insertBefore(heading, stream); -} - -function formatDates(streams) { - streams.forEach(function(stream) { - const date = stream.querySelector('.card-date'); - const startTime = date.getAttribute('datetime'); - date.innerText = `${format(new Date(startTime), 'PP')} at ${format(new Date(startTime), 'p')}`; - }); -} - -function groupStreams(streams) { - let offset = false; - streams.forEach(function(stream) { - const startTime = stream.querySelector('.card-date').getAttribute('datetime'); - // Stream is today. - if (isToday(new Date(startTime))) { - if (!offset) { - insertHeading(stream, 'Today’s Livestreams'); - offset = '0'; - } - } - - // Stream is tomorrow. - if (isTomorrow(new Date(startTime))) { - if (!offset || offset === '0') { - insertHeading(stream, 'Tomorrow’s Livestreams'); - offset = '1'; - } - } - - // Stream is in two days. - if (isAfter(new Date(startTime), endOfTomorrow()) && isBefore(new Date(startTime), addDays(endOfTomorrow(), 1))) { - if (offset === '1') { - insertHeading(stream, `${format(new Date(startTime), 'EEEE')}’s Livestreams`); - offset = '2'; - } - } - // Stream is in three days. - if (isAfter(new Date(startTime), addDays(endOfTomorrow(), 1)) && isBefore(new Date(startTime), addDays(endOfTomorrow(), 2))) { - if (offset === '2') { - insertHeading(stream, `${format(new Date(startTime), 'EEEE')}’s Livestreams`); - offset = '3'; - } - } - // Stream is in four days. - if (isAfter(new Date(startTime), addDays(endOfTomorrow(), 2)) && isBefore(new Date(startTime), addDays(endOfTomorrow(), 3))) { - if (offset === '3') { - insertHeading(stream, `${format(new Date(startTime), 'EEEE')}’s Livestreams`); - offset = '4'; - } - } - // Stream is in five days. - if (isAfter(new Date(startTime), addDays(endOfTomorrow(), 3)) && isBefore(new Date(startTime), addDays(endOfTomorrow(), 4))) { - if (offset === '4') { - insertHeading(stream, `${format(new Date(startTime), 'EEEE')}’s Livestreams`); - offset = '5'; - } - } - // Stream is in six days. - if (isAfter(new Date(startTime), addDays(endOfTomorrow(), 4)) && isBefore(new Date(startTime), addDays(endOfTomorrow(), 5))) { - if (offset === '5') { - insertHeading(stream, `${format(new Date(startTime), 'EEEE')}’s Livestreams`); - offset = '6'; - } - } - // Stream is next week. - if (isAfter(new Date(startTime), addDays(endOfTomorrow(), 5)) && isBefore(new Date(startTime), addDays(endOfTomorrow(), 13))) { - if (offset === '6') { - insertHeading(stream, 'Next Week’s Livestreams'); - offset = 'nextweek'; - } - } - // Stream is later. - if (isAfter(new Date(startTime), addDays(endOfTomorrow(), 13))) { - if (offset === 'nextweek') { - insertHeading(stream, 'Future Livestreams'); - offset = 'later'; - } +function filterStreams() { + const streams = document.querySelectorAll('article.card'); + const ageRange = document.getElementById('ages').value !== '' ? document.getElementById('ages').value : false; + const subject = document.getElementById('subject').value !== '' ? document.getElementById('subject').value : false; + + const matchedStreams = [...streams].filter(stream => { + if (ageRange && subject) { + return stream.dataset.ages === ageRange && stream.dataset.subject === subject; + } else if (ageRange) { + return stream.dataset.ages === ageRange; + } else if (subject) { + return stream.dataset.subject === subject; + } else { + return stream; } }); -} - -function updateStreams() { - const streams = document.querySelectorAll('article.card'); - streams.forEach(function(stream) { - const date = stream.querySelector('.card-date'); - const startTime = date.getAttribute('datetime'); - const endTime = date.dataset.end; - - // Stream end time is in the past. - if (isPast(new Date(endTime))) { - stream.dataset.status = 'done'; - stream.querySelector('.badge .label').innerText = 'done'; - } - // Stream start time is within the next hour. - if (isAfter(new Date(startTime), new Date()) && isBefore(new Date(startTime), addHours(new Date(), 1))) { - stream.dataset.status = 'soon'; - stream.querySelector('.badge .label').innerText = 'soon'; - } - // Stream is live. - if (isAfter(new Date(), new Date(startTime)) && isBefore(new Date(), new Date(endTime))) { - stream.dataset.status = 'live'; - stream.querySelector('.badge .label').innerText = 'live'; + streams.forEach((stream) => { + if (!matchedStreams.includes(stream)) { + stream.setAttribute('hidden', ''); + } else { + stream.removeAttribute('hidden'); } }); } -function timedUpdate() { - updateStreams(); - setTimeout(timedUpdate, 60000); -} - -const streams = document.querySelectorAll('article.card'); - -if (streams.length > 0) { - formatDates(streams); - groupStreams(streams); - timedUpdate(); -} +document.addEventListener('input', function (event) { + if (event.target.id !== 'ages' && event.target.id !=='subject') return; + filterStreams(); +}, false); diff --git a/style.css b/style.css index ac3a721..0c4485a 100644 --- a/style.css +++ b/style.css @@ -282,10 +282,6 @@ p { transition: all 0.3s ease; } -.card--brand, .card--subhead, .card--navigation { - height: 14rem; -} - .card--subhead p.subhead { font-size: 1.5em; padding: 1.5rem 0.5rem 0; @@ -310,6 +306,7 @@ header .card.card--navigation { header .card { background: var(--green-dark); + height: 14rem; padding: 1rem; } @@ -360,6 +357,35 @@ header .card a:hover { transition: all 0.1s ease; } +/* Filters Card */ +.card.card--filters { + padding: 1.5rem 0.5rem; +} + +.card.card--filters h2 { + font-size: 1.5em; + color: var(--white); +} + +.card.card--filters p { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + color: var(--white); + padding: 1.5rem 0 0; +} + +.card.card--filters label { + margin-right: 0.5rem; + text-align: right; + width: calc(60% - 0.5rem); +} + +.card.card--filters select { + width: 40%; +} + /* Stream Card */ .card--stream:hover, @@ -374,7 +400,8 @@ header .card a:hover { transition: all 0.1s ease; } -.card[data-status="done"] { +.card[data-status="done"], +.card[hidden] { display: none; } @@ -510,8 +537,8 @@ header .card a:hover { text-align: center; } - .card--brand, .card--subhead, .card--navigation { - height: auto; + .card--brand, .card--subhead, .card--navigation, .card--filters { + height: auto !important; width: 90%; padding: 0.625rem; }