Barebones implementation of 'list' action (#2)
This commit is contained in:
@@ -17,7 +17,12 @@ var defaultSettings = {
|
||||
};
|
||||
|
||||
// handle incoming messages
|
||||
browser.runtime.onMessage.addListener(receiveMessage);
|
||||
browser.runtime.onMessage.addListener(function(message, sender, sendResponse) {
|
||||
receiveMessage(message, sender, sendResponse);
|
||||
|
||||
// allow async responses after this function returns
|
||||
return true;
|
||||
});
|
||||
|
||||
//----------------------------------- Function definitions ----------------------------------//
|
||||
/**
|
||||
@@ -49,7 +54,7 @@ function getLocalSettings() {
|
||||
* @param function(mixed) sendResponse Callback to send response
|
||||
* @return void
|
||||
*/
|
||||
function handleMessage(settings, message, sendResponse) {
|
||||
async function handleMessage(settings, message, sendResponse) {
|
||||
// check that action is present
|
||||
if (typeof message !== "object" || !message.hasOwnProperty("action")) {
|
||||
sendResponse({ status: "error", message: "Action is missing" });
|
||||
@@ -66,6 +71,14 @@ function handleMessage(settings, message, sendResponse) {
|
||||
saveSettings(message.settings);
|
||||
sendResponse({ status: "ok" });
|
||||
break;
|
||||
case "listFiles":
|
||||
try {
|
||||
var response = await hostAction(settings, "list");
|
||||
sendResponse(response.data.files);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sendResponse({
|
||||
status: "error",
|
||||
@@ -75,6 +88,28 @@ function handleMessage(settings, message, sendResponse) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request to the host app
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param object settings Live settings object
|
||||
* @param string action Action to run
|
||||
* @param params object Additional params to pass to the host app
|
||||
* @return Promise
|
||||
*/
|
||||
function hostAction(settings, action, params = {}) {
|
||||
var request = {
|
||||
settings: settings,
|
||||
action: action
|
||||
};
|
||||
for (var key in params) {
|
||||
request[key] = params[key];
|
||||
}
|
||||
|
||||
return browser.runtime.sendNativeMessage(appID, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap inbound messages to fetch native configuration
|
||||
*
|
||||
@@ -105,7 +140,7 @@ async function receiveMessage(message, sender, sendResponse) {
|
||||
for (var key in settings.stores) {
|
||||
if (response.data.storeSettings.hasOwnProperty(key)) {
|
||||
var fileSettings = JSON.parse(response.data.storeSettings[key]);
|
||||
if (typeof(settings.stores[key].settings) !== "object") {
|
||||
if (typeof settings.stores[key].settings !== "object") {
|
||||
settings.stores[key].settings = {};
|
||||
}
|
||||
var storeSettings = settings.stores[key].settings;
|
||||
@@ -130,9 +165,6 @@ async function receiveMessage(message, sender, sendResponse) {
|
||||
console.log(e);
|
||||
sendResponse({ status: "error", message: e.toString() });
|
||||
}
|
||||
|
||||
// allow async responses after this function returns
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
6
src/global.css
Normal file
6
src/global.css
Normal file
@@ -0,0 +1,6 @@
|
||||
html, body {
|
||||
font-family: sans;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
@@ -20,14 +20,7 @@
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"permissions": [
|
||||
"tabs",
|
||||
"activeTab",
|
||||
"nativeMessaging",
|
||||
"notifications",
|
||||
"storage",
|
||||
"webRequest",
|
||||
"webRequestBlocking",
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
"nativeMessaging"
|
||||
]
|
||||
}
|
||||
|
@@ -15,7 +15,10 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"chrome-extension-async": "^3.0.0"
|
||||
"chrome-extension-async": "^3.0.0",
|
||||
"fuzzysort": "^1.1.0",
|
||||
"mithril": "^1.1.0",
|
||||
"tldjs": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browserify": "^16.2.0",
|
||||
|
28
src/popup.css
Normal file
28
src/popup.css
Normal file
@@ -0,0 +1,28 @@
|
||||
html, body {
|
||||
height: 100%;
|
||||
min-width: 260px;
|
||||
}
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.part {
|
||||
box-sizing: border-box;
|
||||
padding: 4px 4px 0 4px;
|
||||
}
|
||||
|
||||
.part:last-child {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.part.error {
|
||||
color: #F00;
|
||||
}
|
||||
|
||||
.part.notice {
|
||||
color: #090;
|
||||
}
|
||||
|
@@ -1,9 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="global.css" />
|
||||
<link rel="stylesheet" type="text/css" href="popup.css" />
|
||||
<script src="popup.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>TODO</p>
|
||||
<div class="part notice">Loading available logins...</div>
|
||||
</body>
|
||||
</html>
|
||||
|
201
src/popup.src.js
201
src/popup.src.js
@@ -2,23 +2,208 @@
|
||||
"use strict";
|
||||
|
||||
require("chrome-extension-async");
|
||||
var Mithril = require("mithril");
|
||||
var TldJS = require("tldjs");
|
||||
var FuzzySort = require("fuzzysort");
|
||||
|
||||
var startTime = Date.now();
|
||||
var settings = null;
|
||||
var error = null;
|
||||
var notice = null;
|
||||
var logins = [];
|
||||
var domainLogins = [];
|
||||
|
||||
if (typeof browser === "undefined") {
|
||||
var browser = chrome;
|
||||
}
|
||||
|
||||
browser.runtime.sendMessage({ action: "getSettings" }).then(
|
||||
function(response) {
|
||||
// performance debugging function - TODO remove once extension is ready for release
|
||||
function checkpoint(activity) {
|
||||
console.log("Elapsed: " + (Date.now() - startTime) + "ms (" + activity + ")");
|
||||
}
|
||||
|
||||
// wrap with current tab & settings
|
||||
checkpoint("start");
|
||||
browser.tabs.query({ active: true, currentWindow: true }, async function(tabs) {
|
||||
checkpoint("after tab");
|
||||
try {
|
||||
var response = browser.runtime.sendMessage({ action: "getSettings" });
|
||||
checkpoint("after getSettings");
|
||||
settings = response;
|
||||
settings.tab = tabs[0];
|
||||
settings.host = new URL(settings.tab.url).hostname;
|
||||
run();
|
||||
},
|
||||
function(response) {
|
||||
console.log(response); // TODO
|
||||
} catch (e) {
|
||||
console.log(e.toString()); // TODO
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
//----------------------------------- Function definitions ----------------------------------//
|
||||
function run() {
|
||||
console.log(settings); // TODO
|
||||
|
||||
/**
|
||||
* Get the logins which match the provided domain
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param string domain Domain to filter against
|
||||
* @return array
|
||||
*/
|
||||
function getDomainLogins(domain) {
|
||||
var domainLogins = [];
|
||||
var t = TldJS.parse(domain);
|
||||
|
||||
// ignore invalid domains
|
||||
if (!t.isValid || t.domain === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// filter against the domain
|
||||
for (var key in logins) {
|
||||
if (logins[key].domain === t.hostname) {
|
||||
domainLogins.push(logins[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// recurse and add matching domains to the list
|
||||
domainLogins = domainLogins.concat(getDomainLogins(t.hostname.replace(/^.+?\./, "")));
|
||||
|
||||
return domainLogins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the deepest available domain component of a path
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param string path Path to parse
|
||||
* @return string|null Extracted domain
|
||||
*/
|
||||
function pathToDomain(path) {
|
||||
var parts = path.split(/\//).reverse();
|
||||
for (var key in parts) {
|
||||
if (parts[key].indexOf("@") >= 0) {
|
||||
continue;
|
||||
}
|
||||
var t = TldJS.parse(parts[key]);
|
||||
if (t.isValid && t.domain !== null) {
|
||||
return t.hostname;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the popup contents
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function render() {
|
||||
var body = document.getElementsByTagName("body")[0];
|
||||
Mithril.mount(body, {
|
||||
view: function() {
|
||||
return [renderError(), renderNotice(), renderList()];
|
||||
}
|
||||
});
|
||||
checkpoint("after render");
|
||||
}
|
||||
|
||||
/**
|
||||
* Render any error messages
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @return Vnode
|
||||
*/
|
||||
function renderError() {
|
||||
return error === null ? null : Mithril("div.part.error", error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render any notices
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @return Vnode
|
||||
*/
|
||||
function renderNotice() {
|
||||
return notice === null ? null : Mithril("div.part.notice", notice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the list of available logins
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @return []Vnode
|
||||
*/
|
||||
function renderList() {
|
||||
if (!logins.length) {
|
||||
showError("There are no matching logins available");
|
||||
return null;
|
||||
}
|
||||
|
||||
var list = [];
|
||||
domainLogins.forEach(function(login) {
|
||||
list.push(
|
||||
Mithril("div.part.login", { title: login.domain }, login.store + ":" + login.login)
|
||||
);
|
||||
});
|
||||
if (!list.length) {
|
||||
showNotice("There are no logins matching " + settings.host + ".");
|
||||
}
|
||||
|
||||
checkpoint("after renderList");
|
||||
return Mithril("div.logins", list);
|
||||
}
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
// get list of logins
|
||||
var response = await browser.runtime.sendMessage({ action: "listFiles" });
|
||||
checkpoint("after listFiles");
|
||||
for (var store in response) {
|
||||
for (var key in response[store]) {
|
||||
var login = {
|
||||
store: store,
|
||||
login: response[store][key].replace(/\.gpg$/i, "")
|
||||
};
|
||||
login.domain = pathToDomain(login.store + "/" + login.login);
|
||||
logins.push(login);
|
||||
}
|
||||
}
|
||||
checkpoint("after listFiles post-processing");
|
||||
|
||||
domainLogins = getDomainLogins(settings.host);
|
||||
|
||||
render();
|
||||
} catch (e) {
|
||||
showError(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an error message
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param string message Message text
|
||||
*/
|
||||
function showError(message) {
|
||||
error = message;
|
||||
Mithril.redraw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an informational message
|
||||
*
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @param string message Message text
|
||||
*/
|
||||
function showNotice(message) {
|
||||
notice = message;
|
||||
Mithril.redraw();
|
||||
}
|
||||
|
@@ -400,6 +400,10 @@ function-bind@^1.0.2:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
|
||||
fuzzysort@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fuzzysort/-/fuzzysort-1.1.1.tgz#bf128f1a4cc6e6b7188665ac5676de46a3d81768"
|
||||
|
||||
glob@^7.1.0:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
|
||||
@@ -572,6 +576,10 @@ minimist@^1.1.0, minimist@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
|
||||
mithril@^1.1.0:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/mithril/-/mithril-1.1.6.tgz#bd2cc0de3d3c86076a6a7a30367a601a1bd108f3"
|
||||
|
||||
mkdirp@^0.5.0:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
@@ -680,7 +688,7 @@ punycode@1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
|
||||
|
||||
punycode@^1.3.2:
|
||||
punycode@^1.3.2, punycode@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
|
||||
|
||||
@@ -837,6 +845,12 @@ timers-browserify@^1.0.1:
|
||||
dependencies:
|
||||
process "~0.11.0"
|
||||
|
||||
tldjs@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/tldjs/-/tldjs-2.3.1.tgz#cf09c3eb5d7403a9e214b7d65f3cf9651c0ab039"
|
||||
dependencies:
|
||||
punycode "^1.4.1"
|
||||
|
||||
to-arraybuffer@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
|
||||
|
Reference in New Issue
Block a user