Added src/js/misc/newsfeed.js

A small script to pull feed from blog.fiery.me as news thingy.
This commit is contained in:
Bobby Wibowo 2020-07-28 03:46:15 +07:00
parent 08db3e55a8
commit 6daa1e529e
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
11 changed files with 214 additions and 8 deletions

2
dist/css/home.css vendored
View File

@ -1,2 +1,2 @@
#b{width:200px;height:200px;border-radius:100%;display:inline-block;margin-bottom:40px;vertical-align:top;-webkit-animation-delay:.5s;animation-delay:.5s;-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:floatUp;animation-name:floatUp;-webkit-animation-timing-function:cubic-bezier(0,.71,.29,1);animation-timing-function:cubic-bezier(0,.71,.29,1)}.logo{max-height:200px}.logo.is-2x{display:none}#dropzone *{pointer-events:none}#panel,#tokenContainer{display:none}#maxSize{font-size:1rem}.dz-preview .dz-details{display:flex}.dz-preview .dz-details .dz-filename,.dz-preview .dz-details .dz-size{flex:1}.dz-preview .dz-error-mark,.dz-preview .dz-success-mark,.dz-preview img{display:none}@-webkit-keyframes floatUp{0%{opacity:0;transform:scale(.86)}25%{opacity:1}67%{transform:scale(1)}to{transform:scale(1)}}@keyframes floatUp{0%{opacity:0;transform:scale(.86)}25%{opacity:1}67%{transform:scale(1)}to{transform:scale(1)}}.uploads{display:flex;flex-direction:column}.uploads.is-reversed{flex-direction:column-reverse}.uploads>div{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s;margin:.75rem!important}.uploads.is-reversed>div{flex:0 0 auto}.uploads>div:first-child{margin-top:1.5rem}.uploads.nojs{margin-bottom:0}.uploads>div>.icon:not(.icon-block){color:#209cee}.uploads>div>.icon.icon-block{color:#da4453}.uploads .descriptive-progress{color:#bdc3c7}.uploads img{max-width:200px}.name{font-size:1rem;color:#eff0f1}.link>a,.name{word-break:break-all}.clipboard-mobile{margin-top:5px}#albumDiv{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#albumDiv .control{text-align:inherit}#linksColumn{margin-top:-.25rem;margin-left:-.25rem;margin-right:-.25rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#linksColumn .column{padding:.25rem}#linksColumn>span{padding:0 .3rem;color:#7f8c8d}.git-commit a{display:inline-block;word-break:break-all}#tabs{margin-bottom:1rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#tabs ul{border-bottom:1px solid #585858}#tabs li a{color:#bdc3c7;border-bottom-color:#585858}#tabs.is-boxed li.is-active a{color:#209cee;background:#000;border-color:#585858 #585858 #000}#tabs.is-boxed li:not(.is-active) a:hover{background:#585858}.tab-content{margin-bottom:-.75rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#tab-config.tab-content form{margin-bottom:.75rem}#urlMaxSize{font-weight:700}.render{position:fixed;right:0;bottom:0;font-size:1rem;color:#bdc3c7;cursor:pointer}.render.button{border-bottom-left-radius:0;border-bottom-right-radius:0;right:1%;opacity:.25;transition:opacity .25s}.render.button:hover{opacity:1}input[type=file].is-fullwidth{width:100%}
#b{width:200px;height:200px;border-radius:100%;display:inline-block;margin-bottom:40px;vertical-align:top;-webkit-animation-delay:.5s;animation-delay:.5s;-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:floatUp;animation-name:floatUp;-webkit-animation-timing-function:cubic-bezier(0,.71,.29,1);animation-timing-function:cubic-bezier(0,.71,.29,1)}.logo{max-height:200px}.logo.is-2x{display:none}#dropzone *{pointer-events:none}#panel,#tokenContainer{display:none}#maxSize{font-size:1rem}.dz-preview .dz-details{display:flex}.dz-preview .dz-details .dz-filename,.dz-preview .dz-details .dz-size{flex:1}.dz-preview .dz-error-mark,.dz-preview .dz-success-mark,.dz-preview img{display:none}@-webkit-keyframes floatUp{0%{opacity:0;transform:scale(.86)}25%{opacity:1}67%{transform:scale(1)}to{transform:scale(1)}}@keyframes floatUp{0%{opacity:0;transform:scale(.86)}25%{opacity:1}67%{transform:scale(1)}to{transform:scale(1)}}.uploads{display:flex;flex-direction:column}.uploads.is-reversed{flex-direction:column-reverse}.uploads>div{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s;margin:.75rem!important}.uploads.is-reversed>div{flex:0 0 auto}.uploads>div:first-child{margin-top:1.5rem}.uploads.nojs{margin-bottom:0}.uploads>div>.icon:not(.icon-block){color:#209cee}.uploads>div>.icon.icon-block{color:#da4453}.uploads .descriptive-progress{color:#bdc3c7}.uploads img{max-width:200px}.name{font-size:1rem;color:#eff0f1}.link>a,.name{word-break:break-all}.clipboard-mobile{margin-top:5px}#albumDiv{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#albumDiv .control{text-align:inherit}#linksColumn{margin-top:-.25rem;margin-left:-.25rem;margin-right:-.25rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#linksColumn .column{padding:.25rem}#linksColumn>span{padding:0 .3rem;color:#7f8c8d}.git-commit a{display:inline-block;word-break:break-all}#tabs{margin-bottom:1rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#tabs ul{border-bottom:1px solid #585858}#tabs li a{color:#bdc3c7;border-bottom-color:#585858}#tabs.is-boxed li.is-active a{color:#209cee;background:#000;border-color:#585858 #585858 #000}#tabs.is-boxed li:not(.is-active) a:hover{background:#585858}.tab-content{margin-bottom:-.75rem;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}#tab-config.tab-content form{margin-bottom:.75rem}#urlMaxSize{font-weight:700}.render{position:fixed;right:0;bottom:0;font-size:1rem;color:#bdc3c7;cursor:pointer}.render.button{border-bottom-left-radius:0;border-bottom-right-radius:0;right:1%;opacity:.25;transition:opacity .25s}.render.button:hover{opacity:1}input[type=file].is-fullwidth{width:100%}#newsfeed{position:absolute;top:0;right:0;left:0;padding:1.5rem 1.5rem 0}#newsfeed .notification{padding:.9375rem 1.875rem .9375rem 1.125rem;margin-bottom:1.125rem}#newsfeed .notification .content{font-size:.75rem}#newsfeed .news-date.is-recent-week,#newsfeed .news-title{font-weight:700}
/*# sourceMappingURL=home.css.map */

