diff --git a/background.js b/background.js index b8a73f5..8485fed 100644 --- a/background.js +++ b/background.js @@ -1,12 +1,55 @@ -trektor.runtime.onMessage((msg) => { +trektor.runtime.onMessage.addListener((msg) => { switch (msg.action) { - case 'fetchJSON': - return fetchJSON(...msg.args); + case 'addTask': + return addTask(...msg.args); default: - return Promise.reject('unknown action'); + return Promise.reject(new Error('unknown action')); } }); -function fetchJSON(url, options = {}) { - return fetch(url, options).then((response) => response.json()); +async function 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]; + const taskName = `${taskPrefix}_${card.idShort}`; + const cardTaskName = card.name.match(/(?<=#)[a-z0-9]+_[0-9]+/)?.[0]; + + if (cardTaskName === undefined) { + await trektor.trelloGateway.updateCard(card.id, { + name: `${card.name} #${taskName}`, + }); + } else if (cardTaskName !== taskName) { + throw new Error('Card name includes invalid tracking task.'); + } + 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); + if (tasks.some((task) => task.name === taskName)) return; + + await trektor.togglGateway.createTask(projects[0].id, taskName); } diff --git a/content_script.js b/content_script.js index 778fd4c..1b15810 100644 --- a/content_script.js +++ b/content_script.js @@ -1,146 +1,55 @@ -function addButton() { - const sidebar = document.querySelector(".window-sidebar") - const button = document.createElement('span'); - button.classList.add('button-link'); - button.addEventListener('click', onClick); - sidebar.prepend(button); - - const buttonIcon = document.createElement('span'); - buttonIcon.classList.add('icon-sm', 'plugin-icon'); - buttonIcon.innerText = '+'; - button.append(buttonIcon); - - const buttonText = document.createElement('span'); - buttonText.innerText = 'Toggl Task'; - button.append(buttonText); - - sidebar.querySelector(".mod-no-top-margin").classList.remove("mod-no-top-margin") - sidebar.querySelector(".js-sidebar-add-heading").classList.remove("mod-no-top-margin") +async function addButton() { + const sidebar = await awaitSelector(".window-sidebar", 10000); + sidebar.querySelector(".mod-no-top-margin")?.classList?.remove("mod-no-top-margin"); + sidebar.querySelector(".js-sidebar-add-heading")?.classList?.remove("mod-no-top-margin"); + + const button = document.createElement("span"); + button.classList.add("button-link"); + sidebar.prepend(button); + + const buttonIcon = document.createElement("span"); + buttonIcon.classList.add("icon-sm", "plugin-icon"); + buttonIcon.innerText = "+"; + button.append(buttonIcon); + + const buttonText = document.createElement("span"); + buttonText.innerText = "Toggl Task"; + button.append(buttonText); + + button.addEventListener("click", async () => { + const response = await trektor.runtime.sendMessage({ + action: 'addTask', + args: [window.location.pathname.split('/', 3)[2]], + }); + + if (response) window.alert(response); + }); } -const mappings = { - "GG": "gg", - "Audience Builder": "ab", - "Audience Exporter": "ae", - "Cta Calls": "xcta", - "Camper": "camper", - "Praktikum #pr": "pr" -}; - -function onClick() { - let trelloApiKey = "afadffe77f745496f80ebb4bf460c615" - trektor.storage.get(['trello', 'toggl']).then(result => { - const trelloToken = result.trello - const togglToken = result.toggl - - const idLong = window.location.pathname.split("/")[2]; - var url = new URL(`https://api.trello.com/1/cards/${idLong}`) - fetch(url.toString(), { - headers: { - 'Authorization': `OAuth oauth_consumer_key="${trelloApiKey}", oauth_token="${trelloToken}"`, - 'Content-Type': 'application/json' - }, - }).then(response => response.json()).then(response => { - const idShort = response["idShort"] - - var labelShort, labelLong = undefined - for (var i in response["labels"]) { - if (labelShort == undefined) { - labelShort = mappings[response["labels"][i]["name"]] - labelLong = (labelShort != undefined) ? response["labels"][i]["name"] : labelLong - } else if (mappings[response["labels"][i]["name"]] != undefined) { - alert(`Diese Karte hat sowohl "${labelShort}" als auch "${mappings[response["labels"][i]["name"]]}".`) - } - } - - if (labelShort == undefined) { - alert("Diese Karte besitzt kein unterstütztes Projekt Label.") - return false - } - - if (!response["name"].endsWith(`#${labelShort}_${idShort}`)) { - - - url = new URL(`https://api.trello.com/1/cards/${idLong}`) - - var data = { - "name": `${response["name"]} #${labelShort}_${idShort}` - }; - fetch(url.toString(), { - method: 'PUT', - headers: { - 'Authorization': `OAuth oauth_consumer_key="${trelloApiKey}", oauth_token="${trelloToken}"`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data) - }).then(response => { - if (!response.ok) { - alert(`Error ${response.status}:\n${response.statusText}`) - return false - } - - }) - - - } - - url = new URL("https://api.track.toggl.com/api/v8/workspaces") - const togglAuth = btoa(`${togglToken}:api_token`) - trektor.fetchJSON(url.toString(), { - headers: { - 'Authorization': `Basic ${togglAuth}`, - 'Content-Type': 'application/json' - } - }).then(response => { - for (var i in response) { - if (response[i]["name"] == "aboutsource") { - var wid = response[i]["id"] - } - } - if (wid == undefined) { - alert("WorkspaceID undefined\nDies bedeutet, du hast entweder keinen Zugriff zum a:s toggl oder es ist kein API-Token angegeben.") - return false - } - - url = new URL(`https://api.track.toggl.com/api/v8/workspaces/${wid}/projects`) - trektor.fetchJSON(url.toString(), { - headers: { - 'Authorization': `Basic ${togglAuth}`, - 'Content-Type': 'application/json' - } - }).then(response => { - for (var i in response) { - if (response[i]["name"].endsWith(`(${labelShort})`)) { - var pid = response[i]["id"] - - url = new URL('https://api.track.toggl.com/api/v8/tasks') - data = { - "task": { - "name": `${labelShort}_${idShort}`, - "pid": pid - } - } - trektor.fetchJSON(url.toString(), { - method: 'POST', - headers: { - 'Authorization': `Basic ${togglAuth}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data) - }) - } - } - }) - }) - - }) - }) - - +function awaitSelector(selector, timeout) { + const element = document.querySelector(".window-sidebar"); + if (element !== null) return Promise.resolve(element); + + return new Promise((resolve, reject) => { + const interval = window.setInterval(() => { + const element = document.querySelector(".window-sidebar"); + + if (element !== null) { + resolve(element); + window.clearInterval(interval); + } else if (timeout < 0) { + reject(new Error('timeout')); + window.clearInterval(interval); + } + timeout -= 100; + }, 100); + }); } -window.addEventListener("pushstate", function () { - if (window.location.pathname.startsWith("/c/")) { - addButton() - } -}) +window.addEventListener("pushstate", () => { + if (window.location.pathname.startsWith("/c/")) addButton(); +}); + +window.addEventListener('load', () => { + if (window.location.pathname.startsWith("/c/")) addButton(); +}); diff --git a/manifest.json b/manifest.json index 939d602..0c60167 100644 --- a/manifest.json +++ b/manifest.json @@ -6,7 +6,6 @@ "icons": { "64": "icons/64.png" }, - "content_scripts": [ { "matches": [ diff --git a/options/index.html b/options/index.html index 743838d..eb3b60b 100644 --- a/options/index.html +++ b/options/index.html @@ -4,15 +4,31 @@