Merge pull request #1327 from thelounge/xpaw/view-changelog
Changelog viewer and updater checker in the client
This commit is contained in:
commit
32a95c4d75
@ -80,6 +80,7 @@ button {
|
|||||||
|
|
||||||
code,
|
code,
|
||||||
kbd,
|
kbd,
|
||||||
|
pre,
|
||||||
.irc-monospace,
|
.irc-monospace,
|
||||||
textarea#user-specified-css-input {
|
textarea#user-specified-css-input {
|
||||||
font-family: Consolas, Menlo, Monaco, "Lucida Console", "DejaVu Sans Mono", "Courier New", monospace;
|
font-family: Consolas, Menlo, Monaco, "Lucida Console", "DejaVu Sans Mono", "Courier New", monospace;
|
||||||
@ -93,6 +94,19 @@ code {
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
display: block;
|
||||||
|
padding: 9.5px;
|
||||||
|
margin: 0 0 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.42857143;
|
||||||
|
color: #333;
|
||||||
|
word-break: break-all;
|
||||||
|
word-wrap: break-word;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
@ -140,12 +154,25 @@ kbd {
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-width: 1px;
|
||||||
|
letter-spacing: 0;
|
||||||
|
word-spacing: 0;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
margin: 80px auto;
|
margin: 80px auto;
|
||||||
max-width: 480px;
|
max-width: 480px;
|
||||||
touch-action: pan-y;
|
touch-action: pan-y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#help .container,
|
||||||
|
#changelog .container {
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
::-moz-placeholder {
|
::-moz-placeholder {
|
||||||
color: rgba(0, 0, 0, 0.35);
|
color: rgba(0, 0, 0, 0.35);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -208,7 +235,11 @@ kbd {
|
|||||||
#chat .nick .from::before,
|
#chat .nick .from::before,
|
||||||
#chat .action .from::before,
|
#chat .action .from::before,
|
||||||
#chat .toggle-button::after,
|
#chat .toggle-button::after,
|
||||||
|
.changelog-version::before,
|
||||||
.context-menu-item::before,
|
.context-menu-item::before,
|
||||||
|
#help .website-link::before,
|
||||||
|
#help .documentation-link::before,
|
||||||
|
#help .report-issue-link::before,
|
||||||
#nick button::before,
|
#nick button::before,
|
||||||
#image-viewer .previous-image-btn::before,
|
#image-viewer .previous-image-btn::before,
|
||||||
#image-viewer .next-image-btn::before {
|
#image-viewer .next-image-btn::before {
|
||||||
@ -259,6 +290,21 @@ kbd {
|
|||||||
color: #7f8c8d;
|
color: #7f8c8d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#help .website-link::before,
|
||||||
|
#help .documentation-link::before,
|
||||||
|
#help .report-issue-link::before,
|
||||||
|
#chat .toggle-button {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
|
/* These 2 directives are loosely taken from .fa-fw */
|
||||||
|
width: 1.35em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#help .website-link::before { content: "\f0ac"; /* http://fontawesome.io/icon/globe/ */ }
|
||||||
|
#help .documentation-link::before { content: "\f19d"; /* http://fontawesome.io/icon/graduation-cap/ */ }
|
||||||
|
#help .report-issue-link::before { content: "\f188"; /* http://fontawesome.io/icon/bug/ */ }
|
||||||
|
|
||||||
.session-list strong {
|
.session-list strong {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@ -704,6 +750,7 @@ kbd {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#windows li,
|
||||||
#windows p,
|
#windows p,
|
||||||
#windows label,
|
#windows label,
|
||||||
#settings .error {
|
#settings .error {
|
||||||
@ -761,6 +808,11 @@ kbd {
|
|||||||
padding-bottom: 7px;
|
padding-bottom: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#windows .window h2 small {
|
||||||
|
color: inherit;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
#windows .window h3 {
|
#windows .window h3 {
|
||||||
color: #7f8c8d;
|
color: #7f8c8d;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@ -1520,8 +1572,7 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
display: table-row;
|
display: table-row;
|
||||||
}
|
}
|
||||||
|
|
||||||
#help .help-item,
|
#help .help-item {
|
||||||
#help .about {
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1540,10 +1591,6 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#help .about {
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-apple #help .key-all,
|
.is-apple #help .key-all,
|
||||||
#help .key-apple {
|
#help .key-apple {
|
||||||
display: none;
|
display: none;
|
||||||
@ -1572,6 +1619,66 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
grid-column-start: 2;
|
grid-column-start: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.changelog-text {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog-text p {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window#changelog h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
border-bottom: 1px solid #7f8c8d;
|
||||||
|
color: #7f8c8d;
|
||||||
|
margin: 30px 0 10px;
|
||||||
|
padding-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog-version {
|
||||||
|
display: block;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: #d9edf7;
|
||||||
|
color: #31708f;
|
||||||
|
transition: color 0.2s, background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog-version::before {
|
||||||
|
margin-right: 6px;
|
||||||
|
content: "\f250"; /* http://fontawesome.io/icon/hourglass-o/ */
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog-version.new-version {
|
||||||
|
color: #8a6d3b;
|
||||||
|
background-color: #fcf8e3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog-version.new-version::before {
|
||||||
|
content: "\f087"; /* http://fontawesome.io/icon/thumbs-o-up/ */
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog-version.error {
|
||||||
|
color: #a94442;
|
||||||
|
background-color: #f2dede;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog-version.error::before {
|
||||||
|
margin-right: 6px;
|
||||||
|
content: "\f06a"; /* http://fontawesome.io/icon/exclamation-circle/ */
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog-version.up-to-date {
|
||||||
|
background-color: #dff0d8;
|
||||||
|
color: #3c763d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.changelog-version.up-to-date::before {
|
||||||
|
margin-right: 6px;
|
||||||
|
content: "\f00c"; /* http://fontawesome.io/icon/check/ */
|
||||||
|
}
|
||||||
|
|
||||||
#form {
|
#form {
|
||||||
background: #eee;
|
background: #eee;
|
||||||
border-top: 1px solid #ddd;
|
border-top: 1px solid #ddd;
|
||||||
@ -2055,7 +2162,6 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
|||||||
#windows .header .topic,
|
#windows .header .topic,
|
||||||
#settings .error,
|
#settings .error,
|
||||||
#help .help-item,
|
#help .help-item,
|
||||||
#help .about,
|
|
||||||
#loading,
|
#loading,
|
||||||
#context-menu,
|
#context-menu,
|
||||||
#form #input,
|
#form #input,
|
||||||
|
@ -85,6 +85,7 @@
|
|||||||
<div id="connect" class="window"></div>
|
<div id="connect" class="window"></div>
|
||||||
<div id="settings" class="window"></div>
|
<div id="settings" class="window"></div>
|
||||||
<div id="help" class="window"></div>
|
<div id="help" class="window"></div>
|
||||||
|
<div id="changelog" class="window"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -332,65 +332,43 @@ $(function() {
|
|||||||
$(this).closest(".msg.condensed").toggleClass("closed");
|
$(this).closest(".msg.condensed").toggleClass("closed");
|
||||||
});
|
});
|
||||||
|
|
||||||
sidebar.on("click", ".chan, button", function(e, data) {
|
let changelogRequestedAt = 0;
|
||||||
// Pushes states to history web API when clicking elements with a data-target attribute.
|
|
||||||
// States are very trivial and only contain a single `clickTarget` property which
|
|
||||||
// contains a CSS selector that targets elements which takes the user to a different view
|
|
||||||
// when clicked. The `popstate` event listener will trigger synthetic click events using that
|
|
||||||
// selector and thus take the user to a different view/state.
|
|
||||||
if (data && data.pushState === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const self = $(this);
|
|
||||||
const target = self.data("target");
|
|
||||||
if (!target) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const state = {};
|
|
||||||
|
|
||||||
if (self.hasClass("chan")) {
|
const openWindow = function openWindow(e, data) {
|
||||||
state.clickTarget = `#sidebar .chan[data-id="${self.data("id")}"]`;
|
|
||||||
} else {
|
|
||||||
state.clickTarget = `#footer button[data-target="${target}"]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (history && history.pushState) {
|
|
||||||
if (data && data.replaceHistory && history.replaceState) {
|
|
||||||
history.replaceState(state, null, target);
|
|
||||||
} else {
|
|
||||||
history.pushState(state, null, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
sidebar.on("click", ".chan, button", function() {
|
|
||||||
var self = $(this);
|
var self = $(this);
|
||||||
var target = self.data("target");
|
var target = self.data("target");
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
chat.data(
|
// This is a rather gross hack to account for sources that are in the
|
||||||
"id",
|
// sidebar specifically. Needs to be done better when window management gets
|
||||||
self.data("id")
|
// refactored.
|
||||||
);
|
const inSidebar = self.parents("#sidebar").length > 0;
|
||||||
socket.emit(
|
|
||||||
"open",
|
|
||||||
self.data("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
sidebar.find(".active").removeClass("active");
|
if (inSidebar) {
|
||||||
self.addClass("active")
|
chat.data(
|
||||||
.find(".badge")
|
"id",
|
||||||
.removeClass("highlight")
|
self.data("id")
|
||||||
.empty();
|
);
|
||||||
|
socket.emit(
|
||||||
|
"open",
|
||||||
|
self.data("id")
|
||||||
|
);
|
||||||
|
|
||||||
if (sidebar.find(".highlight").length === 0) {
|
sidebar.find(".active").removeClass("active");
|
||||||
utils.toggleNotificationMarkers(false);
|
self.addClass("active")
|
||||||
|
.find(".badge")
|
||||||
|
.removeClass("highlight")
|
||||||
|
.empty();
|
||||||
|
|
||||||
|
if (sidebar.find(".highlight").length === 0) {
|
||||||
|
utils.toggleNotificationMarkers(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
sidebarSlide.toggle(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
sidebarSlide.toggle(false);
|
|
||||||
|
|
||||||
var lastActive = $("#windows > .active");
|
var lastActive = $("#windows > .active");
|
||||||
|
|
||||||
lastActive
|
lastActive
|
||||||
@ -447,8 +425,49 @@ $(function() {
|
|||||||
socket.emit("sessions:get");
|
socket.emit("sessions:get");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target === "#help" || target === "#changelog") {
|
||||||
|
const now = Date.now();
|
||||||
|
// Don't check more than once an hour
|
||||||
|
if (now - changelogRequestedAt > 3600 * 1000) {
|
||||||
|
changelogRequestedAt = now;
|
||||||
|
socket.emit("changelog");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
focus();
|
focus();
|
||||||
});
|
|
||||||
|
// Pushes states to history web API when clicking elements with a data-target attribute.
|
||||||
|
// States are very trivial and only contain a single `clickTarget` property which
|
||||||
|
// contains a CSS selector that targets elements which takes the user to a different view
|
||||||
|
// when clicked. The `popstate` event listener will trigger synthetic click events using that
|
||||||
|
// selector and thus take the user to a different view/state.
|
||||||
|
if (data && data.pushState === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const state = {};
|
||||||
|
|
||||||
|
if (self.attr("id")) {
|
||||||
|
state.clickTarget = `#${self.attr("id")}`;
|
||||||
|
} else if (self.hasClass("chan")) {
|
||||||
|
state.clickTarget = `#sidebar .chan[data-id="${self.data("id")}"]`;
|
||||||
|
} else {
|
||||||
|
state.clickTarget = `#footer button[data-target="${target}"]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (history && history.pushState) {
|
||||||
|
if (data && data.replaceHistory && history.replaceState) {
|
||||||
|
history.replaceState(state, null, target);
|
||||||
|
} else {
|
||||||
|
history.pushState(state, null, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
sidebar.on("click", ".chan, button", openWindow);
|
||||||
|
$("#help").on("click", "#view-changelog, #back-to-help", openWindow);
|
||||||
|
$("#changelog").on("click", "#back-to-help", openWindow);
|
||||||
|
|
||||||
sidebar.on("click", "#sign-out", function() {
|
sidebar.on("click", "#sign-out", function() {
|
||||||
socket.emit("sign-out");
|
socket.emit("sign-out");
|
||||||
|
22
client/js/socket-events/changelog.js
Normal file
22
client/js/socket-events/changelog.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const $ = require("jquery");
|
||||||
|
const socket = require("../socket");
|
||||||
|
const templates = require("../../views");
|
||||||
|
|
||||||
|
socket.on("changelog", function(data) {
|
||||||
|
const container = $("#changelog-version-container");
|
||||||
|
|
||||||
|
if (data.latest) {
|
||||||
|
container.addClass("new-version");
|
||||||
|
container.html(templates.new_version(data));
|
||||||
|
} else if (data.current.changelog) {
|
||||||
|
container.addClass("up-to-date");
|
||||||
|
container.text("The Lounge is up to date!");
|
||||||
|
} else {
|
||||||
|
container.addClass("error");
|
||||||
|
container.text("An error has occurred, try to reload the page.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#changelog").html(templates.windows.changelog(data));
|
||||||
|
});
|
@ -14,6 +14,7 @@ socket.on("configuration", function(data) {
|
|||||||
$("#settings").html(templates.windows.settings(data));
|
$("#settings").html(templates.windows.settings(data));
|
||||||
$("#connect").html(templates.windows.connect(data));
|
$("#connect").html(templates.windows.connect(data));
|
||||||
$("#help").html(templates.windows.help(data));
|
$("#help").html(templates.windows.help(data));
|
||||||
|
$("#changelog").html(templates.windows.changelog());
|
||||||
|
|
||||||
$("#play").on("click", () => {
|
$("#play").on("click", () => {
|
||||||
const pop = new Audio();
|
const pop = new Audio();
|
||||||
|
@ -19,3 +19,4 @@ require("./users");
|
|||||||
require("./sign_out");
|
require("./sign_out");
|
||||||
require("./sessions_list");
|
require("./sessions_list");
|
||||||
require("./configuration");
|
require("./configuration");
|
||||||
|
require("./changelog");
|
||||||
|
@ -26,10 +26,12 @@ module.exports = {
|
|||||||
settings: require("./windows/settings.tpl"),
|
settings: require("./windows/settings.tpl"),
|
||||||
connect: require("./windows/connect.tpl"),
|
connect: require("./windows/connect.tpl"),
|
||||||
help: require("./windows/help.tpl"),
|
help: require("./windows/help.tpl"),
|
||||||
|
changelog: require("./windows/changelog.tpl"),
|
||||||
},
|
},
|
||||||
|
|
||||||
chan: require("./chan.tpl"),
|
chan: require("./chan.tpl"),
|
||||||
chat: require("./chat.tpl"),
|
chat: require("./chat.tpl"),
|
||||||
|
new_version: require("./new_version.tpl"),
|
||||||
contextmenu_divider: require("./contextmenu_divider.tpl"),
|
contextmenu_divider: require("./contextmenu_divider.tpl"),
|
||||||
contextmenu_item: require("./contextmenu_item.tpl"),
|
contextmenu_item: require("./contextmenu_item.tpl"),
|
||||||
date_marker: require("./date-marker.tpl"),
|
date_marker: require("./date-marker.tpl"),
|
||||||
|
6
client/views/new_version.tpl
Normal file
6
client/views/new_version.tpl
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
The Lounge <b>{{latest.version}}</b>{{#if latest.prerelease}} (pre-release){{/if}}
|
||||||
|
is now available.
|
||||||
|
|
||||||
|
<a href="{{latest.url}}" target="_blank" rel="noopener">
|
||||||
|
Read more on GitHub
|
||||||
|
</a>
|
20
client/views/windows/changelog.tpl
Normal file
20
client/views/windows/changelog.tpl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<div class="header">
|
||||||
|
<button class="lt" aria-label="Toggle channel list"></button>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<a href="#" id="back-to-help" data-target="#help">« Help</a>
|
||||||
|
|
||||||
|
{{#if current}}
|
||||||
|
<h1 class="title">Release notes for {{current.version}}</h1>
|
||||||
|
|
||||||
|
{{#if current.changelog}}
|
||||||
|
<h3>Introduction</h3>
|
||||||
|
<div class="changelog-text">{{{current.changelog}}}</div>
|
||||||
|
{{else}}
|
||||||
|
<p>Unable to retrieve releases from GitHub.</p>
|
||||||
|
<p><a href="https://github.com/thelounge/lounge/releases/tag/v{{current.version}}" target="_blank" rel="noopener">View release notes for this version on GitHub</a></p>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
<p>Loading changelog…</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
@ -4,6 +4,51 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="title">Help</h1>
|
<h1 class="title">Help</h1>
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
<small class="pull-right">
|
||||||
|
v{{version}}
|
||||||
|
(<a href="#" id="view-changelog" data-target="#changelog">release notes</a>)
|
||||||
|
</small>
|
||||||
|
About The Lounge
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="about">
|
||||||
|
{{#unless public}}
|
||||||
|
<p id="changelog-version-container" class="changelog-version">
|
||||||
|
Checking for updates...
|
||||||
|
</p>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#if gitCommit}}
|
||||||
|
<p>
|
||||||
|
The Lounge is running from source
|
||||||
|
(<a href="https://github.com/thelounge/lounge/tree/{{gitCommit}}" target="_blank" rel="noopener">commit <code>{{gitCommit}}</code></a>).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Compare
|
||||||
|
<a href="https://github.com/thelounge/lounge/compare/{{gitCommit}}...master" target="_blank" rel="noopener">between <code>{{gitCommit}}</code> and <code>master</code></a>
|
||||||
|
to see what you are missing
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Compare
|
||||||
|
<a href="https://github.com/thelounge/lounge/compare/{{version}}...{{gitCommit}}" target="_blank" rel="noopener">between <code>{{version}}</code> and <code>{{gitCommit}}</code></a>
|
||||||
|
to see your local changes</li>
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="https://thelounge.github.io/" target="_blank" rel="noopener" class="website-link">Website</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://thelounge.github.io/docs/" target="_blank" rel="noopener" class="documentation-link">Documentation</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://github.com/thelounge/lounge/issues/new" target="_blank" rel="noopener" class="report-issue-link">Report an issue…</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Keyboard Shortcuts</h2>
|
<h2>Keyboard Shortcuts</h2>
|
||||||
|
|
||||||
<div class="help-item">
|
<div class="help-item">
|
||||||
@ -11,7 +56,7 @@
|
|||||||
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>↑</kbd> / <kbd>↓</kbd>
|
<kbd class="key-all">Ctrl</kbd><kbd class="key-apple">⌘</kbd> + <kbd>↑</kbd> / <kbd>↓</kbd>
|
||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<p>Switch to the previous/next window in the channel list</p>
|
<p>Switch to the previous/next window in the channel list.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -481,20 +526,4 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>About The Lounge</h2>
|
|
||||||
|
|
||||||
<p class="about">
|
|
||||||
{{#if gitCommit}}
|
|
||||||
The Lounge is running from source
|
|
||||||
(<a href="https://github.com/thelounge/lounge/tree/{{ gitCommit }}" target="_blank" rel="noopener"><code>{{ gitCommit }}</code></a>).<br>
|
|
||||||
{{else}}
|
|
||||||
The Lounge is in version <strong>{{version}}</strong>
|
|
||||||
(<a href="https://github.com/thelounge/lounge/releases/tag/v{{ version }}" target="_blank" rel="noopener">See release notes</a>).<br>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<a href="https://thelounge.github.io/" target="_blank" rel="noopener">Website</a><br>
|
|
||||||
<a href="https://thelounge.github.io/docs/" target="_blank" rel="noopener">Documentation</a><br>
|
|
||||||
<a href="https://github.com/thelounge/lounge/issues/new" target="_blank" rel="noopener">Report a bug</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
79
src/plugins/changelog.js
Normal file
79
src/plugins/changelog.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const pkg = require("../../package.json");
|
||||||
|
const request = require("request");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
fetch,
|
||||||
|
};
|
||||||
|
|
||||||
|
const versions = {
|
||||||
|
current: {
|
||||||
|
version: `v${pkg.version}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function fetch(callback) {
|
||||||
|
// Serving information from cache
|
||||||
|
if (versions.current.changelog) {
|
||||||
|
callback(versions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.get({
|
||||||
|
uri: "https://api.github.com/repos/thelounge/lounge/releases",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/vnd.github.v3.html", // Request rendered markdown
|
||||||
|
"User-Agent": pkg.name + "; +" + pkg.repository.git, // Identify the client
|
||||||
|
},
|
||||||
|
}, (error, response, body) => {
|
||||||
|
if (error || response.statusCode !== 200) {
|
||||||
|
callback(versions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i;
|
||||||
|
let release;
|
||||||
|
let prerelease = false;
|
||||||
|
|
||||||
|
body = JSON.parse(body);
|
||||||
|
|
||||||
|
// Find the current release among releases on GitHub
|
||||||
|
for (i = 0; i < body.length; i++) {
|
||||||
|
release = body[i];
|
||||||
|
if (release.tag_name === versions.current.version) {
|
||||||
|
versions.current.changelog = release.body_html;
|
||||||
|
prerelease = release.prerelease;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the latest release made after the current one if there is one
|
||||||
|
if (i > 0) {
|
||||||
|
for (let j = 0; j < i; j++) {
|
||||||
|
release = body[j];
|
||||||
|
|
||||||
|
// Find latest release or pre-release if current version is also a pre-release
|
||||||
|
if (!release.prerelease || release.prerelease === prerelease) {
|
||||||
|
versions.latest = {
|
||||||
|
prerelease: release.prerelease,
|
||||||
|
version: release.tag_name,
|
||||||
|
url: release.html_url,
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emptying cached information after 15 minutes
|
||||||
|
setTimeout(() => {
|
||||||
|
delete versions.current.changelog;
|
||||||
|
delete versions.latest;
|
||||||
|
}, 15 * 60 * 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
callback(versions);
|
||||||
|
});
|
||||||
|
}
|
@ -14,6 +14,7 @@ var colors = require("colors/safe");
|
|||||||
const net = require("net");
|
const net = require("net");
|
||||||
const Identification = require("./identification");
|
const Identification = require("./identification");
|
||||||
const themes = require("./plugins/themes");
|
const themes = require("./plugins/themes");
|
||||||
|
const changelog = require("./plugins/changelog");
|
||||||
|
|
||||||
// The order defined the priority: the first available plugin is used
|
// The order defined the priority: the first available plugin is used
|
||||||
// ALways keep local auth in the end, which should always be enabled.
|
// ALways keep local auth in the end, which should always be enabled.
|
||||||
@ -217,7 +218,7 @@ function index(req, res, next) {
|
|||||||
|
|
||||||
// If prefetch is enabled, but storage is not, we have to allow mixed content
|
// If prefetch is enabled, but storage is not, we have to allow mixed content
|
||||||
if (Helper.config.prefetchStorage || !Helper.config.prefetch) {
|
if (Helper.config.prefetchStorage || !Helper.config.prefetch) {
|
||||||
policies.push("img-src 'self'");
|
policies.push("img-src 'self' https://user-images.githubusercontent.com");
|
||||||
policies.unshift("block-all-mixed-content");
|
policies.unshift("block-all-mixed-content");
|
||||||
} else {
|
} else {
|
||||||
policies.push("img-src http: https:");
|
policies.push("img-src http: https:");
|
||||||
@ -340,6 +341,12 @@ function initializeClient(socket, client, token, lastMessage) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
socket.on("changelog", function() {
|
||||||
|
changelog.fetch((data) => {
|
||||||
|
socket.emit("changelog", data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("msg:preview:toggle", function(data) {
|
socket.on("msg:preview:toggle", function(data) {
|
||||||
const networkAndChan = client.find(data.target);
|
const networkAndChan = client.find(data.target);
|
||||||
if (!networkAndChan) {
|
if (!networkAndChan) {
|
||||||
|
Loading…
Reference in New Issue
Block a user