diff --git a/client/css/style.css b/client/css/style.css index f643fd1c..f0487c55 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -168,7 +168,8 @@ kbd { touch-action: pan-y; } -#help .container { +#help .container, +#changelog .container { max-width: 600px; } @@ -188,7 +189,6 @@ kbd { #js-copy-hack, #loading pre, #help, -#changelog, #windows .header .title, #windows .header .topic, #chat .messages { @@ -237,6 +237,9 @@ kbd { #chat .toggle-button::after, .changelog-version::before, .context-menu-item::before, +#help .website-link::before, +#help .documentation-link::before, +#help .report-issue-link::before, #nick button::before, #image-viewer .previous-image-btn::before, #image-viewer .next-image-btn::before { @@ -287,6 +290,21 @@ kbd { 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 { display: block; } @@ -732,6 +750,7 @@ kbd { width: 100%; } +#windows li, #windows p, #windows label, #settings .error { @@ -789,6 +808,11 @@ kbd { padding-bottom: 7px; } +#windows .window h2 small { + color: inherit; + line-height: 30px; +} + #windows .window h3 { color: #7f8c8d; font-size: 18px; @@ -1595,16 +1619,7 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ grid-column-start: 2; } -#help .help-links { - margin-right: 20px; -} - -#changelog .container { - max-width: 740px; -} - .changelog-text { - font-size: 16px; line-height: 1.5; } @@ -1627,24 +1642,41 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ border-radius: 2px; background-color: #d9edf7; color: #31708f; + transition: color 0.2s, background-color 0.2s; } .changelog-version::before { margin-right: 6px; - content: "\f00c"; /* http://fontawesome.io/icon/check/ */ + content: "\f250"; /* http://fontawesome.io/icon/hourglass-o/ */ } -.changelog-version-new { +.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-new:hover { - color: #2b542c; -} - -.changelog-version-new::before { - content: "\f0ed"; /* http://fontawesome.io/icon/cloud-download/ */ +.changelog-version.up-to-date::before { + margin-right: 6px; + content: "\f00c"; /* http://fontawesome.io/icon/check/ */ } #form { diff --git a/client/js/lounge.js b/client/js/lounge.js index 826e6036..889c44cf 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -332,65 +332,43 @@ $(function() { $(this).closest(".msg.condensed").toggleClass("closed"); }); - sidebar.on("click", ".chan, button", function(e, data) { - // 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 = {}; + let changelogRequestedAt = 0; - 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); - } - } - }); - - sidebar.on("click", ".chan, button", function() { + const openWindow = function openWindow(e, data) { var self = $(this); var target = self.data("target"); if (!target) { return; } - chat.data( - "id", - self.data("id") - ); - socket.emit( - "open", - self.data("id") - ); + // This is a rather gross hack to account for sources that are in the + // sidebar specifically. Needs to be done better when window management gets + // refactored. + const inSidebar = self.parents("#sidebar").length > 0; - sidebar.find(".active").removeClass("active"); - self.addClass("active") - .find(".badge") - .removeClass("highlight") - .empty(); + if (inSidebar) { + chat.data( + "id", + self.data("id") + ); + socket.emit( + "open", + self.data("id") + ); - if (sidebar.find(".highlight").length === 0) { - utils.toggleNotificationMarkers(false); + sidebar.find(".active").removeClass("active"); + 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"); lastActive @@ -447,8 +425,49 @@ $(function() { socket.emit("sessions:get"); } + if (target === "#help" || target === "#changelog") { + const now = Date.now(); + // Don't check more than once a day + if (now - changelogRequestedAt > 86400 * 1000) { + changelogRequestedAt = now; + socket.emit("changelog"); + } + } + 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() { socket.emit("sign-out"); diff --git a/client/js/socket-events/changelog.js b/client/js/socket-events/changelog.js index 7d32ffb3..5eaf0229 100644 --- a/client/js/socket-events/changelog.js +++ b/client/js/socket-events/changelog.js @@ -5,5 +5,18 @@ 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)); }); diff --git a/client/js/socket-events/configuration.js b/client/js/socket-events/configuration.js index cd2489a0..4d8f8309 100644 --- a/client/js/socket-events/configuration.js +++ b/client/js/socket-events/configuration.js @@ -66,22 +66,4 @@ socket.on("configuration", function(data) { // Store the "previous" value, for next time $(this).data("lastvalue", nick); }); - - $("#view-changelog").on("click", function() { - $("#windows > .active") - .removeClass("active") - .find(".chat") - .unsticky(); - - $("#changelog") - .addClass("active"); - - history.pushState({ - clickTarget: "#view-changelog", - }, null, "#changelog"); - - return false; - }).one("click", function() { - socket.emit("changelog"); - }); }); diff --git a/client/views/index.js b/client/views/index.js index 0bba75d8..4be9cc10 100644 --- a/client/views/index.js +++ b/client/views/index.js @@ -31,6 +31,7 @@ module.exports = { chan: require("./chan.tpl"), chat: require("./chat.tpl"), + new_version: require("./new_version.tpl"), contextmenu_divider: require("./contextmenu_divider.tpl"), contextmenu_item: require("./contextmenu_item.tpl"), date_marker: require("./date-marker.tpl"), diff --git a/client/views/new_version.tpl b/client/views/new_version.tpl new file mode 100644 index 00000000..8825ed16 --- /dev/null +++ b/client/views/new_version.tpl @@ -0,0 +1,6 @@ +The Lounge {{latest.version}}{{#if latest.prerelease}} (pre-release){{/if}} +is now available. + + + Read more on GitHub + diff --git a/client/views/windows/changelog.tpl b/client/views/windows/changelog.tpl index 668a62ec..2ffd5c57 100644 --- a/client/views/windows/changelog.tpl +++ b/client/views/windows/changelog.tpl @@ -2,18 +2,9 @@
- {{#if current}} - {{#if latest}} - - The Lounge {{latest.version}}{{#if latest.prerelease}} (pre-release){{/if}} is now available. - Click to view details on GitHub. - - {{else if current.changelog}} -
- The Lounge is up to date! -
- {{/if}} + « Help + {{#if current}}

Release notes for {{current.version}}

{{#if current.changelog}} @@ -21,7 +12,7 @@
{{{current.changelog}}}
{{else}}

Unable to retrieve releases from GitHub.

-

View release notes for this version on GitHub

+

View release notes for this version on GitHub

{{/if}} {{else}}

Loading changelog…

diff --git a/client/views/windows/help.tpl b/client/views/windows/help.tpl index 86f7d25c..8f1e9b37 100644 --- a/client/views/windows/help.tpl +++ b/client/views/windows/help.tpl @@ -4,25 +4,48 @@

Help

-

About The Lounge

+

+ + v{{version}} + (release notes) + + About The Lounge +

+ {{#unless public}} +

+ Checking for updates... +

+ {{/unless}} + {{#if gitCommit}} -

The Lounge is running from source (commit {{gitCommit}}) based on {{version}}

-

Compare changes between {{gitCommit}} and master to see what you are missing

-

Compare changes between {{version}} and {{gitCommit}} to see the changes made

- {{else}} -

The Lounge is running {{version}}.

+

+ The Lounge is running from source + (commit {{gitCommit}}). +

+ + {{/if}} - {{#unless public}} - - {{/unless}} -

- View website
- View documentation
- Report a bug on GitHub + Website +

+

+ Documentation +

+

+ Report an issue…

diff --git a/src/plugins/changelog.js b/src/plugins/changelog.js index f5fc2c1f..454c3636 100644 --- a/src/plugins/changelog.js +++ b/src/plugins/changelog.js @@ -4,10 +4,10 @@ const pkg = require("../../package.json"); const request = require("request"); module.exports = { - sendChangelog: handleChangelog, + fetch, }; -function handleChangelog(callback) { +function fetch(callback) { const changelog = { current: { version: `v${pkg.version}`, @@ -27,12 +27,13 @@ function handleChangelog(callback) { return; } - let i = 0; + 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 === changelog.current.version) { @@ -43,9 +44,10 @@ function handleChangelog(callback) { } } - if (i > 0 && changelog.current) { - for (i = 0; i < body.length; i++) { - release = body[i]; + // 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) { diff --git a/src/server.js b/src/server.js index aaec0dfc..1522f77a 100644 --- a/src/server.js +++ b/src/server.js @@ -218,7 +218,7 @@ function index(req, res, next) { // If prefetch is enabled, but storage is not, we have to allow mixed content 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"); } else { policies.push("img-src http: https:"); @@ -341,13 +341,11 @@ function initializeClient(socket, client, token, lastMessage) { } ); - if (!Helper.config.public) { - socket.on("changelog", function() { - changelog.sendChangelog((data) => { - socket.emit("changelog", data); - }); + socket.on("changelog", function() { + changelog.fetch((data) => { + socket.emit("changelog", data); }); - } + }); socket.on("msg:preview:toggle", function(data) { const networkAndChan = client.find(data.target);