mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-10-30 18:58:39 -04:00
161 lines
6.1 KiB
JavaScript
161 lines
6.1 KiB
JavaScript
// The ideas as an overlay over the jobs. The links to the ideas open it, and
|
|
// choosing an idea sends it without leaving the page. An idea that was added shows
|
|
// the jobs it made, one after another, and the same jobs slide into the notepad
|
|
// behind. Then the idea makes way and the next takes its place. If anything goes
|
|
// wrong the choice is sent the ordinary way, which shows the ideas on their page.
|
|
|
|
import {csrfToken} from '../api';
|
|
|
|
const reduced = function () {
|
|
return !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
|
|
};
|
|
|
|
// Waits for an animation to be seen; not at all for those who ask for less motion.
|
|
const wait = function (ms) {
|
|
return new Promise(function (resolve) { setTimeout(resolve, reduced() ? 0 : ms); });
|
|
};
|
|
|
|
// The jobs an idea made, as a line to take the idea's place: each job appears a
|
|
// moment after the one before.
|
|
export const addedBlock = function (data) {
|
|
const block = document.createElement('div');
|
|
block.className = 'idea-added';
|
|
const label = document.createElement('p');
|
|
label.className = 'idea-added-label';
|
|
label.textContent = '✓ ' + data.added_label;
|
|
block.appendChild(label);
|
|
const list = document.createElement('ul');
|
|
data.added.forEach(function (job, index) {
|
|
const item = document.createElement('li');
|
|
item.style.animationDelay = (index * 90) + 'ms';
|
|
item.textContent = job.due ? job.due + ' ' + job.name : job.name;
|
|
list.appendChild(item);
|
|
});
|
|
block.appendChild(list);
|
|
return block;
|
|
};
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
const modal = document.getElementById('job-ideas-modal');
|
|
const body = document.getElementById('job-ideas-body');
|
|
if (!modal || !body) return;
|
|
|
|
const request = function (url, options) {
|
|
const settings = options || {};
|
|
return fetch(url, Object.assign({credentials: 'same-origin'}, settings, {
|
|
headers: Object.assign({'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-Token': csrfToken()}, settings.headers),
|
|
}));
|
|
};
|
|
const ok = function (response) {
|
|
if (!response.ok) throw new Error(response.status);
|
|
return response;
|
|
};
|
|
|
|
const keys = function () {
|
|
return Array.from(body.querySelectorAll('.idea')).map(function (idea) { return idea.dataset.key; });
|
|
};
|
|
|
|
// Shows the ideas, those that were not there before sliding in.
|
|
const showList = function (html) {
|
|
const before = new Set(keys());
|
|
body.innerHTML = html;
|
|
body.querySelectorAll('.idea').forEach(function (idea) {
|
|
if (!before.has(idea.dataset.key) && before.size) idea.classList.add('idea-entering');
|
|
});
|
|
};
|
|
|
|
const load = function (url) {
|
|
return request(url, {headers: {Accept: 'text/html'}}).then(ok).then(function (response) { return response.text(); }).then(showList);
|
|
};
|
|
|
|
const open = function () {
|
|
window.bootstrap.Modal.getOrCreateInstance(modal).show();
|
|
};
|
|
|
|
// How many ideas there are, in the box over the jobs.
|
|
const showCount = function (data) {
|
|
document.querySelectorAll('.notepad-ideas').forEach(function (box) {
|
|
const text = box.querySelector('.notepad-ideas-text');
|
|
if (text) text.textContent = data.banner;
|
|
box.hidden = data.count === 0;
|
|
});
|
|
};
|
|
|
|
// The jobs, as they now are, with the new ones marked so they can be seen to
|
|
// arrive.
|
|
const showJobs = function (data) {
|
|
const groups = document.getElementById('notepad-groups');
|
|
if (!groups || !data.groups) return;
|
|
const added = new Set(data.added.map(function (job) { return String(job.id); }));
|
|
groups.innerHTML = data.groups;
|
|
groups.querySelectorAll('.notepad-job').forEach(function (row) {
|
|
if (added.has(row.dataset.id)) row.classList.add('just-added');
|
|
});
|
|
};
|
|
|
|
// Sends the choice. Rejects only if it could not be sent, or the answer was no
|
|
// good: then it is safe to send it again the ordinary way.
|
|
const send = function (form) {
|
|
return request(form.action, {method: 'POST', body: new FormData(form), headers: {Accept: 'application/json'}})
|
|
.then(ok)
|
|
.then(function (response) { return response.json(); });
|
|
};
|
|
|
|
// Shows what the server did. If that goes wrong the choice was made all the
|
|
// same, so the page is loaded again to show how things stand.
|
|
const show = function (form, data) {
|
|
const row = form.closest('.idea');
|
|
return Promise.resolve()
|
|
.then(function () {
|
|
showCount(data);
|
|
showJobs(data);
|
|
if (data.added.length && row) {
|
|
row.replaceChildren(addedBlock(data));
|
|
row.classList.remove('idea-working');
|
|
row.classList.add('idea-confirmed');
|
|
return wait(Math.min(2600, 1100 + data.added.length * 140));
|
|
}
|
|
if (row) row.classList.add('idea-leaving');
|
|
return wait(350);
|
|
})
|
|
.then(function () { showList(data.list); })
|
|
.catch(function () { window.location.reload(); });
|
|
};
|
|
|
|
const choose = function (form) {
|
|
const row = form.closest('.idea');
|
|
if (row) row.classList.add('idea-working');
|
|
return send(form).then(function (data) { return show(form, data); }, function () { form.submit(); });
|
|
};
|
|
|
|
// Sent here from the front page, with the ideas to show.
|
|
const url = new URL(window.location.href);
|
|
if (url.searchParams.has('ideas')) {
|
|
url.searchParams.delete('ideas');
|
|
window.history.replaceState(null, '', url.pathname + url.search + url.hash);
|
|
load(modal.dataset.url).then(open).catch(function () {});
|
|
}
|
|
|
|
document.querySelectorAll('[data-job-ideas-open]').forEach(function (link) {
|
|
link.addEventListener('click', function (event) {
|
|
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
|
|
event.preventDefault();
|
|
load(modal.dataset.url).then(open).catch(function () { window.location.href = link.href; });
|
|
});
|
|
});
|
|
|
|
body.addEventListener('click', function (event) {
|
|
const filter = event.target.closest('.idea-filter');
|
|
if (!filter) return;
|
|
event.preventDefault();
|
|
load(filter.href).catch(function () { window.location.href = filter.href; });
|
|
});
|
|
|
|
body.addEventListener('submit', function (event) {
|
|
const form = event.target.closest('.idea-form');
|
|
if (!form) return;
|
|
event.preventDefault();
|
|
choose(form);
|
|
});
|
|
});
|