diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 6652c7d..0000000 --- a/public/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - Plain HTML site using GitLab Pages - - - - - -

Hello World!

- -

- This is a simple plain-HTML website on GitLab Pages, without any fancy static site generator. -

- - - diff --git a/public/style.css b/public/style.css deleted file mode 100644 index 3eae408..0000000 --- a/public/style.css +++ /dev/null @@ -1,24 +0,0 @@ -body { - font-family: sans-serif; - margin: auto; - max-width: 1280px; -} - -.navbar { - background-color: #313236; - border-radius: 2px; - max-width: 800px; -} - -.navbar a { - color: #aaa; - display: inline-block; - font-size: 15px; - padding: 10px; - text-decoration: none; -} - -.navbar a:hover { - color: #ffffff; -} - diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..f6f25e6 --- /dev/null +++ b/src/index.html @@ -0,0 +1,52 @@ + + + + + Mastodon timeline + + + + + + + + + + +
+
+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/src/mastodon-timeline.css b/src/mastodon-timeline.css new file mode 100644 index 0000000..f5da693 --- /dev/null +++ b/src/mastodon-timeline.css @@ -0,0 +1,165 @@ +/* Mastodon embed timeline */ +/* main container */ +.mt-timeline { + height: calc(100% - 4rem); + padding: 2rem 3rem; + position: relative; + background: lightgray; +} + +.mt-timeline a:link, +.mt-timeline a:active, +.mt-timeline a { + text-decoration: none; + color: darkblue; +} + +.mt-body { + height: 100%; + overflow-y: auto; + white-space: pre-wrap; + word-wrap: break-word; +} + +.mt-body .invisible { + display: none; +} + +/* toots messages */ +.mt-toot { + margin: 0 0.5rem 0 0; + padding: 1rem 0 2rem 65px; + position: relative; + min-height: 60px; + background-color: transparent; + border-bottom: 1px solid #dee2e6; +} + +.mt-toot:hover { + cursor: pointer; + background-color: rgba(124, 124, 124, 0.2); +} + +.mt-toot p:last-child { + margin-bottom: 0; +} + +.mt-user { + display: table; + font-weight: 600; +} + +.mt-avatar { + position: absolute; + top: 20px; + left: 5px; + width: 50px; + height: 50px; + background-color: transparent; + background-repeat: no-repeat; + background-position: 50% 50%; + background-size: contain; + background-color: #fff; + border-radius: 5px; +} + +.mt-avatar-boosted { + width: 40px; + height: 40px; +} + +.mt-avatar-booster { + width: 25px; + height: 25px; + top: 25px; + left: 25px; +} + +.toot-text { + margin-bottom: 0.25rem; +} + +.mt-error { + color: darkred; + background: lightpink; + margin: 5px; + padding: 10px; +} + +/* poll */ +.toot-poll { + margin-bottom: 0.25rem; +} +.toot-poll ul{ + list-style: none; + padding: 0; + margin: 0; +} +.toot-poll ul li:not(:last-child){ + margin-bottom: 0.25rem; +} +.toot-poll ul li:before { + content: "◯"; + padding-right: 0.5rem; +} + +/* medias */ +.toot-media { + overflow: hidden; + margin-bottom: 0.25rem; +} +.toot-media-preview { + position: relative; + margin-top: 0.25rem; + height: auto; + text-align: center; + width: 100%; +} +.toot-media-spoiler > img { + filter: blur(2rem); +} +.toot-media-preview a { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} +.img-ratio14_7 { +position: relative; +padding-top: 48.95%; /* 14:7 */ +width: 100%; +} +.img-ratio14_7 > img { +width: 100%; +height: auto; +position: absolute; +top: 50%; +left: 50%; +transform: translate(-50%, -50%); +text-align: center; +} + +/* date */ +.toot-date { + font-size: 0.75rem; + opacity: 0.5; +} + +/* loading-spinner */ +.loading-spinner { + height: 100%; + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns:svg='http://www.w3.org/2000/svg' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.0' viewBox='0 0 128 128' %3E%3Cg%3E%3Cpath d='M64 128A64 64 0 0 1 18.34 19.16L21.16 22a60 60 0 1 0 52.8-17.17l.62-3.95A64 64 0 0 1 64 128z' fill='%23404040'/%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 64 64' to='360 64 64' dur='1000ms' repeatCount='indefinite'%3E%3C/animateTransform%3E%3C/g%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: center center; + background-color: transparent; + background-size: min(2.5rem, calc(100% - 0.5rem)); +} + +/* see more btn */ +.mt-seeMore{ + margin: 2rem auto; + padding: 0 2rem; + text-align: center; +} diff --git a/src/mastodon-timeline.js b/src/mastodon-timeline.js new file mode 100644 index 0000000..31275d5 --- /dev/null +++ b/src/mastodon-timeline.js @@ -0,0 +1,231 @@ +// Timeline JS (Mastodon) +// Forked from: https://github.com/AzetJP/mastodon-timeline-widget + +// TODO: optimize selectors and add first the #id (target_selector) + +// Account settings +document.addEventListener("DOMContentLoaded", () => { + let mapi = new MastodonApi({ + container_id: 'mt-timeline', + container_body_css: '.mt-body', + instance_uri: 'https://mastodon.online', + account_id: '180745', + profile_name: '@idotj', + toots_limit: 13, + btn_see_more: 'See more posts at Mastodon' + }); +}); + +let MastodonApi = function (params_) { + + // Endpoint access settings + this.INSTANCE_URI = params_.instance_uri; + this.ACCESS_TOKEN = params_.access_token; + this.ACCOUNT_ID = params_.account_id; + this.PROFILE_NAME = params_.profile_name; + this.TOOTS_LIMIT = params_.toots_limit || 20; + this.BTN_SEE_MORE = params_.btn_see_more || 'See more' + + // Target selectors + this.mtIdContainer = document.getElementById(params_.container_id); + this.mtBodyContainer = document.querySelector(params_.container_body_css); + + // Get the toots + this.getToots(); + + // Toot interactions + this.mtIdContainer.addEventListener('click', function (event) { + let urlToot = event.target.closest('.mt-toot').dataset.location; + // Open Toot in new page avoiding any other natural link + if(event.target.localName != 'a' && event.target.localName != 'span' && urlToot){ + window.open(urlToot, '_blank'); + } + }); + +} + +// Listing toots function +MastodonApi.prototype.getToots = function () { + let mapi = this; + + // Get request + fetch(this.INSTANCE_URI + '/api/v1/accounts/' + this.ACCOUNT_ID + '/statuses?limit=' + this.TOOTS_LIMIT, { + method: 'get', + }) + .then(response => response.json()) + .then(jsonData => { + // console.log('jsonData: ', jsonData); + + // Clear the loading message + this.mtBodyContainer.innerHTML = ''; + + // Add toots + for (let i in jsonData) { + if (jsonData[i].visibility == 'public') { + // List only public toots + appendToot.call(mapi, jsonData[i]); + } + } + + // Add target="_blank" to all hashtags + let allHashtags = document.querySelectorAll("#mt-timeline .hashtag"); + for(let j=0; j' + mapi.BTN_SEE_MORE + ''); + }) + .catch(err => { + this.mtBodyContainer.innerHTML = '
✖️
Request Failed:
' + err + '
'; + }); + + // Inner function to add each toot content in container + let appendToot = function (status_) { + let avatar, user, content, url, date; + + if (status_.reblog) { + // BOOSTED toot + // Toot url + url = status_.reblog.url; + + // Boosted avatar + avatar = + '' + + '
' + + '
' + + '
'; + + // User name and url + user = + '
' + + '' + + status_.reblog.account.username + + '' + + '
'; + + // Toot date + date = prepareDateDisplay(status_.reblog.created_at); + } else { + // STANDARD toot + // Toot url + url = status_.url; + + // Avatar + avatar = + '' + + ''; + + // User name and url + user = + '
' + + '' + + status_.account.username + + '' + + '
'; + + // Toot date + date = prepareDateDisplay(status_.created_at); + } + + // Main content + if(status_.spoiler_text != '') { + content = + '
' + + status_.spoiler_text + + ' [Show more...]' + + '
'; + } else { + content = + '
' + + status_.content + + '
'; + } + + // Media attachments + let media = ''; + if (status_.media_attachments.length > 0) { + for (let picid in status_.media_attachments) { + media = this.replaceMedias(status_.media_attachments[picid], status_.sensitive); + } + } + + // Poll + let poll = ''; + let pollOption = ''; + if (status_.poll) { + for (let i in status_.poll.options) { + pollOption += + '
  • ' + + status_.poll.options[i].title + + '
  • '; + } + poll = + '
    ' + + '' + +'
    '; + } + + // Format date + let timestamp = + '
    ' + + '' + + date + + '' + + '
    '; + + // Add all to main toot container + let toot = + '
    ' + + avatar + + user + + content + + media + + poll + + timestamp + + '
    '; + + this.mtBodyContainer.insertAdjacentHTML('beforeend', toot); + }; + + // Display toot date + let prepareDateDisplay = function (date_) { + let displayDate = ""; + const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + ]; + + let date = new Date(date_); + + displayDate = monthNames[date.getMonth()] + + " " + date.getDate() + + ", " + date.getFullYear(); + + return displayDate; + }; + +}; + +// Loading spinner +function removeSpinner(element) { + const spinnerCSS = 'loading-spinner'; + // Find closest parent container (1st, 2nd or 3rd level) + let spinnerContainer = element.closest('.' + spinnerCSS); + if(spinnerContainer){ + spinnerContainer.classList.remove(spinnerCSS); + } +} + +// Place media +MastodonApi.prototype.replaceMedias = function (media_, spoiler_) { + let spoiler = spoiler_ || false; + let pic = + '
    ' + + '' + + '
    '; + + return pic; +}; +