Add update checking to UI

Unblock most http(s) requests to allow usage of the GitHub API.
Requests to fonts.googleapis.com are still blocked to prevent Polymer
trying to load the fonts from there when they're already present
locally.
This commit is contained in:
Oliver Hamlet
2016-11-03 07:02:26 +00:00
parent fedf4e0530
commit f1ca18b744
10 changed files with 198 additions and 31 deletions
+2 -1
View File
@@ -34,6 +34,7 @@
"Jed": "SlexAxton/Jed#^1.1.1",
"jed-gettext-parser": "^1.0.0",
"marked": "^0.3.2",
"lodash": "^4.3.0"
"lodash": "^4.3.0",
"octokat": "^0.4.18"
}
}
+16 -20
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: LOOT 0.10.0\n"
"Report-Msgid-Bugs-To: https://github.com/loot/loot/issues\n"
"POT-Creation-Date: 2016-10-15 12:29+0100\n"
"POT-Creation-Date: 2016-11-02 23:09+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -628,7 +628,7 @@ msgstr ""
msgid "Loading plugin contents..."
msgstr ""
#: src/gui/query/sort_plugins_query.h:68
#: src/gui/query/sort_plugins_query.h:70
msgid "Sorting load order..."
msgstr ""
@@ -652,60 +652,56 @@ msgstr ""
msgid "None of the supported games were detected."
msgstr ""
#: src/backend/game/game.cpp:61
msgid "Invalid game ID supplied."
msgstr ""
#: src/backend/game/game.cpp:68
#: src/backend/game/game.cpp:64
msgid "Game path could not be detected."
msgstr ""
#: src/backend/game/game.cpp:78
#: src/backend/game/game.cpp:74
msgid "Could not create LOOT folder for game. Details:"
msgstr ""
#: src/backend/game/load_order_handler.cpp:49
msgid "Unsupported game ID supplied."
#: src/backend/game/game_cache.cpp:119
msgid "You have not sorted your load order this session."
msgstr ""
#: src/backend/game/load_order_handler.cpp:54
#: src/backend/game/load_order_handler.cpp:46
msgid "Game path is not initialised."
msgstr ""
#: src/backend/game/load_order_handler.cpp:88
#: src/backend/game/load_order_handler.cpp:82
msgid ""
"libloadorder failed to create a game handle. Details could not be fetched."
msgstr ""
#: src/backend/game/load_order_handler.cpp:91
#: src/backend/game/load_order_handler.cpp:85
msgid "libloadorder failed to create a game handle. Details:"
msgstr ""
#: src/backend/game/load_order_handler.cpp:109
#: src/backend/game/load_order_handler.cpp:103
msgid ""
"libloadorder failed to check if a plugin is active. Details could not be "
"fetched."
msgstr ""
#: src/backend/game/load_order_handler.cpp:112
#: src/backend/game/load_order_handler.cpp:106
msgid "libloadorder failed to check if a plugin is active. Details:"
msgstr ""
#: src/backend/game/load_order_handler.cpp:134
#: src/backend/game/load_order_handler.cpp:128
msgid ""
"libloadorder failed to get the load order. Details could not be fetched."
msgstr ""
#: src/backend/game/load_order_handler.cpp:137
#: src/backend/game/load_order_handler.cpp:131
msgid "libloadorder failed to get the load order. Details:"
msgstr ""
#: src/backend/game/load_order_handler.cpp:160
#: src/backend/game/load_order_handler.cpp:154
msgid ""
"libloadorder failed to set the load order. Details could not be fetched."
msgstr ""
#: src/backend/game/load_order_handler.cpp:163
#: src/backend/game/load_order_handler.cpp:157
msgid "libloadorder failed to set the load order. Details:"
msgstr ""
@@ -728,7 +724,7 @@ msgstr ""
msgid "Unable to open \"%1%\" for CRC calculation."
msgstr ""
#: src/backend/helpers/helpers.cpp:122 src/backend/helpers/helpers.cpp:125
#: src/backend/helpers/helpers.cpp:103 src/backend/helpers/helpers.cpp:106
msgid "Failed to open file in its default application."
msgstr ""
+2
View File
@@ -450,6 +450,7 @@
<script src="../../../bower_components/Jed/jed.js"></script>
<script src="../../../bower_components/jed-gettext-parser/jedGettextParser.js"></script>
<script src="../../../bower_components/lodash/dist/lodash.core.min.js"></script>
<script src="../../../bower_components/octokat/dist/octokat.js"></script>
<script src="js/dialog.js"></script>
<script src="js/dom.js"></script>
<script src="js/events.js"></script>
@@ -461,6 +462,7 @@
<script src="js/filters.js"></script>
<script src="js/state.js"></script>
<script src="js/translator.js"></script>
<script src="js/updateExists.js"></script>
<script src="js/initialise.js"></script>
<script>
'use strict';
+18 -7
View File
@@ -34,11 +34,11 @@
return item;
}
function createMessageItem(message) {
function createMessageItem(type, content) {
const li = document.createElement('li');
li.className = 'error';
li.className = type;
/* Use the Marked library for Markdown formatting support. */
li.innerHTML = marked(message);
li.innerHTML = marked(content);
return li;
}
@@ -166,15 +166,26 @@
forceSelectDefaultValue(messageLangSelect);
}
static appendGeneralMessages(messages) {
if (!messages) {
return;
}
const generalMessagesList = document.getElementById('summary').getElementsByTagName('ul')[0];
messages.forEach((message) => {
generalMessagesList.appendChild(createMessageItem(message.type, message.content));
});
}
static listInitErrors(errorMessages) {
if (!errorMessages) {
return;
}
const generalMessagesList = document.getElementById('summary').getElementsByTagName('ul')[0];
errorMessages.forEach((message) => {
generalMessagesList.appendChild(createMessageItem(message));
});
DOM.appendGeneralMessages(errorMessages.map((element) => ({
type: 'error',
content: element,
})));
document.getElementById('filterTotalMessageNo').textContent = errorMessages.length;
document.getElementById('totalMessageNo').textContent = errorMessages.length;
+17 -2
View File
@@ -37,7 +37,8 @@
root.loot.translateStaticText,
root.loot.Plugin,
root.loot.query,
root.loot.Translator);
root.loot.Translator,
root.loot.updateExists);
}
}(this, (Dialog,
dom,
@@ -47,7 +48,8 @@
translateStaticText,
Plugin,
query,
Translator) => {
Translator,
updateExists) => {
function setupEventHandlers() {
/* Set up handlers for filters. */
document.getElementById('hideVersionNumbers').addEventListener('change', onSidebarFilterToggle);
@@ -235,6 +237,19 @@
dom.openDialog('firstRun');
}
})
.then(() => loot.query('getVersion'))
.then(JSON.parse)
.then((version) => updateExists(version.release, version.build))
.then((isUpdateAvailable) => {
if (!isUpdateAvailable) {
return;
}
dom.appendGeneralMessages([{
type: 'warn',
content: loot.l10n.translate('A [new release](%s) of LOOT is available.', 'https://github.com/loot/loot/releases/latest'),
}]);
})
.catch(handlePromiseError);
};
}));
+60
View File
@@ -0,0 +1,60 @@
'use strict';
(function exportModule(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['bower_components/octokat/dist/octokat.js'], factory);
} else {
// Browser globals
root.loot = root.loot || {};
root.loot.updateExists = factory(root.Octokat);
}
}(this, (Octokat) => {
const versionRegex = /^(\d+)\.(\d+)\.(\d+)$/;
function compare(lhs, rhs) {
if (!versionRegex.test(lhs) || !versionRegex.test(rhs)) {
throw new Error(`versions to compare are of unexpected format: ${lhs}, ${rhs}`);
}
const lhsNumbers = lhs.split('.');
const rhsNumbers = rhs.split('.');
for (let i = 0; i < lhsNumbers.length; i += 1) {
const lhsNumber = Number(lhsNumbers[i]);
const rhsNumber = Number(rhsNumbers[i]);
if (lhsNumber < rhsNumber) {
return -1;
}
if (rhsNumber < lhsNumber) {
return 1;
}
}
return 0;
}
return (currentVersion, currentBuild) => {
if (currentVersion === undefined || currentBuild === undefined) {
return Promise.reject(new Error('Invalid arguments, both version and build must be given'));
}
const repo = (new Octokat()).repos('loot', 'loot');
return repo.releases.latest.fetch().then((latestRelease) => {
const comparison = compare(currentVersion, latestRelease.tagName);
if (comparison === -1) {
return true;
} else if (comparison === 1) {
return false;
}
return repo.tags.fetch().then((tags) => {
const tag = tags.find((element) => element.name === latestRelease.tagName);
return !tag.commit.sha.startsWith(currentBuild);
});
});
};
}));
+3 -1
View File
@@ -239,8 +239,10 @@ CefRequestHandler::ReturnValue LootHandler::OnBeforeResourceLoad(CefRefPtr<CefBr
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
CefRefPtr<CefRequestCallback> callback) {
if (boost::starts_with(request->GetURL().ToString(), "http"))
if (boost::starts_with(request->GetURL().ToString(), "https://fonts.googleapis.com")) {
BOOST_LOG_TRIVIAL(warning) << "Blocking load of resource at " << request->GetURL().ToString();
return RV_CANCEL;
}
return RV_CONTINUE;
}
+38
View File
@@ -0,0 +1,38 @@
'use strict';
/* Mock the octokat methods used. */
function Octokat() {
this.repos = function(owner, repo) {
return {
releases: {
latest: {
fetch() {
return Promise.resolve({
tagName: '0.9.2',
});
},
},
},
tags: {
fetch() {
return Promise.resolve([{
name: '0.9.2',
commit: {
sha: '6b58f92a5d41f5d7f149a1263dac78687a065ff5',
},
}, {
name: '0.9.1',
commit: {
sha: 'dc24e10a4774903ede4e94165e7d6fa806466e4a',
},
}, {
name: '0.9.0',
commit: {
sha: '44a0d8505d5402dd24cf0fda9540da9557866c80',
},
}]);
},
},
};
}
}
+3
View File
@@ -13,18 +13,21 @@
<script>mocha.setup('bdd')</script>
<script src="mock_dom.js"></script>
<script src="mock_octokat.js"></script>
<script src="../../../../gui/html/js/filters.js"></script>
<script src="../../../../gui/html/js/plugin.js"></script>
<script src="../../../../gui/html/js/game.js"></script>
<script src="../../../../gui/html/js/query.js"></script>
<script src="../../../../gui/html/js/state.js"></script>
<script src="../../../../gui/html/js/translator.js"></script>
<script src="../../../../gui/html/js/updateExists.js"></script>
<script src="test_filters.js"></script>
<script src="test_game.js"></script>
<script src="test_plugin.js"></script>
<script src="test_query.js"></script>
<script src="test_state.js"></script>
<script src="test_translator.js"></script>
<script src="test_updateExists.js"></script>
<script>
onload = function(){
mocha.checkLeaks();
@@ -0,0 +1,39 @@
'use strict';
describe('updateExists()', () => {
it('should reject if no arguments are passed', () => {
return loot.updateExists().should.be.rejected();
});
it('should reject if one argument is passed', () => {
return loot.updateExists('1.0.0').should.be.rejected();
});
it('should reject if passed a version number has less than three parts', () => {
return loot.updateExists('1.0', 'deadbeef').should.be.rejected();
});
it('should reject if passed a version number has more than three parts', () => {
return loot.updateExists('1.0.0.0', 'deadbeef').should.be.rejected();
});
it('should reject if the version number given contains non-digit, non-period characters', () => {
return loot.updateExists('1.0a.0', 'deadbeef').should.be.rejected();
});
it('should resolve to true if the given version is less than the latest version', () => {
return loot.updateExists('0.9.1', 'deadbeef').should.be.fulfilledWith(true);
});
it('should resolve to false if the given version is greater than the latest version', () => {
return loot.updateExists('0.10.0', 'deadbeef').should.be.fulfilledWith(false);
});
it('should resolve to true if the given version equals the latest version but the short build SHAs are unequal', () => {
return loot.updateExists('0.9.2', 'deadbeef').should.be.fulfilledWith(true);
});
it('should resolve to false if the given version equals the latest version and the short build SHAs are equal', () => {
return loot.updateExists('0.9.2', '6b58f92').should.be.fulfilledWith(false);
});
});