Остання активність 1727320678

chlorine's Avatar chlorine ревизій цього gist 1727320678. До ревизії

1 file changed, 211 insertions

whispe.html(файл створено)

@@ -0,0 +1,211 @@
1 + {{ with resources.Get "css/addon/whisper.css" }}
2 + <style>
3 + {{ .Content | safeCSS }}
4 + </style>
5 + {{ end }}
6 +
7 + <body>
8 + <div id="toots-content" class="toots-container">
9 + <div id="toots"></div>
10 + <div id="toots-loading" class="loading-container" style="display: none;">
11 + <div class="spinner"></div>
12 + </div>
13 + <div class="pt-3 text-center flex justify-center">
14 + <button id="toots-moreButton" onclick="tootsShowMore()">
15 + 加载更多
16 + </button>
17 + </div>
18 + </div>
19 + </body>
20 +
21 + <script src="https://gcore.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.umd.js" crossorigin="anonymous"
22 + defer></script>
23 + <script>
24 + document.addEventListener("DOMContentLoaded", function () {
25 + Fancybox.bind("[data-fancybox]", {});
26 + });
27 + // console.log("Fancybox is ready!");
28 + </script>
29 +
30 + <link rel="preload" href="https://gcore.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css" as="style"
31 + crossorigin="anonymous">
32 + <link rel="stylesheet" href="https://gcore.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css"
33 + crossorigin="anonymous">
34 +
35 + {{- with resources.Get "js/time-fmt.min.js" }}
36 + <script data-swup-reload-script>
37 + function initTimeFmt() {
38 + (function () {
39 + {{ .Content | safeJS }}
40 + // 确保 formatTime 在全局作用域中可用
41 + if (typeof formatTime === 'function') {
42 + window.formatTime = formatTime;
43 + } else {
44 + console.error('formatTime is not defined in time-fmt.min.js');
45 + }
46 + })();
47 + }
48 + // 初始加载时执行
49 + initTimeFmt();
50 + // 监听 Swup 页面切换事件
51 + document.addEventListener('swup:contentReplaced', initTimeFmt);
52 + </script>
53 + {{- end }}
54 +
55 + <script data-swup-reload-script>
56 + (function () {
57 + let maxId = null;
58 + let isFirst = true;
59 + const cacheKey = 'mastodon_toots_cache';
60 + const cacheExpiration = 5 * 60 * 1000; // 5 minutes
61 + const tootsDiv = document.getElementById('toots');
62 + const tootsMoreButton = document.getElementById('toots-moreButton');
63 + const tootsLoading = document.getElementById('toots-loading');
64 + const urlObject = new URL(window.location.href);
65 + const idValue = urlObject.searchParams.get("id");
66 +
67 + async function getPublicToots(forceRefresh = false) {
68 + let limit = "{{ .Get 2 | default 5 }}";
69 + const currentTime = new Date().getTime();
70 +
71 + // Check cache for non-forced refresh
72 + if (!forceRefresh && !maxId) {
73 + const cachedData = localStorage.getItem(cacheKey);
74 + if (cachedData) {
75 + const { data, timestamp } = JSON.parse(cachedData);
76 + if (currentTime - timestamp < cacheExpiration) {
77 + console.log("Cache hit");
78 + return data;
79 + }
80 + }
81 + }
82 +
83 + let toots;
84 + if (idValue != null && isFirst) {
85 + isFirst = false;
86 + const response = await fetch("{{ .Site.Params.Whisper.instance }}/api/v1/statuses/" + idValue, {
87 + headers: {
88 + 'Authorization': "Bearer {{ .Site.Params.bot_token }}"
89 + }
90 + });
91 + toots = [await response.json()];
92 + } else {
93 + const queryParams = maxId ? (`?limit=${limit}&max_id=${maxId}`) : "?limit=" + limit;
94 + const response = await fetch("{{ .Site.Params.Whisper.instance }}/api/v1/accounts/{{ .Site.Params.Whisper.user_id }}/statuses" + queryParams + "&exclude_replies=true", {
95 + headers: {
96 + 'Authorization': "Bearer {{ .Site.Params.Whisper.bot_token }}"
97 + }
98 + });
99 + toots = await response.json();
100 + }
101 +
102 + // Cache only the first page
103 + if (!maxId) {
104 + localStorage.setItem(cacheKey, JSON.stringify({
105 + data: toots,
106 + timestamp: currentTime
107 + }));
108 + }
109 +
110 + return toots;
111 + }
112 +
113 + function createMediaGrid(mediaAttachments) {
114 + if (mediaAttachments.length === 0) return '';
115 +
116 + const mediaClass = mediaAttachments.length === 1 ? 'single' :
117 + mediaAttachments.length === 2 ? 'double' :
118 + mediaAttachments.length === 3 ? 'triple' :
119 + mediaAttachments.length === 4 ? 'quad' : 'multi';
120 +
121 + const mediaHtml = mediaAttachments.map(media => {
122 + if (media.type === 'image') {
123 + return `
124 + <a href="${media.url}"
125 + data-fancybox="gallery"
126 + data-caption="${media.description || ''}"
127 + class="padding-bottom: 15px">
128 + <img src="${media.preview_url}"
129 + alt="${media.description || ''}"
130 + loading="lazy">
131 + </a>
132 + <figcaption class="text-center text-neutral-500 mt-2 md:mt-3 text-xs md:text-sm">
133 + ${media.description || ''}
134 + </figcaption>`;
135 + } else if (media.type === 'video') {
136 + return `
137 + <a href="${media.url}" data-fancybox="gallery" data-type="video" data-caption="${media.description || ''}">
138 + <video src="${media.url}" preload="none" controls></video>
139 + </a>`;
140 + } else {
141 + return ''; // 可以在这里添加对其他类型媒体的处理
142 + }
143 + }).join('');
144 +
145 + return `<div class="toot-media ${mediaClass}">${mediaHtml}</div>`;
146 + }
147 +
148 + async function displayToots(forceRefresh = false) {
149 + try {
150 + tootsLoading.style.display = "block";
151 + tootsMoreButton.style.display = 'none';
152 + const toots = await getPublicToots(forceRefresh);
153 + if (toots && toots.length > 0) {
154 + toots.forEach(toot => {
155 + const tootDiv = document.createElement("div");
156 + tootDiv.classList.add("toot");
157 +
158 + tootDiv.innerHTML = `
159 + <div class="toot-info">
160 + <div class="toot-avatar">
161 + <img src="{{ .Site.Params.Author.avatar }}" alt="${toot.account.display_name}">
162 + </div>
163 + <div class="toot-profile">
164 + <strong>${toot.account.display_name}</strong>
165 + <a href="${toot.url}" target="_blank">@${toot.account.acct}</a>
166 + <small>${window.formatTime(toot.created_at)}</small>
167 + </div>
168 + </div>
169 + <div class="toot-content">
170 + ${toot.content}
171 + </div>
172 + ${createMediaGrid(toot.media_attachments)}
173 + <br>
174 + <div class="toot-stats">
175 + <span><i class="mdi--reply"></i>${toot.replies_count}</span>
176 + <span><i class="mdi--twitter-retweet"></i>${toot.reblogs_count}</span>
177 + <span><i class="mdi--star"></i>${toot.favourites_count}</span>
178 + </div>
179 + `;
180 +
181 + tootsDiv.appendChild(tootDiv);
182 + maxId = toot.id;
183 + });
184 + tootsMoreButton.style.display = 'block';
185 + } else {
186 + tootsMoreButton.style.display = 'none';
187 + }
188 + } catch (error) {
189 + console.error('获取 Toots 时出错:', error);
190 + tootsDiv.innerHTML += `<p>加载失败: ${error.message}</p>`;
191 + }
192 + tootsLoading.style.display = "none";
193 + }
194 +
195 + function tootsShowMore() {
196 + displayToots();
197 + }
198 +
199 + // 将 tootsShowMore 绑定到 window 对象
200 + window.tootsShowMore = tootsShowMore;
201 +
202 + // 初始加载
203 + const isForcedRefresh = performance.navigation.type === 1;
204 + if (isForcedRefresh) {
205 + localStorage.removeItem(cacheKey);
206 + }
207 + displayToots(isForcedRefresh);
208 +
209 + window.ViewImage && ViewImage.init('.toot-content img');
210 + })();
211 + </script>
Новіше Пізніше