Skip to content

Commit df79aa2

Browse files
committed
[feat] 动态友链重磅升级
1 parent ffc4cc9 commit df79aa2

File tree

7 files changed

+314
-5
lines changed

7 files changed

+314
-5
lines changed

_cdn.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ volantis_tags_friends:
4949
npm: true
5050
static: false
5151
cdnjs: false
52+
volantis_tags_friends_and_posts:
53+
name: hexo-theme-volantis
54+
file: source/js/plugins/tags/fposts.js
55+
local: true
56+
npm: true
57+
static: false
58+
cdnjs: false
5259
volantis_tags_contributors:
5360
name: hexo-theme-volantis
5461
file: source/js/plugins/tags/contributors.js

_config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ plugins:
755755
enable: true
756756
friendsjs:
757757
enable: true
758+
fpostsjs:
759+
enable: true
758760
contributorsjs:
759761
enable: true
760762

layout/_plugins/github-api/script.ejs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
volantis.js("<%- theme.cdn.volantis_tags_friends %>")
1313
}
1414
<% } %>
15+
<% if (theme.plugins.fpostsjs.enable) { %>
16+
const fpostsjs = document.getElementById('fposts-api');
17+
if (fpostsjs != undefined && typeof FpostsJS === 'undefined') {
18+
volantis.js("<%- theme.cdn.volantis_tags_friends_and_posts %>")
19+
}
20+
<% } %>
1521
<% if (theme.plugins.contributorsjs.enable) { %>
1622
const contributors_api = document.getElementById('contributors-api');
1723
if (contributors_api != undefined && typeof ContributorsJS === 'undefined') {

scripts/tags/friends.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
'use strict';
99

1010
hexo.extend.tag.register('friends', function(args) {
11-
args = hexo.args.map(args, ['only', 'not', 'repo', 'api']);
11+
args = hexo.args.map(args, ['only', 'not', 'repo', 'api', 'posts']);
1212
if (args.only) {
1313
if(/::/g.test(args.only)){
1414
args.only = args.only.split('::');
@@ -70,12 +70,21 @@ hexo.extend.tag.register('friends', function(args) {
7070
el += groupHeader(group);
7171
}
7272
if (group.api) {
73-
el += '<div class="friendsjs-wrap"';
74-
el += ' id="friends-api"';
73+
if (args.posts) {
74+
el += '<div class="users-posts-wrap"';
75+
el += ' id="fposts-api"';
76+
} else {
77+
el += '<div class="friendsjs-wrap"';
78+
el += ' id="friends-api"';
79+
}
7580
el += ' api="' + group.api + '"';
7681
el += '>';
7782
el += '<div class="loading-wrap"><svg class="loading" style="vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2709"><path d="M832 512c0-176-144-320-320-320V128c211.2 0 384 172.8 384 384h-64zM192 512c0 176 144 320 320 320v64C300.8 896 128 723.2 128 512h64z" p-id="2710"></path></svg><p></p></div>';
78-
el += '<div class="group-body"></div>';
83+
if (args.posts) {
84+
el += '<div class="grid-box"></div>';
85+
} else {
86+
el += '<div class="group-body"></div>';
87+
}
7988
el += '</div>';
8089
} else if (group.items) {
8190
el += '<div class="group-body">';
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
.users-posts-wrap .grid-box
2+
display: grid
3+
grid-template-columns: 1fr
4+
grid-gap: 2px
5+
overflow: hidden
6+
border-radius: $border-card
7+
8+
.users-posts-wrap .user-post-card
9+
display: grid
10+
grid-template-columns: 1fr 2fr
11+
grid-gap: 16px
12+
font-weight: 500
13+
border-radius: 4px
14+
background: var(--color-block)
15+
padding: 1rem
16+
div,span
17+
line-height: 1.2
18+
.labels
19+
margin-top: 4px
20+
display: flex
21+
flex-wrap: wrap
22+
align-items: center
23+
justify-content: center
24+
pointer-events: none
25+
user-select: none
26+
.label
27+
color: white
28+
font-size: 12px
29+
padding: 2px 6px
30+
margin: 4px 2px 0 2px
31+
line-height: 1
32+
border-radius: 16px
33+
.avatar-box
34+
display: flex
35+
justify-content: center
36+
flex-direction: column
37+
align-items: center
38+
padding: 1rem
39+
.card-link
40+
color: var(--text-p1)
41+
display: flex
42+
justify-content: flex-start
43+
flex-direction: column
44+
align-items: center
45+
text-align: center
46+
border-radius: 4px
47+
position: relative
48+
flex-shrink: 0
49+
trans1 width
50+
span
51+
display: -webkit-box
52+
-webkit-box-orient: vertical
53+
overflow: hidden
54+
-webkit-line-clamp: 1
55+
position: relative
56+
.title
57+
font-size: $fs-14
58+
font-weight: 500
59+
margin-top: 1rem
60+
max-width: 100%
61+
img
62+
object-fit: cover
63+
display: block
64+
width: 64px
65+
height: 64px
66+
background: var(--color-card)
67+
border-radius: 64px
68+
floatable-trans()
69+
70+
.previews
71+
display: flex
72+
justify-content: space-between
73+
flex-direction: column
74+
height: 100%
75+
position: relative
76+
.empty
77+
min-height: 1rem
78+
.desc
79+
font-size: 1.2rem
80+
color: var(--text-p3)
81+
font-weight: 400
82+
margin: 1rem 0
83+
margin-right: auto
84+
position: relative
85+
&:before
86+
position: absolute
87+
font-weight: 900
88+
font-size: 32px
89+
color: var(--color-block)
90+
top: 0
91+
content: '“'
92+
left: -16px
93+
trans1 all
94+
.spacer
95+
height: 100%
96+
.posts
97+
margin: 0.5rem 0 1rem 0
98+
display: flex
99+
flex-direction: column
100+
align-items: flex-start
101+
.post-link
102+
display: flex
103+
flex-direction: column
104+
color: var(--text-p1)
105+
position: relative
106+
trans1 color
107+
&+.post-link
108+
margin-top: 1rem
109+
&:before
110+
content: ''
111+
position: absolute
112+
left: -12px
113+
top: 2px
114+
height: 'calc(%s - 2px)' % $fs-14
115+
width: 4px
116+
border-radius: 4px
117+
background: var(--color-block)
118+
trans1 all
119+
&:hover:before
120+
background: $color-hover
121+
// height: calc(100% - 4px)
122+
height: 100%
123+
top: 0
124+
span
125+
display: -webkit-box
126+
-webkit-box-orient: vertical
127+
overflow: hidden
128+
-webkit-line-clamp: 1
129+
position: relative
130+
.title
131+
font-size: $fs-14
132+
font-weight: 500
133+
max-width: 100%
134+
display: -webkit-box
135+
-webkit-box-orient: vertical
136+
overflow: hidden
137+
-webkit-line-clamp: 2
138+
position: relative
139+
.date
140+
font-size: $fs-12
141+
color: var(--text-p3)
142+
margin-top: 0.25rem
143+
.no-post
144+
font-size: $fs-13
145+
color: var(--text-p4)
146+
147+
148+
// transform
149+
.users-posts-wrap .user-post-card
150+
&:hover
151+
.desc
152+
&:before
153+
color: $color-hover
154+
transform: scale(1.2) translateX(-4px)
155+
.card-link:hover
156+
img
157+
transform: scale(1.2) rotate(8deg)
158+
box-shadow: $boxshadow-card-float
159+
.post-link:hover
160+
color: $color-hover

source/css/_style/_tag-plugins/tag.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@
3030
"videos": ["media.styl"],
3131
"frame": ["frame.styl"],
3232
"contributors": ["friends.styl"],
33-
"friends": ["friends.styl"],
33+
"friends": ["friends.styl","fposts.styl"],
3434
"swiper": ["swiper.styl"]
3535
}

source/js/plugins/tags/fposts.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
const FpostsJS = {
2+
requestAPI: (url, callback, timeout) => {
3+
let retryTimes = 5;
4+
5+
function request() {
6+
return new Promise((resolve, reject) => {
7+
let status = 0; // 0 等待 1 完成 2 超时
8+
let timer = setTimeout(() => {
9+
if (status === 0) {
10+
status = 2;
11+
timer = null;
12+
reject('请求超时');
13+
if (retryTimes == 0) {
14+
timeout();
15+
}
16+
}
17+
}, 5000);
18+
fetch(url).then(function (response) {
19+
if (status !== 2) {
20+
clearTimeout(timer);
21+
resolve(response);
22+
timer = null;
23+
status = 1;
24+
}
25+
if (response.ok) {
26+
return response.json();
27+
}
28+
throw new Error('Network response was not ok.');
29+
}).then(function (data) {
30+
retryTimes = 0;
31+
callback(data);
32+
}).catch(function (error) {
33+
if (retryTimes > 0) {
34+
retryTimes -= 1;
35+
setTimeout(() => {
36+
request();
37+
}, 5000);
38+
} else {
39+
timeout();
40+
}
41+
});
42+
});
43+
}
44+
request();
45+
},
46+
layout: (cfg) => {
47+
const el = cfg.el;
48+
const default_avatar = cfg.avatar;
49+
50+
FpostsJS.requestAPI(cfg.api, function (data) {
51+
el.querySelector('.loading-wrap').remove();
52+
const arr = data.content;
53+
var cellALL = "";
54+
arr.forEach((item, i) => {
55+
var cell = `<div class="grid-cell user-post-card">`;
56+
cell += `<div class="avatar-box">`;
57+
cell += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.html_url || item.url}">`;;
58+
cell += `<img src="${item.avatar_url || item.avatar || item.icon || default_avatar}" onerror="javascript:this.removeAttribute(\'data-src\');this.src=\'${default_avatar}\';"/>`;
59+
cell += `<span class="title">${item.title || item.login}</span>`;
60+
cell += `</a>`;
61+
cell += `<div class="labels">`;
62+
for (let label of item.labels) {
63+
if (label.lightness > 75) {
64+
cell += `<div class="label" style="background:#${label.color};color:hsla(${label.hue}, ${label.saturation}%, 20%, 1);">${label.name}</div>`;
65+
} else if (label.saturation > 90 && label.lightness > 40) {
66+
cell += `<div class="label" style="background:#${label.color};color:hsla(${label.hue}, 50%, 20%, 1);">${label.name}</div>`;
67+
} else {
68+
cell += `<div class="label" style="background:#${label.color};color:white">${label.name}</div>`;
69+
}
70+
}
71+
cell += `</div>`;
72+
cell += `</div>`;
73+
cell += `<div class="previews">`;
74+
if (item.description) {
75+
cell += `<div class="desc">${item.description || item.issue_number || ''}</div>`;
76+
} else {
77+
cell += `<div class="desc">#${item.issue_number}</div>`;
78+
}
79+
cell += `<div class="posts">`;
80+
81+
if (item.posts?.length > 0) {
82+
for (let post of item.posts) {
83+
cell += `<a class="post-link" target="_blank" rel="external nofollow noopener noreferrer" href="${post.link}">`;
84+
cell += `<span class="title">${post.title}</span>`;
85+
cell += `<span class="date">${post.published}</span>`;
86+
cell += `</a>`;
87+
}
88+
} else {
89+
cell += `<span class="no-post">${item.feed?.length > 0 ? 'RSS 解析失败' : '未设置 RSS 链接'}</span>`;
90+
}
91+
cell += `</div>`;
92+
cell += `</div>`;
93+
cell += `</div>`;
94+
cellALL += cell;
95+
});
96+
97+
el.querySelector('.grid-box').innerHTML = cellALL;
98+
}, function () {
99+
try {
100+
el.querySelector('.loading-wrap svg').remove();
101+
el.querySelector('.loading-wrap p').innerText('加载失败,请稍后重试。');
102+
} catch (e) { }
103+
});
104+
},
105+
start: () => {
106+
const els = document.getElementsByClassName('users-posts-wrap');
107+
for (var i = 0; i < els.length; i++) {
108+
const el = els[i];
109+
const api = el.getAttribute('api');
110+
if (api == null) {
111+
continue;
112+
}
113+
var cfg = new Object();
114+
cfg.el = el;
115+
cfg.api = api;
116+
cfg.class = el.getAttribute('class');
117+
cfg.avatar = volantis.GLOBAL_CONFIG.default.avatar;
118+
FpostsJS.layout(cfg);
119+
}
120+
}
121+
}
122+
123+
124+
125+
FpostsJS.start();

0 commit comments

Comments
 (0)