chlorine a révisé ce gist . Aller à la révision
1 file changed, 211 insertions
whispe.html(fichier créé)
@@ -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> |
Plus récent
Plus ancien