|
|
@@ -1,326 +1,364 @@ |
|
1 |
+ |
(function () { |
|
2 |
+ |
const TALK_API_URL = 'https://mm.liushen.fun/api/echo/page'; |
|
3 |
+ |
const TALK_CACHE_KEY = 'liushenEchoCacheV2'; |
|
4 |
+ |
const TALK_CACHE_TIME_KEY = 'liushenEchoCacheTimeV2'; |
|
5 |
+ |
const TALK_CACHE_DURATION = 30 * 60 * 1000; |
|
6 |
+ |
const TALK_AVATAR = 'https://p.liiiu.cn/i/2025/03/13/67d2fc82d329c.webp'; |
|
7 |
+ |
const shuoshuoState = window.__liushenShuoshuoState || (window.__liushenShuoshuoState = { |
|
8 |
+ |
resizeHandler: null, |
|
9 |
+ |
afterRenderTimer: null, |
|
10 |
+ |
listenersBound: false |
|
11 |
+ |
}); |
|
12 |
+ |
|
|
13 |
+ |
function cleanupShuoshuo() { |
|
14 |
+ |
if (shuoshuoState.afterRenderTimer) { |
|
15 |
+ |
window.clearTimeout(shuoshuoState.afterRenderTimer); |
|
16 |
+ |
shuoshuoState.afterRenderTimer = null; |
|
17 |
+ |
} |
|
18 |
+ |
|
|
19 |
+ |
if (shuoshuoState.resizeHandler) { |
|
20 |
+ |
window.removeEventListener('resize', shuoshuoState.resizeHandler); |
|
21 |
+ |
shuoshuoState.resizeHandler = null; |
|
22 |
+ |
} |
|
23 |
+ |
} |
|
24 |
+ |
|
| 1 |
25 |
|
function renderTalks() { |
|
26 |
+ |
cleanupShuoshuo(); |
|
27 |
+ |
|
| 2 |
28 |
|
const talkContainer = document.querySelector('#talk'); |
| 3 |
29 |
|
if (!talkContainer) return; |
|
30 |
+ |
|
| 4 |
31 |
|
talkContainer.innerHTML = ''; |
|
32 |
+ |
|
| 5 |
33 |
|
const generateIconSVG = () => { |
| 6 |
|
- |
return `<svg viewBox="0 0 512 512"xmlns="http://www.w3.org/2000/svg"class="is-badge icon"><path d="m512 268c0 17.9-4.3 34.5-12.9 49.7s-20.1 27.1-34.6 35.4c.4 2.7.6 6.9.6 12.6 0 27.1-9.1 50.1-27.1 69.1-18.1 19.1-39.9 28.6-65.4 28.6-11.4 0-22.3-2.1-32.6-6.3-8 16.4-19.5 29.6-34.6 39.7-15 10.2-31.5 15.2-49.4 15.2-18.3 0-34.9-4.9-49.7-14.9-14.9-9.9-26.3-23.2-34.3-40-10.3 4.2-21.1 6.3-32.6 6.3-25.5 0-47.4-9.5-65.7-28.6-18.3-19-27.4-42.1-27.4-69.1 0-3 .4-7.2 1.1-12.6-14.5-8.4-26-20.2-34.6-35.4-8.5-15.2-12.8-31.8-12.8-49.7 0-19 4.8-36.5 14.3-52.3s22.3-27.5 38.3-35.1c-4.2-11.4-6.3-22.9-6.3-34.3 0-27 9.1-50.1 27.4-69.1s40.2-28.6 65.7-28.6c11.4 0 22.3 2.1 32.6 6.3 8-16.4 19.5-29.6 34.6-39.7 15-10.1 31.5-15.2 49.4-15.2s34.4 5.1 49.4 15.1c15 10.1 26.6 23.3 34.6 39.7 10.3-4.2 21.1-6.3 32.6-6.3 25.5 0 47.3 9.5 65.4 28.6s27.1 42.1 27.1 69.1c0 12.6-1.9 24-5.7 34.3 16 7.6 28.8 19.3 38.3 35.1 9.5 15.9 14.3 33.4 14.3 52.4zm-266.9 77.1 105.7-158.3c2.7-4.2 3.5-8.8 2.6-13.7-1-4.9-3.5-8.8-7.7-11.4-4.2-2.7-8.8-3.6-13.7-2.9-5 .8-9 3.2-12 7.4l-93.1 140-42.9-42.8c-3.8-3.8-8.2-5.6-13.1-5.4-5 .2-9.3 2-13.1 5.4-3.4 3.4-5.1 7.7-5.1 12.9 0 5.1 1.7 9.4 5.1 12.9l58.9 58.9 2.9 2.3c3.4 2.3 6.9 3.4 10.3 3.4 6.7-.1 11.8-2.9 15.2-8.7z"fill="#1da1f2"></path></svg>`; |
| 7 |
|
- |
} |
| 8 |
|
- |
const waterfall = (a) => { |
| 9 |
|
- |
function b(a, b) { |
| 10 |
|
- |
var c = window.getComputedStyle(b); |
| 11 |
|
- |
return parseFloat(c["margin" + a]) || 0 |
|
34 |
+ |
return '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" class="is-badge icon"><path d="m512 268c0 17.9-4.3 34.5-12.9 49.7s-20.1 27.1-34.6 35.4c.4 2.7.6 6.9.6 12.6 0 27.1-9.1 50.1-27.1 69.1-18.1 19.1-39.9 28.6-65.4 28.6-11.4 0-22.3-2.1-32.6-6.3-8 16.4-19.5 29.6-34.6 39.7-15 10.2-31.5 15.2-49.4 15.2-18.3 0-34.9-4.9-49.7-14.9-14.9-9.9-26.3-23.2-34.3-40-10.3 4.2-21.1 6.3-32.6 6.3-25.5 0-47.4-9.5-65.7-28.6-18.3-19-27.4-42.1-27.4-69.1 0-3 .4-7.2 1.1-12.6-14.5-8.4-26-20.2-34.6-35.4-8.5-15.2-12.8-31.8-12.8-49.7 0-19 4.8-36.5 14.3-52.3s22.3-27.5 38.3-35.1c-4.2-11.4-6.3-22.9-6.3-34.3 0-27 9.1-50.1 27.4-69.1s40.2-28.6 65.7-28.6c11.4 0 22.3 2.1 32.6 6.3 8-16.4 19.5-29.6 34.6-39.7 15-10.1 31.5-15.2 49.4-15.2s34.4 5.1 49.4 15.1c15 10.1 26.6 23.3 34.6 39.7 10.3-4.2 21.1-6.3 32.6-6.3 25.5 0 47.3 9.5 65.4 28.6s27.1 42.1 27.1 69.1c0 12.6-1.9 24-5.7 34.3 16 7.6 28.8 19.3 38.3 35.1 9.5 15.9 14.3 33.4 14.3 52.4zm-266.9 77.1 105.7-158.3c2.7-4.2 3.5-8.8 2.6-13.7-1-4.9-3.5-8.8-7.7-11.4-4.2-2.7-8.8-3.6-13.7-2.9-5 .8-9 3.2-12 7.4l-93.1 140-42.9-42.8c-3.8-3.8-8.2-5.6-13.1-5.4-5 .2-9.3 2-13.1 5.4-3.4 3.4-5.1 7.7-5.1 12.9 0 5.1 1.7 9.4 5.1 12.9l58.9 58.9 2.9 2.3c3.4 2.3 6.9 3.4 10.3 3.4 6.7-.1 11.8-2.9 15.2-8.7z" fill="#1da1f2"></path></svg>'; |
|
35 |
+ |
}; |
|
36 |
+ |
|
|
37 |
+ |
const waterfall = (container) => { |
|
38 |
+ |
function getMargin(side, element) { |
|
39 |
+ |
const styles = window.getComputedStyle(element); |
|
40 |
+ |
return parseFloat(styles[`margin${side}`]) || 0; |
| 12 |
41 |
|
} |
| 13 |
42 |
|
|
| 14 |
|
- |
function c(a) { |
| 15 |
|
- |
return a + "px" |
|
43 |
+ |
function toPx(value) { |
|
44 |
+ |
return `${value}px`; |
| 16 |
45 |
|
} |
| 17 |
46 |
|
|
| 18 |
|
- |
function d(a) { |
| 19 |
|
- |
return parseFloat(a.style.top) |
|
47 |
+ |
function getTop(element) { |
|
48 |
+ |
return parseFloat(element.style.top); |
| 20 |
49 |
|
} |
| 21 |
50 |
|
|
| 22 |
|
- |
function e(a) { |
| 23 |
|
- |
return parseFloat(a.style.left) |
|
51 |
+ |
function getLeft(element) { |
|
52 |
+ |
return parseFloat(element.style.left); |
| 24 |
53 |
|
} |
| 25 |
54 |
|
|
| 26 |
|
- |
function f(a) { |
| 27 |
|
- |
return a.clientWidth |
|
55 |
+ |
function getWidth(element) { |
|
56 |
+ |
return element.clientWidth; |
| 28 |
57 |
|
} |
| 29 |
58 |
|
|
| 30 |
|
- |
function g(a) { |
| 31 |
|
- |
return a.clientHeight |
|
59 |
+ |
function getHeight(element) { |
|
60 |
+ |
return element.clientHeight; |
| 32 |
61 |
|
} |
| 33 |
62 |
|
|
| 34 |
|
- |
function h(a) { |
| 35 |
|
- |
return d(a) + g(a) + b("Bottom", a) |
|
63 |
+ |
function getBottom(element) { |
|
64 |
+ |
return getTop(element) + getHeight(element) + getMargin('Bottom', element); |
| 36 |
65 |
|
} |
| 37 |
66 |
|
|
| 38 |
|
- |
function i(a) { |
| 39 |
|
- |
return e(a) + f(a) + b("Right", a) |
|
67 |
+ |
function getRight(element) { |
|
68 |
+ |
return getLeft(element) + getWidth(element) + getMargin('Right', element); |
| 40 |
69 |
|
} |
| 41 |
70 |
|
|
| 42 |
|
- |
function j(a) { |
| 43 |
|
- |
a = a.sort(function (a, b) { |
| 44 |
|
- |
return h(a) === h(b) ? e(b) - e(a) : h(b) - h(a) |
| 45 |
|
- |
}) |
|
71 |
+ |
function sortColumns(elements) { |
|
72 |
+ |
elements.sort((left, right) => { |
|
73 |
+ |
return getBottom(left) === getBottom(right) |
|
74 |
+ |
? getLeft(right) - getLeft(left) |
|
75 |
+ |
: getBottom(right) - getBottom(left); |
|
76 |
+ |
}); |
| 46 |
77 |
|
} |
| 47 |
78 |
|
|
| 48 |
|
- |
function k(b) { |
| 49 |
|
- |
f(a) != t && (b.target.removeEventListener(b.type, arguments.callee), waterfall(a)) |
|
79 |
+ |
if (typeof container === 'string') { |
|
80 |
+ |
container = document.querySelector(container); |
| 50 |
81 |
|
} |
| 51 |
|
- |
"string" == typeof a && (a = document.querySelector(a)); |
| 52 |
|
- |
var l = [].map.call(a.children, function (a) { |
| 53 |
|
- |
return a.style.position = "absolute", a |
|
82 |
+ |
|
|
83 |
+ |
if (!container) return; |
|
84 |
+ |
|
|
85 |
+ |
const items = Array.from(container.children).map(item => { |
|
86 |
+ |
item.style.position = 'absolute'; |
|
87 |
+ |
return item; |
| 54 |
88 |
|
}); |
| 55 |
|
- |
a.style.position = "relative"; |
| 56 |
|
- |
var m = []; |
| 57 |
|
- |
l.length && (l[0].style.top = "0px", l[0].style.left = c(b("Left", l[0])), m.push(l[0])); |
| 58 |
|
- |
for (var n = 1; n < l.length; n++) { |
| 59 |
|
- |
var o = l[n - 1], |
| 60 |
|
- |
p = l[n], |
| 61 |
|
- |
q = i(o) + f(p) <= f(a); |
| 62 |
|
- |
if (!q) break; |
| 63 |
|
- |
p.style.top = o.style.top, p.style.left = c(i(o) + b("Left", p)), m.push(p) |
|
89 |
+ |
|
|
90 |
+ |
container.style.position = 'relative'; |
|
91 |
+ |
|
|
92 |
+ |
const columns = []; |
|
93 |
+ |
if (items.length) { |
|
94 |
+ |
items[0].style.top = '0px'; |
|
95 |
+ |
items[0].style.left = toPx(getMargin('Left', items[0])); |
|
96 |
+ |
columns.push(items[0]); |
| 64 |
97 |
|
} |
| 65 |
|
- |
for (; n < l.length; n++) { |
| 66 |
|
- |
j(m); |
| 67 |
|
- |
var p = l[n], |
| 68 |
|
- |
r = m.pop(); |
| 69 |
|
- |
p.style.top = c(h(r) + b("Top", p)), p.style.left = c(e(r)), m.push(p) |
|
98 |
+ |
|
|
99 |
+ |
let index = 1; |
|
100 |
+ |
for (; index < items.length; index += 1) { |
|
101 |
+ |
const previous = items[index - 1]; |
|
102 |
+ |
const current = items[index]; |
|
103 |
+ |
const fits = getRight(previous) + getWidth(current) <= getWidth(container); |
|
104 |
+ |
|
|
105 |
+ |
if (!fits) break; |
|
106 |
+ |
|
|
107 |
+ |
current.style.top = previous.style.top; |
|
108 |
+ |
current.style.left = toPx(getRight(previous) + getMargin('Left', current)); |
|
109 |
+ |
columns.push(current); |
| 70 |
110 |
|
} |
| 71 |
|
- |
j(m); |
| 72 |
|
- |
var s = m[0]; |
| 73 |
|
- |
a.style.height = c(h(s) + b("Bottom", s)); |
| 74 |
|
- |
var t = f(a); |
| 75 |
|
- |
window.addEventListener ? window.addEventListener("resize", k) : document.body.onresize = k |
| 76 |
|
- |
}; |
| 77 |
111 |
|
|
| 78 |
|
- |
const fetchAndRenderTalks = () => { |
| 79 |
|
- |
const url = 'https://says.liushen.fun/api'; |
| 80 |
|
- |
const cacheKey = 'talksCache'; |
| 81 |
|
- |
const cacheTimeKey = 'talksCacheTime'; |
| 82 |
|
- |
const cacheDuration = 30 * 60 * 1000; // 半个小时 (30 分钟) |
| 83 |
|
- |
|
| 84 |
|
- |
const cachedData = localStorage.getItem(cacheKey); |
| 85 |
|
- |
const cachedTime = localStorage.getItem(cacheTimeKey); |
| 86 |
|
- |
const currentTime = new Date().getTime(); |
| 87 |
|
- |
|
| 88 |
|
- |
// 判断缓存是否有效 |
| 89 |
|
- |
if (cachedData && cachedTime && (currentTime - cachedTime < cacheDuration)) { |
| 90 |
|
- |
const data = JSON.parse(cachedData); |
| 91 |
|
- |
renderTalks(data); // 使用缓存渲染数据 |
| 92 |
|
- |
} else { |
| 93 |
|
- |
if (talkContainer) { |
| 94 |
|
- |
talkContainer.innerHTML = ''; |
| 95 |
|
- |
fetch(url + "/messages/page", { |
| 96 |
|
- |
method: 'POST', |
| 97 |
|
- |
headers: { |
| 98 |
|
- |
'Content-Type': 'application/json', |
| 99 |
|
- |
}, |
| 100 |
|
- |
body: JSON.stringify({ |
| 101 |
|
- |
page: 1, |
| 102 |
|
- |
pageSize: 30 |
| 103 |
|
- |
}) |
| 104 |
|
- |
}) |
| 105 |
|
- |
.then(res => res.json()) |
| 106 |
|
- |
.then(data => { |
| 107 |
|
- |
if (data.code === 1 && data.data && Array.isArray(data.data.items)) { |
| 108 |
|
- |
// 缓存数据 |
| 109 |
|
- |
localStorage.setItem(cacheKey, JSON.stringify(data.data.items)); |
| 110 |
|
- |
localStorage.setItem(cacheTimeKey, currentTime.toString()); |
| 111 |
|
- |
renderTalks(data.data.items); // 渲染数据 |
| 112 |
|
- |
} |
| 113 |
|
- |
}) |
| 114 |
|
- |
.catch(error => { |
| 115 |
|
- |
console.error('Error fetching data:', error); |
| 116 |
|
- |
}); |
| 117 |
|
- |
} |
|
112 |
+ |
for (; index < items.length; index += 1) { |
|
113 |
+ |
sortColumns(columns); |
|
114 |
+ |
const current = items[index]; |
|
115 |
+ |
const column = columns.pop(); |
|
116 |
+ |
|
|
117 |
+ |
current.style.top = toPx(getBottom(column) + getMargin('Top', current)); |
|
118 |
+ |
current.style.left = toPx(getLeft(column)); |
|
119 |
+ |
columns.push(current); |
| 118 |
120 |
|
} |
| 119 |
|
- |
|
| 120 |
|
- |
// 渲染函数 |
| 121 |
|
- |
function renderTalks(list) { |
| 122 |
|
- |
// 确保 data 是一个数组 |
| 123 |
|
- |
if (Array.isArray(list)) { |
| 124 |
|
- |
let items = list.map(item => formatTalk(item, url)); |
| 125 |
|
- |
items.forEach(item => talkContainer.appendChild(generateTalkElement(item))); |
| 126 |
|
- |
waterfall('#talk'); |
| 127 |
|
- |
} else { |
| 128 |
|
- |
console.error('Data is not an array:', list); |
|
121 |
+ |
|
|
122 |
+ |
sortColumns(columns); |
|
123 |
+ |
const tallestColumn = columns[0]; |
|
124 |
+ |
container.style.height = tallestColumn ? toPx(getBottom(tallestColumn) + getMargin('Bottom', tallestColumn)) : '0px'; |
|
125 |
+ |
|
|
126 |
+ |
const currentWidth = getWidth(container); |
|
127 |
+ |
shuoshuoState.resizeHandler = () => { |
|
128 |
+ |
const currentContainer = document.querySelector('#talk'); |
|
129 |
+ |
if (!currentContainer || !document.body.contains(currentContainer)) { |
|
130 |
+ |
cleanupShuoshuo(); |
|
131 |
+ |
return; |
|
132 |
+ |
} |
|
133 |
+ |
|
|
134 |
+ |
if (getWidth(currentContainer) !== currentWidth) { |
|
135 |
+ |
waterfall(currentContainer); |
| 129 |
136 |
|
} |
|
137 |
+ |
}; |
|
138 |
+ |
|
|
139 |
+ |
window.addEventListener('resize', shuoshuoState.resizeHandler); |
|
140 |
+ |
}; |
|
141 |
+ |
|
|
142 |
+ |
const parseMaybeJson = (value) => { |
|
143 |
+ |
return value && typeof value === 'object' ? value : null; |
|
144 |
+ |
}; |
|
145 |
+ |
|
|
146 |
+ |
const getEchoExtension = (item) => { |
|
147 |
+ |
return parseMaybeJson(item?.extension); |
|
148 |
+ |
}; |
|
149 |
+ |
|
|
150 |
+ |
const getEchoExtensionType = (item) => { |
|
151 |
+ |
return getEchoExtension(item)?.type || ''; |
|
152 |
+ |
}; |
|
153 |
+ |
|
|
154 |
+ |
const getEchoExtensionPayload = (item) => { |
|
155 |
+ |
const extension = getEchoExtension(item); |
|
156 |
+ |
return extension?.payload || null; |
|
157 |
+ |
}; |
|
158 |
+ |
|
|
159 |
+ |
const getEchoImages = (item) => { |
|
160 |
+ |
if (!Array.isArray(item?.echo_files)) return []; |
|
161 |
+ |
|
|
162 |
+ |
return item.echo_files |
|
163 |
+ |
.map(entry => entry?.file || entry) |
|
164 |
+ |
.filter(file => { |
|
165 |
+ |
const category = String(file?.category || '').toLowerCase(); |
|
166 |
+ |
const contentType = String(file?.content_type || '').toLowerCase(); |
|
167 |
+ |
return category === 'image' || contentType.startsWith('image/'); |
|
168 |
+ |
}) |
|
169 |
+ |
.map(file => { |
|
170 |
+ |
let url = file?.url; |
|
171 |
+ |
if (!url) return null; |
|
172 |
+ |
|
|
173 |
+ |
// 如果是相对路径,补全为完整 URL |
|
174 |
+ |
if (url.startsWith('/')) { |
|
175 |
+ |
url = 'https://mm.liushen.fun' + url; |
|
176 |
+ |
} else if (!url.startsWith('http://') && !url.startsWith('https://')) { |
|
177 |
+ |
url = 'https://mm.liushen.fun/' + url; |
|
178 |
+ |
} |
|
179 |
+ |
|
|
180 |
+ |
return url; |
|
181 |
+ |
}) |
|
182 |
+ |
.filter(Boolean); |
|
183 |
+ |
}; |
|
184 |
+ |
|
|
185 |
+ |
const getEchoTags = (item) => { |
|
186 |
+ |
if (!Array.isArray(item?.tags) || !item.tags.length) return ['无标签']; |
|
187 |
+ |
return item.tags.map(tag => tag?.name || tag).filter(Boolean); |
|
188 |
+ |
}; |
|
189 |
+ |
|
|
190 |
+ |
const formatTime = (time) => { |
|
191 |
+ |
// 如果是 Unix 时间戳(秒),转换为毫秒 |
|
192 |
+ |
const timestamp = time < 10000000000 ? time * 1000 : time; |
|
193 |
+ |
const date = new Date(timestamp); |
|
194 |
+ |
const pad = value => String(value).padStart(2, '0'); |
|
195 |
+ |
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`; |
|
196 |
+ |
}; |
|
197 |
+ |
|
|
198 |
+ |
const renderTextContent = (content) => { |
|
199 |
+ |
return (content || '') |
|
200 |
+ |
.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" rel="nofollow noopener">@$1</a>') |
|
201 |
+ |
.replace(/- \[ \]/g, '[]') |
|
202 |
+ |
.replace(/- \[x\]/gi, '[x]') |
|
203 |
+ |
.replace(/\n/g, '<br>'); |
|
204 |
+ |
}; |
|
205 |
+ |
|
|
206 |
+ |
const buildImageHtml = (images) => { |
|
207 |
+ |
if (!images.length) return ''; |
|
208 |
+ |
|
|
209 |
+ |
const imageLinks = images.map(url => { |
|
210 |
+ |
const safeUrl = `${url}?fmt=webp&q=75`; |
|
211 |
+ |
return `<a href="${safeUrl}" data-fancybox="gallery" class="fancybox"><img src="${safeUrl}" loading="lazy"></a>`; |
|
212 |
+ |
}).join(''); |
|
213 |
+ |
|
|
214 |
+ |
return `<div class="zone_imgbox">${imageLinks}</div>`; |
|
215 |
+ |
}; |
|
216 |
+ |
|
|
217 |
+ |
const getGithubTitle = (repoUrl) => { |
|
218 |
+ |
if (!repoUrl) return ''; |
|
219 |
+ |
|
|
220 |
+ |
const match = repoUrl.match(/^https?:\/\/github\.com\/[^/]+\/([^/?#]+)/i); |
|
221 |
+ |
if (match) return match[1]; |
|
222 |
+ |
|
|
223 |
+ |
try { |
|
224 |
+ |
const parts = new URL(repoUrl).pathname.split('/').filter(Boolean); |
|
225 |
+ |
return parts.pop() || repoUrl; |
|
226 |
+ |
} catch (error) { |
|
227 |
+ |
return repoUrl; |
| 130 |
228 |
|
} |
| 131 |
229 |
|
}; |
| 132 |
|
- |
|
| 133 |
|
- |
|
| 134 |
|
- |
const formatTalk = (item, url) => { |
| 135 |
|
- |
let date = formatTime(new Date(item.created_at).toString()); |
| 136 |
|
- |
let content = item.content; |
| 137 |
|
- |
const baseUrl = url; |
| 138 |
|
- |
let imgs = Array.isArray(item.images) ? item.images.map(img => { |
| 139 |
|
- |
const imageUrl = img.image_url; |
| 140 |
|
- |
// 如果是相对地址(不以 http 或 https 开头),则拼接 baseUrl |
| 141 |
|
- |
return /^https?:\/\//.test(imageUrl) ? imageUrl : `${baseUrl}${imageUrl}`; |
| 142 |
|
- |
}) : []; |
| 143 |
|
- |
let text = content; |
| 144 |
|
- |
content = text.replace(/\[(.*?)\]\((.*?)\)/g, `<a href="$2" target="_blank" rel="nofollow noopener noreferrer">@$1</a>`) |
| 145 |
|
- |
.replace(/- \[ \]/g, '⚪') |
| 146 |
|
- |
.replace(/- \[x\]/g, '⚫'); |
| 147 |
|
- |
// 保留换行符,转换 \n 为 <br> |
| 148 |
|
- |
content = content.replace(/\n/g, '<br>'); |
| 149 |
|
- |
// 将content用一个类包裹,便于后续处理 |
| 150 |
|
- |
content = `<div class="talk_content_text">${content}</div>`; |
| 151 |
|
- |
if (imgs.length > 0) { |
| 152 |
|
- |
const imgDiv = document.createElement('div'); |
| 153 |
|
- |
imgDiv.className = 'zone_imgbox'; |
| 154 |
|
- |
imgs.forEach(e => { |
| 155 |
|
- |
const imgLink = document.createElement('a'); |
| 156 |
|
- |
imgLink.href = e; |
| 157 |
|
- |
imgLink.setAttribute('data-fancybox', 'gallery'); |
| 158 |
|
- |
imgLink.className = 'fancybox'; |
| 159 |
|
- |
imgLink.setAttribute('data-thumb', e); |
| 160 |
|
- |
const imgTag = document.createElement('img'); |
| 161 |
|
- |
imgTag.src = e; |
| 162 |
|
- |
imgLink.appendChild(imgTag); |
| 163 |
|
- |
imgDiv.appendChild(imgLink); |
| 164 |
|
- |
}); |
| 165 |
|
- |
content += imgDiv.outerHTML; |
|
230 |
+ |
|
|
231 |
+ |
const buildExternalHtml = (type, payload) => { |
|
232 |
+ |
if (!payload) return ''; |
|
233 |
+ |
|
|
234 |
+ |
let siteUrl = ''; |
|
235 |
+ |
let title = ''; |
|
236 |
+ |
let background = 'https://p.liiiu.cn/i/2024/07/27/66a4632bbf06e.webp'; |
|
237 |
+ |
|
|
238 |
+ |
if (type === 'WEBSITE') { |
|
239 |
+ |
siteUrl = payload.site || payload.url || ''; |
|
240 |
+ |
title = payload.title || siteUrl; |
| 166 |
241 |
|
} |
| 167 |
242 |
|
|
| 168 |
|
- |
// 外链分享功能 |
| 169 |
|
- |
if (item.extension_type ==="WEBSITE") { |
| 170 |
|
- |
let extension = item.extension ? JSON.parse(item.extension) : {}; |
| 171 |
|
- |
const externalUrl = extension.site; |
| 172 |
|
- |
const externalTitle = extension.title; |
| 173 |
|
- |
const externalFavicon = "https://p.liiiu.cn/i/2025/03/13/67d2fc82d329c.webp" // item.externalFavicon; |
|
243 |
+ |
if (type === 'GITHUBPROJ') { |
|
244 |
+ |
siteUrl = payload.repoUrl || payload.url || ''; |
|
245 |
+ |
title = payload.title || getGithubTitle(siteUrl); |
|
246 |
+ |
background = 'https://p.liiiu.cn/i/2024/07/27/66a461a3098aa.webp'; |
|
247 |
+ |
} |
|
248 |
+ |
|
|
249 |
+ |
if (!siteUrl) return ''; |
| 174 |
250 |
|
|
| 175 |
|
- |
const externalContainer = ` |
|
251 |
+ |
return ` |
| 176 |
252 |
|
<div class="shuoshuo-external-link"> |
| 177 |
|
- |
<a class="external-link" href="${externalUrl}" target="_blank" rel="nofollow noopener noreferrer"> |
| 178 |
|
- |
<div class="external-link-left" style="background-image: url(${externalFavicon})"></div> |
|
253 |
+ |
<a class="external-link" href="${siteUrl}" target="_blank" rel="nofollow noopener"> |
|
254 |
+ |
<div class="external-link-left" style="background-image:url(${background})"></div> |
| 179 |
255 |
|
<div class="external-link-right"> |
| 180 |
|
- |
<div class="external-link-title">${externalTitle}</div> |
|
256 |
+ |
<div class="external-link-title">${title}</div> |
| 181 |
257 |
|
<div>点击跳转<i class="fa-solid fa-angle-right"></i></div> |
| 182 |
258 |
|
</div> |
| 183 |
259 |
|
</a> |
| 184 |
|
- |
</div>`; |
|
260 |
+ |
</div> |
|
261 |
+ |
`; |
|
262 |
+ |
}; |
|
263 |
+ |
|
|
264 |
+ |
const getMusicInfo = (payload) => { |
|
265 |
+ |
const link = payload?.url; |
|
266 |
+ |
if (!link) return null; |
|
267 |
+ |
|
|
268 |
+ |
let server = ''; |
|
269 |
+ |
if (link.includes('music.163.com')) server = 'netease'; |
|
270 |
+ |
if (link.includes('y.qq.com')) server = 'tencent'; |
|
271 |
+ |
|
|
272 |
+ |
const idMatch = link.match(/id=(\d+)/); |
|
273 |
+ |
if (!server || !idMatch) return null; |
|
274 |
+ |
|
|
275 |
+ |
return { server, id: idMatch[1] }; |
|
276 |
+ |
}; |
| 185 |
277 |
|
|
| 186 |
|
- |
content += externalContainer; |
|
278 |
+ |
const buildMusicHtml = (payload) => { |
|
279 |
+ |
const music = getMusicInfo(payload); |
|
280 |
+ |
if (!music) return ''; |
|
281 |
+ |
|
|
282 |
+ |
return `<meting-js server="${music.server}" type="song" id="${music.id}" api="https://met.liiiu.cn/meting/api?server=:server&type=:type&id=:id&auth=:auth&r=:r"></meting-js>`; |
|
283 |
+ |
}; |
|
284 |
+ |
|
|
285 |
+ |
const getYoutubeVideoId = (value) => { |
|
286 |
+ |
if (!value) return ''; |
|
287 |
+ |
if (/^[a-zA-Z0-9_-]{11}$/.test(value)) return value; |
|
288 |
+ |
|
|
289 |
+ |
try { |
|
290 |
+ |
const url = new URL(value); |
|
291 |
+ |
if (url.hostname.includes('youtu.be')) return url.pathname.replace('/', ''); |
|
292 |
+ |
if (url.hostname.includes('youtube.com')) { |
|
293 |
+ |
return url.searchParams.get('v') || url.pathname.split('/').filter(Boolean).pop() || ''; |
|
294 |
+ |
} |
|
295 |
+ |
} catch (error) { |
|
296 |
+ |
return ''; |
| 187 |
297 |
|
} |
| 188 |
298 |
|
|
| 189 |
|
- |
// const ext = JSON.parse(item.ext || '{}'); |
| 190 |
|
- |
|
| 191 |
|
- |
else if (item.extension_type === "MUSIC") { |
| 192 |
|
- |
const musicUrl = item.extension; |
| 193 |
|
- |
if (musicUrl && musicUrl.includes('music.163.com')) { |
| 194 |
|
- |
const match = musicUrl.match(/id=(\d+)/); |
| 195 |
|
- |
const musicServer = "netease"; |
| 196 |
|
- |
const musicType = "song"; |
| 197 |
|
- |
const musicId = match ? match[1] : ''; |
| 198 |
|
- |
|
| 199 |
|
- |
content += ` |
| 200 |
|
- |
<meting-js server="${musicServer}" type="${musicType}" id="${musicId}" api="https://met.liiiu.cn/meting/api?server=:server&type=:type&id=:id&r=:r"></meting-js> |
| 201 |
|
- |
`; |
| 202 |
|
- |
} else { |
| 203 |
|
- |
// 懒得适配,自己搞 |
|
299 |
+ |
return ''; |
|
300 |
+ |
}; |
|
301 |
+ |
|
|
302 |
+ |
const buildVideoHtml = (payload) => { |
|
303 |
+ |
const rawValue = payload?.videoId || payload?.url || ''; |
|
304 |
+ |
|
|
305 |
+ |
if (!rawValue) return ''; |
|
306 |
+ |
|
|
307 |
+ |
let embedUrl = ''; |
|
308 |
+ |
|
|
309 |
+ |
if (/^BV[0-9A-Za-z]+$/i.test(rawValue)) { |
|
310 |
+ |
embedUrl = `https://www.bilibili.com/blackboard/html5mobileplayer.html?bvid=${rawValue}&as_wide=1&high_quality=1&danmaku=0`; |
|
311 |
+ |
} else { |
|
312 |
+ |
const youtubeId = getYoutubeVideoId(rawValue); |
|
313 |
+ |
if (youtubeId) { |
|
314 |
+ |
embedUrl = `https://www.youtube.com/embed/${youtubeId}`; |
| 204 |
315 |
|
} |
| 205 |
316 |
|
} |
| 206 |
317 |
|
|
| 207 |
|
- |
// if (ext.doubanMovie && ext.doubanMovie.id) { |
| 208 |
|
- |
// const doubanMovie = ext.doubanMovie; |
| 209 |
|
- |
// const doubanMovieUrl = doubanMovie.url; |
| 210 |
|
- |
// const doubanTitle = doubanMovie.title; |
| 211 |
|
- |
// // const doubanDesc = doubanMovie.desc || '暂无描述'; |
| 212 |
|
- |
// const doubanImage = doubanMovie.image; |
| 213 |
|
- |
// const doubanDirector = doubanMovie.director || '未知导演'; |
| 214 |
|
- |
// const doubanRating = doubanMovie.rating || '暂无评分'; |
| 215 |
|
- |
// // const doubanReleaseDate = doubanMovie.releaseDate || '未知上映时间'; |
| 216 |
|
- |
// // const doubanActors = doubanMovie.actors || '未知演员'; |
| 217 |
|
- |
// const doubanRuntime = doubanMovie.runtime || '未知时长'; |
| 218 |
|
- |
|
| 219 |
|
- |
// content += ` |
| 220 |
|
- |
// <a class="douban-card" href="${doubanMovieUrl}" target="_blank" rel="nofollow noopener noreferrer"> |
| 221 |
|
- |
// <div class="douban-card-bgimg" style="background-image: url('${doubanImage}');"></div> |
| 222 |
|
- |
// <div class="douban-card-left"> |
| 223 |
|
- |
// <div class="douban-card-img" style="background-image: url('${doubanImage}');"></div> |
| 224 |
|
- |
// </div> |
| 225 |
|
- |
// <div class="douban-card-right"> |
| 226 |
|
- |
// <div class="douban-card-item"><span>电影名: </span><strong>${doubanTitle}</strong></div> |
| 227 |
|
- |
// <div class="douban-card-item"><span>导演: </span><span>${doubanDirector}</span></div> |
| 228 |
|
- |
// <div class="douban-card-item"><span>评分: </span><span>${doubanRating}</span></div> |
| 229 |
|
- |
// <div class="douban-card-item"><span>时长: </span><span>${doubanRuntime}</span></div> |
| 230 |
|
- |
// </div> |
| 231 |
|
- |
// </a> |
| 232 |
|
- |
// `; |
| 233 |
|
- |
// } |
| 234 |
|
- |
|
| 235 |
|
- |
// if (ext.doubanBook && ext.doubanBook.id) { |
| 236 |
|
- |
// const doubanBook = ext.doubanBook; |
| 237 |
|
- |
// const bookUrl = doubanBook.url; |
| 238 |
|
- |
// const bookTitle = doubanBook.title; |
| 239 |
|
- |
// // const bookDesc = doubanBook.desc; |
| 240 |
|
- |
// const bookImage = doubanBook.image; |
| 241 |
|
- |
// const bookAuthor = doubanBook.author; |
| 242 |
|
- |
// const bookRating = doubanBook.rating; |
| 243 |
|
- |
// const bookPubDate = doubanBook.pubDate; |
| 244 |
|
- |
|
| 245 |
|
- |
// const bookTemplate = ` |
| 246 |
|
- |
// <a class="douban-card" href="${bookUrl}" target="_blank" rel="nofollow noopener noreferrer"> |
| 247 |
|
- |
// <div class="douban-card-bgimg" style="background-image: url('${bookImage}');"></div> |
| 248 |
|
- |
// <div class="douban-card-left"> |
| 249 |
|
- |
// <div class="douban-card-img" style="background-image: url('${bookImage}');"></div> |
| 250 |
|
- |
// </div> |
| 251 |
|
- |
// <div class="douban-card-right"> |
| 252 |
|
- |
// <div class="douban-card-item"> |
| 253 |
|
- |
// <span>书名: </span><strong>${bookTitle}</strong> |
| 254 |
|
- |
// </div> |
| 255 |
|
- |
// <div class="douban-card-item"> |
| 256 |
|
- |
// <span>作者: </span><span>${bookAuthor}</span> |
| 257 |
|
- |
// </div> |
| 258 |
|
- |
// <div class="douban-card-item"> |
| 259 |
|
- |
// <span>出版年份: </span><span>${bookPubDate}</span> |
| 260 |
|
- |
// </div> |
| 261 |
|
- |
// <div class="douban-card-item"> |
| 262 |
|
- |
// <span>评分: </span><span>${bookRating}</span> |
| 263 |
|
- |
// </div> |
| 264 |
|
- |
// </div> |
| 265 |
|
- |
// </a> |
| 266 |
|
- |
// `; |
| 267 |
|
- |
|
| 268 |
|
- |
// content += bookTemplate; |
| 269 |
|
- |
// } |
| 270 |
|
- |
|
| 271 |
|
- |
else if (item.extension_type === "VIDEO") { |
| 272 |
|
- |
// const videoType = ext.video.type; |
| 273 |
|
- |
const BVId = item.extension; |
| 274 |
|
- |
// Bilibili 视频模板 |
| 275 |
|
- |
// const NewVideoUrl = videoUrl.replace("player.bilibili.com/player.html", "www.bilibili.com/blackboard/html5mobileplayer.html") |
| 276 |
|
- |
const biliTemplate = ` |
| 277 |
|
- |
<div style="position: relative; padding: 30% 45%; margin-top: 10px;"> |
| 278 |
|
- |
<iframe |
| 279 |
|
- |
style="position: absolute; width: 100%; height: 100%; left: 0; top: 0; border-radius: 12px;" |
| 280 |
|
- |
src="https://www.bilibili.com/blackboard/html5mobileplayer.html?bvid=${BVId}&as_wide=1&high_quality=1&danmaku=0" |
| 281 |
|
- |
scrolling="no" |
| 282 |
|
- |
border="0" |
| 283 |
|
- |
frameborder="no" |
| 284 |
|
- |
framespacing="0" |
| 285 |
|
- |
allowfullscreen="true" |
| 286 |
|
- |
loading="lazy" |
| 287 |
|
- |
> |
| 288 |
|
- |
</iframe> |
| 289 |
|
- |
</div> |
| 290 |
|
- |
`; |
| 291 |
|
- |
// 将模板插入到 DOM 中 |
| 292 |
|
- |
content += biliTemplate; |
| 293 |
|
- |
|
| 294 |
|
- |
|
| 295 |
|
- |
// else if (videoType === 'youtube') { |
| 296 |
|
- |
// // YouTube 视频模板 |
| 297 |
|
- |
// // 从形如https://youtu.be/2V6lvCUPT8I?si=DVhUas6l6qlAr6Ru的链接中提取视频 ID2V6lvCUPT8I |
| 298 |
|
- |
// const youtubeTemplate = ` |
| 299 |
|
- |
// <div style="position: relative; padding: 30% 45%; margin-top: 10px;"> |
| 300 |
|
- |
// <iframe width="100%" |
| 301 |
|
- |
// style="position: absolute; width: 100%; height: 100%; left: 0; top: 0; border-radius: 12px;" |
| 302 |
|
- |
// src="${videoUrl}" |
| 303 |
|
- |
// title="YouTube video player" |
| 304 |
|
- |
// frameborder="0" |
| 305 |
|
- |
// allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" |
| 306 |
|
- |
// referrerpolicy="strict-origin-when-cross-origin" |
| 307 |
|
- |
// allowfullscreen> |
| 308 |
|
- |
// </iframe> |
| 309 |
|
- |
// </div> |
| 310 |
|
- |
// `; |
| 311 |
|
- |
// // 将模板插入到 DOM 中 |
| 312 |
|
- |
// content += youtubeTemplate; |
| 313 |
|
- |
// } |
|
318 |
+ |
if (!embedUrl) return ''; |
|
319 |
+ |
|
|
320 |
+ |
return ` |
|
321 |
+ |
<div style="position: relative; padding: 30% 45%; margin-top: 10px;"> |
|
322 |
+ |
<iframe |
|
323 |
+ |
style="position:absolute;width:100%;height:100%;left:0;top:0;border-radius:12px;" |
|
324 |
+ |
src="${embedUrl}" |
|
325 |
+ |
frameborder="0" |
|
326 |
+ |
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" |
|
327 |
+ |
allowfullscreen |
|
328 |
+ |
loading="lazy"> |
|
329 |
+ |
</iframe> |
|
330 |
+ |
</div> |
|
331 |
+ |
`; |
|
332 |
+ |
}; |
|
333 |
+ |
|
|
334 |
+ |
const normalizeTalk = (item) => { |
|
335 |
+ |
const extensionType = getEchoExtensionType(item); |
|
336 |
+ |
const extensionPayload = getEchoExtensionPayload(item); |
|
337 |
+ |
const textContent = item?.content || ''; |
|
338 |
+ |
const images = getEchoImages(item); |
|
339 |
+ |
|
|
340 |
+ |
let content = `<div class="talk_content_text">${renderTextContent(textContent)}</div>`; |
|
341 |
+ |
content += buildImageHtml(images); |
|
342 |
+ |
|
|
343 |
+ |
if (extensionType === 'WEBSITE' || extensionType === 'GITHUBPROJ') { |
|
344 |
+ |
content += buildExternalHtml(extensionType, extensionPayload); |
|
345 |
+ |
} |
|
346 |
+ |
|
|
347 |
+ |
if (extensionType === 'MUSIC') { |
|
348 |
+ |
content += buildMusicHtml(extensionPayload); |
|
349 |
+ |
} |
|
350 |
+ |
|
|
351 |
+ |
if (extensionType === 'VIDEO') { |
|
352 |
+ |
content += buildVideoHtml(extensionPayload); |
| 314 |
353 |
|
} |
| 315 |
354 |
|
|
| 316 |
355 |
|
return { |
| 317 |
|
- |
content: content, |
| 318 |
|
- |
user: item.username || '匿名', |
| 319 |
|
- |
avatar: 'https://p.liiiu.cn/i/2024/03/29/66061417537af.png', |
| 320 |
|
- |
date: date, |
| 321 |
|
- |
location: item.location || '陕西西安', |
| 322 |
|
- |
tags: item.tags ? item.tags.split(',').filter(tag => tag.trim() !== '') : ['日常点滴'], |
| 323 |
|
- |
text: content.replace(/\[(.*?)\]\((.*?)\)/g, '[链接]' + `${imgs.length ? '[图片]' : ''}`) |
|
356 |
+ |
content, |
|
357 |
+ |
user: item?.username || '匿名', |
|
358 |
+ |
avatar: TALK_AVATAR, |
|
359 |
+ |
date: formatTime(item?.created_at), |
|
360 |
+ |
tags: getEchoTags(item), |
|
361 |
+ |
quoteText: textContent |
| 324 |
362 |
|
}; |
| 325 |
363 |
|
}; |
| 326 |
364 |
|
|
|
|
@@ -338,13 +376,18 @@ function renderTalks() { |
| 338 |
376 |
|
const info = document.createElement('div'); |
| 339 |
377 |
|
info.className = 'info'; |
| 340 |
378 |
|
|
| 341 |
|
- |
const talkNick = document.createElement('span'); |
| 342 |
|
- |
talkNick.className = 'talk_nick'; |
| 343 |
|
- |
talkNick.innerHTML = `${item.user} ${generateIconSVG()}`; |
|
379 |
+ |
const nick = document.createElement('span'); |
|
380 |
+ |
nick.className = 'talk_nick'; |
|
381 |
+ |
nick.innerHTML = `${item.user} ${generateIconSVG()}`; |
| 344 |
382 |
|
|
| 345 |
|
- |
const talkDate = document.createElement('span'); |
| 346 |
|
- |
talkDate.className = 'talk_date'; |
| 347 |
|
- |
talkDate.textContent = item.date; |
|
383 |
+ |
const date = document.createElement('span'); |
|
384 |
+ |
date.className = 'talk_date'; |
|
385 |
+ |
date.textContent = item.date; |
|
386 |
+ |
|
|
387 |
+ |
info.appendChild(nick); |
|
388 |
+ |
info.appendChild(date); |
|
389 |
+ |
talkMeta.appendChild(avatar); |
|
390 |
+ |
talkMeta.appendChild(info); |
| 348 |
391 |
|
|
| 349 |
392 |
|
const talkContent = document.createElement('div'); |
| 350 |
393 |
|
talkContent.className = 'talk_content'; |
|
|
@@ -353,78 +396,111 @@ function renderTalks() { |
| 353 |
396 |
|
const talkBottom = document.createElement('div'); |
| 354 |
397 |
|
talkBottom.className = 'talk_bottom'; |
| 355 |
398 |
|
|
| 356 |
|
- |
const TagContainer = document.createElement('div'); |
| 357 |
|
- |
|
| 358 |
|
- |
const talkTag = document.createElement('span'); |
| 359 |
|
- |
talkTag.className = 'talk_tag'; |
| 360 |
|
- |
talkTag.textContent = `🏷️${item.tags}`; |
| 361 |
|
- |
|
| 362 |
|
- |
const locationTag = document.createElement('span'); |
| 363 |
|
- |
locationTag.className = 'location_tag'; |
| 364 |
|
- |
locationTag.textContent = `🌍${item.location}`; |
| 365 |
|
- |
|
| 366 |
|
- |
TagContainer.appendChild(talkTag); |
| 367 |
|
- |
TagContainer.appendChild(locationTag); |
|
399 |
+ |
const tags = document.createElement('div'); |
|
400 |
+ |
const tag = document.createElement('span'); |
|
401 |
+ |
tag.className = 'talk_tag'; |
|
402 |
+ |
tag.textContent = `# ${item.tags.join(' / ')}`; |
|
403 |
+ |
tags.appendChild(tag); |
| 368 |
404 |
|
|
| 369 |
405 |
|
const commentLink = document.createElement('a'); |
| 370 |
406 |
|
commentLink.href = 'javascript:;'; |
| 371 |
|
- |
commentLink.onclick = () => goComment(item.text); |
| 372 |
|
- |
const commentIcon = document.createElement('span'); |
| 373 |
|
- |
commentIcon.className = 'icon'; |
| 374 |
|
- |
const commentIconInner = document.createElement('i'); |
| 375 |
|
- |
commentIconInner.className = 'fa-solid fa-message fa-fw'; |
| 376 |
|
- |
commentIcon.appendChild(commentIconInner); |
| 377 |
|
- |
commentLink.appendChild(commentIcon); |
|
407 |
+ |
commentLink.addEventListener('click', () => goComment(item.quoteText)); |
|
408 |
+ |
|
|
409 |
+ |
const icon = document.createElement('span'); |
|
410 |
+ |
icon.className = 'icon'; |
|
411 |
+ |
icon.innerHTML = '<i class="fa-solid fa-message fa-fw"></i>'; |
|
412 |
+ |
commentLink.appendChild(icon); |
|
413 |
+ |
|
|
414 |
+ |
talkBottom.appendChild(tags); |
|
415 |
+ |
talkBottom.appendChild(commentLink); |
| 378 |
416 |
|
|
| 379 |
|
- |
talkMeta.appendChild(avatar); |
| 380 |
|
- |
info.appendChild(talkNick); |
| 381 |
|
- |
info.appendChild(talkDate); |
| 382 |
|
- |
talkMeta.appendChild(info); |
| 383 |
417 |
|
talkItem.appendChild(talkMeta); |
| 384 |
418 |
|
talkItem.appendChild(talkContent); |
| 385 |
|
- |
talkBottom.appendChild(TagContainer); |
| 386 |
|
- |
talkBottom.appendChild(commentLink); |
| 387 |
419 |
|
talkItem.appendChild(talkBottom); |
| 388 |
420 |
|
|
| 389 |
421 |
|
return talkItem; |
| 390 |
422 |
|
}; |
| 391 |
423 |
|
|
| 392 |
|
- |
const goComment = (e) => { |
| 393 |
|
- |
const match = e.match(/<div class="talk_content_text">([\s\S]*?)<\/div>/); |
| 394 |
|
- |
const textContent = match ? match[1] : ""; |
| 395 |
|
- |
const n = document.querySelector(".atk-textarea"); |
| 396 |
|
- |
n.value = `> ${textContent}\n\n`; |
| 397 |
|
- |
n.focus(); |
| 398 |
|
- |
btf.snackbarShow("已为您引用该说说,不删除空格效果更佳"); |
| 399 |
|
- |
// const n = document.querySelector(".atk-textarea"); |
| 400 |
|
- |
// n.value = `> ${e}\n\n`; |
| 401 |
|
- |
// n.focus(); |
| 402 |
|
- |
// btf.snackbarShow("已为您引用该说说,不删除空格效果更佳"); |
|
424 |
+ |
const goComment = (text) => { |
|
425 |
+ |
const textarea = document.querySelector('.atk-textarea'); |
|
426 |
+ |
if (!textarea) return; |
|
427 |
+ |
|
|
428 |
+ |
textarea.value = `> ${text || ''}\n\n`; |
|
429 |
+ |
textarea.focus(); |
|
430 |
+ |
|
|
431 |
+ |
if (window.btf?.snackbarShow) { |
|
432 |
+ |
btf.snackbarShow('已为您引用该说说,删除空格效果更佳'); |
|
433 |
+ |
} |
| 403 |
434 |
|
}; |
| 404 |
435 |
|
|
| 405 |
|
- |
const formatTime = (time) => { |
| 406 |
|
- |
const d = new Date(time); |
| 407 |
|
- |
const ls = [ |
| 408 |
|
- |
d.getFullYear(), |
| 409 |
|
- |
d.getMonth() + 1, |
| 410 |
|
- |
d.getDate(), |
| 411 |
|
- |
d.getHours(), |
| 412 |
|
- |
d.getMinutes(), |
| 413 |
|
- |
d.getSeconds(), |
| 414 |
|
- |
]; |
| 415 |
|
- |
const r = ls.map((a) => (a.toString().length === 1 ? '0' + a : a)); |
| 416 |
|
- |
return `${r[0]}-${r[1]}-${r[2]} ${r[3]}:${r[4]}`; |
|
436 |
+ |
const afterRender = () => { |
|
437 |
+ |
waterfall('#talk'); |
|
438 |
+ |
|
|
439 |
+ |
if (window.btf?.loadLightbox) { |
|
440 |
+ |
btf.loadLightbox(document.querySelectorAll('#talk img:not(.no-lightbox)')); |
|
441 |
+ |
} |
|
442 |
+ |
|
|
443 |
+ |
if (window.lazyLoadInstance?.update) { |
|
444 |
+ |
lazyLoadInstance.update(); |
|
445 |
+ |
} |
| 417 |
446 |
|
}; |
| 418 |
447 |
|
|
| 419 |
|
- |
fetchAndRenderTalks(); |
|
448 |
+ |
const renderTalksList = (list) => { |
|
449 |
+ |
list.map(normalizeTalk).forEach(item => talkContainer.appendChild(generateTalkElement(item))); |
|
450 |
+ |
afterRender(); |
|
451 |
+ |
|
|
452 |
+ |
const media = talkContainer.querySelectorAll('img, iframe, meting-js'); |
|
453 |
+ |
media.forEach(element => { |
|
454 |
+ |
element.addEventListener('load', afterRender, { once: true }); |
|
455 |
+ |
}); |
|
456 |
+ |
|
|
457 |
+ |
shuoshuoState.afterRenderTimer = window.setTimeout(afterRender, 300); |
|
458 |
+ |
}; |
|
459 |
+ |
|
|
460 |
+ |
const fetchTalks = () => { |
|
461 |
+ |
const cachedData = localStorage.getItem(TALK_CACHE_KEY); |
|
462 |
+ |
const cachedTime = Number(localStorage.getItem(TALK_CACHE_TIME_KEY)); |
|
463 |
+ |
const now = Date.now(); |
|
464 |
+ |
|
|
465 |
+ |
if (cachedData && cachedTime && now - cachedTime < TALK_CACHE_DURATION) { |
|
466 |
+ |
renderTalksList(JSON.parse(cachedData)); |
|
467 |
+ |
return; |
|
468 |
+ |
} |
|
469 |
+ |
|
|
470 |
+ |
fetch(TALK_API_URL, { |
|
471 |
+ |
method: 'POST', |
|
472 |
+ |
headers: { 'Content-Type': 'application/json' }, |
|
473 |
+ |
body: JSON.stringify({ page: 1, pageSize: 30, search: '' }) |
|
474 |
+ |
}) |
|
475 |
+ |
.then(response => response.json()) |
|
476 |
+ |
.then(data => { |
|
477 |
+ |
if (data?.code !== 1 || !Array.isArray(data?.data?.items)) { |
|
478 |
+ |
console.warn('Unexpected API response format:', data); |
|
479 |
+ |
renderTalksList([]); |
|
480 |
+ |
return; |
|
481 |
+ |
} |
|
482 |
+ |
|
|
483 |
+ |
localStorage.setItem(TALK_CACHE_KEY, JSON.stringify(data.data.items)); |
|
484 |
+ |
localStorage.setItem(TALK_CACHE_TIME_KEY, now.toString()); |
|
485 |
+ |
renderTalksList(data.data.items); |
|
486 |
+ |
}) |
|
487 |
+ |
.catch(error => console.error('Error fetching data:', error)); |
|
488 |
+ |
}; |
|
489 |
+ |
|
|
490 |
+ |
fetchTalks(); |
| 420 |
491 |
|
} |
| 421 |
492 |
|
|
| 422 |
|
- |
renderTalks(); |
|
493 |
+ |
function initShuoshuoPage() { |
|
494 |
+ |
renderTalks(); |
|
495 |
+ |
} |
|
496 |
+ |
|
|
497 |
+ |
window.initShuoshuoPage = initShuoshuoPage; |
|
498 |
+ |
|
|
499 |
+ |
if (!shuoshuoState.listenersBound) { |
|
500 |
+ |
document.addEventListener('pjax:send', cleanupShuoshuo); |
|
501 |
+ |
document.addEventListener('pjax:complete', initShuoshuoPage); |
|
502 |
+ |
shuoshuoState.listenersBound = true; |
|
503 |
+ |
} |
| 423 |
504 |
|
|
| 424 |
|
- |
// function whenDOMReady() { |
| 425 |
|
- |
// const talkContainer = document.querySelector('#talk'); |
| 426 |
|
- |
// talkContainer.innerHTML = ''; |
| 427 |
|
- |
// fetchAndRenderTalks(); |
| 428 |
|
- |
// } |
| 429 |
|
- |
// whenDOMReady(); |
| 430 |
|
- |
// document.addEventListener("pjax:complete", whenDOMReady); |
|
505 |
+ |
initShuoshuoPage(); |
|
506 |
+ |
})(); |