Skip to content

Instantly share code, notes, and snippets.

@SMUsamaShah
Last active October 21, 2024 13:41
Show Gist options
  • Save SMUsamaShah/274b50a188b96ae0cda69ff79821cde5 to your computer and use it in GitHub Desktop.
Save SMUsamaShah/274b50a188b96ae0cda69ff79821cde5 to your computer and use it in GitHub Desktop.
Save current link to dynalist
(function() {
const TOKEN = "YOUR_DYNALIST_TOKEN";
const FILE_ID = "YOUR_FILE_ID";
const INBOX_NODE_ID = "YOUR_INBOX_NODE_ID";
let savedNodeId = null;
let updateTimer = null;
// Utility Functions
// DOM Utils
function createDiv() { return document.createElement('div'); }
function appendToBody(element) { document.body.appendChild(element); }
function setInnerHTML(element, html) { element.innerHTML = html; }
function querySelector(parent, selector) { return parent.querySelector(selector); }
function getSelectedText() { return window.getSelection().toString().trim(); }
// UI Utils
function setStatus(statusSpan, message) { statusSpan.textContent = message; }
function hideElement(element) { element.style.display = 'none'; }
function showElement(element) { element.style.display = 'inline'; }
function removeElement(element) { element.remove(); }
function focusElement(element) { element.focus(); }
// API Utils
async function callDynalistApi(endpoint, changes) {
const response = await fetch(endpoint, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
token: TOKEN,
file_id: FILE_ID,
changes: changes
})
});
return await response.json();
}
// DOM Creation
function createPopup(description = "") {
const popupDiv = createDiv();
const popupHtml = `<div id="dynalist-popup" style="position:fixed; top:10px; right:10px; z-index:999999; background:white; padding:15px; border:1px solid #ccc; border-radius:4px; box-shadow:0 2px 8px rgba(0,0,0,0.2); width:300px;">
<h3 id="dyn-title" style="margin:0 0 10px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;"></h3>
<input type="text" id="dyn-description" placeholder="Add description..." style="width:100%; margin-bottom:10px; padding:5px; box-sizing:border-box;" value="${description}">
<div style="display:flex; justify-content:space-between; align-items:center;">
<button id="dyn-delete">Delete</button>
<button id="dyn-close">Close</button>
<span id="dyn-status" style="margin-left:10px;"></span>
</div>
</div>`;
setInnerHTML(popupDiv, popupHtml);
appendToBody(popupDiv);
return popupDiv.firstElementChild;
}
function getElements(popup) {
return {
titleElem: querySelector(popup, '#dyn-title'),
descInput: querySelector(popup, '#dyn-description'),
deleteBtn: querySelector(popup, '#dyn-delete'),
closeBtn: querySelector(popup, '#dyn-close'),
statusSpan: querySelector(popup, '#dyn-status')
};
}
// API Operations
async function saveToDynalist(title, url, description = "") {
const result = await callDynalistApi(
"https://dynalist.io/api/v1/doc/edit",
[{
action: "insert",
parent_id: INBOX_NODE_ID,
index: -1,
content: `${title} ${url}`,
note: `${description}`,
checked: false
}]
);
if (result._code === "Ok" && result.new_node_ids?.length > 0) {
return result.new_node_ids[0];
}
throw new Error(result._msg || 'Failed to save to Dynalist');
}
async function updateDynalist(nodeId, description) {
return await callDynalistApi(
"https://dynalist.io/api/v1/doc/edit",
[{
action: "edit",
node_id: nodeId,
note: description
}]
);
}
async function deleteDynalist(nodeId) {
return await callDynalistApi(
"https://dynalist.io/api/v1/doc/edit",
[{
action: "delete",
node_id: nodeId
}]
);
}
// Event Handlers
function handleDescriptionInput(e, elements) {
if (!savedNodeId) return;
setStatus(elements.statusSpan, 'Updating...');
clearTimeout(updateTimer);
updateTimer = setTimeout(async () => {
try {
const result = await updateDynalist(savedNodeId, e.target.value);
setStatus(elements.statusSpan, result._code === "Ok" ? 'Updated' : 'Error updating');
} catch (err) {
setStatus(elements.statusSpan, 'Error updating');
console.error(err);
}
}, 400);
}
async function handleDelete(elements, popup) {
if (!savedNodeId) return;
setStatus(elements.statusSpan, 'Deleting...');
try {
const result = await deleteDynalist(savedNodeId);
if (result._code === "Ok") {
setStatus(elements.statusSpan, 'Deleted');
setTimeout(() => removeElement(popup), 500);
} else {
setStatus(elements.statusSpan, 'Error deleting');
}
} catch (err) {
setStatus(elements.statusSpan, 'Error deleting');
console.error(err);
}
}
// Initial Save
async function performInitialSave(elements) {
setStatus(elements.statusSpan, 'Saving...');
try {
savedNodeId = await saveToDynalist(document.title, window.location.href, elements.descInput.value);
setStatus(elements.statusSpan, 'Saved');
showElement(elements.deleteBtn);
return true;
} catch (err) {
setStatus(elements.statusSpan, 'Error saving');
console.error(err);
return false;
}
}
// Setup
function setupEventListeners(popup, elements) {
elements.descInput.addEventListener('input', e => handleDescriptionInput(e, elements));
elements.deleteBtn.addEventListener('click', () => handleDelete(elements, popup));
elements.closeBtn.addEventListener('click', () => removeElement(popup));
}
function initializePopup(popup, elements) {
elements.titleElem.textContent = document.title;
hideElement(elements.deleteBtn);
}
// Main function
async function init() {
const selectedText = getSelectedText();
const popup = createPopup(selectedText);
const elements = getElements(popup);
initializePopup(popup, elements);
setupEventListeners(popup, elements);
const saveSuccessful = await performInitialSave(elements);
if (saveSuccessful) {
focusElement(elements.descInput);
}
}
init();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment