Feature/posts limit
This commit is contained in:
parent
a78de16fd9
commit
410e83bce1
10
CHANGELOG
10
CHANGELOG
@ -1,3 +1,11 @@
|
|||||||
|
v4.3.10 - 21/03/2024
|
||||||
|
- Allow to load more than 20 or 40 posts
|
||||||
|
- Add link preview description
|
||||||
|
- Allow to choose a maximum number of lines of text in preview description
|
||||||
|
- Fix carousel conflict with avatar and emojos images
|
||||||
|
- Fix possible error in aria-setsize with values greater than the total number of posts
|
||||||
|
- JS refactoring
|
||||||
|
|
||||||
v4.3.7 - 12/03/2024
|
v4.3.7 - 12/03/2024
|
||||||
- Display medias inside post using CSS grid
|
- Display medias inside post using CSS grid
|
||||||
- Add a placeholder bg-color for images
|
- Add a placeholder bg-color for images
|
||||||
@ -168,7 +176,7 @@ v3.1.1 - 28/01/2023
|
|||||||
|
|
||||||
v3.1.0 - 21/01/2023
|
v3.1.0 - 21/01/2023
|
||||||
- Fix spoiler content show/hide
|
- Fix spoiler content show/hide
|
||||||
- Add feature, choose a maximum number of lines of text
|
- Allow to choose a maximum number of lines of text in posts
|
||||||
- Hide button to user page if 'btn_see_more' is empty
|
- Hide button to user page if 'btn_see_more' is empty
|
||||||
|
|
||||||
v2.12.0 - 02/12/2022
|
v2.12.0 - 02/12/2022
|
||||||
|
88
README.md
88
README.md
@ -65,11 +65,11 @@ This option allows you to start without the need to upload any files on your ser
|
|||||||
Copy the following CSS and JS links to include them in your project:
|
Copy the following CSS and JS links to include them in your project:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.3.7/dist/mastodon-timeline.min.css" integrity="sha256-TxNxDe916jqa7iqnY5d3/1SuHlB+/4r9XEH0kOwh2Nc=" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.3.10/dist/mastodon-timeline.min.css" crossorigin="anonymous">
|
||||||
```
|
```
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.3.7/dist/mastodon-timeline.umd.js" integrity="sha256-VK7I7SRA8gZaOzjlIQ6aeG0vOlkzuRnstJi2fgR3L80=" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@idotj/mastodon-embed-timeline@4.3.10/dist/mastodon-timeline.umd.js" crossorigin="anonymous"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Package manager
|
### Package manager
|
||||||
@ -182,48 +182,54 @@ Here you have all the options available to quickly setup and customize your time
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Id of the <div> containing the timeline
|
// Id of the <div> containing the timeline
|
||||||
|
// Default: "mt-container"
|
||||||
mtContainerId: "mt-container",
|
mtContainerId: "mt-container",
|
||||||
|
|
||||||
// Mastodon instance Url including https://
|
// Mastodon instance Url including https://
|
||||||
|
// Default: "https://mastodon.social"
|
||||||
instanceUrl: "https://mastodon.social",
|
instanceUrl: "https://mastodon.social",
|
||||||
|
|
||||||
// Choose type of posts to show in the timeline: 'local', 'profile', 'hashtag'
|
// Choose type of posts to show in the timeline: 'local', 'profile', 'hashtag'
|
||||||
// Default: local
|
// Default: "local"
|
||||||
timelineType: "local",
|
timelineType: "local",
|
||||||
|
|
||||||
// Your user ID number on Mastodon instance
|
// Your user ID number on Mastodon instance
|
||||||
// Leave it empty if you didn't choose 'profile' as type of timeline
|
// Leave it empty if you didn't choose 'profile' as type of timeline
|
||||||
|
// Default: ""
|
||||||
userId: "",
|
userId: "",
|
||||||
|
|
||||||
// Your user name on Mastodon instance (including the @ symbol at the beginning)
|
// Your user name on Mastodon instance (including the @ symbol at the beginning)
|
||||||
// Leave it empty if you didn't choose 'profile' as type of timeline
|
// Leave it empty if you didn't choose 'profile' as type of timeline
|
||||||
|
// Default: ""
|
||||||
profileName: "",
|
profileName: "",
|
||||||
|
|
||||||
// The name of the hashtag (not including the # symbol)
|
// The name of the hashtag (not including the # symbol)
|
||||||
// Leave it empty if you didn't choose 'hashtag' as type of timeline
|
// Leave it empty if you didn't choose 'hashtag' as type of timeline
|
||||||
|
// Default: ""
|
||||||
hashtagName: "",
|
hashtagName: "",
|
||||||
|
|
||||||
// Class name for the loading spinner (also used in CSS file)
|
// Class name for the loading spinner (also used in CSS file)
|
||||||
|
// Default: "mt-loading-spinner"
|
||||||
spinnerClass: "mt-loading-spinner",
|
spinnerClass: "mt-loading-spinner",
|
||||||
|
|
||||||
// Preferred color theme: 'light', 'dark' or 'auto'
|
// Preferred color theme: "light", "dark" or "auto"
|
||||||
// Default: auto
|
// Default: "auto"
|
||||||
defaultTheme: "auto",
|
defaultTheme: "auto",
|
||||||
|
|
||||||
// Maximum number of posts to request to the server
|
// Maximum number of posts to request to the server
|
||||||
// Default: 20
|
// Default: "20"
|
||||||
maxNbPostFetch: "20",
|
maxNbPostFetch: "20",
|
||||||
|
|
||||||
// Maximum number of posts to show in the timeline
|
// Maximum number of posts to show in the timeline
|
||||||
// Default: 20
|
// Default: "20"
|
||||||
maxNbPostShow: "20",
|
maxNbPostShow: "20",
|
||||||
|
|
||||||
// Specifies the format of the date according to the chosen language/country
|
// Specifies the format of the date according to the chosen language/country
|
||||||
// Default: British English (day-month-year order)
|
// Default: "en-GB" (British English: day-month-year order)
|
||||||
dateLocale: "en-GB",
|
dateLocale: "en-GB",
|
||||||
|
|
||||||
// Customize the date format using the options
|
// Customize the date format using the options for day, month and year
|
||||||
// Default: DD MMM YYYY
|
// Default: day: "2-digit", month: "short", year: "numeric" (DD MMM YYYY)
|
||||||
dateOptions: {
|
dateOptions: {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
month: "short",
|
month: "short",
|
||||||
@ -231,47 +237,64 @@ Here you have all the options available to quickly setup and customize your time
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Hide unlisted posts
|
// Hide unlisted posts
|
||||||
// Default: don't hide
|
// Default: false (don't hide)
|
||||||
hideUnlisted: false,
|
hideUnlisted: false,
|
||||||
|
|
||||||
// Hide boosted posts
|
// Hide boosted posts
|
||||||
// Default: don't hide
|
// Default: false (don't hide)
|
||||||
hideReblog: false,
|
hideReblog: false,
|
||||||
|
|
||||||
// Hide replies posts
|
// Hide replies posts
|
||||||
// Default: don't hide
|
// Default: false (don't hide)
|
||||||
hideReplies: false,
|
hideReplies: false,
|
||||||
|
|
||||||
// Hide pinned posts from the profile timeline
|
// Hide pinned posts from the profile timeline
|
||||||
// Default: don't hide
|
// Default: false (don't hide)
|
||||||
hidePinnedPosts: false,
|
hidePinnedPosts: false,
|
||||||
|
|
||||||
// Hide the user account under the user name
|
// Hide the user account under the user name
|
||||||
// Default: don't hide
|
// Default: false (don't hide)
|
||||||
hideUserAccount: false,
|
hideUserAccount: false,
|
||||||
|
|
||||||
|
// Limit the text content to a maximum number of lines
|
||||||
|
// Use "0" to show no text
|
||||||
|
// Default: "" (unlimited)
|
||||||
|
txtMaxLines: "",
|
||||||
|
|
||||||
|
// Customize the text of the button used for showing/hiding sensitive/spoiler text
|
||||||
|
btnShowMore: "SHOW MORE",
|
||||||
|
btnShowLess: "SHOW LESS",
|
||||||
|
|
||||||
|
// Converts Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag
|
||||||
|
// Default: false (don't apply)
|
||||||
|
markdownBlockquote: false,
|
||||||
|
|
||||||
// Hide custom emojis available on the server
|
// Hide custom emojis available on the server
|
||||||
// Default: don't hide
|
// Default: false (don't hide)
|
||||||
hideEmojos: false,
|
hideEmojos: false,
|
||||||
|
|
||||||
|
// Customize the text of the button used for showing sensitive/spoiler media content
|
||||||
|
btnShowContent: "SHOW CONTENT",
|
||||||
|
|
||||||
// Hide video image preview and load video player instead
|
// Hide video image preview and load video player instead
|
||||||
// Default: don't hide
|
// Default: false (don't hide)
|
||||||
hideVideoPreview: false,
|
hideVideoPreview: false,
|
||||||
|
|
||||||
// Hide preview card if post contains a link, photo or video from a Url
|
// Hide preview card if post contains a link, photo or video from a Url
|
||||||
// Default: don't hide
|
// Default: false (don't hide)
|
||||||
hidePreviewLink: false,
|
hidePreviewLink: false,
|
||||||
|
|
||||||
|
// Limit the preview text description to a maximum number of lines
|
||||||
|
// Use "0" to show no text
|
||||||
|
// Default: "" (unlimited)
|
||||||
|
previewMaxLines: "",
|
||||||
|
|
||||||
// Hide replies, boosts and favourites posts counter
|
// Hide replies, boosts and favourites posts counter
|
||||||
// Default: don't hide
|
// Default: false (don't hide)
|
||||||
hideCounterBar: false,
|
hideCounterBar: false,
|
||||||
|
|
||||||
// Converts Markdown symbol ">" at the beginning of a paragraph into a blockquote HTML tag
|
// Disable a carousel/lightbox when the user clicks on a picture in a post
|
||||||
// Default: don't apply
|
// Default: false (not disabled)
|
||||||
markdownBlockquote: false,
|
|
||||||
|
|
||||||
// Show a carousel/lightbox when the user clicks on a picture in a post
|
|
||||||
// Default: not disabled
|
|
||||||
disableCarousel: false,
|
disableCarousel: false,
|
||||||
|
|
||||||
// Customize the text of the buttons used for the carousel/lightbox
|
// Customize the text of the buttons used for the carousel/lightbox
|
||||||
@ -279,17 +302,6 @@ Here you have all the options available to quickly setup and customize your time
|
|||||||
carouselPrevTxt: "Previous media item",
|
carouselPrevTxt: "Previous media item",
|
||||||
carouselNextTxt: "Next media item",
|
carouselNextTxt: "Next media item",
|
||||||
|
|
||||||
// Limit the text content to a maximum number of lines
|
|
||||||
// Default: 0 (unlimited)
|
|
||||||
txtMaxLines: "0",
|
|
||||||
|
|
||||||
// Customize the text of the button used for showing/hiding sensitive/spoiler text
|
|
||||||
btnShowMore: "SHOW MORE",
|
|
||||||
btnShowLess: "SHOW LESS",
|
|
||||||
|
|
||||||
// Customize the text of the button used for showing sensitive/spoiler media content
|
|
||||||
btnShowContent: "SHOW CONTENT",
|
|
||||||
|
|
||||||
// Customize the text of the button pointing to the Mastodon page placed at the end of the timeline
|
// Customize the text of the button pointing to the Mastodon page placed at the end of the timeline
|
||||||
// Leave the value empty to hide it
|
// Leave the value empty to hide it
|
||||||
btnSeeMore: "See more posts at Mastodon",
|
btnSeeMore: "See more posts at Mastodon",
|
||||||
@ -299,11 +311,11 @@ Here you have all the options available to quickly setup and customize your time
|
|||||||
btnReload: "Refresh",
|
btnReload: "Refresh",
|
||||||
|
|
||||||
// Keep searching for the main <div> container before building the timeline. Useful in some cases where extra time is needed to render the page
|
// Keep searching for the main <div> container before building the timeline. Useful in some cases where extra time is needed to render the page
|
||||||
// Default: don't apply
|
// Default: false (don't apply)
|
||||||
insistSearchContainer: false,
|
insistSearchContainer: false,
|
||||||
|
|
||||||
// Defines the maximum time to continue searching for the main <div> container
|
// Defines the maximum time to continue searching for the main <div> container
|
||||||
// Default: 3 seconds
|
// Default: "3000" (3 seconds)
|
||||||
insistSearchContainerTime: "3000",
|
insistSearchContainerTime: "3000",
|
||||||
|
|
||||||
```
|
```
|
||||||
|
4
dist/mastodon-timeline.esm.js
vendored
4
dist/mastodon-timeline.esm.js
vendored
File diff suppressed because one or more lines are too long
2
dist/mastodon-timeline.min.css
vendored
2
dist/mastodon-timeline.min.css
vendored
File diff suppressed because one or more lines are too long
4
dist/mastodon-timeline.umd.js
vendored
4
dist/mastodon-timeline.umd.js
vendored
File diff suppressed because one or more lines are too long
@ -113,7 +113,7 @@
|
|||||||
<h1>🐘 Mastodon embed timeline</h1>
|
<h1>🐘 Mastodon embed timeline</h1>
|
||||||
<h2>Local timeline (customized)</h2>
|
<h2>Local timeline (customized)</h2>
|
||||||
<p>
|
<p>
|
||||||
This example shows 10 posts from the following instance:
|
This example shows 42 posts from the following instance:
|
||||||
<br />
|
<br />
|
||||||
<a
|
<a
|
||||||
href="https://mastodon.social/public/local"
|
href="https://mastodon.social/public/local"
|
||||||
@ -183,6 +183,8 @@
|
|||||||
const myTimeline = new MastodonTimeline.Init({
|
const myTimeline = new MastodonTimeline.Init({
|
||||||
instanceUrl: "https://mastodon.online",
|
instanceUrl: "https://mastodon.online",
|
||||||
defaultTheme: "light",
|
defaultTheme: "light",
|
||||||
|
maxNbPostFetch: "42",
|
||||||
|
maxNbPostShow: "42",
|
||||||
dateLocale: "en-CA",
|
dateLocale: "en-CA",
|
||||||
dateOptions: {
|
dateOptions: {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
@ -219,7 +221,8 @@
|
|||||||
const myTimeline = new MastodonTimeline.Init({
|
const myTimeline = new MastodonTimeline.Init({
|
||||||
instanceUrl: "https://mastodon.online",
|
instanceUrl: "https://mastodon.online",
|
||||||
defaultTheme: "light",
|
defaultTheme: "light",
|
||||||
maxNbPostShow: "10",
|
maxNbPostFetch: "42",
|
||||||
|
maxNbPostShow: "42",
|
||||||
dateLocale: "en-CA",
|
dateLocale: "en-CA",
|
||||||
dateOptions: {
|
dateOptions: {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
|
@ -123,7 +123,8 @@
|
|||||||
|
|
||||||
<h2>Theme API</h2>
|
<h2>Theme API</h2>
|
||||||
<p>
|
<p>
|
||||||
You can change your timeline color calling the function <strong>mtColorTheme()</strong>
|
You can change your timeline color calling the function
|
||||||
|
<strong>mtColorTheme()</strong>
|
||||||
</p>
|
</p>
|
||||||
<div class="dummy-buttons-container">
|
<div class="dummy-buttons-container">
|
||||||
<button onclick="myTimeline01.mtColorTheme('dark')">
|
<button onclick="myTimeline01.mtColorTheme('dark')">
|
||||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@idotj/mastodon-embed-timeline",
|
"name": "@idotj/mastodon-embed-timeline",
|
||||||
"version": "4.3.7",
|
"version": "4.3.10",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@idotj/mastodon-embed-timeline",
|
"name": "@idotj/mastodon-embed-timeline",
|
||||||
"version": "4.3.7",
|
"version": "4.3.10",
|
||||||
"license": "GNU",
|
"license": "GNU",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-terser": "^0.4.4",
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@idotj/mastodon-embed-timeline",
|
"name": "@idotj/mastodon-embed-timeline",
|
||||||
"version": "4.3.7",
|
"version": "4.3.10",
|
||||||
"description": "Displays Mastodon timeline with posts embed in your website. Very easy to setup, no dependencies, no trackers, cross-browser, WCAG compliant and fully responsive.",
|
"description": "Displays Mastodon timeline with posts embed in your website. Very easy to setup, no dependencies, no trackers, cross-browser, WCAG compliant and fully responsive.",
|
||||||
"license": "GNU",
|
"license": "GNU",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Mastodon embed timeline v4.3.7 */
|
/* Mastodon embed timeline v4.3.10 */
|
||||||
/* More info at: */
|
/* More info at: */
|
||||||
/* https://gitlab.com/idotj/mastodon-embed-timeline */
|
/* https://gitlab.com/idotj/mastodon-embed-timeline */
|
||||||
|
|
||||||
@ -8,6 +8,7 @@
|
|||||||
.mt-container[data-theme="light"],
|
.mt-container[data-theme="light"],
|
||||||
.mt-dialog[data-theme="light"] {
|
.mt-dialog[data-theme="light"] {
|
||||||
--mt-txt-max-lines: none;
|
--mt-txt-max-lines: none;
|
||||||
|
--mt-preview-max-lines: none;
|
||||||
--mt-color-bg: #fff;
|
--mt-color-bg: #fff;
|
||||||
--mt-color-bg-hover: #d9e1e8;
|
--mt-color-bg-hover: #d9e1e8;
|
||||||
--mt-color-line-gray: #c0cdd9;
|
--mt-color-line-gray: #c0cdd9;
|
||||||
@ -438,6 +439,23 @@ body:has(dialog.mt-dialog[open]) {
|
|||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
.mt-post-preview-content:has(.mt-post-preview-description.truncate) {
|
||||||
|
align-self: unset;
|
||||||
|
}
|
||||||
|
.mt-post-preview-description {
|
||||||
|
display: block;
|
||||||
|
color: var(--mt-color-contrast-gray);
|
||||||
|
}
|
||||||
|
.mt-post-preview-description.truncate {
|
||||||
|
display: -webkit-box;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-line-clamp: var(--mt-preview-max-lines);
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
.mt-post-preview-description:not(.truncate) .ellipsis::after {
|
||||||
|
content: "...";
|
||||||
|
}
|
||||||
|
|
||||||
.mt-post-preview-title {
|
.mt-post-preview-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Mastodon embed timeline
|
* Mastodon embed timeline
|
||||||
* @author idotj
|
* @author idotj
|
||||||
* @version 4.3.7
|
* @version 4.3.10
|
||||||
* @url https://gitlab.com/idotj/mastodon-embed-timeline
|
* @url https://gitlab.com/idotj/mastodon-embed-timeline
|
||||||
* @license GNU AGPLv3
|
* @license GNU AGPLv3
|
||||||
*/
|
*/
|
||||||
@ -31,19 +31,20 @@ export class Init {
|
|||||||
hideReplies: false,
|
hideReplies: false,
|
||||||
hidePinnedPosts: false,
|
hidePinnedPosts: false,
|
||||||
hideUserAccount: false,
|
hideUserAccount: false,
|
||||||
|
txtMaxLines: "",
|
||||||
|
btnShowMore: "SHOW MORE",
|
||||||
|
btnShowLess: "SHOW LESS",
|
||||||
|
markdownBlockquote: false,
|
||||||
hideEmojos: false,
|
hideEmojos: false,
|
||||||
|
btnShowContent: "SHOW CONTENT",
|
||||||
hideVideoPreview: false,
|
hideVideoPreview: false,
|
||||||
hidePreviewLink: false,
|
hidePreviewLink: false,
|
||||||
|
previewMaxLines: "",
|
||||||
hideCounterBar: false,
|
hideCounterBar: false,
|
||||||
markdownBlockquote: false,
|
|
||||||
disableCarousel: false,
|
disableCarousel: false,
|
||||||
carouselCloseTxt: "Close carousel",
|
carouselCloseTxt: "Close carousel",
|
||||||
carouselPrevTxt: "Previous media item",
|
carouselPrevTxt: "Previous media item",
|
||||||
carouselNextTxt: "Next media item",
|
carouselNextTxt: "Next media item",
|
||||||
txtMaxLines: "0",
|
|
||||||
btnShowMore: "SHOW MORE",
|
|
||||||
btnShowLess: "SHOW LESS",
|
|
||||||
btnShowContent: "SHOW CONTENT",
|
|
||||||
btnSeeMore: "See more posts at Mastodon",
|
btnSeeMore: "See more posts at Mastodon",
|
||||||
btnReload: "Refresh",
|
btnReload: "Refresh",
|
||||||
insistSearchContainer: false,
|
insistSearchContainer: false,
|
||||||
@ -52,6 +53,9 @@ export class Init {
|
|||||||
|
|
||||||
this.mtSettings = { ...this.defaultSettings, ...customSettings };
|
this.mtSettings = { ...this.defaultSettings, ...customSettings };
|
||||||
|
|
||||||
|
this.#checkMaxNbPost();
|
||||||
|
|
||||||
|
this.linkHeader = {};
|
||||||
this.mtContainerNode = "";
|
this.mtContainerNode = "";
|
||||||
this.mtBodyNode = "";
|
this.mtBodyNode = "";
|
||||||
this.fetchedData = {};
|
this.fetchedData = {};
|
||||||
@ -61,6 +65,21 @@ export class Init {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that the values of posts fetched and showed are consistent
|
||||||
|
*/
|
||||||
|
#checkMaxNbPost() {
|
||||||
|
if (
|
||||||
|
Number(this.mtSettings.maxNbPostShow) >
|
||||||
|
Number(this.mtSettings.maxNbPostFetch)
|
||||||
|
) {
|
||||||
|
console.error(
|
||||||
|
`Please check your settings! The maximum number of posts to show is bigger than the maximum number of posts to fetch. Changing the value of "maxNbPostFetch" to: ${this.mtSettings.maxNbPostShow}`
|
||||||
|
);
|
||||||
|
this.mtSettings.maxNbPostFetch = this.mtSettings.maxNbPostShow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger callback when DOM loaded or completed
|
* Trigger callback when DOM loaded or completed
|
||||||
* @param {function} c Callback executed
|
* @param {function} c Callback executed
|
||||||
@ -146,7 +165,7 @@ export class Init {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply the color theme in the timeline
|
* Apply the color theme in the timeline
|
||||||
* @param {string} themeType Type of color theme
|
* @param {string} themeType Type of color theme ('light' or 'dark')
|
||||||
*/
|
*/
|
||||||
mtColorTheme(themeType) {
|
mtColorTheme(themeType) {
|
||||||
this.#onDOMContentLoaded(() => {
|
this.#onDOMContentLoaded(() => {
|
||||||
@ -178,51 +197,18 @@ export class Init {
|
|||||||
*/
|
*/
|
||||||
#getTimelineData() {
|
#getTimelineData() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const instanceApiUrl = `${this.mtSettings.instanceUrl}/api/v1/`;
|
const instanceApiUrl = this.mtSettings.instanceUrl
|
||||||
let urls = {};
|
? `${this.mtSettings.instanceUrl}/api/v1/`
|
||||||
|
: this.#showError(
|
||||||
if (this.mtSettings.instanceUrl) {
|
|
||||||
if (this.mtSettings.timelineType === "profile") {
|
|
||||||
if (this.mtSettings.userId) {
|
|
||||||
urls.timeline = `${instanceApiUrl}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`;
|
|
||||||
if (!this.mtSettings.hidePinnedPosts) {
|
|
||||||
urls.pinned = `${instanceApiUrl}accounts/${this.mtSettings.userId}/statuses?pinned=true`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.#showError(
|
|
||||||
"Please check your <strong>userId</strong> value",
|
|
||||||
"⚠️"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (this.mtSettings.timelineType === "hashtag") {
|
|
||||||
if (this.mtSettings.hashtagName) {
|
|
||||||
urls.timeline = `${instanceApiUrl}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`;
|
|
||||||
} else {
|
|
||||||
this.#showError(
|
|
||||||
"Please check your <strong>hashtagName</strong> value",
|
|
||||||
"⚠️"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (this.mtSettings.timelineType === "local") {
|
|
||||||
urls.timeline = `${instanceApiUrl}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`;
|
|
||||||
} else {
|
|
||||||
this.#showError(
|
|
||||||
"Please check your <strong>timelineType</strong> value",
|
|
||||||
"⚠️"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.#showError(
|
|
||||||
"Please check your <strong>instanceUrl</strong> value",
|
"Please check your <strong>instanceUrl</strong> value",
|
||||||
"⚠️"
|
"⚠️"
|
||||||
);
|
);
|
||||||
}
|
|
||||||
if (!this.mtSettings.hideEmojos) {
|
const urls = this.#setUrls(instanceApiUrl);
|
||||||
urls.emojos = `${instanceApiUrl}custom_emojis`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const urlsPromises = Object.entries(urls).map(([key, url]) => {
|
const urlsPromises = Object.entries(urls).map(([key, url]) => {
|
||||||
return this.#fetchData(url)
|
const headers = key === "timeline";
|
||||||
|
return this.#fetchData(url, headers)
|
||||||
.then((data) => ({ [key]: data }))
|
.then((data) => ({ [key]: data }))
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
reject(
|
reject(
|
||||||
@ -234,44 +220,12 @@ export class Init {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Fetch all urls simultaneously
|
// Fetch all urls simultaneously
|
||||||
Promise.all(urlsPromises).then((dataObjects) => {
|
Promise.all(urlsPromises).then(async (dataObjects) => {
|
||||||
this.fetchedData = dataObjects.reduce((result, dataItem) => {
|
this.fetchedData = dataObjects.reduce((result, dataItem) => {
|
||||||
return { ...result, ...dataItem };
|
return { ...result, ...dataItem };
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
// console.log("Mastodon timeline data fetched: ", this.fetchedData);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch data from server
|
|
||||||
* @param {string} url address to fetch
|
|
||||||
* @returns {array} List of objects
|
|
||||||
*/
|
|
||||||
async #fetchData(url) {
|
|
||||||
const response = await fetch(url);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`
|
|
||||||
Failed to fetch the following Url:<br/>${url}<hr>Error status: ${response.status}<hr>Error message: ${response.statusText}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter all fetched posts and append them on the timeline
|
|
||||||
* @param {string} t Type of build (new or reload)
|
|
||||||
*/
|
|
||||||
async #buildTimeline(t) {
|
|
||||||
await this.#getTimelineData();
|
|
||||||
|
|
||||||
// Merge pinned posts with timeline posts
|
// Merge pinned posts with timeline posts
|
||||||
let posts;
|
|
||||||
if (
|
if (
|
||||||
!this.mtSettings.hidePinnedPosts &&
|
!this.mtSettings.hidePinnedPosts &&
|
||||||
this.fetchedData.pinned?.length !== undefined &&
|
this.fetchedData.pinned?.length !== undefined &&
|
||||||
@ -281,56 +235,224 @@ export class Init {
|
|||||||
...obj,
|
...obj,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
}));
|
}));
|
||||||
posts = [...pinnedPosts, ...this.fetchedData.timeline];
|
this.fetchedData.timeline = [
|
||||||
} else {
|
...pinnedPosts,
|
||||||
posts = this.fetchedData.timeline;
|
...this.fetchedData.timeline,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty container body
|
// Fetch more posts if maxNbPostFetch is not reached
|
||||||
|
if (this.#isNbPostsFulfilled()) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
await this.#fetchMorePosts();
|
||||||
|
} while (!this.#isNbPostsFulfilled() && this.linkHeader.next);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all urls before fetching the data
|
||||||
|
* @param {string} Instance url api
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
#setUrls(i) {
|
||||||
|
let urls = {};
|
||||||
|
|
||||||
|
if (this.mtSettings.timelineType === "profile") {
|
||||||
|
if (this.mtSettings.userId) {
|
||||||
|
urls.timeline = `${i}accounts/${this.mtSettings.userId}/statuses?limit=${this.mtSettings.maxNbPostFetch}`;
|
||||||
|
if (!this.mtSettings.hidePinnedPosts) {
|
||||||
|
urls.pinned = `${i}accounts/${this.mtSettings.userId}/statuses?pinned=true`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.#showError(
|
||||||
|
"Please check your <strong>userId</strong> value",
|
||||||
|
"⚠️"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (this.mtSettings.timelineType === "hashtag") {
|
||||||
|
if (this.mtSettings.hashtagName) {
|
||||||
|
urls.timeline = `${i}timelines/tag/${this.mtSettings.hashtagName}?limit=${this.mtSettings.maxNbPostFetch}`;
|
||||||
|
} else {
|
||||||
|
this.#showError(
|
||||||
|
"Please check your <strong>hashtagName</strong> value",
|
||||||
|
"⚠️"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (this.mtSettings.timelineType === "local") {
|
||||||
|
urls.timeline = `${i}timelines/public?local=true&limit=${this.mtSettings.maxNbPostFetch}`;
|
||||||
|
} else {
|
||||||
|
this.#showError(
|
||||||
|
"Please check your <strong>timelineType</strong> value",
|
||||||
|
"⚠️"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.mtSettings.hideEmojos) {
|
||||||
|
urls.emojos = `${i}custom_emojis`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch data from server
|
||||||
|
* @param {string} u Url address to fetch
|
||||||
|
* @param {boolean} h gets the link header
|
||||||
|
* @returns {array} List of objects
|
||||||
|
*/
|
||||||
|
async #fetchData(u, h = false) {
|
||||||
|
const response = await fetch(u);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`
|
||||||
|
Failed to fetch the following Url:<br />${u}<hr />Error status: ${response.status}<hr />Error message: ${response.statusText}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Get Link headers for pagination
|
||||||
|
if (h && response.headers.get("Link")) {
|
||||||
|
this.linkHeader = this.#parseLinkHeader(response.headers.get("Link"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there are enough posts to reach the value of maxNbPostFetch
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
#isNbPostsFulfilled() {
|
||||||
|
return (
|
||||||
|
this.fetchedData.timeline.length >= Number(this.mtSettings.maxNbPostFetch)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch extra posts
|
||||||
|
*/
|
||||||
|
#fetchMorePosts() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (this.linkHeader.next) {
|
||||||
|
this.#fetchData(this.linkHeader.next, true).then((data) => {
|
||||||
|
this.fetchedData.timeline = [...this.fetchedData.timeline, ...data];
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse link header into an object
|
||||||
|
* @param {string} l Link header
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
#parseLinkHeader(l) {
|
||||||
|
const linkArray = l.split(", ").map((header) => header.split("; "));
|
||||||
|
const linkMap = linkArray.map((header) => {
|
||||||
|
const linkRel = header[1].replace(/"/g, "").replace("rel=", "");
|
||||||
|
const linkURL = header[0].slice(1, -1);
|
||||||
|
return [linkRel, linkURL];
|
||||||
|
});
|
||||||
|
return Object.fromEntries(linkMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter all fetched posts and append them on the timeline
|
||||||
|
* @param {string} t Type of build (new or reload)
|
||||||
|
*/
|
||||||
|
async #buildTimeline(t) {
|
||||||
|
await this.#getTimelineData();
|
||||||
|
|
||||||
|
// console.log("Mastodon timeline data fetched: ", this.fetchedData);
|
||||||
|
|
||||||
|
const posts = this.fetchedData.timeline;
|
||||||
|
let nbPostToShow = 0;
|
||||||
|
|
||||||
this.mtBodyNode.replaceChildren();
|
this.mtBodyNode.replaceChildren();
|
||||||
|
|
||||||
// Set posts counter to 0
|
posts.forEach((post) => {
|
||||||
let nbPostShow = 0;
|
const isPublicOrUnlisted =
|
||||||
|
post.visibility === "public" ||
|
||||||
|
(!this.mtSettings.hideUnlisted && post.visibility === "unlisted");
|
||||||
|
const shouldHideReblog = this.mtSettings.hideReblog && post.reblog;
|
||||||
|
const shouldHideReplies =
|
||||||
|
this.mtSettings.hideReplies && post.in_reply_to_id;
|
||||||
|
|
||||||
for (let i in posts) {
|
// Filter by (Public / Unlisted)
|
||||||
// First filter (Public / Unlisted)
|
if (isPublicOrUnlisted && !shouldHideReblog && !shouldHideReplies) {
|
||||||
if (
|
if (nbPostToShow < this.mtSettings.maxNbPostShow) {
|
||||||
posts[i].visibility == "public" ||
|
this.#appendPost(post, nbPostToShow);
|
||||||
(!this.mtSettings.hideUnlisted && posts[i].visibility == "unlisted")
|
nbPostToShow++;
|
||||||
) {
|
|
||||||
// Second filter (Reblog / Replies)
|
|
||||||
if (
|
|
||||||
(this.mtSettings.hideReblog && posts[i].reblog) ||
|
|
||||||
(this.mtSettings.hideReplies && posts[i].in_reply_to_id)
|
|
||||||
) {
|
|
||||||
// Nothing here (Don't append posts)
|
|
||||||
} else {
|
} else {
|
||||||
if (nbPostShow < this.mtSettings.maxNbPostShow) {
|
// Reached the limit of maximum number of posts to show
|
||||||
this.#appendPost(posts[i], Number(i));
|
|
||||||
nbPostShow++;
|
|
||||||
} else {
|
|
||||||
// Nothing here (Reached the limit of maximum number of posts to show)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// If there are no posts to display, show an error message
|
// Check if there are posts to display or not
|
||||||
if (this.mtBodyNode.innerHTML === "") {
|
if (this.mtBodyNode.innerHTML !== "") {
|
||||||
const errorMessage = `No posts to show<hr/>${
|
|
||||||
posts?.length || 0
|
|
||||||
} posts have been fetched from the server<hr/>This may be due to an incorrect configuration with the parameters or with the filters applied (to hide certains type of posts)`;
|
|
||||||
this.#showError(errorMessage, "📭");
|
|
||||||
} else {
|
|
||||||
if (t === "newTimeline") {
|
if (t === "newTimeline") {
|
||||||
this.#manageSpinner();
|
this.#manageSpinner();
|
||||||
this.#setPostsInteracion();
|
this.#setCSSvariables();
|
||||||
|
this.#addAriaSetsize(nbPostToShow);
|
||||||
|
this.#addPostListener();
|
||||||
|
if (this.mtSettings.btnSeeMore || this.mtSettings.btnReload)
|
||||||
this.#buildFooter();
|
this.#buildFooter();
|
||||||
} else if (t === "updateTimeline") {
|
} else if (t === "updateTimeline") {
|
||||||
this.#manageSpinner();
|
this.#manageSpinner();
|
||||||
} else {
|
} else {
|
||||||
this.#showError("The function buildTimeline() was expecting a param");
|
this.#showError("The function buildTimeline() was expecting a param");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const errorMessage = `No posts to show<hr />${
|
||||||
|
posts?.length || 0
|
||||||
|
} posts have been fetched from the server<hr />This may be due to an incorrect configuration with the parameters or with the filters applied (to hide certains type of posts)`;
|
||||||
|
this.#showError(errorMessage, "📭");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes the defined CSS variables
|
||||||
|
*/
|
||||||
|
#setCSSvariables() {
|
||||||
|
if (
|
||||||
|
this.mtSettings.txtMaxLines !== "0" &&
|
||||||
|
this.mtSettings.txtMaxLines.length !== 0
|
||||||
|
) {
|
||||||
|
this.mtBodyNode.parentNode.style.setProperty(
|
||||||
|
"--mt-txt-max-lines",
|
||||||
|
this.mtSettings.txtMaxLines
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.mtSettings.previewMaxLines !== "0" &&
|
||||||
|
this.mtSettings.previewMaxLines.length !== 0
|
||||||
|
) {
|
||||||
|
this.mtBodyNode.parentNode.style.setProperty(
|
||||||
|
"--mt-preview-max-lines",
|
||||||
|
this.mtSettings.previewMaxLines
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the attribute Aria-setsize to all posts
|
||||||
|
* @param {number} n The total number of posts showed in the timeline
|
||||||
|
*/
|
||||||
|
#addAriaSetsize(n) {
|
||||||
|
const articles = this.mtBodyNode.getElementsByTagName("article");
|
||||||
|
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
articles[i].setAttribute("aria-setsize", n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,7 +524,7 @@ export class Init {
|
|||||||
|
|
||||||
if (!this.mtSettings.hideUserAccount) {
|
if (!this.mtSettings.hideUserAccount) {
|
||||||
accountName =
|
accountName =
|
||||||
'<br/><span class="mt-post-header-user-account">@' +
|
'<br /><span class="mt-post-header-user-account">@' +
|
||||||
c.reblog.account.username +
|
c.reblog.account.username +
|
||||||
"@" +
|
"@" +
|
||||||
new URL(c.reblog.account.url).hostname +
|
new URL(c.reblog.account.url).hostname +
|
||||||
@ -464,7 +586,7 @@ export class Init {
|
|||||||
|
|
||||||
if (!this.mtSettings.hideUserAccount) {
|
if (!this.mtSettings.hideUserAccount) {
|
||||||
accountName =
|
accountName =
|
||||||
'<br/><span class="mt-post-header-user-account">@' +
|
'<br /><span class="mt-post-header-user-account">@' +
|
||||||
c.account.username +
|
c.account.username +
|
||||||
"@" +
|
"@" +
|
||||||
new URL(c.account.url).hostname +
|
new URL(c.account.url).hostname +
|
||||||
@ -511,16 +633,11 @@ export class Init {
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
// Main text
|
// Main text
|
||||||
let txtCss = "";
|
|
||||||
if (this.mtSettings.txtMaxLines !== "0") {
|
|
||||||
txtCss = " truncate";
|
|
||||||
this.mtBodyNode.parentNode.style.setProperty(
|
|
||||||
"--mt-txt-max-lines",
|
|
||||||
this.mtSettings.txtMaxLines
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let content = "";
|
let content = "";
|
||||||
|
if (this.mtSettings.txtMaxLines !== "0") {
|
||||||
|
const txtCss =
|
||||||
|
this.mtSettings.txtMaxLines.length !== 0 ? " truncate" : "";
|
||||||
|
|
||||||
if (c.spoiler_text !== "") {
|
if (c.spoiler_text !== "") {
|
||||||
content =
|
content =
|
||||||
'<div class="mt-post-txt">' +
|
'<div class="mt-post-txt">' +
|
||||||
@ -570,6 +687,7 @@ export class Init {
|
|||||||
"</div>" +
|
"</div>" +
|
||||||
"</div>";
|
"</div>";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Media attachments
|
// Media attachments
|
||||||
let media = [];
|
let media = [];
|
||||||
@ -643,8 +761,6 @@ export class Init {
|
|||||||
const post =
|
const post =
|
||||||
'<article class="mt-post" aria-posinset="' +
|
'<article class="mt-post" aria-posinset="' +
|
||||||
(i + 1) +
|
(i + 1) +
|
||||||
'" aria-setsize="' +
|
|
||||||
this.mtSettings.maxNbPostFetch +
|
|
||||||
'" data-location="' +
|
'" data-location="' +
|
||||||
url +
|
url +
|
||||||
'" tabindex="0">' +
|
'" tabindex="0">' +
|
||||||
@ -795,8 +911,8 @@ export class Init {
|
|||||||
* @param {boolean} s Sensitive/spoiler status
|
* @param {boolean} s Sensitive/spoiler status
|
||||||
* @returns {string} Media in HTML format
|
* @returns {string} Media in HTML format
|
||||||
*/
|
*/
|
||||||
#createMedia(m, s) {
|
#createMedia(m, s = false) {
|
||||||
const spoiler = s || false;
|
const spoiler = s;
|
||||||
const type = m.type;
|
const type = m.type;
|
||||||
let media = "";
|
let media = "";
|
||||||
|
|
||||||
@ -806,7 +922,7 @@ export class Init {
|
|||||||
(spoiler ? "mt-post-media-spoiler " : "") +
|
(spoiler ? "mt-post-media-spoiler " : "") +
|
||||||
this.mtSettings.spinnerClass +
|
this.mtSettings.spinnerClass +
|
||||||
'" data-media-type="' +
|
'" data-media-type="' +
|
||||||
m.type +
|
type +
|
||||||
'" data-media-url-hd="' +
|
'" data-media-url-hd="' +
|
||||||
m.url +
|
m.url +
|
||||||
'" data-media-alt-txt="' +
|
'" data-media-alt-txt="' +
|
||||||
@ -838,7 +954,7 @@ export class Init {
|
|||||||
(spoiler ? "mt-post-media-spoiler " : "") +
|
(spoiler ? "mt-post-media-spoiler " : "") +
|
||||||
this.mtSettings.spinnerClass +
|
this.mtSettings.spinnerClass +
|
||||||
'" data-media-type="' +
|
'" data-media-type="' +
|
||||||
m.type +
|
type +
|
||||||
'" data-media-url-hd="' +
|
'" data-media-url-hd="' +
|
||||||
m.preview_url +
|
m.preview_url +
|
||||||
'" data-media-alt-txt="' +
|
'" data-media-alt-txt="' +
|
||||||
@ -869,7 +985,7 @@ export class Init {
|
|||||||
'<div class="mt-post-media ' +
|
'<div class="mt-post-media ' +
|
||||||
(spoiler ? "mt-post-media-spoiler " : "") +
|
(spoiler ? "mt-post-media-spoiler " : "") +
|
||||||
'" data-media-type="' +
|
'" data-media-type="' +
|
||||||
m.type +
|
type +
|
||||||
'">' +
|
'">' +
|
||||||
(spoiler
|
(spoiler
|
||||||
? '<button class="mt-btn-dark mt-btn-spoiler">' +
|
? '<button class="mt-btn-dark mt-btn-spoiler">' +
|
||||||
@ -890,7 +1006,7 @@ export class Init {
|
|||||||
(spoiler ? "mt-post-media-spoiler " : "") +
|
(spoiler ? "mt-post-media-spoiler " : "") +
|
||||||
this.mtSettings.spinnerClass +
|
this.mtSettings.spinnerClass +
|
||||||
'" data-media-type="' +
|
'" data-media-type="' +
|
||||||
m.type +
|
type +
|
||||||
'" data-media-url-hd="' +
|
'" data-media-url-hd="' +
|
||||||
m.url +
|
m.url +
|
||||||
'" data-media-alt-txt="' +
|
'" data-media-alt-txt="' +
|
||||||
@ -919,7 +1035,7 @@ export class Init {
|
|||||||
'<div class="mt-post-media ' +
|
'<div class="mt-post-media ' +
|
||||||
(spoiler ? "mt-post-media-spoiler " : "") +
|
(spoiler ? "mt-post-media-spoiler " : "") +
|
||||||
'" data-media-type="' +
|
'" data-media-type="' +
|
||||||
m.type +
|
type +
|
||||||
'" data-media-url-hd="' +
|
'" data-media-url-hd="' +
|
||||||
m.url +
|
m.url +
|
||||||
'" data-media-alt-txt="' +
|
'" data-media-alt-txt="' +
|
||||||
@ -968,7 +1084,7 @@ export class Init {
|
|||||||
* Build a carousel/lightbox with the media content in the post clicked
|
* Build a carousel/lightbox with the media content in the post clicked
|
||||||
* @param {event} e User interaction trigger
|
* @param {event} e User interaction trigger
|
||||||
*/
|
*/
|
||||||
#buildCarousel(e) {
|
#showCarousel(e) {
|
||||||
// List all medias in the post and remove sensitive/spoiler medias
|
// List all medias in the post and remove sensitive/spoiler medias
|
||||||
const mediaSiblings = Array.from(
|
const mediaSiblings = Array.from(
|
||||||
e.target.parentNode.parentNode.children
|
e.target.parentNode.parentNode.children
|
||||||
@ -1214,6 +1330,19 @@ export class Init {
|
|||||||
* @returns {string} Preview link in HTML format
|
* @returns {string} Preview link in HTML format
|
||||||
*/
|
*/
|
||||||
#createPreviewLink(c) {
|
#createPreviewLink(c) {
|
||||||
|
let previewDescription = "";
|
||||||
|
if (this.mtSettings.previewMaxLines !== "0" && c.description) {
|
||||||
|
const txtCss =
|
||||||
|
this.mtSettings.previewMaxLines.length !== 0 ? " truncate" : "";
|
||||||
|
|
||||||
|
previewDescription =
|
||||||
|
'<span class="mt-post-preview-description' +
|
||||||
|
txtCss +
|
||||||
|
'">' +
|
||||||
|
this.#parseHTMLstring(c.description) +
|
||||||
|
"</span>";
|
||||||
|
}
|
||||||
|
|
||||||
const card =
|
const card =
|
||||||
'<a href="' +
|
'<a href="' +
|
||||||
c.url +
|
c.url +
|
||||||
@ -1237,6 +1366,7 @@ export class Init {
|
|||||||
'<span class="mt-post-preview-title">' +
|
'<span class="mt-post-preview-title">' +
|
||||||
c.title +
|
c.title +
|
||||||
"</span>" +
|
"</span>" +
|
||||||
|
previewDescription +
|
||||||
(c.author_name
|
(c.author_name
|
||||||
? '<span class="mt-post-preview-author">' +
|
? '<span class="mt-post-preview-author">' +
|
||||||
this.#parseHTMLstring(c.author_name) +
|
this.#parseHTMLstring(c.author_name) +
|
||||||
@ -1263,15 +1393,8 @@ export class Init {
|
|||||||
* Build footer after last post
|
* Build footer after last post
|
||||||
*/
|
*/
|
||||||
#buildFooter() {
|
#buildFooter() {
|
||||||
if (this.mtSettings.btnSeeMore || this.mtSettings.btnReload) {
|
let btnSeeMoreHTML = "";
|
||||||
// Add footer container
|
let btnReloadHTML = "";
|
||||||
this.mtBodyNode.parentNode.insertAdjacentHTML(
|
|
||||||
"beforeend",
|
|
||||||
'<div class="mt-footer"></div>'
|
|
||||||
);
|
|
||||||
|
|
||||||
const containerFooter =
|
|
||||||
this.mtContainerNode.getElementsByClassName("mt-footer")[0];
|
|
||||||
|
|
||||||
// Create button to open Mastodon page
|
// Create button to open Mastodon page
|
||||||
if (this.mtSettings.btnSeeMore) {
|
if (this.mtSettings.btnSeeMore) {
|
||||||
@ -1290,7 +1413,7 @@ export class Init {
|
|||||||
} else if (this.mtSettings.timelineType === "local") {
|
} else if (this.mtSettings.timelineType === "local") {
|
||||||
btnSeeMorePath = "public/local";
|
btnSeeMorePath = "public/local";
|
||||||
}
|
}
|
||||||
const btnSeeMoreHTML = `
|
btnSeeMoreHTML = `
|
||||||
<a class="mt-btn-violet btn-see-more" href="${
|
<a class="mt-btn-violet btn-see-more" href="${
|
||||||
this.mtSettings.instanceUrl
|
this.mtSettings.instanceUrl
|
||||||
}/${this.#escapeHTML(
|
}/${this.#escapeHTML(
|
||||||
@ -1298,21 +1421,24 @@ export class Init {
|
|||||||
)}" rel="nofollow noopener noreferrer" target="_blank">
|
)}" rel="nofollow noopener noreferrer" target="_blank">
|
||||||
${this.mtSettings.btnSeeMore}
|
${this.mtSettings.btnSeeMore}
|
||||||
</a>`;
|
</a>`;
|
||||||
|
|
||||||
containerFooter.insertAdjacentHTML("beforeend", btnSeeMoreHTML);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create button to refresh the timeline
|
// Create button to refresh the timeline
|
||||||
if (this.mtSettings.btnReload) {
|
if (this.mtSettings.btnReload) {
|
||||||
const btnReloadHTML = `
|
btnReloadHTML = `
|
||||||
<button class="mt-btn-violet btn-refresh">
|
<button class="mt-btn-violet btn-refresh">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M21 3v5m0 0h-5m5 0l-3-2.708C16.408 3.867 14.305 3 12 3a9 9 0 1 0 0 18c4.283 0 7.868-2.992 8.777-7" stroke="var(--mt-color-btn-txt)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M21 3v5m0 0h-5m5 0l-3-2.708C16.408 3.867 14.305 3 12 3a9 9 0 1 0 0 18c4.283 0 7.868-2.992 8.777-7" stroke="var(--mt-color-btn-txt)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
${this.mtSettings.btnReload}
|
${this.mtSettings.btnReload}
|
||||||
</button>`;
|
</button>`;
|
||||||
|
|
||||||
containerFooter.insertAdjacentHTML("beforeend", btnReloadHTML);
|
// Add footer container
|
||||||
|
this.mtBodyNode.parentNode.insertAdjacentHTML(
|
||||||
|
"beforeend",
|
||||||
|
'<div class="mt-footer">' + btnSeeMoreHTML + btnReloadHTML + "</div>"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add event listener to the button "Refresh"
|
||||||
const reloadBtn =
|
const reloadBtn =
|
||||||
this.mtContainerNode.getElementsByClassName("btn-refresh")[0];
|
this.mtContainerNode.getElementsByClassName("btn-refresh")[0];
|
||||||
reloadBtn.addEventListener("click", () => {
|
reloadBtn.addEventListener("click", () => {
|
||||||
@ -1320,12 +1446,11 @@ export class Init {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add EventListeners for timeline interactions and trigger functions
|
* Add EventListeners for timeline interactions and trigger functions
|
||||||
*/
|
*/
|
||||||
#setPostsInteracion() {
|
#addPostListener() {
|
||||||
this.mtBodyNode.addEventListener("click", (e) => {
|
this.mtBodyNode.addEventListener("click", (e) => {
|
||||||
const target = e.target;
|
const target = e.target;
|
||||||
const localName = target.localName;
|
const localName = target.localName;
|
||||||
@ -1335,10 +1460,8 @@ export class Init {
|
|||||||
if (
|
if (
|
||||||
localName == "article" ||
|
localName == "article" ||
|
||||||
target.offsetParent?.localName == "article" ||
|
target.offsetParent?.localName == "article" ||
|
||||||
(localName == "img" &&
|
(this.mtSettings.disableCarousel &&
|
||||||
this.mtSettings.disableCarousel &&
|
parentNode.getAttribute("data-media-type") === "image")
|
||||||
parentNode.getAttribute("data-media-type") !== "video" &&
|
|
||||||
parentNode.getAttribute("data-media-type") !== "gifv")
|
|
||||||
) {
|
) {
|
||||||
this.#openPostUrl(e);
|
this.#openPostUrl(e);
|
||||||
}
|
}
|
||||||
@ -1355,11 +1478,10 @@ export class Init {
|
|||||||
if (
|
if (
|
||||||
!this.mtSettings.disableCarousel &&
|
!this.mtSettings.disableCarousel &&
|
||||||
localName == "img" &&
|
localName == "img" &&
|
||||||
!parentNode.classList.contains("mt-post-preview-image") &&
|
(parentNode.getAttribute("data-media-type") === "image" ||
|
||||||
parentNode.getAttribute("data-media-type") !== "video" &&
|
parentNode.getAttribute("data-media-type") === "audio")
|
||||||
parentNode.getAttribute("data-media-type") !== "gifv"
|
|
||||||
) {
|
) {
|
||||||
this.#buildCarousel(e);
|
this.#showCarousel(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if video preview image or play icon/button was clicked
|
// Check if video preview image or play icon/button was clicked
|
||||||
@ -1400,6 +1522,7 @@ export class Init {
|
|||||||
e.target.className !== "mt-post-preview-noImage" &&
|
e.target.className !== "mt-post-preview-noImage" &&
|
||||||
e.target.parentNode.className !== "mt-post-avatar-image-big" &&
|
e.target.parentNode.className !== "mt-post-avatar-image-big" &&
|
||||||
e.target.parentNode.className !== "mt-post-avatar-image-small" &&
|
e.target.parentNode.className !== "mt-post-avatar-image-small" &&
|
||||||
|
e.target.parentNode.className !== "mt-post-header-user-name" &&
|
||||||
e.target.parentNode.className !== "mt-post-preview-image" &&
|
e.target.parentNode.className !== "mt-post-preview-image" &&
|
||||||
e.target.parentNode.className !== "mt-post-preview" &&
|
e.target.parentNode.className !== "mt-post-preview" &&
|
||||||
urlPost
|
urlPost
|
||||||
|
Loading…
x
Reference in New Issue
Block a user