File diff suppressed because one or more lines are too long

2
dist/js/home.js vendored

File diff suppressed because one or more lines are too long

2
dist/js/home.js.map vendored

File diff suppressed because one or more lines are too long

2
dist/js/misc/newsfeed.js vendored Normal file
View File

@ -0,0 +1,2 @@
var newsfeed={lsKey:"newsfeed",feedUrl:"https://blog.fiery.me/rss-newsfeed.xml",maxItems:3,dismissed:{},done:!1,simpleParseDate:function(e){var t={Jan:0,Feb:1,Mar:2,Apr:3,May:4,Jun:5,Jul:6,Aug:7,Sep:8,Oct:9,Nov:10,Dec:11},n=e.match(/[a-zA-Z]*,\s(\d{2})\s([a-zA-Z]{3})\s(\d{4})\s(\d{2}):(\d{2}):(\d{2})\sGMT/);if(n&&void 0!==t[n[2]]){var i=new Date;return i.setUTCDate(n[1]),i.setUTCMonth(t[n[2]]),i.setUTCFullYear(n[3]),i.setUTCHours(n[4]),i.setUTCMinutes(n[5]),i.setUTCSeconds(n[6]),i}},formatRelativeDate:function(e){var t,n;return e<60?(t=e,n="second"):e<3600?(t=Math.floor(e/60),n="minute"):e<86400?(t=Math.floor(e/3600),n="hour"):e<604800?(t=Math.floor(e/86400),n="day"):(t=Math.floor(e/604800),n="week"),t+" "+n+(1!==t?"s":"")+" ago"},formatNotification:function(e){var t=newsfeed.simpleParseDate(e.pubDate),n=Math.round((+new Date-t)/1e3),i=n<=604800,s=document.createElement("div");return s.dataset.identifier=e.identifier,s.className="notification is-info",s.innerHTML='\n <button class="delete" title="Dismiss"></button>\n <div class="content">\n <div class="news-title">\n <a href="'+e.link+'" target="_blank">'+(e.title||"Untitled")+'</a>\n </div>\n <div class="news-excerpt">\n '+(e.description?""+("…"===e.description.slice(-1)?e.description.slice(0,-1)+' <a href="'+e.link+'" target="_blank">[…]</a>':e.description):"N/A")+'\n </div>\n <div class="news-date'+(i?" is-recent-week":"")+'" title="'+t.toLocaleString()+'">\n '+newsfeed.formatRelativeDate(n)+"\n </div>\n <div>\n ",s},dismissNotification:function(e){if(e&&e.dataset.identifier){newsfeed.dismissed[e.dataset.identifier]=1,e.parentNode.removeChild(e);var t=Object.keys(newsfeed.dismissed);if(t.length>newsfeed.maxItems)for(var n=0;n<t.length-newsfeed.maxItems;n++)delete newsfeed.dismissed[t[n]];localStorage[newsfeed.lsKey]=JSON.stringify(newsfeed.dismissed)}},do:function(){return axios.get(newsfeed.feedUrl,{responseType:"document"}).then((function(e){if(!(e&&e.data&&e.data.documentElement instanceof Element))throw Error("response.data.documentElement is NOT an instance of Element");var t=e.data.documentElement.querySelectorAll("item");if(t.length){var n=localStorage[newsfeed.lsKey];n&&(newsfeed.dismissed=JSON.parse(n));var i=document.createElement("section");i.id="newsfeed",i.className="section",i.innerHTML='\n <div class="columns is-gapless">\n <div class="column is-hidden-mobile"></div>\n <div class="column is-hidden-mobile"></div>\n <div class="column"></div>\n </div>\n ';for(var s=i.querySelector(".columns > .column:last-child"),d=0;d<Math.min(newsfeed.maxItems,t.length);d++){var a=t[d].querySelector("title"),o=t[d].querySelector("description"),r=t[d].querySelector("pubDate"),l=t[d].querySelector("link"),c=a?a.textContent:"",f=o?o.textContent:"",m=r?r.textContent:"",u=l?l.textContent:"",v=c+"|"+f+"|"+m+"|"+u;if(!newsfeed.dismissed[v]){var w=newsfeed.formatNotification({title:c,description:f,pubDate:m,link:u,identifier:v});w.querySelector(".delete").addEventListener("click",(function(){newsfeed.dismissNotification(event.target.parentNode)})),s.appendChild(w)}}document.body.appendChild(i)}})).catch(console.error)},onloaded:function(){"undefined"!=typeof page&&page.apiChecked&&!newsfeed.done&&newsfeed.do()}};"interactive"===document.readyState||"complete"===document.readyState?newsfeed.onloaded():window.addEventListener("DOMContentLoaded",(function(){return newsfeed.onloaded()}));
//# sourceMappingURL=newsfeed.js.map

