@okjavis/nodebb-theme-javis 3.0.1 → 3.0.2

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": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Modern, premium NodeBB theme for JAVIS Community - Extends Harmony with custom styling",
5
5
  "main": "theme.js",
6
6
  "scripts": {
@@ -12,6 +12,9 @@
12
12
  $(document).ready(function() {
13
13
  console.log('JAVIS Community Theme initialized');
14
14
 
15
+ // Initialize sidebar toggle handler
16
+ initSidebarToggle();
17
+
15
18
  // Auto-expand sidebar on desktop (Reddit-style default)
16
19
  initSidebarAutoExpand();
17
20
 
@@ -198,6 +201,46 @@
198
201
  console.log('JAVIS: Created carousel with ' + $images.length + ' images');
199
202
  }
200
203
 
204
+ /**
205
+ * Initialize sidebar toggle click handler
206
+ * Ensures the sidebar can be toggled and saves user preference
207
+ */
208
+ function initSidebarToggle() {
209
+ var $toggle = $('[component="sidebar/toggle"]');
210
+ if (!$toggle.length) {
211
+ console.log('JAVIS: Sidebar toggle element not found');
212
+ return;
213
+ }
214
+
215
+ // Remove any existing handlers to avoid duplicates, then add our handler
216
+ $toggle.off('click.javis').on('click.javis', function(e) {
217
+ e.preventDefault();
218
+ e.stopPropagation();
219
+
220
+ var $sidebar = $('.sidebar-left');
221
+ $sidebar.toggleClass('open');
222
+
223
+ console.log('JAVIS: Sidebar toggled, open:', $sidebar.hasClass('open'));
224
+
225
+ // Save preference if user is logged in
226
+ if (typeof app !== 'undefined' && app.user && app.user.uid) {
227
+ require(['api'], function(api) {
228
+ api.put('/users/' + app.user.uid + '/settings', {
229
+ settings: {
230
+ openSidebars: $sidebar.hasClass('open') ? 'on' : 'off',
231
+ },
232
+ }).catch(function(err) {
233
+ console.warn('JAVIS: Could not save sidebar preference', err);
234
+ });
235
+ });
236
+ }
237
+
238
+ $(window).trigger('action:sidebar.toggle');
239
+ });
240
+
241
+ console.log('JAVIS: Sidebar toggle initialized');
242
+ }
243
+
201
244
  /**
202
245
  * Auto-expand sidebar on desktop (Reddit-style default)
203
246
  * The sidebar will be expanded by default on desktop, but users can still toggle it.
@@ -0,0 +1,148 @@
1
+ <!--
2
+ JAVIS Community Theme - Feed Page
3
+ Overrides nodebb-plugin-feed/public/templates/feed.tpl
4
+ Structure: Content first, Image second, Action bar third (separated)
5
+ -->
6
+ <div data-widget-area="header">
7
+ {{{each widgets.header}}}
8
+ {{widgets.header.html}}
9
+ {{{end}}}
10
+ </div>
11
+ <style>.feed .post-body .content > p:last-child { margin-bottom: 0px; }</style>
12
+ <div class="feed">
13
+ <div class="row">
14
+ <div data-widget-area="left" class="col-lg-3 col-sm-12 {{{ if !widgets.left.length }}}hidden{{{ end }}}">
15
+ {{{each widgets.left}}}
16
+ {{widgets.left.html}}
17
+ {{{end}}}
18
+ </div>
19
+ {{{ if ((widgets.left.length && widgets.right.length) || (!widgets.left.length && !widgets.right.length))}}}
20
+ <div class="col-lg-6 col-sm-12 mx-auto">
21
+ {{{ end }}}
22
+ {{{ if (widgets.left.length && !widgets.right.length) }}}
23
+ <div class="col-lg-6 col-sm-12 me-auto">
24
+ {{{ end }}}
25
+ {{{ if (!widgets.left.length && widgets.right.length) }}}
26
+ <div class="col-lg-6 col-sm-12 ms-auto">
27
+ {{{ end }}}
28
+
29
+ <div class="d-flex justify-content-between py-2 mb-2 gap-1">
30
+ {{{ if canPost }}}
31
+ <button id="new_topic" class="btn btn-primary btn-sm">[[category:new-topic-button]]</button>
32
+ {{{ end }}}
33
+ {{{ if (!loggedIn && !canPost) }}}
34
+ <a href="{config.relative_path}/login" class="btn btn-primary btn-sm">[[category:guest-login-post]]</a>
35
+ {{{ end }}}
36
+
37
+ <div class="d-flex justify-content-end gap-1">
38
+ <!-- IMPORT partials/category/filter-dropdown-right.tpl -->
39
+
40
+ <div id="options-dropdown" class="btn-group dropdown dropdown-right bottom-sheet">
41
+ <button type="button" class="btn btn-ghost btn-sm d-flex align-items-center gap-2 ff-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
42
+ <i class="fa fa-fw fa-gear text-primary"></i>
43
+ </button>
44
+ <ul class="dropdown-menu p-1 text-sm" role="menu">
45
+ <li class="py-1 px-3">
46
+ <div class="form-check form-switch d-flex px-0 align-items-center justify-content-between gap-3">
47
+ <label class="form-check-label text-nowrap" for="showAllPosts">[[feed:show-all-posts]]</label>
48
+ <input class="form-check-input float-none m-0 pointer" type="checkbox" role="switch" id="showAllPosts" {{{ if showAllPosts }}}checked{{{ end }}}>
49
+ </div>
50
+ </li>
51
+ {{{ if loggedIn }}}
52
+ <li class="py-1 px-3">
53
+ <div class="form-check form-switch d-flex px-0 align-items-center justify-content-between gap-3">
54
+ <label class="form-check-label text-nowrap" for="showFollowedUsers">[[feed:followed-users-only]]</label>
55
+ <input class="form-check-input float-none m-0 pointer" type="checkbox" role="switch" id="showFollowedUsers" {{{ if showFollowed }}}checked{{{ end }}}>
56
+ </div>
57
+ </li>
58
+ {{{ end }}}
59
+ </ul>
60
+ </div>
61
+ </div>
62
+ </div>
63
+
64
+ {{{ if !posts.length }}}
65
+ <div class="alert alert-warning text-center">[[feed:no-posts-found]] {{{ if !following.length }}}[[feed:are-you-following-anyone]] {{{ end }}}</div>
66
+ {{{ end }}}
67
+
68
+ <ul component="posts" class="list-unstyled" data-nextstart="{nextStart}">
69
+ {{{ each posts }}}
70
+ <li component="post" class="shadow-sm mb-3 rounded-2 border posts-list-item {{{ if ./deleted }}} deleted{{{ else }}}{{{ if ./topic.deleted }}} deleted{{{ end }}}{{{ end }}}{{{ if ./topic.scheduled }}} scheduled{{{ end }}}" data-pid="{./pid}" data-uid="{./uid}">
71
+
72
+ <!-- 1. CONTENT (first) -->
73
+ <div class="d-flex gap-2 p-3">
74
+ <div class="d-none d-lg-block">
75
+ <a class="lh-1 text-decoration-none" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "40px", true, "not-responsive")}</a>
76
+ </div>
77
+ <div class="post-body d-flex flex-column gap-2 flex-grow-1 hover-parent" style="min-width: 0px;">
78
+ {{{ if ./isMainPost }}}
79
+ <a class="lh-1 topic-title fw-semibold fs-5 text-reset text-break d-block" href="{config.relative_path}/topic/{./topic.slug}">
80
+ {./topic.title}
81
+ </a>
82
+ {{{ end }}}
83
+
84
+ <div class="d-flex gap-1 post-info text-sm align-items-center">
85
+ <div class="post-author d-flex align-items-center gap-1">
86
+ <a class="d-inline d-lg-none lh-1 text-decoration-none" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "16px", true, "not-responsive")}</a>
87
+ <a class="lh-normal fw-semibold text-nowrap" href="{config.relative_path}/user/{./user.userslug}">{./user.displayname}</a>
88
+ </div>
89
+ {{{ if !./isMainPost}}}{./repliedString}{{{ else }}}<span class="timeago text-muted lh-normal" title="{./timestampISO}"></span>{{{ end}}}
90
+ </div>
91
+
92
+ <div component="post/content" class="content text-sm text-break position-relative truncate-post-content">
93
+ <a href="{config.relative_path}/post/{./pid}" class="stretched-link"></a>
94
+ {./content}
95
+ </div>
96
+ <div class="position-relative hover-visible">
97
+ <button component="show/more" class="btn btn-light btn-sm rounded-pill position-absolute start-50 translate-middle-x bottom-0 z-1 hidden ff-secondary">[[feed:see-more]]</button>
98
+ </div>
99
+ </div>
100
+ </div>
101
+
102
+ <!-- 2. IMAGE (second) -->
103
+ {{{ if (showThumbs && ./topic.thumbs.length)}}}
104
+ <div class="p-1 position-relative">
105
+ <div class="overflow-hidden rounded-1" style="max-height: 300px;">
106
+ <a href="{config.relative_path}/topic/{./topic.slug}">
107
+ <img class="w-100" src="{./topic.thumbs.0.url}">
108
+ </a>
109
+ </div>
110
+
111
+ <div class="position-absolute end-0 bottom-0 p-3 d-flex gap-2 align-items-center pe-none">
112
+ {{{ each ./topic.thumbs }}}
113
+ {{{ if (@index != 0) }}}
114
+ <img class="rounded-1" style="max-height: 64px; object-fit: contain;" src="{./url}">
115
+ {{{ end }}}
116
+ {{{ end }}}
117
+ </div>
118
+ </div>
119
+ {{{ end }}}
120
+
121
+ <!-- 3. ACTION BAR (third - separated from content) -->
122
+ <div class="feed-action-bar d-flex justify-content-between px-3 py-2 border-top">
123
+ <a href="{config.relative_path}/post/{{{ if ./topic.teaserPid }}}{./topic.teaserPid}{{{ else }}}{./pid}{{{ end }}}" class="btn btn-link btn-sm text-body {{{ if !./isMainPost }}}invisible{{{ end }}}"><i class="fa-fw fa-regular fa-message text-muted"></i> {humanReadableNumber(./topic.postcount)}</a>
124
+
125
+ <a href="#" data-pid="{./pid}" data-action="bookmark" data-bookmarked="{./bookmarked}" data-bookmarks="{./bookmarks}" class="btn btn-link btn-sm text-body"><i class="fa-fw fa-bookmark {{{ if ./bookmarked }}}fa text-primary{{{ else }}}fa-regular text-muted{{{ end }}}"></i> <span component="bookmark-count">{humanReadableNumber(./bookmarks)}</span></a>
126
+
127
+ <a href="#" data-pid="{./pid}" data-action="upvote" data-upvoted="{./upvoted}" data-upvotes="{./upvotes}" class="btn btn-link btn-sm text-body"><i class="fa-fw fa-heart {{{ if ./upvoted }}}fa text-danger{{{ else }}}fa-regular text-muted{{{ end }}}"></i> <span component="upvote-count">{humanReadableNumber(./upvotes)}</span></a>
128
+
129
+ <a href="#" data-pid="{./pid}" data-is-main="{./isMainPost}" data-tid="{./tid}" data-action="reply" class="btn btn-link btn-sm text-body"><i class="fa-fw fa fa-reply text-muted"></i> [[topic:reply]]</a>
130
+ </div>
131
+ </li>
132
+ {{{ end }}}
133
+ </ul>
134
+ </div>
135
+
136
+ <div data-widget-area="right" class="col-lg-3 col-sm-12 {{{ if !widgets.right.length }}}hidden{{{ end }}}">
137
+ {{{each widgets.right}}}
138
+ {{widgets.right.html}}
139
+ {{{end}}}
140
+ </div>
141
+ </div>
142
+ </div>
143
+
144
+ <div data-widget-area="footer">
145
+ {{{each widgets.footer}}}
146
+ {{widgets.footer.html}}
147
+ {{{end}}}
148
+ </div>