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

Asana api implementation #10

Merged
merged 6 commits into from
Nov 5, 2024
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
49 changes: 49 additions & 0 deletions icons/loader.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Asana task name extension",
"description": "Asana task name extension",
"version": "2.0",
"version": "2.1",
"permissions": ["clipboardWrite", "storage"],
"action": {
"default_popup": "/popup/hello.html",
Expand Down Expand Up @@ -32,7 +32,8 @@
"/icons/cross.svg",
"/icons/start.svg",
"/icons/start-cr.svg",
"/icons/analysis.svg"
"/icons/analysis.svg",
"/icons/loader.svg"
]
}
],
Expand Down
4 changes: 4 additions & 0 deletions popup/hello.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ <h1 class="title">Asana task name</h1>
<label for="togglApiKey">Toggl Api key</label>
<input type="text" id="togglApiKey">
</li>
<li>
<label for="asanaApiKey">Asana Api key</label>
<input type="text" id="asanaApiKey">
</li>
<li>
<label for="copyButton">Button - task name</label>
<input type="checkbox" id="copyButton">
Expand Down
8 changes: 8 additions & 0 deletions popup/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ document.addEventListener("DOMContentLoaded", function () {
}
});

chrome.storage.sync.get("asanaApiKey", function (data) {
if (data.asanaApiKey) {
document.getElementById("asanaApiKey").value = data.asanaApiKey;
}
});