1
dist/js/misc/newsfeed.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -233,3 +233,28 @@
input[type="file"].is-fullwidth {
width: 100%
}
#newsfeed {
position: absolute;
top: 0;
right: 0;
left: 0;
padding: 1.5rem 1.5rem 0 1.5rem
}
#newsfeed .notification {
padding: 0.9375rem 1.875rem 0.9375rem 1.125rem;
margin-bottom: 1.125rem
}
#newsfeed .notification .content {
font-size: 0.75rem
}
#newsfeed .news-title {
font-weight: bold
}
#newsfeed .news-date.is-recent-week {
font-weight: bold
}

View File

@ -1,4 +1,4 @@
/* global render, swal, axios, Dropzone, ClipboardJS, LazyLoad */
/* global swal, axios, Dropzone, ClipboardJS, LazyLoad */
const lsKeys = {
token: 'token',
@ -157,10 +157,17 @@ page.checkClientVersion = apiVersion => {
page.checkIfPublic = () => {
return axios.get('api/check', {
onDownloadProgress: () => {
// Only load render after this request has been initiated to avoid blocking
// Only do render and/or newsfeed after this request has been initiated to avoid blocking
/* global render */
if (typeof render !== 'undefined' && !render.done)
render.do()
else if (!page.apiChecked)
/* global newsfeed */
if (typeof newsfeed !== 'undefined' && !newsfeed.done)
newsfeed.do()
if (!page.apiChecked)
page.apiChecked = true
}
}).then(response => {

170
src/js/misc/newsfeed.js Normal file
View File

@ -0,0 +1,170 @@
/* global page, axios */
const newsfeed = {
lsKey: 'newsfeed',
feedUrl: 'https://blog.fiery.me/rss-newsfeed.xml',
maxItems: 3,
dismissed: {},
done: false
}
newsfeed.simpleParseDate = string => {
// For now limited to support the following examples (used in blog.fiery.me):
// Mon, 27 Jul 2020 18:30:00 GMT
// Sat, 16 May 2020 14:55:00 GMT
// Probably better to use a library if it needs to support other formats.
const months = { Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11 }
const match = string.match(/[a-zA-Z]*,\s(\d{2})\s([a-zA-Z]{3})\s(\d{4})\s(\d{2}):(\d{2}):(\d{2})\sGMT/)
if (match && (months[match[2]] !== undefined)) {
const date = new Date()
date.setUTCDate(match[1])
date.setUTCMonth(months[match[2]])
date.setUTCFullYear(match[3])
date.setUTCHours(match[4])
date.setUTCMinutes(match[5])
date.setUTCSeconds(match[6])
return date
}
}
newsfeed.formatRelativeDate = delta => {
// https://stackoverflow.com/a/7641812
const minute = 60
const hour = minute * 60
const day = hour * 24
const week = day * 7
let fuzzy
let unit
if (delta < minute) {
fuzzy = delta
unit = 'second'
} else if (delta < hour) {
fuzzy = Math.floor(delta / minute)
unit = 'minute'
} else if (delta < day) {
fuzzy = Math.floor(delta / hour)
unit = 'hour'
} else if (delta < week) {
fuzzy = Math.floor(delta / day)
unit = 'day'
} else {
fuzzy = Math.floor(delta / week)
unit = 'week'
}
return `${fuzzy} ${unit}${fuzzy !== 1 ? 's' : ''} ago`
}
newsfeed.formatNotification = item => {
const parsedDate = newsfeed.simpleParseDate(item.pubDate)
const dateDelta = Math.round((+new Date() - parsedDate) / 1000)
const isRecentWeek = dateDelta <= 604800
const element = document.createElement('div')
element.dataset.identifier = item.identifier
element.className = 'notification is-info'
element.innerHTML = `
<button class="delete" title="Dismiss"></button>
<div class="content">
<div class="news-title">
<a href="${item.link}" target="_blank">${item.title || 'Untitled'}</a>
</div>
<div class="news-excerpt">
${item.description
? `${item.description.slice(-1) === '…'
? `${item.description.slice(0, -1)} <a href="${item.link}" target="_blank">[…]</a>`
: item.description}`
: 'N/A'}
</div>
<div class="news-date${isRecentWeek ? ' is-recent-week' : ''}" title="${parsedDate.toLocaleString()}">
${newsfeed.formatRelativeDate(dateDelta)}
</div>
<div>
`
return element
}
newsfeed.dismissNotification = element => {
if (!element || !element.dataset.identifier) return
newsfeed.dismissed[element.dataset.identifier] = 1
element.parentNode.removeChild(element)
const keys = Object.keys(newsfeed.dismissed)
if (keys.length > newsfeed.maxItems)
for (let i = 0; i < keys.length - newsfeed.maxItems; i++)
delete newsfeed.dismissed[keys[i]]
localStorage[newsfeed.lsKey] = JSON.stringify(newsfeed.dismissed)
}
newsfeed.do = () => {
return axios.get(newsfeed.feedUrl, {
responseType: 'document'
}).then(response => {
if (response && response.data && response.data.documentElement instanceof Element) {
const items = response.data.documentElement.querySelectorAll('item')
if (items.length) {
const dismissed = localStorage[newsfeed.lsKey]
if (dismissed)
newsfeed.dismissed = JSON.parse(dismissed)
const element = document.createElement('section')
element.id = 'newsfeed'
element.className = 'section'
element.innerHTML = `
<div class="columns is-gapless">
<div class="column is-hidden-mobile"></div>
<div class="column is-hidden-mobile"></div>
<div class="column"></div>
</div>
`
const column = element.querySelector('.columns > .column:last-child')
for (let i = 0; i < Math.min(newsfeed.maxItems, items.length); i++) {
const titleElement = items[i].querySelector('title')
const descriptionElement = items[i].querySelector('description')
const pubDateElement = items[i].querySelector('pubDate')
const linkElement = items[i].querySelector('link')
const title = titleElement ? titleElement.textContent : ''
const description = descriptionElement ? descriptionElement.textContent : ''
const pubDate = pubDateElement ? pubDateElement.textContent : ''
const link = linkElement ? linkElement.textContent : ''
const identifier = title + '|' + description + '|' + pubDate + '|' + link
if (!newsfeed.dismissed[identifier]) {
const notificationElement = newsfeed.formatNotification({
title, description, pubDate, link, identifier
})
notificationElement.querySelector('.delete').addEventListener('click', function () {
newsfeed.dismissNotification(event.target.parentNode)
})
column.appendChild(notificationElement)
}
}
document.body.appendChild(element)
}
} else {
throw Error('response.data.documentElement is NOT an instance of Element')
}
}).catch(console.error)
}
newsfeed.onloaded = () => {
// If the main script had already done its API check, yet newsfeed haven't been triggered, do it
// This would only happen if this newsfeed script only gets loaded after the main script's API check
if (typeof page !== 'undefined' && page.apiChecked && !newsfeed.done)
newsfeed.do()
}
if (document.readyState === 'interactive' || document.readyState === 'complete')
newsfeed.onloaded()
else
window.addEventListener('DOMContentLoaded', () => newsfeed.onloaded())

View File

@ -1,5 +1,5 @@
{
"1": "1595157999",
"1": "1595882711",
"2": "1589010026",
"3": "1581416390",
"4": "1581416390",

View File

@ -31,6 +31,7 @@
{%- endif %}
{# We assign an ID for this so that the script can find out version string for render images #}
<script id="renderScript" data-version="{{ versions[4] }}" src="js/misc/render.js{{ versions[1] }}" async></script>
<script src="js/misc/newsfeed.js{{ versions[1] }}" async></script>
{# We assign an ID for this so that the script can find out its own version #}
<script id="mainScript" src="js/home.js{{ versions[1] }}"></script>
<script src="js/misc/utils.js{{ versions[1] }}"></script>