@okjavis/nodebb-theme-javis 6.0.0 → 6.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scss/_feed.scss +98 -0
- package/templates/feed.tpl +107 -10
- package/templates/partials/notifications_list.tpl +45 -0
package/package.json
CHANGED
package/scss/_feed.scss
CHANGED
|
@@ -1138,6 +1138,104 @@ ul.categories-list {
|
|
|
1138
1138
|
}
|
|
1139
1139
|
}
|
|
1140
1140
|
|
|
1141
|
+
// ===========================================================
|
|
1142
|
+
// SUGGESTED USERS – Following Empty State
|
|
1143
|
+
// ===========================================================
|
|
1144
|
+
.javis-suggested-users {
|
|
1145
|
+
background: $jv-surface;
|
|
1146
|
+
border: 1px solid $jv-border-subtle;
|
|
1147
|
+
border-radius: $jv-radius-lg;
|
|
1148
|
+
padding: $jv-space-4 $jv-space-6;
|
|
1149
|
+
margin-bottom: $jv-space-4;
|
|
1150
|
+
box-shadow: $jv-shadow-sm;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
.javis-suggested-users-title {
|
|
1154
|
+
font-size: $jv-font-size-xs;
|
|
1155
|
+
font-weight: 700;
|
|
1156
|
+
text-transform: uppercase;
|
|
1157
|
+
letter-spacing: 0.6px;
|
|
1158
|
+
color: $jv-text-muted;
|
|
1159
|
+
padding: $jv-space-3 0 $jv-space-2;
|
|
1160
|
+
margin: 0;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
.javis-sug-card {
|
|
1164
|
+
display: flex !important;
|
|
1165
|
+
flex-direction: row !important;
|
|
1166
|
+
align-items: center !important;
|
|
1167
|
+
gap: $jv-space-3;
|
|
1168
|
+
padding: $jv-space-3 0;
|
|
1169
|
+
border-bottom: 1px solid $jv-border-subtle;
|
|
1170
|
+
|
|
1171
|
+
&:last-child {
|
|
1172
|
+
border-bottom: none;
|
|
1173
|
+
padding-bottom: $jv-space-2;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
&:first-child {
|
|
1177
|
+
padding-top: $jv-space-2;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// Anchor wrapping the avatar — must be inline-flex
|
|
1181
|
+
> a:first-child {
|
|
1182
|
+
display: inline-flex !important;
|
|
1183
|
+
flex-shrink: 0;
|
|
1184
|
+
line-height: 0;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
.javis-sug-avatar {
|
|
1189
|
+
width: 40px !important;
|
|
1190
|
+
height: 40px !important;
|
|
1191
|
+
min-width: 40px;
|
|
1192
|
+
min-height: 40px;
|
|
1193
|
+
border-radius: 50% !important;
|
|
1194
|
+
flex-shrink: 0;
|
|
1195
|
+
display: block;
|
|
1196
|
+
object-fit: cover;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
.javis-sug-avatar-icon {
|
|
1200
|
+
color: #fff;
|
|
1201
|
+
display: inline-flex !important;
|
|
1202
|
+
align-items: center !important;
|
|
1203
|
+
justify-content: center !important;
|
|
1204
|
+
font-size: 16px;
|
|
1205
|
+
font-weight: 700;
|
|
1206
|
+
font-family: $jv-font-sans;
|
|
1207
|
+
line-height: 1;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
.javis-sug-info {
|
|
1211
|
+
flex: 1 1 0;
|
|
1212
|
+
min-width: 0;
|
|
1213
|
+
display: flex;
|
|
1214
|
+
flex-direction: column;
|
|
1215
|
+
gap: 2px;
|
|
1216
|
+
overflow: hidden;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
.javis-sug-name {
|
|
1220
|
+
font-size: $jv-font-size-sm;
|
|
1221
|
+
font-weight: 600;
|
|
1222
|
+
color: $jv-text-main;
|
|
1223
|
+
text-decoration: none;
|
|
1224
|
+
white-space: nowrap;
|
|
1225
|
+
overflow: hidden;
|
|
1226
|
+
text-overflow: ellipsis;
|
|
1227
|
+
display: block;
|
|
1228
|
+
|
|
1229
|
+
&:hover {
|
|
1230
|
+
color: $jv-primary;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
.javis-sug-meta {
|
|
1235
|
+
font-size: $jv-font-size-xs;
|
|
1236
|
+
color: $jv-text-soft;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1141
1239
|
// ===========================================================
|
|
1142
1240
|
// MOBILE FIX - Category dropdown positioning
|
|
1143
1241
|
// Fix dropdown opening from bottom left corner on mobile
|
package/templates/feed.tpl
CHANGED
|
@@ -26,20 +26,34 @@
|
|
|
26
26
|
<div class="col-lg-6 col-sm-12 ms-auto">
|
|
27
27
|
{{{ end }}}
|
|
28
28
|
|
|
29
|
-
<!--
|
|
30
|
-
<div class="d-flex justify-content-between py-2 mb-0 gap-1">
|
|
31
|
-
{{{ if
|
|
32
|
-
<
|
|
29
|
+
<!-- For You / Following tabs + filter -->
|
|
30
|
+
<div class="d-flex justify-content-between align-items-center py-2 mb-0 gap-1">
|
|
31
|
+
{{{ if loggedIn }}}
|
|
32
|
+
<div class="d-flex gap-1">
|
|
33
|
+
<a href="{config.relative_path}/feed" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if !showFollowed }}}active{{{ end }}}">For You</a>
|
|
34
|
+
<a href="{config.relative_path}/feed?users=followed" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if showFollowed }}}active{{{ end }}}">Following</a>
|
|
35
|
+
</div>
|
|
33
36
|
{{{ end }}}
|
|
37
|
+
<div class="d-flex align-items-center gap-1 ms-auto">
|
|
38
|
+
<!-- IMPORT partials/category/filter-dropdown-right.tpl -->
|
|
39
|
+
{{{ if canPost }}}
|
|
40
|
+
<button id="new_topic" class="btn btn-primary btn-sm d-none">[[category:new-topic-button]]</button>
|
|
41
|
+
{{{ end }}}
|
|
42
|
+
</div>
|
|
34
43
|
</div>
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
{{{ if !posts.length }}}
|
|
46
|
+
{{{ if showFollowed }}}
|
|
47
|
+
<div class="alert alert-info text-center mb-3">Follow new members to see their posts here.</div>
|
|
48
|
+
<div class="javis-suggested-users">
|
|
49
|
+
<p style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:0.6px;color:#6b7280;margin:0 0 4px;">People you should follow</p>
|
|
50
|
+
<div id="javis-suggested-users-list">
|
|
51
|
+
<div class="text-center text-muted py-4"><i class="fa fa-spinner fa-spin me-2"></i></div>
|
|
52
|
+
</div>
|
|
39
53
|
</div>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
{{{ else }}}
|
|
55
|
+
<div class="alert alert-warning text-center">[[feed:no-posts-found]]</div>
|
|
56
|
+
{{{ end }}}
|
|
43
57
|
{{{ end }}}
|
|
44
58
|
|
|
45
59
|
<ul component="posts" class="list-unstyled" data-nextstart="{nextStart}">
|
|
@@ -126,9 +140,92 @@ $(document).ready(function() {
|
|
|
126
140
|
require(['share'], function(share) {
|
|
127
141
|
share.addShareHandlers('{title}');
|
|
128
142
|
});
|
|
143
|
+
|
|
129
144
|
});
|
|
130
145
|
</script>
|
|
131
146
|
|
|
147
|
+
<script>
|
|
148
|
+
// Suggested users – standalone, same pattern as video embed
|
|
149
|
+
(function() {
|
|
150
|
+
function loadSuggestedUsers() {
|
|
151
|
+
var $list = $('#javis-suggested-users-list');
|
|
152
|
+
if (!$list.length) return;
|
|
153
|
+
|
|
154
|
+
fetch((config.relative_path || '') + '/api/users?section=top', {
|
|
155
|
+
credentials: 'same-origin',
|
|
156
|
+
headers: { 'Accept': 'application/json' }
|
|
157
|
+
})
|
|
158
|
+
.then(function(r) { return r.json(); })
|
|
159
|
+
.then(function(data) {
|
|
160
|
+
var users = (data.users || []).filter(function(u) {
|
|
161
|
+
return u.uid && String(u.uid) !== String(config.uid);
|
|
162
|
+
}).slice(0, 6);
|
|
163
|
+
|
|
164
|
+
if (!users.length) {
|
|
165
|
+
$list.html('<p class="text-muted text-center py-2 mb-0">No members to suggest yet.</p>');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
$list.html(users.map(function(u, i) {
|
|
170
|
+
var bg = u['icon:bgColor'] || '#0051ff';
|
|
171
|
+
var icon = u['icon:text'] || (u.displayname || 'U')[0].toUpperCase();
|
|
172
|
+
var isLast = i === users.length - 1;
|
|
173
|
+
var avatarHtml = u.picture
|
|
174
|
+
? '<img src="' + u.picture + '" style="width:40px;height:40px;min-width:40px;border-radius:50%;object-fit:cover;display:block;" data-bg="' + bg + '" data-icon="' + icon + '" class="javis-sug-avatar-img">'
|
|
175
|
+
: '<span style="width:40px;height:40px;min-width:40px;border-radius:50%;background:' + bg + ';color:#fff;display:inline-flex;align-items:center;justify-content:center;font-size:15px;font-weight:700;font-family:system-ui,sans-serif;flex-shrink:0;">' + icon + '</span>';
|
|
176
|
+
return '<div style="display:flex;align-items:center;gap:12px;padding:10px 0;' + (isLast ? '' : 'border-bottom:1px solid rgba(0,0,0,0.07);') + '">' +
|
|
177
|
+
'<a href="' + (config.relative_path || '') + '/user/' + u.userslug + '" style="flex-shrink:0;display:inline-flex;line-height:0;">' + avatarHtml + '</a>' +
|
|
178
|
+
'<div style="flex:1;min-width:0;">' +
|
|
179
|
+
'<a href="' + (config.relative_path || '') + '/user/' + u.userslug + '" style="font-size:13px;font-weight:600;color:#111;text-decoration:none;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">' + (u.displayname || u.username) + '</a>' +
|
|
180
|
+
'<span style="font-size:12px;color:#9ca3af;">' + (u.postcount || 0) + ' posts</span>' +
|
|
181
|
+
'</div>' +
|
|
182
|
+
'<button class="btn btn-sm rounded-pill javis-follow-btn" style="flex-shrink:0;font-size:12px;font-weight:600;padding:4px 14px;border:1.5px solid #0051ff;color:#0051ff;background:transparent;white-space:nowrap;" data-userslug="' + u.userslug + '">Follow</button>' +
|
|
183
|
+
'</div>';
|
|
184
|
+
}).join(''));
|
|
185
|
+
|
|
186
|
+
$list.find('img.javis-sug-avatar-img').on('error', function() {
|
|
187
|
+
var $img = $(this);
|
|
188
|
+
$img.replaceWith('<span class="javis-sug-avatar javis-sug-avatar-icon" style="background:' + ($img.data('bg') || '#0051ff') + '">' + ($img.data('icon') || 'U') + '</span>');
|
|
189
|
+
});
|
|
190
|
+
})
|
|
191
|
+
.catch(function() {
|
|
192
|
+
$list.closest('.javis-suggested-users').remove();
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Initial load
|
|
197
|
+
if (document.readyState === 'loading') {
|
|
198
|
+
document.addEventListener('DOMContentLoaded', function() { setTimeout(loadSuggestedUsers, 100); });
|
|
199
|
+
} else {
|
|
200
|
+
setTimeout(loadSuggestedUsers, 100);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// SPA navigation
|
|
204
|
+
$(window).on('action:ajaxify.end', function() {
|
|
205
|
+
setTimeout(loadSuggestedUsers, 100);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Follow button
|
|
209
|
+
$(document).on('click', '.javis-follow-btn', function() {
|
|
210
|
+
var $btn = $(this);
|
|
211
|
+
var userslug = $btn.data('userslug');
|
|
212
|
+
$btn.prop('disabled', true).text('...');
|
|
213
|
+
fetch((config.relative_path || '') + '/api/v3/users/' + userslug + '/follow', {
|
|
214
|
+
method: 'PUT',
|
|
215
|
+
credentials: 'same-origin',
|
|
216
|
+
headers: { 'Content-Type': 'application/json', 'x-csrf-token': config.csrf_token },
|
|
217
|
+
body: JSON.stringify({})
|
|
218
|
+
}).then(function(r) {
|
|
219
|
+
if (r.ok) {
|
|
220
|
+
$btn.text('Following').css({'background':'#0051ff','color':'#fff','border-color':'#0051ff'}).prop('disabled',true);
|
|
221
|
+
} else { throw new Error(); }
|
|
222
|
+
}).catch(function() {
|
|
223
|
+
$btn.prop('disabled', false).text('Follow');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
})();
|
|
227
|
+
</script>
|
|
228
|
+
|
|
132
229
|
<script>
|
|
133
230
|
// Embed videos on feed page - runs independently
|
|
134
231
|
(function() {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{{{ if !notifications.length }}}
|
|
2
|
+
<div class="no-notifs text-center p-4 d-flex flex-column">
|
|
3
|
+
<div class="p-4"><i class="fa-solid fa-wind fs-2 text-muted"></i></div>
|
|
4
|
+
<div class="text-xs fw-semibold text-muted">[[notifications:no-notifs]]</div>
|
|
5
|
+
</div>
|
|
6
|
+
{{{ end }}}
|
|
7
|
+
|
|
8
|
+
{{{ each notifications }}}
|
|
9
|
+
<div class="{./readClass}" data-nid="{./nid}" data-path="{./path}" {{{ if ./pid }}}data-pid="{./pid}"{{{ end }}}{{{ if ./tid }}}data-tid="{./tid}"{{{ end }}}>
|
|
10
|
+
<div class="d-flex gap-1 justify-content-between">
|
|
11
|
+
<div class="btn btn-ghost btn-sm d-flex gap-2 flex-grow-1 text-start align-items-start">
|
|
12
|
+
<a class="flex-grow-0 flex-shrink-0" href="{{{ if ./user.userslug}}}{config.relative_path}/user/{./user.userslug}{{{ else }}}#{{{ end }}}">
|
|
13
|
+
{{{ if (./image && ./from) }}}
|
|
14
|
+
<img class="avatar avatar-rounded" style="--avatar-size: 32px;" src="{./image}" onerror="this.onerror=null;this.style.display='none';this.nextElementSibling.classList.remove('hidden');" />
|
|
15
|
+
<div class="avatar avatar-rounded hidden" style="--avatar-size: 32px; background-color: {./user.icon:bgColor};">{./user.icon:text}</div>
|
|
16
|
+
{{{ else }}}
|
|
17
|
+
{{{ if ./icon }}}
|
|
18
|
+
<div class="avatar avatar-rounded" style="--avatar-size: 32px;"><i class="text-secondary fa {./icon}"></i></div>
|
|
19
|
+
{{{ else }}}
|
|
20
|
+
<div class="avatar avatar-rounded" style="--avatar-size: 32px; background-color: {./user.icon:bgColor};">{./user.icon:text}</div>
|
|
21
|
+
{{{ end }}}
|
|
22
|
+
{{{ end }}}
|
|
23
|
+
</a>
|
|
24
|
+
|
|
25
|
+
<div class="d-flex flex-grow-1 flex-column align-items-start position-relative">
|
|
26
|
+
<a href="{./path}" class="text-decoration-none d-inline-block text-reset text-break text-sm ff-sans stretched-link" component="notifications/item/link">
|
|
27
|
+
{./bodyShort}
|
|
28
|
+
</a>
|
|
29
|
+
<div class="text-xs text-muted">{{{ if ./timeagoLong }}}{./timeagoLong}{{{ else }}}<span class="timeago" title="{./datetimeISO}"></span>{{{ end }}}</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div>
|
|
33
|
+
{{{ if ./nid }}}
|
|
34
|
+
<button class="mark-read btn btn-ghost btn-sm d-flex align-items-center justify-content-center flex-grow-0 flex-shrink-0 p-1" style="width: 1.5rem; height: 1.5rem;">
|
|
35
|
+
<i class="unread fa fa-2xs fa-circle text-primary {{{ if ./read }}}hidden{{{ end }}}" aria-label="[[unread:mark-as-read]]"></i>
|
|
36
|
+
<i class="read fa fa-2xs fa-circle-o text-secondary {{{ if !./read }}}hidden{{{ end }}}" aria-label="[[unread:mark-as-unread]]"></i>
|
|
37
|
+
</button>
|
|
38
|
+
{{{ end }}}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
{{{ if !@last }}}
|
|
43
|
+
<hr class="my-1" />
|
|
44
|
+
{{{ end }}}
|
|
45
|
+
{{{ end }}}
|