user browser polyfill with Promise for options access

This commit is contained in:
Dima Gerasimov 2022-12-27 23:26:10 +00:00 committed by karlicoss
parent 8dc554fd61
commit f906728bf8
11 changed files with 93 additions and 62 deletions

View file

@ -5,6 +5,7 @@
[libs]
node_modules/flow-interfaces-chrome/interfaces/
src/js/browser_types.js
[lints]

View file

@ -46,6 +46,7 @@
"style-loader": "^3.3.1",
"web-ext": "^7.4.0",
"web-ext-submit": "^7.4.0",
"webextension-polyfill": "^0.10.0",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1",

View file

@ -1,8 +1,9 @@
/* @flow */
// TODO do I really need to annotate all files with @flow??
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'
@ -87,7 +88,7 @@ function capture(comment: ?string = null, tag_str: ?string = null) {
// TODO await properly and handle errors
const has_scripting = 'scripting' in chrome
get_options(opts => {
getOptions().then(opts => {
if (has_scripting) {
// $FlowFixMe
chrome.scripting.executeScript({

View 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,
}

View file

@ -6,31 +6,23 @@ export type Options = {
notification: boolean;
}
function default_options(): Options {
function defaultOptions(): Options {
return {
endpoint: "http://localhost:12212/capture",
default_tags: "grasp",
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 function getOptions(): Promise<Options> {
return new Promise((resolve) => {
// TODO FIXME reject?
get_options(resolve);
});
export async function getOptions(): Promise<Options> {
const res = await browser.storage.local.get(null)
return {...defaultOptions(), ...res}
}
export async function setOptions(opts: Options): Promise<void> {
console.log('Saving %o', opts)
await browser.storage.local.set(opts)
}

View file

@ -1,15 +1,15 @@
/* @flow */
import {get_options, set_options} from './options';
import {ensurePermissions, hasPermissions} from './permissions';
import {getOptions, setOptions} from './options'
import {ensurePermissions, hasPermissions} from './permissions'
const ENDPOINT_ID = 'endpoint_id';
const HAS_PERMISSION_ID = 'has_permission_id';
const NOTIFICATION_ID = 'notification_id';
const DEFAULT_TAGS_ID = 'default_tags_id';
// TODO specify capture path here?
const SAVE_ID = 'save_id';
function getEndpoint(): HTMLInputElement {
return ((document.getElementById(ENDPOINT_ID): any): HTMLInputElement);
}
@ -31,47 +31,50 @@ function getHasPermission(): HTMLElement {
return ((document.getElementById(HAS_PERMISSION_ID): any): HTMLElement);
}
// ugh. needs a better name..
function refreshPermissionValidation(endpoint: string) {
hasPermissions(endpoint).then(has => {
const pstyle = getHasPermission().style;
async function refreshPermissionValidation(endpoint: string) {
const has = await hasPermissions(endpoint)
const pstyle = getHasPermission().style
if (has) {
console.debug('Got permisssions, nothing to worry about.');
pstyle.display = 'none';
return;
console.debug('Got permisssions, nothing to worry about.')
pstyle.display = 'none'
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..
pstyle.display = 'block';
});
pstyle.display = 'block'
}
function restoreOptions() {
getSaveButton().addEventListener('click', saveOptions);
get_options(opts => {
const ep = opts.endpoint;
getEndpoint().value = ep;
getDefaultTags().value = opts.default_tags;
getEnableNotification().checked = opts.notification;
refreshPermissionValidation(ep);
});
async function restoreOptions() {
getSaveButton().addEventListener('click', saveOptions)
const opts = await getOptions()
getEndpoint().value = opts.endpoint
getDefaultTags().value = opts.default_tags
getEnableNotification().checked = opts.notification
await refreshPermissionValidation(opts.endpoint)
}
function saveOptions() {
async function saveOptions() {
// TODO could also check for permissions and display message?
const endpoint = getEndpoint().value;
ensurePermissions(endpoint).finally(() => {
refreshPermissionValidation(endpoint);
});
const endpoint = getEndpoint().value
await ensurePermissions(endpoint)
refreshPermissionValidation(endpoint)
const opts = {
endpoint: endpoint,
default_tags: getDefaultTags().value,
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)

View file

@ -1,7 +1,8 @@
/* @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?
const BUTTON_ID = 'button_id';
@ -73,14 +74,15 @@ function getUiState(): State {
}
function restoreState(state: ?State) {
async function restoreState(state: ?State) {
window.justSubmitted = false
if (state == null) {
// comment just relies on default
get_options(opts => {getTags().value = opts.default_tags;});
const opts = await getOptions()
getTags().value = opts.default_tags
} else {
getComment().value = state.comment;
getTags().value = state.tag_str;
getComment().value = state.comment
getTags().value = state.tag_str
}
}

View file

@ -2,7 +2,10 @@
"name": "<webpack managed>",
"version": "<webpack managed>",
"background": {
"scripts": ["background.js"],
"scripts": [
"browser-polyfill.js",
"background.js"
],
"persistent": false
},
"options_ui": {

View file

@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="browser-polyfill.js"/></script>
<script type="text/javascript" src="options_page.js"/></script>
<style>
input:invalid {

View file

@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="browser-polyfill.js"></script>
<script type="text/javascript" src="popup.js"></script>
</head>
<body>

View file

@ -166,6 +166,7 @@ const options = {
patterns: [
{ context: 'src', from: '**/*.html' },
{ context: 'src', from: '**/*.png' },
{ from: 'node_modules/webextension-polyfill/dist/browser-polyfill.js'},
]
}),
new WebpackExtensionManifestPlugin({