/**
* @file This file makes table cells editable and send the edited content to a server and create a GitHub issue for each edit. It uses the Octokit library to interact with the GitHub API and the awesome-notifications library to display notifications to the user.
It also uses a MutationObserver to observe changes in the element. When a change is detected, it collects the data of the edited cell (like row, column, row number, column number, column name, proposed text, term) and stores it in the mutation object.
* @author Kor Dwarshuis
* @version 1.0.0
* @since 2023-03-15
*/
import { Octokit, App } from 'octokit';
// https://f3oall.github.io/awesome-notifications/docs/why-such-awesome
// https://www.npmjs.com/package/awesome-notifications
// https://github.com/f3oall/awesome-notifications#readme
// import AWN from 'awesome-notifications';
import AWN from './libs/awesome-notifications.js';
// Initialize instance of AWN
let notifier = new AWN({
maxNotifications: 6,
durations: {
alert: 0,
success: 4000,
},
icons: {
enabled: false,
},
});
const writeChanges = (element) => {
const el = document.querySelector(element);
const buttonTextEdit = 'Edit';
const buttonTextSave = 'Send';
const buttonTextCancel = 'Cancel';
let explanationAboutGithubIssueShown = false;
const domainReceivingChanges =
'https://dwarshuis.com/test/wot-terms/php_scripts/saveEdits.php';
if (el !== null) {
write();
}
function write() {
const makeEditable = el;
let mutation = {};
// // Create an edit/save button and insert before the element we want to edit
// const editSaveButton = document.createElement('button');
// editSaveButton.classList.add('button');
// editSaveButton.classList.add('button--secondary');
// editSaveButton.classList.add('margin--md');
// editSaveButton.classList.add('edit-save');
// editSaveButton.innerText = buttonTextEdit;
// el.parentNode.insertBefore(editSaveButton, el);
// editSaveButton.addEventListener('click', makeContentEditable);
// Create an edit/save button in every table cell
const tableCells = document.querySelectorAll('.googlesheet td');
tableCells.forEach((cell) => {
if (cell.dataset.columnnr !== '0') {
// Surround the cell content with a div
cell.innerHTML =
'<div class="cell-content">' + cell.innerHTML + '</div>';
const div = document.createElement('div');
div.classList.add('buttons');
cell.appendChild(div);
const editSaveButton = document.createElement('button');
editSaveButton.classList.add('button');
editSaveButton.classList.add('button--secondary');
editSaveButton.classList.add('margin--md');
editSaveButton.classList.add('edit-save');
editSaveButton.innerText = buttonTextEdit;
div.appendChild(editSaveButton);
editSaveButton.addEventListener('click', makeTableCellEditable);
const cancelButton = document.createElement('button');
cancelButton.classList.add('button');
cancelButton.classList.add('button--secondary');
cancelButton.classList.add('margin--md');
cancelButton.classList.add('cancel');
cancelButton.innerText = buttonTextCancel;
div.appendChild(cancelButton);
cancelButton.addEventListener('click', cancelTableCellEditable);
}
});
function makeTableCellEditable() {
if (this.parentElement.parentElement.contentEditable !== 'true') {
this.parentElement.parentElement.contentEditable = 'true';
this.parentElement.parentElement.classList.add('editable');
this.parentElement.parentElement.focus();
this.innerText = buttonTextSave;
if (explanationAboutGithubIssueShown === false) {
notifier.info(
`After editing, click the “${buttonTextSave}” button, and a Github issue will be generated.`
);
explanationAboutGithubIssueShown = true;
}
} else {
this.parentElement.parentElement.contentEditable = 'false';
this.innerText = buttonTextEdit;
sendContent();
}
}
function cancelTableCellEditable() {
this.parentElement.parentElement.contentEditable = 'false';
this.parentElement.parentElement.classList.remove('editable');
this.parentElement.querySelector('button.edit-save').innerText =
buttonTextEdit;
}
async function sendContent() {
var formData = new FormData();
formData.append('content', JSON.stringify(mutation));
/**
* Write to a textfile on a domain
*/
// TODO: improve fetch
fetch(domainReceivingChanges, { method: 'POST', body: formData });
// .then(
// function (response) {
// return response.text();
// }
// );
// .then(function (body) {
// });
// // Octokit.js
// // https://github.com/octokit/core.js#readme
// const octokit = new Octokit({
// auth: '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',
// });
// octokit.request('POST /repos/kordwarshuis/WOT-terms-edits/dispatches', {
// owner: 'kordwarshuis',
// repo: 'WOT-terms-edits',
// event_type: 'edit',
// client_payload: {
// unit: false,
// integration: true,
// },
// headers: {
// 'X-GitHub-Api-Version': '2022-11-28',
// },
// });
/**
* Create an issue on Github
*/
let auth = prompt('Enter token');
// Initialize the Octokit client
const octokit = new Octokit({
auth: auth,
});
// Create the issue payload
const payload = {
owner: 'kordwarshuis',
repo: 'WOT-terms-edits',
title: `New edit for “${mutation.columnname}” for the term: “${mutation.term}”.`,
body: `An edit has been made in column “${mutation.columnname}” for the term: “${mutation.term}”.\n\nThe new text is: “${mutation.proposedText}”\n\n(Column: ${mutation.columnnr}, Row: ${mutation.rownr})`,
};
// Send the request to create the issue
const response = await octokit.rest.issues.create(payload);
let onOk = () => {
// notifier.info('You pressed OK');
};
notifier.confirm(
`A new issue has been created on Github at: <a target="_blank" rel="noopener" href="${response.data.html_url}">${response.data.html_url}</a>`,
onOk,
false,
{
labels: {
confirm: 'Info',
},
}
);
}
// https://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/
// TODO: implement observer.disconnect();
const observer = new MutationObserver((mutationRecords) => {
// Collect the data like row, column, rownr, columnnr, columnname, proposedText, term of the edited cell
mutation.row =
mutationRecords[0].target.parentElement.parentElement.dataset.row;
mutation.rownr =
mutationRecords[0].target.parentElement.parentElement.dataset.rownr;
mutation.column =
mutationRecords[0].target.parentElement.parentElement.dataset.column;
mutation.columnnr =
mutationRecords[0].target.parentElement.parentElement.dataset.columnnr;
mutation.columnname = document.querySelectorAll(
`.googlesheet th[data-columnnr]`
)[
mutationRecords[0].target.parentElement.parentElement.dataset.columnnr
].innerText;
// The text that is being edited
mutation.proposedText =
mutationRecords[0].target.parentElement.parentElement.innerText;
// Remove the edit button text from the text that is being edited
mutation.proposedText = mutation.proposedText.substring(
0,
mutation.proposedText.length -
buttonTextSave.length -
buttonTextCancel.length -
1
);
// The term that is being edited
mutation.term = document.querySelectorAll(
`.googlesheet tr[data-rownr="${mutation.rownr}"] td[data-columnnr="4"]`
)[0].innerText;
// Remove the edit button text from the term
mutation.term = mutation.term.substring(
0,
mutation.term.length -
document.querySelectorAll(
`.googlesheet tr[data-rownr="${mutation.rownr}"] td[data-columnnr="4"] button.cancel`
)[0].innerText.length -
document.querySelectorAll(
`.googlesheet tr[data-rownr="${mutation.rownr}"] td[data-columnnr="4"] button.edit-save`
)[0].innerText.length -
1
);
});
observer.observe(el, {
characterData: true,
subtree: true,
});
}
};
export function onRouteDidUpdate({ location, previousLocation }) {
// Don't execute if we are still on the same page; the lifecycle may be fired
// because the hash changes (e.g. when navigating between headings)
// if (location.pathname === previousLocation?.pathname) return;
writeChanges('.googlesheet');
}