@okjavis/nodebb-theme-javis 2.4.0 → 2.5.0
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/plugin.json +6 -1
- package/scss/_base.scss +7 -0
- package/scss/_composer.scss +8 -0
- package/scss/_feed.scss +15 -2
- package/static/lib/theme.js +49 -11
- package/templates/admin/plugins/javis.tpl +76 -0
- package/theme.js +35 -4
- package/templates/feed.tpl +0 -163
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
"library": "./theme.js",
|
|
7
7
|
"baseTheme": "nodebb-theme-harmony",
|
|
8
8
|
"hooks": [
|
|
9
|
+
{ "hook": "static:app.load", "method": "init" },
|
|
9
10
|
{ "hook": "filter:widgets.getAreas", "method": "defineWidgetAreas" },
|
|
10
11
|
{ "hook": "filter:admin.header.build", "method": "addAdminNavigation" },
|
|
11
|
-
{ "hook": "filter:config.get", "method": "getThemeConfig" }
|
|
12
|
+
{ "hook": "filter:config.get", "method": "getThemeConfig" },
|
|
13
|
+
{ "hook": "filter:settings.get", "method": "getAdminSettings" }
|
|
12
14
|
],
|
|
13
15
|
"staticDirs": {
|
|
14
16
|
"static": "./static"
|
|
@@ -16,6 +18,9 @@
|
|
|
16
18
|
"scripts": [
|
|
17
19
|
"static/lib/theme.js"
|
|
18
20
|
],
|
|
21
|
+
"modules": {
|
|
22
|
+
"../admin/plugins/javis.js": "public/admin.js"
|
|
23
|
+
},
|
|
19
24
|
"templates": "templates",
|
|
20
25
|
"screenshot": "screenshot.png",
|
|
21
26
|
"version": "1.5.0",
|
package/scss/_base.scss
CHANGED
|
@@ -28,6 +28,13 @@ nav[component="sidebar/left"] {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// Sidebar toggle link padding
|
|
32
|
+
.sidebar-toggle {
|
|
33
|
+
[component="sidebar/toggle"] {
|
|
34
|
+
padding: $jv-space-4 $jv-space-5 !important; // 16px vertical, 20px horizontal
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
// Reddit-style layout: Equal spacing and wider feed
|
|
32
39
|
.feed > .row {
|
|
33
40
|
max-width: 1400px; // Reddit's approximate max-width
|
package/scss/_composer.scss
CHANGED
|
@@ -74,6 +74,14 @@ html.composing .composer .mobile-navbar {
|
|
|
74
74
|
display: none !important;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
// ===========================================================
|
|
78
|
+
// HIDE PREVIEW AND HELP BUTTONS
|
|
79
|
+
// ===========================================================
|
|
80
|
+
html.composing .composer [data-action="preview"],
|
|
81
|
+
html.composing .composer [data-action="help"] {
|
|
82
|
+
display: none !important;
|
|
83
|
+
}
|
|
84
|
+
|
|
77
85
|
// ===========================================================
|
|
78
86
|
// HEADER
|
|
79
87
|
// ===========================================================
|
package/scss/_feed.scss
CHANGED
|
@@ -24,6 +24,18 @@
|
|
|
24
24
|
display: none !important;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
// Hide the original "New Topic" button via CSS to prevent flicker
|
|
29
|
+
// The composer prompt card will replace it via JS
|
|
30
|
+
#new_topic {
|
|
31
|
+
display: none !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Hide the controls row (category dropdown, options)
|
|
35
|
+
// The composer prompt card replaces this functionality
|
|
36
|
+
> .row > div > .d-flex.justify-content-between.py-2.mb-2 {
|
|
37
|
+
display: none !important;
|
|
38
|
+
}
|
|
27
39
|
}
|
|
28
40
|
|
|
29
41
|
// ===========================================================
|
|
@@ -33,7 +45,8 @@
|
|
|
33
45
|
background: $jv-surface;
|
|
34
46
|
border: 1px solid $jv-border-subtle;
|
|
35
47
|
border-radius: $jv-radius-lg;
|
|
36
|
-
padding: $jv-space-4;
|
|
48
|
+
padding: $jv-space-4 $jv-space-6; // 16px top/bottom, 24px left/right
|
|
49
|
+
margin-top: $jv-space-4; // Align with trending tags padding-top
|
|
37
50
|
margin-bottom: $jv-space-4;
|
|
38
51
|
box-shadow: $jv-shadow-sm;
|
|
39
52
|
transition: box-shadow $jv-transition-fast, border-color $jv-transition-fast;
|
|
@@ -358,7 +371,7 @@
|
|
|
358
371
|
font-weight: 700 !important; // Bolder for better hierarchy
|
|
359
372
|
line-height: 1.4 !important; // Better readability
|
|
360
373
|
color: $jv-text-main !important;
|
|
361
|
-
margin-bottom:
|
|
374
|
+
margin-bottom: 0;
|
|
362
375
|
letter-spacing: -0.02em; // Tighter for modern feel
|
|
363
376
|
|
|
364
377
|
&:hover {
|
package/static/lib/theme.js
CHANGED
|
@@ -375,27 +375,65 @@
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
/**
|
|
378
|
-
* Initialize
|
|
379
|
-
*
|
|
380
|
-
*
|
|
378
|
+
* Initialize LinkedIn-style composer prompt card on feed page
|
|
379
|
+
* Injects the card HTML and sets up click handlers
|
|
380
|
+
* The original "New Topic" button is hidden via CSS to prevent flicker
|
|
381
381
|
*/
|
|
382
382
|
function initFeedComposerPromptHandler() {
|
|
383
|
-
// Only run on feed page
|
|
384
|
-
|
|
385
|
-
if (!$card.length) {
|
|
383
|
+
// Only run on feed page
|
|
384
|
+
if (!$('.feed').length) {
|
|
386
385
|
return;
|
|
387
386
|
}
|
|
388
387
|
|
|
389
|
-
//
|
|
390
|
-
if ($
|
|
388
|
+
// Don't re-inject if already present
|
|
389
|
+
if ($('.composer-prompt-card').length) {
|
|
391
390
|
return;
|
|
392
391
|
}
|
|
393
|
-
$card.data('handler-initialized', true);
|
|
394
392
|
|
|
395
|
-
|
|
393
|
+
// Find the existing controls row (contains New Topic button)
|
|
394
|
+
var $controlsRow = $('.feed .d-flex.justify-content-between.py-2.mb-2').first();
|
|
395
|
+
if (!$controlsRow.length) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Get user info for avatar - clone from header avatar which is already correctly rendered
|
|
400
|
+
var isLoggedIn = false;
|
|
401
|
+
var avatarHtml = '';
|
|
402
|
+
|
|
403
|
+
if (typeof app !== 'undefined' && app.user && app.user.uid) {
|
|
404
|
+
isLoggedIn = true;
|
|
405
|
+
// Clone the avatar from the header - it's already correctly rendered by NodeBB
|
|
406
|
+
var $headerAvatar = $('[component="header/avatar"] img, [component="header/avatar"] [component="avatar/picture"]').first();
|
|
407
|
+
if ($headerAvatar.length) {
|
|
408
|
+
avatarHtml = '<img src="' + $headerAvatar.attr('src') + '" alt="Avatar" class="avatar avatar-rounded" />';
|
|
409
|
+
} else {
|
|
410
|
+
// Fallback to icon-based avatar from header
|
|
411
|
+
var $headerIcon = $('[component="header/avatar"] [component="avatar/icon"]').first();
|
|
412
|
+
if ($headerIcon.length) {
|
|
413
|
+
avatarHtml = $headerIcon.clone().addClass('avatar avatar-rounded').get(0).outerHTML;
|
|
414
|
+
} else {
|
|
415
|
+
// Last resort: use placeholder
|
|
416
|
+
avatarHtml = '<div class="avatar avatar-rounded avatar-placeholder"><i class="fa fa-user"></i></div>';
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
avatarHtml = '<div class="avatar avatar-rounded avatar-placeholder"><i class="fa fa-user"></i></div>';
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
var cardHtml = '<div class="composer-prompt-card' + (isLoggedIn ? '' : ' composer-prompt-guest') + '">' +
|
|
424
|
+
'<div class="composer-prompt-inner">' +
|
|
425
|
+
'<div class="composer-prompt-avatar">' + avatarHtml + '</div>' +
|
|
426
|
+
'<button class="composer-prompt-input" type="button" id="composer-prompt-btn">' +
|
|
427
|
+
'<span class="composer-prompt-placeholder">' + (isLoggedIn ? 'Start a discussion...' : 'Sign in to start a discussion...') + '</span>' +
|
|
428
|
+
'</button>' +
|
|
429
|
+
'</div>' +
|
|
430
|
+
'</div>';
|
|
431
|
+
|
|
432
|
+
// Insert the card before the controls row
|
|
433
|
+
$controlsRow.before(cardHtml);
|
|
396
434
|
|
|
397
435
|
// Bind click handler to open composer
|
|
398
|
-
$card.on('click', '.composer-prompt-input, .composer-prompt-action', function(e) {
|
|
436
|
+
$('.composer-prompt-card').on('click', '.composer-prompt-input, .composer-prompt-action', function(e) {
|
|
399
437
|
e.preventDefault();
|
|
400
438
|
|
|
401
439
|
if (!isLoggedIn) {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<div class="acp-page-container">
|
|
2
|
+
<!-- IMPORT admin/partials/settings/header.tpl -->
|
|
3
|
+
|
|
4
|
+
<div class="row m-0">
|
|
5
|
+
<div id="spy-container" class="col-12 col-md-8 px-0 mb-4" tabindex="0">
|
|
6
|
+
<form role="form" class="javis-settings">
|
|
7
|
+
<div class="mb-4">
|
|
8
|
+
<h5 class="fw-bold">JAVIS Theme Settings</h5>
|
|
9
|
+
<p class="text-muted">Configure the default settings for the JAVIS Community theme.</p>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="form-check form-switch mb-3">
|
|
13
|
+
<input type="checkbox" class="form-check-input" id="enableQuickReply" name="enableQuickReply" />
|
|
14
|
+
<label for="enableQuickReply" class="form-check-label">Enable Quick Reply</label>
|
|
15
|
+
<p class="form-text">Allow users to quickly reply to topics without opening the full composer.</p>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="form-check form-switch mb-3">
|
|
19
|
+
<input type="checkbox" class="form-check-input" id="enableBreadcrumbs" name="enableBreadcrumbs" />
|
|
20
|
+
<label for="enableBreadcrumbs" class="form-check-label">Enable Breadcrumbs</label>
|
|
21
|
+
<p class="form-text">Show navigation breadcrumbs at the top of pages.</p>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="form-check form-switch mb-3">
|
|
25
|
+
<input type="checkbox" class="form-check-input" id="centerHeaderElements" name="centerHeaderElements" />
|
|
26
|
+
<label for="centerHeaderElements" class="form-check-label">Center Header Elements</label>
|
|
27
|
+
<p class="form-text">Center-align the header navigation elements.</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="form-check form-switch mb-3">
|
|
31
|
+
<input type="checkbox" class="form-check-input" id="mobileTopicTeasers" name="mobileTopicTeasers" />
|
|
32
|
+
<label for="mobileTopicTeasers" class="form-check-label">Mobile Topic Teasers</label>
|
|
33
|
+
<p class="form-text">Show topic teasers on mobile devices.</p>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="form-check form-switch mb-3">
|
|
37
|
+
<input type="checkbox" class="form-check-input" id="stickyToolbar" name="stickyToolbar" />
|
|
38
|
+
<label for="stickyToolbar" class="form-check-label">Sticky Toolbar</label>
|
|
39
|
+
<p class="form-text">Keep the topic toolbar visible when scrolling.</p>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="form-check form-switch mb-3">
|
|
43
|
+
<input type="checkbox" class="form-check-input" id="topicSidebarTools" name="topicSidebarTools" />
|
|
44
|
+
<label for="topicSidebarTools" class="form-check-label">Topic Sidebar Tools</label>
|
|
45
|
+
<p class="form-text">Show topic tools in the sidebar instead of inline.</p>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="form-check form-switch mb-3">
|
|
49
|
+
<input type="checkbox" class="form-check-input" id="autohideBottombar" name="autohideBottombar" />
|
|
50
|
+
<label for="autohideBottombar" class="form-check-label">Auto-hide Bottom Bar</label>
|
|
51
|
+
<p class="form-text">Automatically hide the bottom navigation bar when scrolling down.</p>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="form-check form-switch mb-3">
|
|
55
|
+
<input type="checkbox" class="form-check-input" id="topMobilebar" name="topMobilebar" />
|
|
56
|
+
<label for="topMobilebar" class="form-check-label">Top Mobile Bar</label>
|
|
57
|
+
<p class="form-text">Show mobile navigation at the top instead of bottom.</p>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="form-check form-switch mb-3">
|
|
61
|
+
<input type="checkbox" class="form-check-input" id="openSidebars" name="openSidebars" />
|
|
62
|
+
<label for="openSidebars" class="form-check-label">Open Sidebars by Default</label>
|
|
63
|
+
<p class="form-text">Keep sidebars expanded by default (JAVIS default: enabled).</p>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="form-check form-switch mb-3">
|
|
67
|
+
<input type="checkbox" class="form-check-input" id="chatModals" name="chatModals" />
|
|
68
|
+
<label for="chatModals" class="form-check-label">Chat Modals</label>
|
|
69
|
+
<p class="form-text">Open chats in modal windows instead of navigating to the chat page.</p>
|
|
70
|
+
</div>
|
|
71
|
+
</form>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<!-- IMPORT admin/partials/settings/toc.tpl -->
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
package/theme.js
CHANGED
|
@@ -9,9 +9,11 @@ const meta = require.main.require('./src/meta');
|
|
|
9
9
|
const user = require.main.require('./src/user');
|
|
10
10
|
const _ = require.main.require('lodash');
|
|
11
11
|
|
|
12
|
+
const controllers = require('./lib/controllers');
|
|
13
|
+
|
|
12
14
|
const theme = {};
|
|
13
15
|
|
|
14
|
-
//
|
|
16
|
+
// JAVIS defaults - openSidebars is 'on' by default
|
|
15
17
|
const defaults = {
|
|
16
18
|
enableQuickReply: 'on',
|
|
17
19
|
enableBreadcrumbs: 'on',
|
|
@@ -26,12 +28,24 @@ const defaults = {
|
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
|
-
*
|
|
31
|
+
* Hook: static:app.load
|
|
32
|
+
* Initialize routes for admin panel
|
|
33
|
+
*/
|
|
34
|
+
theme.init = async function (params) {
|
|
35
|
+
const { router } = params;
|
|
36
|
+
const routeHelpers = require.main.require('./src/routes/helpers');
|
|
37
|
+
|
|
38
|
+
// Admin panel route
|
|
39
|
+
routeHelpers.setupAdminPageRoute(router, '/admin/plugins/javis', [], controllers.renderAdminPage);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Load theme config
|
|
30
44
|
* Uses JAVIS defaults (with openSidebars: 'on')
|
|
31
45
|
*/
|
|
32
46
|
async function loadThemeConfig(uid) {
|
|
33
47
|
const [themeConfig, userConfig] = await Promise.all([
|
|
34
|
-
meta.settings.get('
|
|
48
|
+
meta.settings.get('javis'),
|
|
35
49
|
user.getSettings(uid),
|
|
36
50
|
]);
|
|
37
51
|
|
|
@@ -53,6 +67,9 @@ async function loadThemeConfig(uid) {
|
|
|
53
67
|
return config;
|
|
54
68
|
}
|
|
55
69
|
|
|
70
|
+
// Export loadThemeConfig for potential use by other modules
|
|
71
|
+
theme.loadThemeConfig = loadThemeConfig;
|
|
72
|
+
|
|
56
73
|
/**
|
|
57
74
|
* Hook: filter:config.get
|
|
58
75
|
* Sets config.theme with JAVIS-specific defaults
|
|
@@ -63,6 +80,20 @@ theme.getThemeConfig = async function (config) {
|
|
|
63
80
|
return config;
|
|
64
81
|
};
|
|
65
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Hook: filter:settings.get
|
|
85
|
+
* Provide default values for admin settings
|
|
86
|
+
*/
|
|
87
|
+
theme.getAdminSettings = async function (hookData) {
|
|
88
|
+
if (hookData.plugin === 'javis') {
|
|
89
|
+
hookData.values = {
|
|
90
|
+
...defaults,
|
|
91
|
+
...hookData.values,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return hookData;
|
|
95
|
+
};
|
|
96
|
+
|
|
66
97
|
theme.defineWidgetAreas = async (areas) => {
|
|
67
98
|
// Define widget areas like Harmony does
|
|
68
99
|
const locations = ['header', 'sidebar', 'footer'];
|
|
@@ -125,7 +156,7 @@ theme.defineWidgetAreas = async (areas) => {
|
|
|
125
156
|
|
|
126
157
|
theme.addAdminNavigation = (header) => {
|
|
127
158
|
header.plugins.push({
|
|
128
|
-
route: '/plugins/javis
|
|
159
|
+
route: '/plugins/javis',
|
|
129
160
|
icon: 'fa-paint-brush',
|
|
130
161
|
name: 'JAVIS Theme',
|
|
131
162
|
});
|
package/templates/feed.tpl
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
<div data-widget-area="header">
|
|
2
|
-
{{{each widgets.header}}}
|
|
3
|
-
{{widgets.header.html}}
|
|
4
|
-
{{{end}}}
|
|
5
|
-
</div>
|
|
6
|
-
<style>.feed .post-body .content > p:last-child { margin-bottom: 0px; }</style>
|
|
7
|
-
<div class="feed">
|
|
8
|
-
<div class="row">
|
|
9
|
-
<div data-widget-area="left" class="col-lg-3 col-sm-12 {{{ if !widgets.left.length }}}hidden{{{ end }}}">
|
|
10
|
-
{{{each widgets.left}}}
|
|
11
|
-
{{widgets.left.html}}
|
|
12
|
-
{{{end}}}
|
|
13
|
-
</div>
|
|
14
|
-
{{{ if ((widgets.left.length && widgets.right.length) || (!widgets.left.length && !widgets.right.length))}}}
|
|
15
|
-
<div class="col-lg-6 col-sm-12 mx-auto">
|
|
16
|
-
{{{ end }}}
|
|
17
|
-
{{{ if (widgets.left.length && !widgets.right.length) }}}
|
|
18
|
-
<div class="col-lg-6 col-sm-12 me-auto">
|
|
19
|
-
{{{ end }}}
|
|
20
|
-
{{{ if (!widgets.left.length && widgets.right.length) }}}
|
|
21
|
-
<div class="col-lg-6 col-sm-12 ms-auto">
|
|
22
|
-
{{{ end }}}
|
|
23
|
-
|
|
24
|
-
<!-- JAVIS: LinkedIn-style Composer Prompt Card -->
|
|
25
|
-
<div class="composer-prompt-card{{{ if !loggedIn }}} composer-prompt-guest{{{ end }}}">
|
|
26
|
-
<div class="composer-prompt-inner">
|
|
27
|
-
<div class="composer-prompt-avatar">
|
|
28
|
-
{{{ if loggedIn }}}
|
|
29
|
-
{buildAvatar(loggedInUser, "48px", true, "avatar avatar-rounded")}
|
|
30
|
-
{{{ else }}}
|
|
31
|
-
<div class="avatar avatar-rounded avatar-placeholder"><i class="fa fa-user"></i></div>
|
|
32
|
-
{{{ end }}}
|
|
33
|
-
</div>
|
|
34
|
-
<button class="composer-prompt-input" type="button" id="composer-prompt-btn">
|
|
35
|
-
<span class="composer-prompt-placeholder">{{{ if loggedIn }}}Start a discussion...{{{ else }}}Sign in to start a discussion...{{{ end }}}</span>
|
|
36
|
-
</button>
|
|
37
|
-
{{{ if loggedIn }}}
|
|
38
|
-
<div class="composer-prompt-actions">
|
|
39
|
-
<button class="composer-prompt-action" type="button" title="Add image"><i class="fa fa-image"></i></button>
|
|
40
|
-
<button class="composer-prompt-action" type="button" title="Add link"><i class="fa fa-link"></i></button>
|
|
41
|
-
</div>
|
|
42
|
-
{{{ end }}}
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<!-- Hidden controls row - New Topic button kept for JS functionality -->
|
|
47
|
-
<div class="d-flex justify-content-between py-2 mb-2 gap-1 feed-controls-row">
|
|
48
|
-
{{{ if canPost }}}
|
|
49
|
-
<button id="new_topic" class="btn btn-primary btn-sm d-none">[[category:new-topic-button]]</button>
|
|
50
|
-
{{{ end }}}
|
|
51
|
-
{{{ if (!loggedIn && !canPost) }}}
|
|
52
|
-
<a href="{config.relative_path}/login" class="btn btn-primary btn-sm d-none">[[category:guest-login-post]]</a>
|
|
53
|
-
{{{ end }}}
|
|
54
|
-
|
|
55
|
-
<div class="d-flex justify-content-end gap-1 ms-auto">
|
|
56
|
-
<!-- IMPORT partials/category/filter-dropdown-right.tpl -->
|
|
57
|
-
|
|
58
|
-
<div id="options-dropdown" class="btn-group dropdown dropdown-right bottom-sheet">
|
|
59
|
-
<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">
|
|
60
|
-
<i class="fa fa-fw fa-gear text-primary"></i>
|
|
61
|
-
</button>
|
|
62
|
-
<ul class="dropdown-menu p-1 text-sm" role="menu">
|
|
63
|
-
<li class="py-1 px-3">
|
|
64
|
-
<div class="form-check form-switch d-flex px-0 align-items-center justify-content-between gap-3">
|
|
65
|
-
<label class="form-check-label text-nowrap" for="showAllPosts">[[feed:show-all-posts]]</label>
|
|
66
|
-
<input class="form-check-input float-none m-0 pointer" type="checkbox" role="switch" id="showAllPosts" {{{ if showAllPosts }}}checked{{{ end }}}>
|
|
67
|
-
</div>
|
|
68
|
-
</li>
|
|
69
|
-
{{{ if loggedIn }}}
|
|
70
|
-
<li class="py-1 px-3">
|
|
71
|
-
<div class="form-check form-switch d-flex px-0 align-items-center justify-content-between gap-3">
|
|
72
|
-
<label class="form-check-label text-nowrap" for="showFollowedUsers">[[feed:followed-users-only]]</label>
|
|
73
|
-
<input class="form-check-input float-none m-0 pointer" type="checkbox" role="switch" id="showFollowedUsers" {{{ if showFollowed }}}checked{{{ end }}}>
|
|
74
|
-
</div>
|
|
75
|
-
</li>
|
|
76
|
-
{{{ end }}}
|
|
77
|
-
</ul>
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
|
|
82
|
-
{{{ if !posts.length }}}
|
|
83
|
-
<div class="alert alert-warning text-center">[[feed:no-posts-found]] {{{ if !following.length }}}[[feed:are-you-following-anyone]] {{{ end }}}</div>
|
|
84
|
-
{{{ end }}}
|
|
85
|
-
|
|
86
|
-
<ul component="posts" class="list-unstyled" data-nextstart="{nextStart}">
|
|
87
|
-
{{{ each posts }}}
|
|
88
|
-
<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}">
|
|
89
|
-
|
|
90
|
-
{{{ if (showThumbs && ./topic.thumbs.length)}}}
|
|
91
|
-
<div class="p-1 position-relative">
|
|
92
|
-
<div class="overflow-hidden rounded-1" style="max-height: 300px;">
|
|
93
|
-
<a href="{config.relative_path}/topic/{./topic.slug}">
|
|
94
|
-
<img class="w-100" src="{./topic.thumbs.0.url}">
|
|
95
|
-
</a>
|
|
96
|
-
</div>
|
|
97
|
-
|
|
98
|
-
<div class="position-absolute end-0 bottom-0 p-3 d-flex gap-2 align-items-center pe-none">
|
|
99
|
-
{{{ each ./topic.thumbs }}}
|
|
100
|
-
{{{ if (@index != 0) }}}
|
|
101
|
-
<img class="rounded-1" style="max-height: 64px; object-fit: contain;" src="{./url}">
|
|
102
|
-
{{{ end }}}
|
|
103
|
-
{{{ end }}}
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
{{{ end }}}
|
|
107
|
-
|
|
108
|
-
<div class="d-flex gap-2 p-3">
|
|
109
|
-
<div class="d-none d-lg-block">
|
|
110
|
-
<a class="lh-1 text-decoration-none" href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "40px", true, "not-responsive")}</a>
|
|
111
|
-
</div>
|
|
112
|
-
<div class="post-body d-flex flex-column gap-2 flex-grow-1 hover-parent" style="min-width: 0px;">
|
|
113
|
-
{{{ if ./isMainPost }}}
|
|
114
|
-
<a class="lh-1 topic-title fw-semibold fs-5 text-reset text-break d-block" href="{config.relative_path}/topic/{./topic.slug}">
|
|
115
|
-
{./topic.title}
|
|
116
|
-
</a>
|
|
117
|
-
{{{ end }}}
|
|
118
|
-
|
|
119
|
-
<div class="d-flex gap-1 post-info text-sm align-items-center">
|
|
120
|
-
<div class="post-author d-flex align-items-center gap-1">
|
|
121
|
-
<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>
|
|
122
|
-
<a class="lh-normal fw-semibold text-nowrap" href="{config.relative_path}/user/{./user.userslug}">{./user.displayname}</a>
|
|
123
|
-
</div>
|
|
124
|
-
{{{ if !./isMainPost}}}{./repliedString}{{{ else }}}<span class="timeago text-muted lh-normal" title="{./timestampISO}"></span>{{{ end}}}
|
|
125
|
-
</div>
|
|
126
|
-
|
|
127
|
-
<div component="post/content" class="content text-sm text-break position-relative truncate-post-content">
|
|
128
|
-
<a href="{config.relative_path}/post/{./pid}" class="stretched-link"></a>
|
|
129
|
-
{./content}
|
|
130
|
-
</div>
|
|
131
|
-
<div class="position-relative hover-visible">
|
|
132
|
-
<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>
|
|
133
|
-
</div>
|
|
134
|
-
<hr class="my-2"/>
|
|
135
|
-
<div class="d-flex justify-content-between">
|
|
136
|
-
<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>
|
|
137
|
-
|
|
138
|
-
<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>
|
|
139
|
-
|
|
140
|
-
<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>
|
|
141
|
-
|
|
142
|
-
<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>
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
</div>
|
|
146
|
-
</li>
|
|
147
|
-
{{{ end }}}
|
|
148
|
-
</ul>
|
|
149
|
-
</div>
|
|
150
|
-
|
|
151
|
-
<div data-widget-area="right" class="col-lg-3 col-sm-12 {{{ if !widgets.right.length }}}hidden{{{ end }}}">
|
|
152
|
-
{{{each widgets.right}}}
|
|
153
|
-
{{widgets.right.html}}
|
|
154
|
-
{{{end}}}
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
<div data-widget-area="footer">
|
|
160
|
-
{{{each widgets.footer}}}
|
|
161
|
-
{{widgets.footer.html}}
|
|
162
|
-
{{{end}}}
|
|
163
|
-
</div>
|