Skip to content
This repository was archived by the owner on Nov 11, 2025. It is now read-only.
Merged
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
55 changes: 49 additions & 6 deletions background.js
Original file line number Diff line number Diff line change
@@ -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);
}
193 changes: 51 additions & 142 deletions content_script.js
Original file line number Diff line number Diff line change
@@ -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();
});
1 change: 0 additions & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"icons": {
"64": "icons/64.png"
},

"content_scripts": [
{
"matches": [
Expand Down
30 changes: 23 additions & 7 deletions options/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,31 @@
<link rel="icon" href="icons/64.png">
<title>Trektor settings</title>
<meta charset="UTF-8">
<link href="style.css" rel="stylesheet" />
<style>
a {
display: block;
margin-bottom: 10px;
}
input {
width: 100%;
box-sizing: border-box;
margin-bottom: 10px;
}
</style>
</head>
<body class="browser-style">
<h2>Trektor settings</h2>

<body>
<a href="https://trello.com/1/authorize?expiration=never&scope=read,write,account&response_type=token&name=Trektor&key=afadffe77f745496f80ebb4bf460c615">
Trello Token generieren</a><br>
<input type="text" id='trello_token' name="trello" class="chrome-style browser-style" aria-label="Trello Token"/><br><a href="https://track.toggl.com/profile">Toggl Token (ziemlich weit runter scrollen)</a> <br>
<input type="text" id="toggl_token" name="toggl" /><br><br><br>
Trello Token generieren
</a>

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

<a href="https://track.toggl.com/profile">
Toggl Token (ziemlich weit runter scrollen)
</a>

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

<script src="../trektor.js"></script>
<script src="./script.js"></script>
</body>
Expand Down
10 changes: 5 additions & 5 deletions options/script.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
let trelloTextField = document.querySelector("#trello_token")
let togglTextField = document.querySelector("#toggl_token")
let trelloTextField = document.querySelector("input[name='trello']");
let togglTextField = document.querySelector("input[name='toggl']");

trelloTextField.addEventListener("input", onChange)
togglTextField.addEventListener("input", onChange)

function onChange(e) {
trektor.storage.set({[e.target.name]: e.target.value});
trektor.storage.set({[e.target.name]: e.target.value});
}

trektor.storage.get(['trello', 'toggl']).then(result => {
trelloTextField.value = result.trello;
togglTextField.value = result.toggl;
trelloTextField.value = result.trello;
togglTextField.value = result.toggl;
})
3 changes: 0 additions & 3 deletions options/style.css

This file was deleted.

Loading