user browser polyfill with Promise for options access
This commit is contained in:
parent
8dc554fd61
commit
f906728bf8
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
[libs]
|
[libs]
|
||||||
node_modules/flow-interfaces-chrome/interfaces/
|
node_modules/flow-interfaces-chrome/interfaces/
|
||||||
|
src/js/browser_types.js
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"web-ext": "^7.4.0",
|
"web-ext": "^7.4.0",
|
||||||
"web-ext-submit": "^7.4.0",
|
"web-ext-submit": "^7.4.0",
|
||||||
|
"webextension-polyfill": "^0.10.0",
|
||||||
"webpack": "^5.75.0",
|
"webpack": "^5.75.0",
|
||||||
"webpack-cli": "^5.0.1",
|
"webpack-cli": "^5.0.1",
|
||||||
"webpack-dev-server": "^4.11.1",
|
"webpack-dev-server": "^4.11.1",
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
// TODO do I really need to annotate all files with @flow??
|
// TODO do I really need to annotate all files with @flow??
|
||||||
|
|
||||||
|
|
||||||
import {COMMAND_CAPTURE_SIMPLE, METHOD_CAPTURE_WITH_EXTRAS, showNotification} from './common'
|
import {COMMAND_CAPTURE_SIMPLE, METHOD_CAPTURE_WITH_EXTRAS, showNotification} from './common'
|
||||||
import {get_options} from './options'
|
import {getOptions} from './options'
|
||||||
import type {Options} from './options'
|
import type {Options} from './options'
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ function capture(comment: ?string = null, tag_str: ?string = null) {
|
||||||
// TODO await properly and handle errors
|
// TODO await properly and handle errors
|
||||||
const has_scripting = 'scripting' in chrome
|
const has_scripting = 'scripting' in chrome
|
||||||
|
|
||||||
get_options(opts => {
|
getOptions().then(opts => {
|
||||||
if (has_scripting) {
|
if (has_scripting) {
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
chrome.scripting.executeScript({
|
chrome.scripting.executeScript({
|
||||||
|
|
25
extension/src/js/browser_types.js
Normal file
25
extension/src/js/browser_types.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/* @flow */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on https://github.com/Shraymonks/flow-interfaces-chrome
|
||||||
|
* not sure if browser polyfill has flow types??
|
||||||
|
* https://github.com/mozilla/webextension-polyfill/issues/421
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
type browser$StorageArea = {
|
||||||
|
get: (keys: string | Array<string> | Object | null) => Promise<Object>,
|
||||||
|
set: (items: Object) => Promise<void>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type browser$storage = {
|
||||||
|
local: browser$StorageArea,
|
||||||
|
managed: browser$StorageArea,
|
||||||
|
sync: browser$StorageArea,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
declare var browser: {
|
||||||
|
storage: browser$storage,
|
||||||
|
}
|
|
@ -6,31 +6,23 @@ export type Options = {
|
||||||
notification: boolean;
|
notification: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function default_options(): Options {
|
|
||||||
|
function defaultOptions(): Options {
|
||||||
return {
|
return {
|
||||||
endpoint: "http://localhost:12212/capture",
|
endpoint: "http://localhost:12212/capture",
|
||||||
default_tags: "grasp",
|
default_tags: "grasp",
|
||||||
notification: true,
|
notification: true,
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_options(cb: (Options) => void) {
|
|
||||||
chrome.storage.local.get(null, res => {
|
|
||||||
res = {...default_options(), ...res};
|
|
||||||
cb(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function set_options(opts: Options, cb: () => void) {
|
|
||||||
console.log('Saving %s', JSON.stringify(opts));
|
|
||||||
chrome.storage.local.set(opts, cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function getOptions(): Promise<Options> {
|
||||||
export function getOptions(): Promise<Options> {
|
const res = await browser.storage.local.get(null)
|
||||||
return new Promise((resolve) => {
|
return {...defaultOptions(), ...res}
|
||||||
// TODO FIXME reject?
|
}
|
||||||
get_options(resolve);
|
|
||||||
});
|
|
||||||
|
export async function setOptions(opts: Options): Promise<void> {
|
||||||
|
console.log('Saving %o', opts)
|
||||||
|
await browser.storage.local.set(opts)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
import {get_options, set_options} from './options';
|
import {getOptions, setOptions} from './options'
|
||||||
import {ensurePermissions, hasPermissions} from './permissions';
|
import {ensurePermissions, hasPermissions} from './permissions'
|
||||||
|
|
||||||
|
|
||||||
const ENDPOINT_ID = 'endpoint_id';
|
const ENDPOINT_ID = 'endpoint_id';
|
||||||
const HAS_PERMISSION_ID = 'has_permission_id';
|
const HAS_PERMISSION_ID = 'has_permission_id';
|
||||||
const NOTIFICATION_ID = 'notification_id';
|
const NOTIFICATION_ID = 'notification_id';
|
||||||
const DEFAULT_TAGS_ID = 'default_tags_id';
|
const DEFAULT_TAGS_ID = 'default_tags_id';
|
||||||
// TODO specify capture path here?
|
|
||||||
|
|
||||||
const SAVE_ID = 'save_id';
|
const SAVE_ID = 'save_id';
|
||||||
|
|
||||||
|
|
||||||
function getEndpoint(): HTMLInputElement {
|
function getEndpoint(): HTMLInputElement {
|
||||||
return ((document.getElementById(ENDPOINT_ID): any): HTMLInputElement);
|
return ((document.getElementById(ENDPOINT_ID): any): HTMLInputElement);
|
||||||
}
|
}
|
||||||
|
@ -31,47 +31,50 @@ function getHasPermission(): HTMLElement {
|
||||||
return ((document.getElementById(HAS_PERMISSION_ID): any): HTMLElement);
|
return ((document.getElementById(HAS_PERMISSION_ID): any): HTMLElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ugh. needs a better name..
|
// ugh. needs a better name..
|
||||||
function refreshPermissionValidation(endpoint: string) {
|
async function refreshPermissionValidation(endpoint: string) {
|
||||||
hasPermissions(endpoint).then(has => {
|
const has = await hasPermissions(endpoint)
|
||||||
const pstyle = getHasPermission().style;
|
const pstyle = getHasPermission().style
|
||||||
if (has) {
|
if (has) {
|
||||||
console.debug('Got permisssions, nothing to worry about.');
|
console.debug('Got permisssions, nothing to worry about.')
|
||||||
pstyle.display = 'none';
|
pstyle.display = 'none'
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
console.debug('Whoops, no permissions to access %s', endpoint);
|
console.debug('Whoops, no permissions to access %s', endpoint)
|
||||||
// TODO maybe just show button? but then it would need to be reactive..
|
// TODO maybe just show button? but then it would need to be reactive..
|
||||||
pstyle.display = 'block';
|
pstyle.display = 'block'
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreOptions() {
|
|
||||||
getSaveButton().addEventListener('click', saveOptions);
|
async function restoreOptions() {
|
||||||
get_options(opts => {
|
getSaveButton().addEventListener('click', saveOptions)
|
||||||
const ep = opts.endpoint;
|
|
||||||
getEndpoint().value = ep;
|
const opts = await getOptions()
|
||||||
getDefaultTags().value = opts.default_tags;
|
getEndpoint().value = opts.endpoint
|
||||||
getEnableNotification().checked = opts.notification;
|
getDefaultTags().value = opts.default_tags
|
||||||
refreshPermissionValidation(ep);
|
getEnableNotification().checked = opts.notification
|
||||||
});
|
await refreshPermissionValidation(opts.endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveOptions() {
|
|
||||||
|
async function saveOptions() {
|
||||||
// TODO could also check for permissions and display message?
|
// TODO could also check for permissions and display message?
|
||||||
|
|
||||||
const endpoint = getEndpoint().value;
|
const endpoint = getEndpoint().value
|
||||||
ensurePermissions(endpoint).finally(() => {
|
await ensurePermissions(endpoint)
|
||||||
refreshPermissionValidation(endpoint);
|
refreshPermissionValidation(endpoint)
|
||||||
});
|
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
default_tags: getDefaultTags().value,
|
default_tags: getDefaultTags().value,
|
||||||
notification: getEnableNotification().checked,
|
notification: getEnableNotification().checked,
|
||||||
};
|
}
|
||||||
set_options(opts, () => { alert('Saved!'); });
|
await setOptions(opts)
|
||||||
|
|
||||||
|
// hmm seems that regular alert() doesn't work in chrome anymore
|
||||||
|
chrome.extension.getBackgroundPage().alert('Saved!')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', restoreOptions);
|
document.addEventListener('DOMContentLoaded', restoreOptions)
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import {METHOD_CAPTURE_WITH_EXTRAS} from './common';
|
|
||||||
import {get_options} from './options';
|
import {METHOD_CAPTURE_WITH_EXTRAS} from './common'
|
||||||
|
import {getOptions} from './options'
|
||||||
|
|
||||||
// TODO template it in html too?
|
// TODO template it in html too?
|
||||||
const BUTTON_ID = 'button_id';
|
const BUTTON_ID = 'button_id';
|
||||||
|
@ -73,14 +74,15 @@ function getUiState(): State {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function restoreState(state: ?State) {
|
async function restoreState(state: ?State) {
|
||||||
window.justSubmitted = false
|
window.justSubmitted = false
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
// comment just relies on default
|
// comment just relies on default
|
||||||
get_options(opts => {getTags().value = opts.default_tags;});
|
const opts = await getOptions()
|
||||||
|
getTags().value = opts.default_tags
|
||||||
} else {
|
} else {
|
||||||
getComment().value = state.comment;
|
getComment().value = state.comment
|
||||||
getTags().value = state.tag_str;
|
getTags().value = state.tag_str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
"name": "<webpack managed>",
|
"name": "<webpack managed>",
|
||||||
"version": "<webpack managed>",
|
"version": "<webpack managed>",
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": ["background.js"],
|
"scripts": [
|
||||||
|
"browser-polyfill.js",
|
||||||
|
"background.js"
|
||||||
|
],
|
||||||
"persistent": false
|
"persistent": false
|
||||||
},
|
},
|
||||||
"options_ui": {
|
"options_ui": {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
<script type="text/javascript" src="browser-polyfill.js"/></script>
|
||||||
<script type="text/javascript" src="options_page.js"/></script>
|
<script type="text/javascript" src="options_page.js"/></script>
|
||||||
<style>
|
<style>
|
||||||
input:invalid {
|
input:invalid {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
<script type="text/javascript" src="browser-polyfill.js"></script>
|
||||||
<script type="text/javascript" src="popup.js"></script>
|
<script type="text/javascript" src="popup.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -166,6 +166,7 @@ const options = {
|
||||||
patterns: [
|
patterns: [
|
||||||
{ context: 'src', from: '**/*.html' },
|
{ context: 'src', from: '**/*.html' },
|
||||||
{ context: 'src', from: '**/*.png' },
|
{ context: 'src', from: '**/*.png' },
|
||||||
|
{ from: 'node_modules/webextension-polyfill/dist/browser-polyfill.js'},
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
new WebpackExtensionManifestPlugin({
|
new WebpackExtensionManifestPlugin({
|
||||||
|
|
Loading…
Reference in a new issue