mirror of
https://github.com/thelounge/thelounge
synced 2024-11-26 22:10:22 +00:00
Merge pull request #1897 from thelounge/astorije/improve-version-checker
Improve the version checking and changelog features
This commit is contained in:
commit
1fc2051c1d
11 changed files with 152 additions and 61 deletions
|
@ -137,6 +137,10 @@ kbd {
|
||||||
cursor: pointer; /* This is useful for `<button>` elements */
|
cursor: pointer; /* This is useful for `<button>` elements */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-small {
|
||||||
|
padding: 5px 13px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn:disabled,
|
.btn:disabled,
|
||||||
.btn:hover,
|
.btn:hover,
|
||||||
.btn:focus {
|
.btn:focus {
|
||||||
|
@ -235,7 +239,7 @@ 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,
|
#version-checker::before,
|
||||||
.context-menu-item::before,
|
.context-menu-item::before,
|
||||||
#help .website-link::before,
|
#help .website-link::before,
|
||||||
#help .documentation-link::before,
|
#help .documentation-link::before,
|
||||||
|
@ -991,9 +995,7 @@ kbd {
|
||||||
#sidebar .join-form .btn {
|
#sidebar .join-form .btn {
|
||||||
display: block;
|
display: block;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
padding: 1px;
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
height: 29px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar .add-channel-tooltip {
|
#sidebar .add-channel-tooltip {
|
||||||
|
@ -1635,47 +1637,70 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
|
||||||
padding-bottom: 7px;
|
padding-bottom: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-version {
|
#version-checker {
|
||||||
display: block;
|
display: flex;
|
||||||
padding: 16px;
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-color: #d9edf7;
|
|
||||||
color: #31708f;
|
|
||||||
transition: color 0.2s, background-color 0.2s;
|
transition: color 0.2s, background-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-version::before {
|
#version-checker p,
|
||||||
margin-right: 6px;
|
#version-checker button {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#version-checker p {
|
||||||
|
flex: 1;
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#version-checker::before {
|
||||||
|
margin-left: 6px;
|
||||||
|
margin-right: 12px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#version-checker.loading {
|
||||||
|
background-color: #d9edf7;
|
||||||
|
color: #31708f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#version-checker.loading::before {
|
||||||
content: "\f250"; /* http://fontawesome.io/icon/hourglass-o/ */
|
content: "\f250"; /* http://fontawesome.io/icon/hourglass-o/ */
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-version.new-version {
|
#version-checker.new-version {
|
||||||
color: #8a6d3b;
|
color: #8a6d3b;
|
||||||
background-color: #fcf8e3;
|
background-color: #fcf8e3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-version.new-version::before {
|
#version-checker.new-version::before {
|
||||||
content: "\f087"; /* http://fontawesome.io/icon/thumbs-o-up/ */
|
content: "\f087"; /* http://fontawesome.io/icon/thumbs-o-up/ */
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-version.error {
|
#version-checker.error {
|
||||||
color: #a94442;
|
color: #a94442;
|
||||||
background-color: #f2dede;
|
background-color: #f2dede;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-version.error::before {
|
#version-checker.error::before {
|
||||||
margin-right: 6px;
|
|
||||||
content: "\f06a"; /* http://fontawesome.io/icon/exclamation-circle/ */
|
content: "\f06a"; /* http://fontawesome.io/icon/exclamation-circle/ */
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-version.up-to-date {
|
#version-checker.up-to-date {
|
||||||
background-color: #dff0d8;
|
background-color: #dff0d8;
|
||||||
color: #3c763d;
|
color: #3c763d;
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-version.up-to-date::before {
|
#version-checker.up-to-date #check-now {
|
||||||
margin-right: 6px;
|
/* "Check now" button is hidden until data expires */
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#version-checker.up-to-date::before {
|
||||||
content: "\f00c"; /* http://fontawesome.io/icon/check/ */
|
content: "\f00c"; /* http://fontawesome.io/icon/check/ */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ const utils = require("./utils");
|
||||||
require("./webpush");
|
require("./webpush");
|
||||||
require("./keybinds");
|
require("./keybinds");
|
||||||
require("./clipboard");
|
require("./clipboard");
|
||||||
|
const Changelog = require("./socket-events/changelog");
|
||||||
const JoinChannel = require("./join-channel");
|
const JoinChannel = require("./join-channel");
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
@ -332,8 +333,6 @@ $(function() {
|
||||||
$(this).closest(".msg.condensed").toggleClass("closed");
|
$(this).closest(".msg.condensed").toggleClass("closed");
|
||||||
});
|
});
|
||||||
|
|
||||||
let changelogRequestedAt = 0;
|
|
||||||
|
|
||||||
const openWindow = function openWindow(e, data) {
|
const openWindow = function openWindow(e, data) {
|
||||||
var self = $(this);
|
var self = $(this);
|
||||||
var target = self.data("target");
|
var target = self.data("target");
|
||||||
|
@ -426,12 +425,7 @@ $(function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target === "#help" || target === "#changelog") {
|
if (target === "#help" || target === "#changelog") {
|
||||||
const now = Date.now();
|
Changelog.requestIfNeeded();
|
||||||
// Don't check more than once an hour
|
|
||||||
if (now - changelogRequestedAt > 3600 * 1000) {
|
|
||||||
changelogRequestedAt = now;
|
|
||||||
socket.emit("changelog");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
focus();
|
focus();
|
||||||
|
|
|
@ -99,7 +99,7 @@ function handleImageInPreview(content, container) {
|
||||||
|
|
||||||
const imageViewer = $("#image-viewer");
|
const imageViewer = $("#image-viewer");
|
||||||
|
|
||||||
$("#chat").on("click", ".toggle-thumbnail", function(event, data = {}) {
|
$("#windows").on("click", ".toggle-thumbnail", function(event, data = {}) {
|
||||||
const link = $(this);
|
const link = $(this);
|
||||||
|
|
||||||
// Passing `data`, specifically `data.pushState`, to not add the action to the
|
// Passing `data`, specifically `data.pushState`, to not add the action to the
|
||||||
|
@ -158,7 +158,7 @@ function openImageViewer(link, {pushState = true} = {}) {
|
||||||
imageViewer.html(templates.image_viewer({
|
imageViewer.html(templates.image_viewer({
|
||||||
image: link.find("img").attr("src"),
|
image: link.find("img").attr("src"),
|
||||||
link: link.attr("href"),
|
link: link.attr("href"),
|
||||||
type: link.parent().hasClass("toggle-type-image") ? "image" : "link",
|
type: link.parent().hasClass("toggle-type-link") ? "link" : "image",
|
||||||
hasPreviousImage: previousImage.length > 0,
|
hasPreviousImage: previousImage.length > 0,
|
||||||
hasNextImage: nextImage.length > 0,
|
hasNextImage: nextImage.length > 0,
|
||||||
}));
|
}));
|
||||||
|
@ -171,10 +171,14 @@ function openImageViewer(link, {pushState = true} = {}) {
|
||||||
|
|
||||||
// History management
|
// History management
|
||||||
if (pushState) {
|
if (pushState) {
|
||||||
const clickTarget =
|
let clickTarget = "";
|
||||||
`#${link.closest(".msg").attr("id")} ` +
|
// Images can be in a message (channel URL previews) or not (window URL
|
||||||
`a.toggle-thumbnail[href="${link.attr("href")}"] ` +
|
// preview, e.g. changelog). This is sub-optimal and needs improvement to
|
||||||
"img";
|
// make image preview more generic and not specific for channel previews.
|
||||||
|
if (link.closest(".msg").length > 0) {
|
||||||
|
clickTarget = `#${link.closest(".msg").attr("id")} `;
|
||||||
|
}
|
||||||
|
clickTarget += `a.toggle-thumbnail[href="${link.attr("href")}"] img`;
|
||||||
history.pushState({clickTarget}, null, null);
|
history.pushState({clickTarget}, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,66 @@ const $ = require("jquery");
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
const templates = require("../../views");
|
const templates = require("../../views");
|
||||||
|
|
||||||
socket.on("changelog", function(data) {
|
module.exports = {
|
||||||
const container = $("#changelog-version-container");
|
requestIfNeeded,
|
||||||
|
};
|
||||||
|
|
||||||
if (data.latest) {
|
// Requests version information if it hasn't been retrieved before (or if it has
|
||||||
container.addClass("new-version");
|
// been removed from the page, i.e. when clicking on "Check now". Displays a
|
||||||
container.html(templates.new_version(data));
|
// loading state until received.
|
||||||
} else if (data.current.changelog) {
|
function requestIfNeeded() {
|
||||||
container.addClass("up-to-date");
|
if ($("#version-checker").is(":empty")) {
|
||||||
container.text("The Lounge is up to date!");
|
renderVersionChecker({status: "loading"});
|
||||||
} else {
|
socket.emit("changelog");
|
||||||
container.addClass("error");
|
}
|
||||||
container.text("An error has occurred, try to reload the page.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#changelog").html(templates.windows.changelog(data));
|
socket.on("changelog", function(data) {
|
||||||
|
// 1. Release notes window for the current version
|
||||||
|
$("#changelog").html(templates.windows.changelog(data.current));
|
||||||
|
|
||||||
|
const links = $("#changelog .changelog-text a");
|
||||||
|
// Make sure all links will open a new tab instead of exiting the application
|
||||||
|
links.attr("target", "_blank");
|
||||||
|
// Add required metadata to image links, to support built-in image viewer
|
||||||
|
links.has("img").addClass("toggle-thumbnail");
|
||||||
|
|
||||||
|
// 2. Version checker visible in Help window
|
||||||
|
let status;
|
||||||
|
|
||||||
|
if (data.latest) {
|
||||||
|
status = "new-version";
|
||||||
|
} else if (data.current.changelog) {
|
||||||
|
status = "up-to-date";
|
||||||
|
} else {
|
||||||
|
status = "error";
|
||||||
|
}
|
||||||
|
|
||||||
|
renderVersionChecker({
|
||||||
|
latest: data.latest,
|
||||||
|
status,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// When there is a button to refresh the checker available, display it when
|
||||||
|
// data is expired. Before that, server would return same information anyway.
|
||||||
|
if (data.expiresAt) {
|
||||||
|
setTimeout(
|
||||||
|
() => $("#version-checker #check-now").show(),
|
||||||
|
data.expiresAt - Date.now()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// When clicking the "Check now" button, remove current checker information and
|
||||||
|
// request a new one. Loading will be displayed in the meantime.
|
||||||
|
$("#help").on("click", "#check-now", () => {
|
||||||
|
$("#version-checker").empty();
|
||||||
|
requestIfNeeded();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Given a status and latest release information, update the version checker
|
||||||
|
// (CSS class and content)
|
||||||
|
function renderVersionChecker({status, latest}) {
|
||||||
|
$("#version-checker").attr("class", status)
|
||||||
|
.html(templates.version_checker({latest, status}));
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ module.exports = {
|
||||||
|
|
||||||
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"),
|
||||||
|
@ -50,4 +49,5 @@ module.exports = {
|
||||||
user: require("./user.tpl"),
|
user: require("./user.tpl"),
|
||||||
user_filtered: require("./user_filtered.tpl"),
|
user_filtered: require("./user_filtered.tpl"),
|
||||||
user_name: require("./user_name.tpl"),
|
user_name: require("./user_name.tpl"),
|
||||||
|
version_checker: require("./version_checker.tpl"),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<form id="join-channel-{{id}}" class="join-form" method="post" action="" autocomplete="off">
|
<form id="join-channel-{{id}}" class="join-form" method="post" action="" autocomplete="off">
|
||||||
<input type="text" class="input" name="channel" placeholder="Channel" pattern="[^\s]+" maxlength="200" title="The channel name may not contain spaces" required>
|
<input type="text" class="input" name="channel" placeholder="Channel" pattern="[^\s]+" maxlength="200" title="The channel name may not contain spaces" required>
|
||||||
<input type="password" class="input" name="key" placeholder="Password (optional)" pattern="[^\s]+" maxlength="200" title="The channel password may not contain spaces">
|
<input type="password" class="input" name="key" placeholder="Password (optional)" pattern="[^\s]+" maxlength="200" title="The channel password may not contain spaces">
|
||||||
<button type="submit" class="btn joinchan:submit" data-id="{{id}}">Join</button>
|
<button type="submit" class="btn btn-small" data-id="{{id}}">Join</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
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>
|
|
27
client/views/version_checker.tpl
Normal file
27
client/views/version_checker.tpl
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{{#equal status "loading"}}
|
||||||
|
<p>
|
||||||
|
Checking for updates...
|
||||||
|
</p>
|
||||||
|
{{else equal status "new-version"}}
|
||||||
|
<p>
|
||||||
|
The Lounge <b>{{latest.version}}</b>{{#if latest.prerelease}} (pre-release){{/if}}
|
||||||
|
is now available.
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<a href="{{latest.url}}" target="_blank" rel="noopener">
|
||||||
|
Read more on GitHub
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{{else equal status "up-to-date"}}
|
||||||
|
<p>
|
||||||
|
The Lounge is up to date!
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button id="check-now" class="btn btn-small">Check now</button>
|
||||||
|
{{else equal status "error"}}
|
||||||
|
<p>
|
||||||
|
Information about latest releases could not be retrieved.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button id="check-now" class="btn btn-small">Try again</button>
|
||||||
|
{{/equal}}
|
|
@ -4,15 +4,15 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a href="#" id="back-to-help" data-target="#help">« Help</a>
|
<a href="#" id="back-to-help" data-target="#help">« Help</a>
|
||||||
|
|
||||||
{{#if current}}
|
{{#if version}}
|
||||||
<h1 class="title">Release notes for {{current.version}}</h1>
|
<h1 class="title">Release notes for {{version}}</h1>
|
||||||
|
|
||||||
{{#if current.changelog}}
|
{{#if changelog}}
|
||||||
<h3>Introduction</h3>
|
<h3>Introduction</h3>
|
||||||
<div class="changelog-text">{{{current.changelog}}}</div>
|
<div class="changelog-text">{{{changelog}}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<p>Unable to retrieve releases from GitHub.</p>
|
<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>
|
<p><a href="https://github.com/thelounge/lounge/releases/tag/v{{version}}" target="_blank" rel="noopener">View release notes for this version on GitHub</a></p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<p>Loading changelog…</p>
|
<p>Loading changelog…</p>
|
||||||
|
|
|
@ -13,11 +13,7 @@
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="about">
|
<div class="about">
|
||||||
{{#unless public}}
|
<div id="version-checker"></div>
|
||||||
<p id="changelog-version-container" class="changelog-version">
|
|
||||||
Checking for updates...
|
|
||||||
</p>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#if gitCommit}}
|
{{#if gitCommit}}
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
const pkg = require("../../package.json");
|
const pkg = require("../../package.json");
|
||||||
const request = require("request");
|
const request = require("request");
|
||||||
|
|
||||||
|
const TIME_TO_LIVE = 15 * 60 * 1000; // 15 minutes, in milliseconds
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
fetch,
|
fetch,
|
||||||
};
|
};
|
||||||
|
@ -67,12 +69,14 @@ function fetch(callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emptying cached information after 15 minutes
|
// Add expiration date to the data to send to the client for later refresh
|
||||||
|
versions.expiresAt = Date.now() + TIME_TO_LIVE;
|
||||||
|
|
||||||
|
// Emptying cached information after reaching said expiration date
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
delete versions.current.changelog;
|
delete versions.current.changelog;
|
||||||
delete versions.latest;
|
delete versions.latest;
|
||||||
}, 15 * 60 * 1000
|
}, TIME_TO_LIVE);
|
||||||
);
|
|
||||||
|
|
||||||
callback(versions);
|
callback(versions);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue