Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve options page #20

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 0 additions & 76 deletions background.js

This file was deleted.

24 changes: 11 additions & 13 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
{
"manifest_version": 2,
"manifest_version": 3,
"name": "Trektor",
"description": "Browser-Extension zum automatischen Anlegen von Toggl tracking tasks",
"version": "0.0.11",
"browser_specific_settings": {
"gecko": {
"id": "[email protected]"
}
},
"icons": {
"64": "icons/64.png"
},
Expand All @@ -17,26 +12,29 @@
"https://trello.com/*"
],
"js": [
"vendor/browser-polyfill.js",
"content_script.js"
"scripts/content_script.js",
"scripts/trektor.js"
],
"css": [
"content_style.css"
]
}
],
"background": {
"service_worker": "scripts/chromium.js",
"scripts": [
"vendor/browser-polyfill.js",
"trektor.js",
"background.js"
"scripts/trektor.js",
"scripts/background.js",
"scripts/firefox.js"
]
},
"permissions": [
"https://api.trello.com/*",
"https://api.track.toggl.com/*",
"storage"
],
"host_permissions": [
"https://api.trello.com/*",
"https://api.track.toggl.com/*"
],
"options_ui": {
"page": "options/index.html"
}
Expand Down
27 changes: 9 additions & 18 deletions options/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,25 @@
<html lang="en">
<head>
<link rel="icon" href="icons/64.png">
<link rel="stylesheet" href="style.css">
<title>Trektor settings</title>
<meta charset="UTF-8">
<style>
a {
display: block;
margin-bottom: 10px;
}
input {
width: 100%;
box-sizing: border-box;
margin-bottom: 10px;
}
</style>
</head>
<body>
<h2>Willkommen bei Trektor!</h2>
<div class="notification" id="permissions-granted">✔ Alle Berechtigungen vorhanden</div>
<div class="notification" id="permissions-not-granted">✘ Berechtigung fehlt! <button id="request">Anfordern</button></div>

<a href="https://trello.com/1/authorize?expiration=30days&scope=read,write&response_type=token&name=Trektor&key=2379d540412e417f6f0696c1397f38a6" target="_blank">
Trello Token
Trello Token
</a>

<input type="text" name="trello" />
<input type="text" placeholder="trello token..." name="trello" />

<a href="https://track.toggl.com/profile" target="_blank">
Toggl Token
Toggl Token
</a>
<input type="text" placeholder="toggl token..." name="toggl" />

<input type="text" name="toggl" />

<script src="../vendor/browser-polyfill.js"></script>
<script src="./script.js"></script>
</body>
</html>
35 changes: 34 additions & 1 deletion options/script.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
if (typeof browser == "undefined") {
globalThis.browser = chrome
}

const permissions = {
origins: [
"https://api.trello.com/*",
"https://api.track.toggl.com/*",
"https://trello.com/*"
]
}

document.querySelectorAll("input").forEach((field) => {
field.addEventListener("input", (e) => {
browser.storage.local.set({ [e.target.name]: e.target.value });
});

browser.storage.local.get(field.name).then(({ [field.name]: value }) => {
field.value = (value === undefined) ? '' : value;
field.value = (value === undefined) ? "" : value;
});
});

checkPermissions()

document.getElementById("request").addEventListener("click", () => { requestPermissions() })

function requestPermissions() {
console.log("hallo")
browser.permissions.request(permissions, () => { checkPermissions() })
}

function checkPermissions() {
browser.permissions.contains(permissions, (contains) => {
if (contains) {
document.getElementById("permissions-granted").style.display = "block";
document.getElementById("permissions-not-granted").style.display = "none";
} else {
document.getElementById("permissions-granted").style.display = "none";
document.getElementById("permissions-not-granted").style.display = "block";
}
})
}
30 changes: 30 additions & 0 deletions options/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
a {
display: block;
color: blue;
margin: 10px;
}
input {
width: 100%;
box-sizing: border-box;
margin-bottom: 10px;
}
body {
font-family: sans-serif;
width: 90%;
max-width: 500px;
margin: 1.5rem auto;
}

#permissions-granted {
background-color: rgba(0, 255, 0, 0.5);
border: 2px green solid;
}
#permissions-not-granted {
background-color: rgba(255, 20, 0, 0.5);
border: 2px red solid;
}
.notification {
text-align: center;
padding: 10px;
border-radius: 10px;
}
90 changes: 90 additions & 0 deletions scripts/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
class BackgroundWorker {

constructor() {
}

run() {
trektor.browser.runtime.onMessage.addListener(async (msg) => {
switch (msg.action) {
case "track":
await this.track(...msg.args);
return;
case "addTask":
await this.addTask(...msg.args);
return;
case "options":
trektor.browser.runtime.openOptionsPage();
default:
throw new Error(`unknown action: ${msg.action}`);
}
});

trektor.browser.runtime.onInstalled.addListener((details) => {
if (details.reason == "install") trektor.browser.runtime.openOptionsPage();
})
}

async track(cardId) {
const task = await this.addTask(cardId);
const card = await trektor.trelloGateway.getCard(cardId);
const cardName = this.stripStoryPointsAndTaskToken(card.name);
const response = await trektor.togglGateway.startTimeEntry(task.id, cardName);
return response.data;
}

async addTask(cardId) {
const card = await trektor.trelloGateway.getCard(cardId);

const taskPrefixes = card.labels
.map((label) => label.name.match(/(?<=#)[a-z0-9]+$/)?.[0])
.filter((prefix) => prefix !== undefined);

if (taskPrefixes.length === 0) {
throw new Error("Card has no valid project labels.");
}
if (taskPrefixes.length > 1) {
throw new Error("Card has multiple project labels.");
}
const taskPrefix = taskPrefixes[0];

let taskName = card.name.match(/(?<=#)[A-Za-z0-9_-]+/)?.[0];

if (taskName === undefined) {
taskName = `${taskPrefix}_${card.idShort}`;

await trektor.trelloGateway.updateCard(card.id, {
name: `${card.name} #${taskName}`,
});
}

const workspaces = await trektor.togglGateway.getWorkspaces();

if (workspaces.length === 0) {
throw new Error("Could not find any toggl workspaces.");
}
if (workspaces.length > 1) {
throw new Error("Found multiple toggl workspaces. Not sure how to deal with that...");
}
const allProjects = await trektor.togglGateway.getProjects(workspaces[0].id);
const projects = allProjects.filter((project) => project.name.endsWith(`(${taskPrefix})`));

if (projects.length === 0) {
throw new Error("Could not find any matching toggl project.");
}
if (projects.length > 1) {
throw new Error("Found multiple matching toggl projects. Not sure how to deal with that...");
}
const tasks = await trektor.togglGateway.getTasks(projects[0].id);
const task = tasks.find((task) => task.name === taskName);
if (task !== undefined) return task;

const response = await trektor.togglGateway.createTask(projects[0].id, taskName)
return response.data;
}

stripStoryPointsAndTaskToken(cardName) {
return cardName
.replace(/^(\s*\(\d+\))?\s*/, "") // story points, e.g. (3)
.replace(/\s*#[a-z0-9_]+\s*$/, ""); // task token, e.g. #orga_5417
}
}
3 changes: 3 additions & 0 deletions scripts/chromium.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
importScripts('background.js', 'trektor.js');

new BackgroundWorker().run()
14 changes: 5 additions & 9 deletions content_script.js → scripts/content_script.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,27 @@ async function addButton() {
trackButtonIcon.classList.add("trektor-state-loading");

try {
await browser.runtime.sendMessage({
await trektor.browser.runtime.sendMessage({
action: "track",
args: [window.location.pathname.split("/", 3)[2]],
});
trackButtonIcon.classList.replace("icon-clock", "icon-check-circle");
window.setTimeout(() => trackButtonIcon.classList.replace("icon-check-circle", "icon-clock"), 2000);
} catch (err) {
window.alert(err);
if (window.confirm(err + "\n\n Open Trektor options page now?")) trektor.browser.runtime.sendMessage({ action: "options" });
} finally {
trackButtonIcon.classList.remove("trektor-state-loading");
}
});

addButton.addEventListener("click", async () => {
try {
await browser.runtime.sendMessage({
await trektor.browser.runtime.sendMessage({
action: "addTask",
args: [window.location.pathname.split("/", 3)[2]],
});
} catch (err) {
window.alert(err);
if (window.confirm(err + "\n\n Open Trektor options page now?")) trektor.browser.runtime.sendMessage({ action: "options" });
}
});
}
Expand All @@ -71,7 +71,7 @@ function awaitSelector(selector, timeout) {
resolve(element);
window.clearInterval(interval);
} else if (timeout < 0) {
reject(new Error('timeout'));
reject(new Error("timeout"));
window.clearInterval(interval);
}
timeout -= 100;
Expand All @@ -82,7 +82,3 @@ function awaitSelector(selector, timeout) {
window.addEventListener("pushstate", () => {
if (window.location.pathname.startsWith("/c/")) addButton();
});

window.addEventListener('load', () => {
if (window.location.pathname.startsWith("/c/")) addButton();
});
1 change: 1 addition & 0 deletions scripts/firefox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
new BackgroundWorker().run()
Loading