Add info page (#1102)

* Add api.getAnkiConnectVersion

* Enable basic functionality when certain features/elements aren't present

* Add info page

* Update information links

* Update info link on the settings v2 page
This commit is contained in:
toasted-nutbread 2020-12-12 14:57:24 -05:00 committed by GitHub
parent 92cfd31c0f
commit 5948229176
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 240 additions and 14 deletions

View File

@ -32,7 +32,7 @@
</button> </button>
<a class="nav-button action-open-options" data-icon="cog" title="Options&#10;(Middle click to open in new tab)"></a> <a class="nav-button action-open-options" data-icon="cog" title="Options&#10;(Middle click to open in new tab)"></a>
<a class="nav-button action-open-search" data-icon="magnifying-glass" title="Search (Alt + Insert)&#10;(Middle click to open in new tab)"></a> <a class="nav-button action-open-search" data-icon="magnifying-glass" title="Search (Alt + Insert)&#10;(Middle click to open in new tab)"></a>
<a class="nav-button action-open-help" data-icon="question-mark" title="Help"></a> <a class="nav-button action-open-help" data-icon="question-mark" title="Information"></a>
</div> </div>
</div> </div>

71
ext/bg/info.html Normal file
View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Yomichan Info</title>
<link rel="icon" type="image/png" href="/mixed/img/icon16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/mixed/img/icon19.png" sizes="19x19">
<link rel="icon" type="image/png" href="/mixed/img/icon32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/mixed/img/icon38.png" sizes="38x38">
<link rel="icon" type="image/png" href="/mixed/img/icon48.png" sizes="48x48">
<link rel="icon" type="image/png" href="/mixed/img/icon64.png" sizes="64x64">
<link rel="icon" type="image/png" href="/mixed/img/icon128.png" sizes="128x128">
<link rel="stylesheet" type="text/css" href="/bg/css/settings2.css">
</head>
<body>
<!-- Main content -->
<div class="content-outer"><div class="content">
<div class="content-center">
<span tabindex="-1" id="content-scroll-focus"></span>
<h1>Yomichan Info</h1>
<h2 id="general">General</h2>
<div class="settings-group">
<div class="settings-item"><div class="settings-item-inner"><div class="settings-item-left"><div class="settings-item-label">
<ul>
<li>Extension version: <span id="version"></span></li>
<li>Platform: <span id="platform"></span></li>
<li>Browser: <span id="browser"></span></li>
<li>User agent: <span id="user-agent"></span></li>
<li>AnkiConnect version: <span id="anki-connect-version-container"><span id="anki-connect-version">...</span><em class="light" id="anki-connect-version-unknown-message" hidden> (Anki not running or connected)</em></span></li>
<li>Installed dictionaries: <span id="installed-dictionaries">...</span><em id="installed-dictionaries-none" hidden>None installed</em></li>
<li><a id="settings-export-button">Export settings</a></li>
</ul>
</div></div></div></div>
</div>
<h2 id="links">Links</h2>
<div class="settings-group">
<div class="settings-item"><div class="settings-item-inner"><div class="settings-item-left"><div class="settings-item-label">
<ul>
<li>Information and downloadable dictionaries: <a href="https://foosoft.net/projects/yomichan/" rel="noreferrer noopener">Homepage</a></li>
<li>Source code, bug reporting, and feedback: <a href="https://github.com/FooSoft/yomichan" rel="noreferrer noopener">Github</a></li>
<li>Release notes: <a href="https://github.com/FooSoft/yomichan/releases" rel="noreferrer noopener" data-href-format="https://github.com/FooSoft/yomichan/releases/tag/{version}" id="release-notes-this-version-link">This version</a> | <a href="https://github.com/FooSoft/yomichan/releases" rel="noreferrer noopener">All versions</a></li>
<li>More extension information: <a href="/bg/permissions.html">Permissions</a> | <a href="/bg/legal.html">Licenses</a></li>
</ul>
</div></div></div></div>
</div>
<div class="footer-padding"></div>
</div>
</div></div>
<!-- Scripts -->
<script src="/mixed/js/core.js"></script>
<script src="/mixed/js/yomichan.js"></script>
<script src="/mixed/js/comm.js"></script>
<script src="/mixed/js/api.js"></script>
<script src="/mixed/js/html-template-collection.js"></script>
<script src="/bg/js/settings/settings-controller.js"></script>
<script src="/bg/js/settings/backup-controller.js"></script>
<script src="/bg/js/info-main.js"></script>
</body>
</html>

View File

@ -49,6 +49,12 @@ class AnkiConnect {
} }
} }
async getVersion() {
if (!this._enabled) { return null; }
await this._checkVersion();
return await this._invoke('version', {});
}
async addNote(note) { async addNote(note) {
if (!this._enabled) { return null; } if (!this._enabled) { return null; }
await this._checkVersion(); await this._checkVersion();

View File

@ -90,6 +90,7 @@ class Backend {
['kanjiFind', {async: true, contentScript: true, handler: this._onApiKanjiFind.bind(this)}], ['kanjiFind', {async: true, contentScript: true, handler: this._onApiKanjiFind.bind(this)}],
['termsFind', {async: true, contentScript: true, handler: this._onApiTermsFind.bind(this)}], ['termsFind', {async: true, contentScript: true, handler: this._onApiTermsFind.bind(this)}],
['textParse', {async: true, contentScript: true, handler: this._onApiTextParse.bind(this)}], ['textParse', {async: true, contentScript: true, handler: this._onApiTextParse.bind(this)}],
['getAnkiConnectVersion', {async: true, contentScript: true, handler: this._onApGetAnkiConnectVersion.bind(this)}],
['isAnkiConnected', {async: true, contentScript: true, handler: this._onApiIsAnkiConnected.bind(this)}], ['isAnkiConnected', {async: true, contentScript: true, handler: this._onApiIsAnkiConnected.bind(this)}],
['addAnkiNote', {async: true, contentScript: true, handler: this._onApiAddAnkiNote.bind(this)}], ['addAnkiNote', {async: true, contentScript: true, handler: this._onApiAddAnkiNote.bind(this)}],
['getAnkiNoteInfo', {async: true, contentScript: true, handler: this._onApiGetAnkiNoteInfo.bind(this)}], ['getAnkiNoteInfo', {async: true, contentScript: true, handler: this._onApiGetAnkiNoteInfo.bind(this)}],
@ -428,6 +429,10 @@ class Backend {
return results; return results;
} }
async _onApGetAnkiConnectVersion() {
return await this._anki.getVersion();
}
async _onApiIsAnkiConnected() { async _onApiIsAnkiConnected() {
return await this._anki.isConnected(); return await this._anki.isConnected();
} }
@ -786,7 +791,8 @@ class Backend {
} }
_onCommandHelp() { _onCommandHelp() {
chrome.tabs.create({url: 'https://foosoft.net/projects/yomichan/'}); const url = chrome.runtime.getURL('/bg/info.html');
chrome.tabs.create({url});
} }
_onCommandOptions(params) { _onCommandOptions(params) {

View File

@ -30,7 +30,7 @@ class DisplayController {
this._showExtensionInfo(manifest); this._showExtensionInfo(manifest);
this._setupEnvironment(); this._setupEnvironment();
this._setupButtonEvents('.action-open-search', 'search', chrome.runtime.getURL('/bg/search.html')); this._setupButtonEvents('.action-open-search', 'search', chrome.runtime.getURL('/bg/search.html'));
this._setupButtonEvents('.action-open-help', 'help', 'https://foosoft.net/projects/yomichan/'); this._setupButtonEvents('.action-open-help', 'help', chrome.runtime.getURL('/bg/info.html'));
const optionsFull = await api.optionsGetFull(); const optionsFull = await api.optionsGetFull();
this._optionsFull = optionsFull; this._optionsFull = optionsFull;

123
ext/bg/js/info-main.js Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2020 Yomichan Authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* global
* BackupController
* SettingsController
* api
*/
function getBrowserDisplayName(browser) {
switch (browser) {
case 'chrome': return 'Chrome';
case 'firefox': return 'Firefox';
case 'firefox-mobile': return 'Firefox for Android';
case 'edge': return 'Edge';
case 'edge-legacy': return 'Edge Legacy';
default: return `${browser}`;
}
}
function getOperatingSystemDisplayName(os) {
switch (os) {
case 'mac': return 'Mac OS';
case 'win': return 'Windows';
case 'android': return 'Android';
case 'cros': return 'Chrome OS';
case 'linux': return 'Linux';
case 'openbsd': return 'Open BSD';
case 'unknown': return 'Unknown';
default: return `${os}`;
}
}
(async () => {
try {
document.querySelector('#content-scroll-focus').focus();
const manifest = chrome.runtime.getManifest();
api.forwardLogsToBackend();
await yomichan.prepare();
const {userAgent} = navigator;
const {version} = manifest;
const {browser, platform: {os}} = await api.getEnvironmentInfo();
const thisVersionLink = document.querySelector('#release-notes-this-version-link');
thisVersionLink.href = thisVersionLink.dataset.hrefFormat.replace(/\{version\}/g, version);
document.querySelector('#version').textContent = `${version}`;
document.querySelector('#browser').textContent = getBrowserDisplayName(browser);
document.querySelector('#platform').textContent = getOperatingSystemDisplayName(os);
document.querySelector('#user-agent').textContent = userAgent;
(async () => {
let ankiConnectVersion = null;
try {
ankiConnectVersion = await api.getAnkiConnectVersion();
} catch (e) {
// NOP
}
document.querySelector('#anki-connect-version').textContent = (ankiConnectVersion !== null ? `${ankiConnectVersion}` : 'Unknown');
document.querySelector('#anki-connect-version-container').hasError = `${ankiConnectVersion === null}`;
document.querySelector('#anki-connect-version-unknown-message').hidden = (ankiConnectVersion !== null);
})();
(async () => {
let dictionaryInfos;
try {
dictionaryInfos = await api.getDictionaryInfo();
} catch (e) {
return;
}
const fragment = document.createDocumentFragment();
let first = true;
for (const {title} of dictionaryInfos) {
if (first) {
first = false;
} else {
fragment.appendChild(document.createTextNode(', '));
}
const node = document.createElement('span');
node.className = 'installed-dictionary';
node.textContent = title;
fragment.appendChild(node);
}
document.querySelector('#installed-dictionaries-none').hidden = (dictionaryInfos.length !== 0);
const container = document.querySelector('#installed-dictionaries');
container.textContent = '';
container.appendChild(fragment);
})();
const settingsController = new SettingsController();
settingsController.prepare();
const backupController = new BackupController(settingsController, null);
await backupController.prepare();
await promiseTimeout(100);
document.documentElement.dataset.loaded = 'true';
} catch (e) {
yomichan.logError(e);
}
})();

View File

@ -31,25 +31,41 @@ class BackupController {
this._settingsResetModal = null; this._settingsResetModal = null;
this._settingsImportErrorModal = null; this._settingsImportErrorModal = null;
this._settingsImportWarningModal = null; this._settingsImportWarningModal = null;
this._optionsUtil = null;
try {
this._optionsUtil = new OptionsUtil(); this._optionsUtil = new OptionsUtil();
} catch (e) {
// NOP
}
} }
async prepare() { async prepare() {
if (this._optionsUtil !== null) {
await this._optionsUtil.prepare(); await this._optionsUtil.prepare();
}
if (this._modalController !== null) {
this._settingsResetModal = this._modalController.getModal('settings-reset'); this._settingsResetModal = this._modalController.getModal('settings-reset');
this._settingsImportErrorModal = this._modalController.getModal('settings-import-error'); this._settingsImportErrorModal = this._modalController.getModal('settings-import-error');
this._settingsImportWarningModal = this._modalController.getModal('settings-import-warning'); this._settingsImportWarningModal = this._modalController.getModal('settings-import-warning');
}
document.querySelector('#settings-export-button').addEventListener('click', this._onSettingsExportClick.bind(this), false); this._addNodeEventListener('#settings-export-button', 'click', this._onSettingsExportClick.bind(this), false);
document.querySelector('#settings-import-button').addEventListener('click', this._onSettingsImportClick.bind(this), false); this._addNodeEventListener('#settings-import-button', 'click', this._onSettingsImportClick.bind(this), false);
document.querySelector('#settings-import-file').addEventListener('change', this._onSettingsImportFileChange.bind(this), false); this._addNodeEventListener('#settings-import-file', 'change', this._onSettingsImportFileChange.bind(this), false);
document.querySelector('#settings-reset-button').addEventListener('click', this._onSettingsResetClick.bind(this), false); this._addNodeEventListener('#settings-reset-button', 'click', this._onSettingsResetClick.bind(this), false);
document.querySelector('#settings-reset-confirm-button').addEventListener('click', this._onSettingsResetConfirmClick.bind(this), false); this._addNodeEventListener('#settings-reset-confirm-button', 'click', this._onSettingsResetConfirmClick.bind(this), false);
} }
// Private // Private
_addNodeEventListener(selector, ...args) {
const node = document.querySelector(selector);
if (node === null) { return; }
node.addEventListener(...args);
}
_getSettingsExportDateString(date, dateSeparator, dateTimeSeparator, timeSeparator, resolution) { _getSettingsExportDateString(date, dateSeparator, dateTimeSeparator, timeSeparator, resolution) {
const values = [ const values = [
date.getUTCFullYear().toString(), date.getUTCFullYear().toString(),

View File

@ -40,7 +40,7 @@
<label class="outline-item"><span class="outline-item-inner"><span class="outline-item-left"> <label class="outline-item"><span class="outline-item-inner"><span class="outline-item-left">
<label class="toggle"><input id="advanced-checkbox" type="checkbox" data-setting="general.showAdvanced" data-transform="setDocumentAttribute" data-document-attribute="data-advanced"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label> <label class="toggle"><input id="advanced-checkbox" type="checkbox" data-setting="general.showAdvanced" data-transform="setDocumentAttribute" data-document-attribute="data-advanced"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label>
</span><span class="outline-item-label">Advanced</span></span></label> </span><span class="outline-item-label">Advanced</span></span></label>
<a class="outline-item"><span class="outline-item-inner"><span class="outline-item-left outline-item-icon" data-icon="about"></span><span class="outline-item-label">About Yomichan</span></span></a> <a href="/bg/info.html" class="outline-item"><span class="outline-item-inner"><span class="outline-item-left outline-item-icon" data-icon="about"></span><span class="outline-item-label">About Yomichan</span></span></a>
</div> </div>
</div></div> </div></div>
</div> </div>

View File

@ -73,6 +73,10 @@ const api = (() => {
return this._invoke('isAnkiConnected'); return this._invoke('isAnkiConnected');
} }
getAnkiConnectVersion() {
return this._invoke('getAnkiConnectVersion');
}
addAnkiNote(note) { addAnkiNote(note) {
return this._invoke('addAnkiNote', {note}); return this._invoke('addAnkiNote', {note});
} }