Files

266 lines
8.0 KiB
JavaScript

(function () {
'use strict';
if (window.__nqsTalkEmbedInitialized) {
return;
}
window.__nqsTalkEmbedInitialized = true;
var path = String(window.location && window.location.pathname ? window.location.pathname : '');
var isTalkPath = /\/apps\/spreed(\/|$)/.test(path)
|| /^\/call\/[^/?#]+/i.test(path)
|| /^\/room\/[^/?#]+/i.test(path)
|| /^\/conversation\/[^/?#]+/i.test(path);
if (!isTalkPath) {
return;
}
var placementTimer = null;
var activeToken = '';
var elements = buildUi();
function requestToken() {
if (typeof OC !== 'undefined' && OC.requestToken) {
return OC.requestToken;
}
var meta = document.querySelector('meta[name="requesttoken"]');
return meta && meta.content ? meta.content : '';
}
function appUrl(pathPart) {
if (typeof OC !== 'undefined' && typeof OC.generateUrl === 'function') {
return OC.generateUrl('/apps/nuqloud_scrum' + pathPart);
}
return '/apps/nuqloud_scrum' + pathPart;
}
function parseJsonResponse(response) {
return response.text().then(function (text) {
var data = {};
if (text) {
try {
data = JSON.parse(text);
} catch (_error) {
data = {};
}
}
if (!response.ok || (data && data.ok === false)) {
throw new Error((data && data.error) ? data.error : 'Request failed.');
}
return data;
});
}
function getJson(url) {
return fetch(url, {
method: 'GET',
credentials: 'same-origin',
headers: {
Accept: 'application/json',
requesttoken: requestToken()
}
}).then(parseJsonResponse);
}
function postJson(url, payload) {
return fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
requesttoken: requestToken()
},
body: JSON.stringify(payload || {})
}).then(parseJsonResponse);
}
function extractConversationToken() {
var currentPath = String(window.location && window.location.pathname ? window.location.pathname : '');
var hashPath = String(window.location && window.location.hash ? window.location.hash : '');
var patterns = [
/^\/call\/([^/?#]+)/i,
/^\/room\/([^/?#]+)/i,
/^\/conversation\/([^/?#]+)/i,
/\/apps\/spreed\/call\/([^/?#]+)/i,
/\/apps\/spreed\/room\/([^/?#]+)/i,
/\/apps\/spreed\/conversation\/([^/?#]+)/i
];
for (var index = 0; index < patterns.length; index += 1) {
var match = currentPath.match(patterns[index]);
if (match && match[1]) {
return decodeURIComponent(match[1]);
}
}
for (var hashIndex = 0; hashIndex < patterns.length; hashIndex += 1) {
var hashMatch = hashPath.match(patterns[hashIndex]);
if (hashMatch && hashMatch[1]) {
return decodeURIComponent(hashMatch[1]);
}
}
var params = new URLSearchParams(window.location.search);
var queryCandidates = ['conversationToken', 'conversation', 'token'];
for (var keyIndex = 0; keyIndex < queryCandidates.length; keyIndex += 1) {
var value = params.get(queryCandidates[keyIndex]);
if (value && value.trim()) {
return value.trim();
}
}
return '';
}
function conversationNameFallback() {
var title = String(document.title || '').replace(/\s+-\s+Nextcloud.*$/i, '').trim();
return title || activeToken;
}
function buildUi() {
var section = document.createElement('details');
section.className = 'nqs-talk-settings-section';
section.open = true;
var summary = document.createElement('summary');
summary.textContent = 'NuQloud Scrum';
var body = document.createElement('div');
body.className = 'nqs-talk-settings-body';
body.innerHTML = ''
+ '<p class="nqs-talk-note">Use this Talk channel as a Scrum project. Talk threads become tasks; Deck comments are the next mirror step.</p>'
+ '<div class="nqs-talk-status" role="status" aria-live="polite"></div>'
+ '<button type="button" class="button button-primary nqs-talk-toggle">Enable Scrum</button>';
section.appendChild(summary);
section.appendChild(body);
document.body.appendChild(section);
return {
section: section,
status: body.querySelector('.nqs-talk-status'),
toggle: body.querySelector('.nqs-talk-toggle')
};
}
function isVisibleNode(node) {
if (!node || !node.isConnected || node.getAttribute('aria-hidden') === 'true') {
return false;
}
try {
var style = window.getComputedStyle(node);
return style.display !== 'none' && style.visibility !== 'hidden';
} catch (_error) {
return true;
}
}
function findConversationSettingsHost() {
var selectors = [
'[role="dialog"] [data-cy-conversation-settings]',
'[aria-modal="true"] [data-cy-conversation-settings]',
'.modal-container [data-cy-conversation-settings]',
'.modal-wrapper [data-cy-conversation-settings]',
'.nc-modal [data-cy-conversation-settings]',
'.v--modal-box [data-cy-conversation-settings]',
'[role="dialog"] [id*="conversation-settings"]',
'[aria-modal="true"] [id*="conversation-settings"]',
'[role="dialog"] [class*="conversation-settings"]',
'[aria-modal="true"] [class*="conversation-settings"]'
];
for (var index = 0; index < selectors.length; index += 1) {
var nodes = document.querySelectorAll(selectors[index]);
for (var nodeIndex = 0; nodeIndex < nodes.length; nodeIndex += 1) {
if (isVisibleNode(nodes[nodeIndex])) {
return nodes[nodeIndex];
}
}
}
return null;
}
function setStatus(message, isError) {
if (!elements.status) {
return;
}
elements.status.textContent = message;
elements.status.classList.toggle('nqs-talk-error', Boolean(isError));
}
function renderStatus(status) {
var enabled = Boolean(status && status.enabled);
elements.toggle.textContent = enabled ? 'Disable Scrum' : 'Enable Scrum';
elements.toggle.dataset.enabled = enabled ? '1' : '0';
setStatus(enabled ? 'Scrum is enabled for this project.' : 'Scrum is disabled for this project.', false);
}
function loadStatus() {
var token = extractConversationToken();
if (!token) {
activeToken = '';
elements.toggle.disabled = true;
setStatus('Open a Talk conversation to manage Scrum.', false);
return Promise.resolve();
}
activeToken = token;
elements.toggle.disabled = true;
setStatus('Loading Scrum status...', false);
return getJson(appUrl('/api/conversations/' + encodeURIComponent(token) + '/scrum'))
.then(function (result) {
var data = result && result.data ? result.data : {};
renderStatus(data);
})
.catch(function (error) {
setStatus('Unable to load Scrum status for this conversation. ' + error.message, true);
})
.finally(function () {
elements.toggle.disabled = false;
});
}
function toggleScrum() {
var token = activeToken || extractConversationToken();
if (!token) {
setStatus('Open a Talk conversation to manage Scrum.', true);
return;
}
var enabled = elements.toggle.dataset.enabled !== '1';
elements.toggle.disabled = true;
setStatus(enabled ? 'Enabling Scrum...' : 'Disabling Scrum...', false);
postJson(appUrl('/api/conversations/' + encodeURIComponent(token) + '/scrum'), {
enabled: enabled,
talkRoomName: conversationNameFallback()
}).then(function (result) {
var data = result && result.data ? result.data : {};
renderStatus(data);
}).catch(function (error) {
setStatus('Unable to update Scrum for this conversation. ' + error.message, true);
}).finally(function () {
elements.toggle.disabled = false;
});
}
function syncPlacement() {
var host = findConversationSettingsHost();
if (!host) {
if (elements.section.parentNode !== document.body) {
document.body.appendChild(elements.section);
}
elements.section.classList.add('nqs-hidden');
return;
}
if (elements.section.parentNode !== host) {
host.appendChild(elements.section);
loadStatus();
}
elements.section.classList.remove('nqs-hidden');
}
elements.toggle.addEventListener('click', toggleScrum);
placementTimer = window.setInterval(syncPlacement, 1100);
document.addEventListener('click', function () {
window.setTimeout(syncPlacement, 100);
}, true);
window.addEventListener('hashchange', loadStatus);
window.addEventListener('popstate', loadStatus);
syncPlacement();
}());