2024-08-03 13:35:19 +08:00
|
|
|
{{ define "main" }}
|
|
|
|
|
<div id="pjax-container">
|
2026-01-22 12:54:52 +08:00
|
|
|
<div class="post-content" itemprop="articleBody">
|
|
|
|
|
{{ .Content }}
|
|
|
|
|
<div id="friend-circle-container" class="friend-circle-list">
|
|
|
|
|
<div class="loading-container">
|
|
|
|
|
<div class="loading-spinner"></div>
|
|
|
|
|
<p>正在探索朋友们的新动态...</p>
|
|
|
|
|
</div>
|
2024-08-10 21:38:04 +08:00
|
|
|
</div>
|
2026-01-22 12:54:52 +08:00
|
|
|
</div>
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
<style>
|
|
|
|
|
/* Container Layout */
|
|
|
|
|
.friend-circle-list {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: repeat(2, 1fr);
|
2026-05-19 19:30:34 +08:00
|
|
|
gap: 12px;
|
2026-01-22 12:54:52 +08:00
|
|
|
margin-top: 20px;
|
2026-05-19 19:30:34 +08:00
|
|
|
padding: 0;
|
2026-01-22 12:54:52 +08:00
|
|
|
width: 100%;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
/* Mobile Layout */
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.friend-circle-list {
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-05-19 19:30:34 +08:00
|
|
|
/* Card Style */
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
position: relative;
|
|
|
|
|
height: 160px;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
background: rgba(255, 255, 255, 0.4);
|
2026-01-31 21:27:06 +08:00
|
|
|
backdrop-filter: blur(12px) saturate(120%);
|
|
|
|
|
-webkit-backdrop-filter: blur(12px) saturate(120%);
|
2026-01-22 12:54:52 +08:00
|
|
|
border: 1px solid #111827;
|
|
|
|
|
box-shadow: none;
|
2026-05-19 19:30:34 +08:00
|
|
|
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 0.3s ease, background-color 0.3s ease, border-color 0.3s ease;
|
2026-01-22 12:54:52 +08:00
|
|
|
will-change: transform;
|
|
|
|
|
text-decoration: none;
|
2026-05-19 19:30:34 +08:00
|
|
|
transform: translateZ(0);
|
|
|
|
|
overflow: hidden;
|
2026-01-22 12:54:52 +08:00
|
|
|
}
|
|
|
|
|
|
2026-01-31 21:27:06 +08:00
|
|
|
@media (max-width: 767px) {
|
2026-05-19 19:30:34 +08:00
|
|
|
.fc-item {
|
|
|
|
|
backdrop-filter: none !important;
|
|
|
|
|
-webkit-backdrop-filter: none !important;
|
|
|
|
|
background: rgba(255, 255, 255, 0.8);
|
|
|
|
|
}
|
|
|
|
|
[data-theme="dark"] .fc-item {
|
|
|
|
|
background: rgba(30, 30, 30, 0.8);
|
|
|
|
|
}
|
2026-01-31 21:27:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.friend-circle-list a {
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:root {
|
|
|
|
|
--fc-text: #333;
|
|
|
|
|
--fc-meta-bg: rgba(0, 0, 0, 0.05);
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
[data-theme="dark"] {
|
|
|
|
|
--fc-text: #fff;
|
|
|
|
|
--fc-meta-bg: rgba(255, 255, 255, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fc-item:hover {
|
|
|
|
|
transform: translateY(-6px) scale(1.015);
|
2026-05-19 19:30:34 +08:00
|
|
|
box-shadow: 0 12px 28px rgba(0, 0, 0, 0.1), 0 2px 8px rgba(0, 0, 0, 0.06);
|
2026-01-22 12:54:52 +08:00
|
|
|
background: rgba(255, 255, 255, 0.7);
|
|
|
|
|
border-color: #1a1a1a;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-item:active {
|
|
|
|
|
transform: translateY(-3px) scale(1.005);
|
|
|
|
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
|
|
|
|
|
transition: all 0.15s ease;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-title {
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: var(--fc-text);
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
display: -webkit-box;
|
|
|
|
|
-webkit-line-clamp: 2;
|
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
overflow: hidden;
|
2026-05-19 19:30:34 +08:00
|
|
|
margin-right: 60px;
|
2026-01-22 12:54:52 +08:00
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-item:hover .fc-title {
|
|
|
|
|
color: #000000;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-bg-image {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: -20px;
|
|
|
|
|
bottom: -20px;
|
|
|
|
|
width: 140px;
|
|
|
|
|
height: 140px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
opacity: 0.15;
|
|
|
|
|
transition: all 0.5s ease;
|
|
|
|
|
z-index: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
filter: grayscale(20%);
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-item:hover .fc-bg-image {
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
transform: scale(1.1) rotate(deg);
|
|
|
|
|
filter: grayscale(0%);
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-bottom {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-author-pill {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
background: var(--fc-meta-bg);
|
|
|
|
|
padding: 4px 10px 4px 4px;
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
transition: background 0.3s;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-item:hover .fc-author-pill {
|
|
|
|
|
background: rgba(52, 152, 219, 0.1);
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-avatar {
|
|
|
|
|
width: 28px;
|
|
|
|
|
height: 28px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
object-fit: cover;
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-author-name {
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--fc-text);
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-date {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: var(--fc-text);
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-date i {
|
|
|
|
|
margin-right: 4px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-icon-calendar::before {
|
|
|
|
|
content: "\f133";
|
|
|
|
|
font-family: "Font Awesome 5 Free", "FontAwesome";
|
|
|
|
|
font-weight: 900;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.loading-container {
|
|
|
|
|
grid-column: 1 / -1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 60px;
|
|
|
|
|
color: #888;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.loading-spinner {
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
border: 3px solid rgba(127, 127, 127, 0.2);
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
border-top-color: #3498db;
|
|
|
|
|
animation: spin 1s ease-in-out infinite;
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
[data-theme="dark"] .fc-item {
|
2026-05-19 19:30:34 +08:00
|
|
|
background: rgba(35, 35, 35, 0.65);
|
2026-01-22 12:54:52 +08:00
|
|
|
box-shadow: none;
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
[data-theme="dark"] .fc-author-pill {
|
|
|
|
|
background: #1a1a1a;
|
|
|
|
|
border: 1px solid #333;
|
|
|
|
|
}
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
[data-theme="dark"] .fc-bg-image {
|
|
|
|
|
opacity: 0.2;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 17:33:31 +08:00
|
|
|
[data-theme="dark"] .fc-item:hover {
|
|
|
|
|
background: rgba(60, 60, 60, 0.85);
|
|
|
|
|
border-color: rgba(255, 255, 255, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .fc-item:hover .fc-title {
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
@keyframes spin {
|
2026-05-19 19:30:34 +08:00
|
|
|
to { transform: rotate(360deg); }
|
2026-01-22 12:54:52 +08:00
|
|
|
}
|
2026-05-19 19:30:34 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
.fc-load-more-btn {
|
|
|
|
|
grid-column: 1 / -1;
|
2026-01-22 19:41:47 +08:00
|
|
|
padding: 5px 30px;
|
2026-01-22 12:54:52 +08:00
|
|
|
margin: 30px auto 10px;
|
2026-05-19 19:30:34 +08:00
|
|
|
background: #000;
|
2026-01-22 12:54:52 +08:00
|
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
|
|
border-radius: 30px;
|
|
|
|
|
color: #fff;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
backdrop-filter: blur(10px);
|
|
|
|
|
-webkit-backdrop-filter: blur(10px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fc-load-more-btn:hover {
|
|
|
|
|
background: #333;
|
|
|
|
|
color: #fff;
|
|
|
|
|
transform: translateY(-2px);
|
|
|
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .fc-load-more-btn {
|
|
|
|
|
background: #000;
|
|
|
|
|
border-color: rgba(255, 255, 255, 0.2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .fc-load-more-btn:hover {
|
|
|
|
|
background: #222;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .artalk .atk-main-editor {
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
|
|
|
|
}
|
2026-01-22 13:28:49 +08:00
|
|
|
|
|
|
|
|
.fc-tooltip {
|
|
|
|
|
position: fixed;
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
background: rgba(255, 255, 255, 0.95);
|
|
|
|
|
backdrop-filter: blur(10px);
|
|
|
|
|
-webkit-backdrop-filter: blur(10px);
|
|
|
|
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
width: 300px;
|
|
|
|
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transform: scale(0.95) translateY(10px);
|
|
|
|
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
color: #333;
|
2026-05-19 19:30:34 +08:00
|
|
|
display: none;
|
2026-01-22 13:28:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fc-tooltip.is-visible {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: scale(1) translateY(0);
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fc-tooltip-title {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
color: #000;
|
|
|
|
|
border-bottom: 1px solid rgba(0,0,0,0.05);
|
|
|
|
|
padding-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fc-tooltip-content {
|
|
|
|
|
color: #555;
|
|
|
|
|
text-align: justify;
|
|
|
|
|
display: -webkit-box;
|
2026-05-19 19:30:34 +08:00
|
|
|
-webkit-line-clamp: 6;
|
2026-01-22 13:28:49 +08:00
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .fc-tooltip {
|
|
|
|
|
background: rgba(35, 35, 35, 0.95);
|
|
|
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
|
|
|
color: #eee;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .fc-tooltip-title {
|
|
|
|
|
color: #fff;
|
|
|
|
|
border-bottom-color: rgba(255,255,255,0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .fc-tooltip-content {
|
|
|
|
|
color: #ccc;
|
|
|
|
|
}
|
2026-01-22 12:54:52 +08:00
|
|
|
</style>
|
|
|
|
|
|
2026-01-22 13:28:49 +08:00
|
|
|
<div id="fc-tooltip" class="fc-tooltip">
|
|
|
|
|
<div class="fc-tooltip-title"></div>
|
|
|
|
|
<div class="fc-tooltip-content"></div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
<script>
|
|
|
|
|
window.FriendCircle = {
|
|
|
|
|
allPosts: [],
|
|
|
|
|
currentIndex: 0,
|
|
|
|
|
BATCH_SIZE: 10,
|
2026-05-20 14:33:05 +08:00
|
|
|
CACHE_KEY: 'friend-circle-v1',
|
|
|
|
|
CACHE_OPTIONS: {
|
|
|
|
|
version: 2,
|
|
|
|
|
softTtl: 5 * 60 * 1000,
|
|
|
|
|
hardTtl: 60 * 60 * 1000,
|
|
|
|
|
validate: function(data) {
|
|
|
|
|
return !!data && Array.isArray(data.posts);
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-01-22 12:54:52 +08:00
|
|
|
|
|
|
|
|
timeAgo: function (dateStr) {
|
|
|
|
|
const date = new Date(dateStr);
|
|
|
|
|
const year = date.getFullYear();
|
|
|
|
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
|
|
|
const day = String(date.getDate()).padStart(2, "0");
|
2026-05-19 19:30:34 +08:00
|
|
|
return year + "-" + month + "-" + day;
|
2026-01-22 12:54:52 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
renderPosts: function (posts) {
|
|
|
|
|
const container = document.getElementById("friend-circle-container");
|
|
|
|
|
if (!container) return;
|
|
|
|
|
|
|
|
|
|
const existingBtn = document.getElementById("fc-load-more-btn");
|
|
|
|
|
if (existingBtn) existingBtn.remove();
|
|
|
|
|
|
|
|
|
|
let html = "";
|
|
|
|
|
posts.forEach((post, index) => {
|
|
|
|
|
const dateStr = this.timeAgo(post.date);
|
2026-01-22 13:28:49 +08:00
|
|
|
const bgImageSrc = post.avatar;
|
|
|
|
|
const description = post.description ? post.description.replace(/"/g, '"') : '';
|
2026-02-01 18:22:19 +08:00
|
|
|
const delay = index * 100;
|
2026-01-22 12:54:52 +08:00
|
|
|
|
2026-05-19 19:30:34 +08:00
|
|
|
html += '<a href="' + post.link + '" target="_blank" class="fc-item" data-desc="' + description + '" data-title="' + post.title + '" uk-scrollspy="cls: uk-animation-slide-bottom-small; delay: ' + delay + '">'
|
|
|
|
|
+ '<div class="fc-title" title="' + post.title + '">' + post.title + '</div>'
|
|
|
|
|
+ '<img src="' + bgImageSrc + '" class="fc-bg-image" onerror="this.style.opacity=0">'
|
|
|
|
|
+ '<div class="fc-bottom">'
|
|
|
|
|
+ '<div class="fc-author-pill">'
|
|
|
|
|
+ '<img src="' + post.avatar + '" class="fc-avatar" onerror="this.src=\'https://cravatar.cn/avatar/default?s=50\'">'
|
|
|
|
|
+ '<span class="fc-author-name">' + post.author + '</span>'
|
|
|
|
|
+ '</div>'
|
|
|
|
|
+ '<div class="fc-date"><i class="ri-calendar-line"></i> ' + dateStr + '</div>'
|
|
|
|
|
+ '</div>'
|
|
|
|
|
+ '</a>';
|
2026-01-22 12:54:52 +08:00
|
|
|
});
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
container.insertAdjacentHTML("beforeend", html);
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
if (this.currentIndex < this.allPosts.length) {
|
|
|
|
|
const btn = document.createElement("button");
|
|
|
|
|
btn.id = "fc-load-more-btn";
|
|
|
|
|
btn.className = "fc-load-more-btn";
|
|
|
|
|
btn.innerText = "加载更多";
|
2026-05-19 19:30:34 +08:00
|
|
|
btn.onclick = function() { window.FriendCircle.loadMore(); };
|
2026-01-22 12:54:52 +08:00
|
|
|
container.appendChild(btn);
|
|
|
|
|
} else {
|
|
|
|
|
if (window.fcUpdateTime) {
|
|
|
|
|
const footer = document.createElement("div");
|
2026-05-19 19:30:34 +08:00
|
|
|
footer.style.cssText = "grid-column: 1 / -1; text-align: center; margin-top: 20px; font-size: 12px; color: #999; opacity: 0.6;";
|
|
|
|
|
footer.innerText = "最近更新: " + window.fcUpdateTime;
|
2026-01-22 12:54:52 +08:00
|
|
|
container.appendChild(footer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
loadMore: function () {
|
|
|
|
|
const nextBatch = this.allPosts.slice(
|
|
|
|
|
this.currentIndex,
|
2026-05-19 19:30:34 +08:00
|
|
|
this.currentIndex + this.BATCH_SIZE
|
2026-01-22 12:54:52 +08:00
|
|
|
);
|
|
|
|
|
this.currentIndex += this.BATCH_SIZE;
|
2026-05-19 19:30:34 +08:00
|
|
|
requestAnimationFrame(function() {
|
|
|
|
|
window.FriendCircle.renderPosts(nextBatch);
|
2026-01-22 12:54:52 +08:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2026-05-20 14:33:05 +08:00
|
|
|
renderEmpty: function () {
|
|
|
|
|
const container = document.getElementById("friend-circle-container");
|
|
|
|
|
if (!container) return;
|
|
|
|
|
container.innerHTML = '<div class="empty" style="grid-column: 1/-1; text-align:center; padding:20px;">暂无动态</div>';
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
renderError: function () {
|
|
|
|
|
const container = document.getElementById("friend-circle-container");
|
|
|
|
|
if (!container) return;
|
|
|
|
|
container.innerHTML = '<div class="error" style="grid-column: 1/-1; text-align:center; padding:20px; color:#d9534f;">加载失败,请稍后刷新重试</div>';
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
applyData: function (data) {
|
|
|
|
|
const container = document.getElementById("friend-circle-container");
|
|
|
|
|
if (!container) return;
|
|
|
|
|
|
|
|
|
|
if (!data.posts || data.posts.length === 0) {
|
|
|
|
|
this.renderEmpty();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.FriendCircle.allPosts = data.posts;
|
|
|
|
|
window.fcUpdateTime = new Date(data.updated).toLocaleString();
|
|
|
|
|
window.FriendCircle.currentIndex = 0;
|
|
|
|
|
container.innerHTML = "";
|
|
|
|
|
window.FriendCircle.loadMore();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
fetchData: function () {
|
|
|
|
|
if (window.BrowserCache && typeof window.BrowserCache.log === 'function') {
|
|
|
|
|
window.BrowserCache.log('[FriendCircleCache][fetch]', {
|
|
|
|
|
key: window.BrowserCache ? window.BrowserCache.buildKey(window.FriendCircle.CACHE_KEY) : window.FriendCircle.CACHE_KEY,
|
|
|
|
|
softTtl: window.BrowserCache ? window.BrowserCache.formatDuration(window.FriendCircle.CACHE_OPTIONS.softTtl) : window.FriendCircle.CACHE_OPTIONS.softTtl,
|
|
|
|
|
hardTtl: window.BrowserCache ? window.BrowserCache.formatDuration(window.FriendCircle.CACHE_OPTIONS.hardTtl) : window.FriendCircle.CACHE_OPTIONS.hardTtl
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-05-21 13:45:17 +08:00
|
|
|
return fetch("https://api.usj.cc/api/articles?limit=50")
|
2026-05-20 14:33:05 +08:00
|
|
|
.then(function(res) {
|
|
|
|
|
if (!res.ok) throw new Error("Network response was not ok");
|
|
|
|
|
return res.json();
|
|
|
|
|
})
|
|
|
|
|
.then(function(apiData) {
|
|
|
|
|
const posts = (apiData.articles || []).map(function(a) {
|
|
|
|
|
return {
|
|
|
|
|
title: a.title || '',
|
|
|
|
|
link: a.link || '',
|
|
|
|
|
date: a.pubDate || '',
|
|
|
|
|
author: a.feedName || a.author || '',
|
|
|
|
|
avatar: a.favicon || '',
|
|
|
|
|
blogUrl: a.siteUrl || '',
|
|
|
|
|
image: null,
|
|
|
|
|
description: ''
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
|
posts: posts,
|
|
|
|
|
updated: apiData.timestamp || new Date().toISOString()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (window.BrowserCache) {
|
|
|
|
|
window.BrowserCache.write(window.FriendCircle.CACHE_KEY, window.FriendCircle.CACHE_OPTIONS, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2026-01-22 13:28:49 +08:00
|
|
|
initTooltip: function() {
|
|
|
|
|
if (this.tooltipInitialized) return;
|
|
|
|
|
this.tooltipInitialized = true;
|
|
|
|
|
|
|
|
|
|
const tooltip = document.getElementById('fc-tooltip');
|
|
|
|
|
const titleEl = tooltip.querySelector('.fc-tooltip-title');
|
|
|
|
|
const contentEl = tooltip.querySelector('.fc-tooltip-content');
|
|
|
|
|
const container = document.getElementById('friend-circle-container');
|
|
|
|
|
|
|
|
|
|
if (!tooltip || !container) return;
|
|
|
|
|
|
|
|
|
|
if (tooltip.parentNode !== document.body) {
|
2026-05-19 19:30:34 +08:00
|
|
|
document.body.appendChild(tooltip);
|
2026-01-22 13:28:49 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-19 19:30:34 +08:00
|
|
|
const cleanup = function() {
|
|
|
|
|
if (tooltip && tooltip.parentNode === document.body) {
|
|
|
|
|
tooltip.remove();
|
|
|
|
|
}
|
|
|
|
|
document.removeEventListener('pjax:send', cleanup);
|
2026-01-22 13:28:49 +08:00
|
|
|
};
|
|
|
|
|
document.addEventListener('pjax:send', cleanup);
|
|
|
|
|
|
2026-05-19 19:30:34 +08:00
|
|
|
const updateTooltip = function(e) {
|
|
|
|
|
if (window.innerWidth <= 768) return;
|
|
|
|
|
const item = e.target.closest('.fc-item');
|
|
|
|
|
if (item) {
|
|
|
|
|
const desc = item.getAttribute('data-desc');
|
|
|
|
|
const title = item.getAttribute('data-title');
|
|
|
|
|
if (desc) {
|
|
|
|
|
titleEl.innerText = title;
|
|
|
|
|
contentEl.innerText = desc;
|
|
|
|
|
tooltip.classList.add('is-visible');
|
|
|
|
|
const tooltipWidth = tooltip.offsetWidth;
|
|
|
|
|
const tooltipHeight = tooltip.offsetHeight;
|
|
|
|
|
const winWidth = window.innerWidth;
|
|
|
|
|
const winHeight = window.innerHeight;
|
|
|
|
|
const gap = 15;
|
|
|
|
|
let left = e.clientX + gap;
|
|
|
|
|
let top = e.clientY + gap;
|
|
|
|
|
if (left + tooltipWidth > winWidth - gap) {
|
|
|
|
|
left = e.clientX - tooltipWidth - gap;
|
|
|
|
|
if (left < gap) left = gap;
|
|
|
|
|
}
|
|
|
|
|
if (top + tooltipHeight > winHeight - gap) {
|
|
|
|
|
top = e.clientY - tooltipHeight - gap;
|
|
|
|
|
if (top < gap) top = gap;
|
|
|
|
|
}
|
|
|
|
|
tooltip.style.left = left + 'px';
|
|
|
|
|
tooltip.style.top = top + 'px';
|
2026-01-22 13:28:49 +08:00
|
|
|
} else {
|
2026-05-19 19:30:34 +08:00
|
|
|
tooltip.classList.remove('is-visible');
|
2026-01-22 13:28:49 +08:00
|
|
|
}
|
2026-05-19 19:30:34 +08:00
|
|
|
} else {
|
|
|
|
|
tooltip.classList.remove('is-visible');
|
|
|
|
|
}
|
2026-01-22 13:28:49 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
container.addEventListener('mousemove', updateTooltip);
|
2026-05-19 19:30:34 +08:00
|
|
|
container.addEventListener('mouseleave', function() {
|
|
|
|
|
tooltip.classList.remove('is-visible');
|
2026-01-22 13:28:49 +08:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2026-01-22 12:54:52 +08:00
|
|
|
init: function () {
|
|
|
|
|
const container = document.getElementById("friend-circle-container");
|
|
|
|
|
if (!container) return;
|
2026-05-19 19:30:34 +08:00
|
|
|
|
2026-01-22 13:28:49 +08:00
|
|
|
this.initTooltip();
|
2026-01-22 12:54:52 +08:00
|
|
|
|
|
|
|
|
if (this.isFetching) return;
|
|
|
|
|
this.isFetching = true;
|
|
|
|
|
|
2026-05-20 14:33:05 +08:00
|
|
|
const cached = window.BrowserCache && window.BrowserCache.read(this.CACHE_KEY, this.CACHE_OPTIONS);
|
|
|
|
|
if (cached) {
|
|
|
|
|
if (window.BrowserCache && typeof window.BrowserCache.log === 'function') {
|
|
|
|
|
window.BrowserCache.log('[FriendCircleCache][render]', {
|
|
|
|
|
key: cached.key,
|
|
|
|
|
age: cached.ageText,
|
|
|
|
|
isFresh: cached.isFresh,
|
|
|
|
|
softTtl: cached.softTtlText,
|
|
|
|
|
hardTtl: cached.hardTtlText
|
2026-05-19 19:30:34 +08:00
|
|
|
});
|
2026-05-20 14:33:05 +08:00
|
|
|
}
|
|
|
|
|
this.applyData(cached.data);
|
|
|
|
|
if (cached.isFresh) {
|
|
|
|
|
this.isFetching = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-19 19:30:34 +08:00
|
|
|
|
2026-05-20 14:33:05 +08:00
|
|
|
this.fetchData()
|
|
|
|
|
.then(function(data) {
|
|
|
|
|
window.FriendCircle.applyData(data);
|
2026-01-22 12:54:52 +08:00
|
|
|
})
|
2026-05-19 19:30:34 +08:00
|
|
|
.catch(function(err) {
|
2026-05-20 14:33:05 +08:00
|
|
|
if (window.BrowserCache && window.BrowserCache.isDebug && window.BrowserCache.isDebug()) {
|
|
|
|
|
console.error("Friend Circle Error:", err);
|
|
|
|
|
}
|
|
|
|
|
if (!cached) {
|
|
|
|
|
window.FriendCircle.renderError();
|
|
|
|
|
}
|
2026-01-22 12:54:52 +08:00
|
|
|
})
|
2026-05-19 19:30:34 +08:00
|
|
|
.finally(function() {
|
|
|
|
|
window.FriendCircle.isFetching = false;
|
2026-01-22 12:54:52 +08:00
|
|
|
});
|
2026-05-19 19:30:34 +08:00
|
|
|
}
|
2026-01-22 12:54:52 +08:00
|
|
|
};
|
2026-01-21 23:18:35 +08:00
|
|
|
|
2026-05-20 14:33:05 +08:00
|
|
|
function initFriendCirclePage() {
|
|
|
|
|
if (window.FriendCircle && typeof window.FriendCircle.init === 'function') {
|
2026-05-19 19:30:34 +08:00
|
|
|
window.FriendCircle.init();
|
2026-05-20 14:33:05 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.addEventListener('pjax:complete', initFriendCirclePage);
|
|
|
|
|
|
|
|
|
|
if (document.readyState === "loading") {
|
|
|
|
|
document.addEventListener("DOMContentLoaded", initFriendCirclePage);
|
2026-01-22 12:54:52 +08:00
|
|
|
} else {
|
2026-05-20 14:33:05 +08:00
|
|
|
initFriendCirclePage();
|
2026-01-22 12:54:52 +08:00
|
|
|
}
|
|
|
|
|
</script>
|
2024-08-03 13:35:19 +08:00
|
|
|
</div>
|
2026-01-22 12:54:52 +08:00
|
|
|
{{ end }}
|