chrome.storage.sync.get("copyButton", function (data) {
if (data.copyButton) {
document.getElementById("copyButton").checked = data.copyButton;
Expand All @@ -31,13 +37,15 @@ document.addEventListener("DOMContentLoaded", function () {

document.getElementById("save").addEventListener("click", function () {
const togglApiKey = document.getElementById("togglApiKey").value;
const asanaApiKey = document.getElementById("asanaApiKey").value;
const copyButton = document.getElementById("copyButton").checked;
const togglReportButton = document.getElementById("togglReportButton").checked;
const trackingButton = document.getElementById("trackingButton").checked;
const crTrackingButton = document.getElementById("crTrackingButton").checked;

chrome.storage.sync.set({
"togglApiKey": togglApiKey,
"asanaApiKey": asanaApiKey,
"copyButton": copyButton,
"togglReportButton": togglReportButton,
"trackingButton": trackingButton,
Expand Down
152 changes: 119 additions & 33 deletions scripts/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ const TASK_TITLE_SELECTOR = 'textarea.BaseTextarea.simpleTextarea--dynamic.simpl
const PARENT_TASK_TITLE_SELECTOR = 'div.Breadcrumb.TaskAncestryBreadcrumb.TaskAncestry-taskAncestryBreadcrumb > a';
const HEADING_SELECTOR = 'div.TaskPaneToolbar.TaskPane-header.Stack.Stack--align-center.Stack--direction-row.Stack--display-block.Stack--justify-space-between';
const ALL_COMMENTS_SELECTOR = 'div.FeedBlockStory.TaskStoryFeed-blockStory';
const COMMENT_TEXT_DIV_SELECTOR = 'div.TypographyPresentation.TypographyPresentation--m.RichText3-paragraph--withVSpacingNormal.RichText3-paragraph';
const COMMENT_TEXT_DIV_SELECTOR = 'div.TypographyPresentation.TypographyPresentation--medium.RichText3-paragraph--withVSpacingNormal.RichText3-paragraph';
const COMMENT_BUTTON_DIV_SELECTOR = 'div.ThemeableIconButtonPresentation--isEnabled.ThemeableIconButtonPresentation.ThemeableIconButtonPresentation--medium.SubtleIconButton--standardTheme.SubtleIconButton.BlockStoryDropdown.FeedBlockStory-actionsDropdownButton';
const PIN_TO_TOP_BUTTON_SELECTOR = '.TypographyPresentation.TypographyPresentation--overflowTruncate.TypographyPresentation--m.LeftIconItemStructure-label';
const PIN_TO_TOP_BUTTON_SELECTOR = '.TypographyPresentation.TypographyPresentation--overflowTruncate.TypographyPresentation--medium.LeftIconItemStructure-label';
const COMMENT_SECTION_CLASS_NAME = 'TaskStoryFeed';
const MR_DELIMITERS = ['MR: ', 'MR - '];
const COPY_BUTTON_ID = 'asana-task-name-extension-copy-button';
Expand All @@ -13,14 +13,20 @@ const START_TRACKING_BUTTON_ID = 'asana-task-name-extension-start-tracking-butto
const START_CODE_REVIEW_TRACKING_BUTTON_ID = 'asana-task-name-extension-start-code-review-tracking-button';
const TASK_PROJECT_SELECTOR = 'div.TaskProjectTokenPill-name';
const TASK_PROJECT_FALLBACK_SELECTOR = 'a.HiddenNavigationLink.TaskAncestry-ancestorProject';
const COPY_TASK_LINK_SELECTOR = 'div.TaskPaneToolbar-copyLinkButton';
const BASE_API_URL = 'https://backend.involve.cz/api/v1';
const TOGGL_REPORT_URL = 'https://track.toggl.com/reports/summary/1033184/description/__taskName__/period/last12Months';


const ICONS_TO_BUTTON = {
[COPY_BUTTON_ID]: 'copy',
[START_TRACKING_BUTTON_ID]: 'start',
[START_CODE_REVIEW_TRACKING_BUTTON_ID]: 'start-cr',
}

function getSvgIcon(name) {
const svgPath = chrome.runtime.getURL('icons/' + name + '.svg');

console.log(svgPath);

return fetch(svgPath)
.then(response => response.text())
.catch(error => {
Expand All @@ -45,6 +51,23 @@ const handleRunTracking = (apiKey, taskName, project, tags) => {
}).catch(err => console.error(err));
};

const getTaskInfo = async (apiKey, taskId) => {
const response = await fetch(BASE_API_URL + '/asana-extension/task-information', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
apiKey,
taskId,
})
});

const jsonResponse = await response.json();

return jsonResponse?.data
};

function extractTaskInfo(onlyChildTask) {
const taskTitleElement = document.querySelector(TASK_TITLE_SELECTOR);
const parentTaskElements = document.querySelectorAll(PARENT_TASK_TITLE_SELECTOR);
Expand Down Expand Up @@ -93,39 +116,102 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
}
});

const copyTaskInfo = (shiftPressed, startTracking = false, tags = []) => {
const taskInfo = extractTaskInfo(shiftPressed);
const button = document.getElementById(COPY_BUTTON_ID);

if (taskInfo && startTracking) {
chrome.storage.sync.get('togglApiKey', function (data) {
if (data.togglApiKey) {
const project = extractTaskProjectName();
handleRunTracking(data.togglApiKey, taskInfo, project, tags);
const fetchData = async (taskId) => {
return new Promise((resolve, reject) => {
chrome.storage.sync.get('asanaApiKey', async function (data) {
if (data.asanaApiKey) {
try {
const taskInfo = await getTaskInfo(
data.asanaApiKey,
taskId
);
resolve(taskInfo);
} catch (error) {
reject(error);
}
} else {
reject('API Key not found');
}
});
});
};

const copyTaskInfo = async (shiftPressed, startTracking = false, tags = [], buttonId = null) => {
const copyButton = document.querySelector(COPY_TASK_LINK_SELECTOR);
copyButton.click();
let link;

try {
const clipboardContents = await navigator.clipboard.read();

if (!clipboardContents.length){
return;
}

const blob = await clipboardContents[0].getType("text/plain");
link = await blob.text();
} catch (error) {
console.error(error); // Log any errors that occur

return;
}

if (taskInfo) {
navigator.clipboard.writeText(taskInfo)
.then(async () => {
button.innerHTML = await getSvgIcon('check');
const urlParts = link?.replace('/f', '').split('/');
let taskId;

setTimeout(async function () {
button.innerHTML = await getSvgIcon('copy');
}, 5000);
if (!urlParts || !urlParts.length) {
return;
}

taskId = urlParts.pop();

const button = document.getElementById(buttonId || COPY_BUTTON_ID);

console.log('Task info copied to clipboard:', taskInfo);
})
.catch(async err => {
button.innerHTML = await getSvgIcon('cross');
try {
button.innerHTML = await getSvgIcon('loader');

setTimeout(async function () {
button.innerHTML = await getSvgIcon('copy');
}, 5000);
const taskInfoApi = await fetchData(taskId);
let taskInfo;

if (shiftPressed) {
taskInfo = taskInfoApi?.taskName
} else {
taskInfo = taskInfoApi?.wholeTaskName;
}

console.error('Failed to copy task info to clipboard:', err);
if (taskInfoApi && startTracking) {
chrome.storage.sync.get('togglApiKey', function (data) {
if (data.togglApiKey) {
const project = taskInfoApi.project;
const taskInfo = taskInfoApi.wholeTaskName;
handleRunTracking(data.togglApiKey, taskInfo, project, tags);
}
});
}

if (taskInfo) {
navigator.clipboard.writeText(taskInfo)
.then(async () => {
button.innerHTML = await getSvgIcon('check');

console.log('Task info copied to clipboard:', taskInfo);
})
.catch(async err => {
button.innerHTML = await getSvgIcon('cross');

console.error('Failed to copy task info to clipboard:', err);
})
.finally(() => {
setTimeout(async function () {
const iconName = ICONS_TO_BUTTON[button.id];
button.innerHTML = await getSvgIcon(iconName);
}, 5000);
});
}
} catch (error) {
const iconName = ICONS_TO_BUTTON[button.id];
button.innerHTML = await getSvgIcon(iconName);
console.error(error); // Log any errors that occur
}
};

Expand All @@ -137,7 +223,7 @@ const appendCopyButton = async (elementToAppendButton) => {
elementToAppendButton,
COPY_BUTTON_ID,
await getSvgIcon('copy'),
(e) => copyTaskInfo(e.shiftKey),
(e) => copyTaskInfo(e.shiftKey, false, [], COPY_BUTTON_ID),
'Copy task name',
);
}
Expand Down Expand Up @@ -172,24 +258,24 @@ const appendTrackingButtons = (elementToAppendButton) => {
chrome.storage.sync.get('togglApiKey', async function (data) {
if (data.togglApiKey) {
chrome.storage.sync.get('trackingButton', async function (data) {
if (data.trackingButton){
if (data.trackingButton) {
appendButton(
elementToAppendButton,
START_TRACKING_BUTTON_ID,
await getSvgIcon('start'),
(e) => copyTaskInfo(e.shiftKey, true),
(e) => copyTaskInfo(e.shiftKey, true, [], START_TRACKING_BUTTON_ID),
'Start tracking',
);
}
});

chrome.storage.sync.get('crTrackingButton', async function (data) {
if (data.crTrackingButton){
if (data.crTrackingButton) {
appendButton(
elementToAppendButton,
START_CODE_REVIEW_TRACKING_BUTTON_ID,
await getSvgIcon('start-cr'),
(e) => copyTaskInfo(e.shiftKey, true, ['code review']),
(e) => copyTaskInfo(e.shiftKey, true, ['code review'], START_CODE_REVIEW_TRACKING_BUTTON_ID),
'Start Code review tracking',
);
}
Expand Down