aboutsummaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-08-09 13:11:41 -0400
committerGitHub <noreply@github.com>2020-08-09 13:11:41 -0400
commit8ee717cdf7c7701b9ec2d72799fcdcb621703e9e (patch)
tree04b0f66d0d9b1d1987a6ceb92d5650650d901c55 /ext
parent04d47bf8a92a71ae49c320d8d7eccd50c254348e (diff)
Persistent display mode (#714)
* Simplify calls using chrome.tabs.sendMessage and getMessageResponseResult * Rename message handlers * Move onMessage handler into Display * Assign search popup mode which persists across refreshes * Update clipboard monitor on the search page * Remove mode param
Diffstat (limited to 'ext')
-rw-r--r--ext/bg/js/backend.js85
-rw-r--r--ext/bg/js/search.js117
-rw-r--r--ext/fg/js/float.js2
-rw-r--r--ext/mixed/js/display.js51
4 files changed, 152 insertions, 103 deletions
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js
index 85b9b5e6..07920e01 100644
--- a/ext/bg/js/backend.js
+++ b/ext/bg/js/backend.js
@@ -899,24 +899,8 @@ class Backend {
);
});
if (tab !== null) {
- const isValidTab = await new Promise((resolve) => {
- chrome.tabs.sendMessage(
- tabId,
- {action: 'getUrl', params: {}},
- {frameId: 0},
- (response) => {
- let result = false;
- try {
- const {url} = yomichan.getMessageResponseResult(response);
- result = url.startsWith(baseUrl);
- } catch (e) {
- // NOP
- }
- resolve(result);
- }
- );
- });
- // windowId
+ const url = await this._getTabUrl(tabId);
+ const isValidTab = (url !== null && url.startsWith(baseUrl));
if (isValidTab) {
return {tab, created: false};
}
@@ -935,7 +919,7 @@ class Backend {
const popupWindow = await new Promise((resolve, reject) => {
chrome.windows.create(
{
- url: `${baseUrl}?mode=popup`,
+ url: baseUrl,
width: popupWidth,
height: popupHeight,
type: 'popup'
@@ -959,23 +943,22 @@ class Backend {
const tab = tabs[0];
await this._waitUntilTabFrameIsReady(tab.id, 0, 2000);
+ await this._sendMessageTab(
+ tab.id,
+ {action: 'setMode', params: {mode: 'popup'}},
+ {frameId: 0}
+ );
+
this._searchPopupTabId = tab.id;
return {tab, created: true};
}
_updateSearchQuery(tabId, text, animate) {
- return new Promise((resolve, reject) => {
- const callback = (response) => {
- try {
- resolve(yomichan.getMessageResponseResult(response));
- } catch (error) {
- reject(error);
- }
- };
-
- const message = {action: 'updateSearchQuery', params: {text, animate}};
- chrome.tabs.sendMessage(tabId, message, callback);
- });
+ return this._sendMessageTab(
+ tabId,
+ {action: 'updateSearchQuery', params: {text, animate}},
+ {frameId: 0}
+ );
}
_sendMessageAllTabs(action, params={}) {
@@ -1381,18 +1364,20 @@ class Backend {
return typeof templates === 'string' ? templates : this._defaultAnkiFieldTemplates;
}
- _getTabUrl(tab) {
- return new Promise((resolve) => {
- chrome.tabs.sendMessage(tab.id, {action: 'getUrl'}, {frameId: 0}, (response) => {
- let url = null;
- try {
- ({url} = yomichan.getMessageResponseResult(response));
- } catch (error) {
- // NOP
- }
- resolve({tab, url});
- });
- });
+ async _getTabUrl(tabId) {
+ try {
+ const {url} = await this._sendMessageTab(
+ tabId,
+ {action: 'getUrl', params: {}},
+ {frameId: 0}
+ );
+ if (typeof url === 'string') {
+ return url;
+ }
+ } catch (e) {
+ // NOP
+ }
+ return null;
}
async _findTab(timeout, checkUrl) {
@@ -1538,4 +1523,18 @@ class Backend {
}
return await (json ? response.json() : response.text());
}
+
+ _sendMessageTab(...args) {
+ return new Promise((resolve, reject) => {
+ const callback = (response) => {
+ try {
+ resolve(yomichan.getMessageResponseResult(response));
+ } catch (error) {
+ reject(error);
+ }
+ };
+
+ chrome.tabs.sendMessage(...args, callback);
+ });
+ }
}
diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js
index d95fd5e4..d2ec5090 100644
--- a/ext/bg/js/search.js
+++ b/ext/bg/js/search.js
@@ -37,6 +37,7 @@ class DisplaySearch extends Display {
this._clipboardMonitor = new ClipboardMonitor({
getClipboard: api.clipboardGet.bind(api)
});
+ this._clipboardMonitorEnabled = false;
this._onKeyDownIgnoreKeys = new Map([
['ANY_MOD', new Set([
'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'PageDown', 'PageUp', 'Home', 'End',
@@ -51,9 +52,6 @@ class DisplaySearch extends Display {
['AltGraph', new Set()],
['Shift', new Set()]
]);
- this._runtimeMessageHandlers = new Map([
- ['updateSearchQuery', {async: false, handler: this._onExternalSearchUpdate.bind(this)}]
- ]);
}
async prepare() {
@@ -62,18 +60,16 @@ class DisplaySearch extends Display {
yomichan.on('optionsUpdated', () => this.updateOptions());
this.on('contentUpdating', this._onContentUpdating.bind(this));
+ this.on('modeChange', this._onModeChange.bind(this));
+
+ this.registerMessageHandlers([
+ ['updateSearchQuery', {async: false, handler: this._onExternalSearchUpdate.bind(this)}]
+ ]);
this.queryParserVisible = true;
this.setHistorySettings({useBrowserHistory: true});
const options = this.getOptions();
-
- const urlSearchParams = new URLSearchParams(location.search);
- let mode = urlSearchParams.get('mode');
- if (mode === null) { mode = ''; }
-
- document.documentElement.dataset.searchMode = mode;
-
if (options.general.enableWanakana === true) {
this._wanakanaEnable.checked = true;
wanakana.bind(this._query);
@@ -81,25 +77,15 @@ class DisplaySearch extends Display {
this._wanakanaEnable.checked = false;
}
- if (mode !== 'popup') {
- if (options.general.enableClipboardMonitor === true) {
- this._clipboardMonitorEnable.checked = true;
- this._clipboardMonitor.start();
- } else {
- this._clipboardMonitorEnable.checked = false;
- }
- this._clipboardMonitorEnable.addEventListener('change', this._onClipboardMonitorEnableChange.bind(this));
- }
-
- chrome.runtime.onMessage.addListener(this._onRuntimeMessage.bind(this));
-
this._search.addEventListener('click', this._onSearch.bind(this), false);
this._query.addEventListener('input', this._onSearchInput.bind(this), false);
this._wanakanaEnable.addEventListener('change', this._onWanakanaEnableChange.bind(this));
window.addEventListener('copy', this._onCopy.bind(this));
this._clipboardMonitor.on('change', this._onExternalSearchUpdate.bind(this));
+ this._clipboardMonitorEnable.addEventListener('change', this._onClipboardMonitorEnableChange.bind(this));
this._updateSearchButton();
+ this._onModeChange();
await this._prepareNestedPopups();
@@ -209,12 +195,6 @@ class DisplaySearch extends Display {
this._onSearchQueryUpdated(query, true);
}
- _onRuntimeMessage({action, params}, sender, callback) {
- const messageHandler = this._runtimeMessageHandlers.get(action);
- if (typeof messageHandler === 'undefined') { return false; }
- return yomichan.invokeMessageHandler(messageHandler, params, callback, sender);
- }
-
_onCopy() {
// ignore copy from search page
this._clipboardMonitor.setPreviousText(window.getSelection().toString().trim());
@@ -261,34 +241,15 @@ class DisplaySearch extends Display {
}
_onClipboardMonitorEnableChange(e) {
- if (e.target.checked) {
- chrome.permissions.request(
- {permissions: ['clipboardRead']},
- (granted) => {
- if (granted) {
- this._clipboardMonitor.start();
- api.modifySettings([{
- action: 'set',
- path: 'general.enableClipboardMonitor',
- value: true,
- scope: 'profile',
- optionsContext: this.getOptionsContext()
- }], 'search');
- } else {
- e.target.checked = false;
- }
- }
- );
- } else {
- this._clipboardMonitor.stop();
- api.modifySettings([{
- action: 'set',
- path: 'general.enableClipboardMonitor',
- value: false,
- scope: 'profile',
- optionsContext: this.getOptionsContext()
- }], 'search');
- }
+ const enabled = e.target.checked;
+ this._setClipboardMonitorEnabled(enabled);
+ }
+
+ _onModeChange() {
+ let mode = this.mode;
+ if (mode === null) { mode = ''; }
+ document.documentElement.dataset.searchMode = mode;
+ this._updateClipboardMonitorEnabled();
}
_isWanakanaEnabled() {
@@ -381,4 +342,48 @@ class DisplaySearch extends Display {
await onOptionsUpdated();
}
+
+ async _setClipboardMonitorEnabled(value) {
+ let modify = true;
+ if (value) {
+ value = await this._requestPermissions(['clipboardRead']);
+ modify = value;
+ }
+
+ this._clipboardMonitorEnabled = value;
+ this._updateClipboardMonitorEnabled();
+
+ if (!modify) { return; }
+
+ await api.modifySettings([{
+ action: 'set',
+ path: 'general.enableClipboardMonitor',
+ value,
+ scope: 'profile',
+ optionsContext: this.getOptionsContext()
+ }], 'search');
+ }
+
+ _updateClipboardMonitorEnabled() {
+ const mode = this.mode;
+ const enabled = this._clipboardMonitorEnabled;
+ this._clipboardMonitorEnable.checked = enabled;
+ if (enabled && mode !== 'popup') {
+ this._clipboardMonitor.start();
+ } else {
+ this._clipboardMonitor.stop();
+ }
+ }
+
+ _requestPermissions(permissions) {
+ return new Promise((resolve) => {
+ chrome.permissions.request(
+ {permissions},
+ (granted) => {
+ const e = chrome.runtime.lastError;
+ resolve(!e && granted);
+ }
+ );
+ });
+ }
}
diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js
index 794ccc6e..83c542d1 100644
--- a/ext/fg/js/float.js
+++ b/ext/fg/js/float.js
@@ -44,7 +44,7 @@ class DisplayFloat extends Display {
async prepare() {
await super.prepare();
- this.registerMessageHandlers([
+ this.registerDirectMessageHandlers([
['configure', {async: true, handler: this._onMessageConfigure.bind(this)}],
['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}]
]);
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index 93272666..418707ca 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -65,6 +65,7 @@ class Display extends EventDispatcher {
this._hotkeys = new Map();
this._actions = new Map();
this._messageHandlers = new Map();
+ this._directMessageHandlers = new Map();
this._history = new DisplayHistory({clearable: true, useBrowserHistory: false});
this._historyChangeIgnore = false;
this._historyHasChanged = false;
@@ -80,6 +81,7 @@ class Display extends EventDispatcher {
getOptionsContext: this.getOptionsContext.bind(this),
setSpinnerVisible: this.setSpinnerVisible.bind(this)
});
+ this._mode = null;
this.registerActions([
['close', () => { this.onEscape(); }],
@@ -114,6 +116,9 @@ class Display extends EventDispatcher {
{key: 'V', modifiers: ['alt'], action: 'viewNote'}
]);
this.registerMessageHandlers([
+ ['setMode', {async: false, handler: this._onMessageSetMode.bind(this)}]
+ ]);
+ this.registerDirectMessageHandlers([
['setOptionsContext', {async: false, handler: this._onMessageSetOptionsContext.bind(this)}],
['setContent', {async: false, handler: this._onMessageSetContent.bind(this)}],
['clearAutoPlayTimer', {async: false, handler: this._onMessageClearAutoPlayTimer.bind(this)}],
@@ -138,7 +143,12 @@ class Display extends EventDispatcher {
this._updateQueryParserVisibility();
}
+ get mode() {
+ return this._mode;
+ }
+
async prepare() {
+ this._updateMode();
this._setInteractive(true);
await this._displayGenerator.prepare();
await this._queryParser.prepare();
@@ -146,8 +156,9 @@ class Display extends EventDispatcher {
this._history.on('stateChanged', this._onStateChanged.bind(this));
this._queryParser.on('searched', this._onQueryParserSearch.bind(this));
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
+ chrome.runtime.onMessage.addListener(this._onMessage.bind(this));
api.crossFrame.registerHandlers([
- ['popupMessage', {async: 'dynamic', handler: this._onMessage.bind(this)}]
+ ['popupMessage', {async: 'dynamic', handler: this._onDirectMessage.bind(this)}]
]);
}
@@ -313,6 +324,12 @@ class Display extends EventDispatcher {
}
}
+ registerDirectMessageHandlers(handlers) {
+ for (const [name, handlerInfo] of handlers) {
+ this._directMessageHandlers.set(name, handlerInfo);
+ }
+ }
+
async setupNestedPopups(frontendInitializationData) {
await dynamicLoader.loadScripts([
'/mixed/js/text-scanner.js',
@@ -343,10 +360,16 @@ class Display extends EventDispatcher {
// Message handlers
- _onMessage(data) {
+ _onMessage({action, params}, sender, callback) {
+ const messageHandler = this._messageHandlers.get(action);
+ if (typeof messageHandler === 'undefined') { return false; }
+ return yomichan.invokeMessageHandler(messageHandler, params, callback, sender);
+ }
+
+ _onDirectMessage(data) {
data = this.authenticateMessageData(data);
const {action, params} = data;
- const handlerInfo = this._messageHandlers.get(action);
+ const handlerInfo = this._directMessageHandlers.get(action);
if (typeof handlerInfo === 'undefined') {
throw new Error(`Invalid action: ${action}`);
}
@@ -356,6 +379,10 @@ class Display extends EventDispatcher {
return {async, result};
}
+ _onMessageSetMode({mode}) {
+ this._setMode(mode, true);
+ }
+
_onMessageSetOptionsContext({optionsContext}) {
this.setOptionsContext(optionsContext);
}
@@ -1253,4 +1280,22 @@ class Display extends EventDispatcher {
_closePopups() {
yomichan.trigger('closePopups');
}
+
+ _updateMode() {
+ const mode = sessionStorage.getItem('mode');
+ this._setMode(mode, false);
+ }
+
+ _setMode(mode, save) {
+ if (mode === this._mode) { return; }
+ if (save) {
+ if (mode === null) {
+ sessionStorage.removeItem('mode');
+ } else {
+ sessionStorage.setItem('mode', mode);
+ }
+ }
+ this._mode = mode;
+ this.trigger('modeChange', {mode});
+ }
}