@okjavis/nodebb-theme-javis 6.0.2 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okjavis/nodebb-theme-javis",
3
- "version": "6.0.2",
3
+ "version": "6.0.3",
4
4
  "description": "Modern, premium NodeBB theme for JAVIS Community - Extends Harmony with custom styling",
5
5
  "main": "theme.js",
6
6
  "scripts": {
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
@@ -44,7 +44,13 @@
44
44
 
45
45
  {{{ if !posts.length }}}
46
46
  {{{ if showFollowed }}}
47
- <div class="alert alert-info text-center">Follow some members to see their posts here.</div>
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>
53
+ </div>
48
54
  {{{ else }}}
49
55
  <div class="alert alert-warning text-center">[[feed:no-posts-found]]</div>
50
56
  {{{ end }}}
@@ -134,9 +140,92 @@ $(document).ready(function() {
134
140
  require(['share'], function(share) {
135
141
  share.addShareHandlers('{title}');
136
142
  });
143
+
137
144
  });
138
145
  </script>
139
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
+
140
229
  <script>
141
230
  // Embed videos on feed page - runs independently
142
231
  (function() {