354 lines
13 KiB
JavaScript
354 lines
13 KiB
JavaScript
/**
|
|
* AKSRT Public User Home runtime
|
|
*
|
|
* Handles public user-home dashboard tabs, panel fragment loading and cover
|
|
* uploads. The URL stays clean (/author/{name}/) — tabs, follow sub-tabs and
|
|
* pagination are loaded via the aksrt/v1 user-home panel REST fragment.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
/* global aksrtAuthorPage, aksrtData, AksrtFrontend, notyf */
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
function notifyLoadError() {
|
|
if (typeof notyf === 'function') {
|
|
notyf('加载失败,请重试', 'danger');
|
|
}
|
|
}
|
|
|
|
function initPublicDashboards() {
|
|
document.querySelectorAll('.dashboard--public').forEach(function (root) {
|
|
if (root.dataset.aksrtAuthorBound === '1') return;
|
|
root.dataset.aksrtAuthorBound = '1';
|
|
|
|
var tabs = Array.prototype.slice.call(root.querySelectorAll('.dashboard-tabs [data-tab]'));
|
|
if (!tabs.length) return;
|
|
|
|
var defaultTab = root.dataset.defaultTab || tabs[0].getAttribute('data-tab') || 'posts';
|
|
var userId = parseInt(root.dataset.userId || '0', 10);
|
|
var panels = {};
|
|
|
|
tabs.forEach(function (tab) {
|
|
var name = tab.getAttribute('data-tab');
|
|
var panel = root.querySelector('#panel-' + name);
|
|
if (name && panel) {
|
|
panels[name] = {
|
|
tab: tab,
|
|
panel: panel
|
|
};
|
|
}
|
|
});
|
|
|
|
function switchTo(name) {
|
|
if (!panels[name]) {
|
|
name = defaultTab;
|
|
}
|
|
|
|
Object.keys(panels).forEach(function (key) {
|
|
var active = key === name;
|
|
panels[key].tab.classList.toggle('is-active', active);
|
|
panels[key].tab.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
panels[key].panel.classList.toggle('is-active', active);
|
|
if (active) {
|
|
panels[key].panel.removeAttribute('hidden');
|
|
} else {
|
|
panels[key].panel.setAttribute('hidden', '');
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadPanelBody(panel, name, page, sub) {
|
|
var cfg = window.aksrtData || {};
|
|
var restBase = cfg.apiUrl || cfg.restUrl || '';
|
|
if (!userId || !restBase) return Promise.reject(new Error('rest-unavailable'));
|
|
if (panel._aksrtUhRequest) return panel._aksrtUhRequest;
|
|
|
|
var followSub = sub || panel.dataset.uhSub || 'followers';
|
|
panel.dataset.uhPage = String(page);
|
|
panel.dataset.uhSub = followSub;
|
|
panel.classList.add('is-loading');
|
|
|
|
var url = restBase.replace(/\/$/, '') + '/user-home/panel/' + userId + '/' + encodeURIComponent(name)
|
|
+ '?page=' + encodeURIComponent(page) + '&sub=' + encodeURIComponent(followSub);
|
|
|
|
panel._aksrtUhRequest = fetch(url, {
|
|
credentials: 'same-origin',
|
|
headers: {
|
|
'X-WP-Nonce': cfg.restNonce || cfg.nonce || ''
|
|
}
|
|
}).then(function (response) {
|
|
return response.json().then(function (payload) {
|
|
if (!response.ok || !payload || !payload.success || !payload.data || typeof payload.data.html !== 'string') {
|
|
throw new Error('panel-load-failed');
|
|
}
|
|
panel.innerHTML = payload.data.html;
|
|
if (window.AksrtFrontend && typeof window.AksrtFrontend.rehydrate === 'function') {
|
|
window.AksrtFrontend.rehydrate(panel, 'ajax');
|
|
}
|
|
});
|
|
}).finally(function () {
|
|
panel._aksrtUhRequest = null;
|
|
panel.classList.remove('is-loading');
|
|
});
|
|
return panel._aksrtUhRequest;
|
|
}
|
|
|
|
function panelNameOf(panel) {
|
|
var name = (panel.id || '').replace(/^panel-/, '');
|
|
return panels[name] ? name : '';
|
|
}
|
|
|
|
tabs.forEach(function (tab) {
|
|
tab.addEventListener('click', function () {
|
|
switchTo(tab.getAttribute('data-tab'));
|
|
});
|
|
});
|
|
|
|
// Delegate sub-tab / pagination clicks so buttons injected by REST
|
|
// fragments keep working without rebinding.
|
|
root.addEventListener('click', function (event) {
|
|
var target = event.target && event.target.closest ? event.target : null;
|
|
if (!target) return;
|
|
|
|
var subBtn = target.closest('[data-uh-sub]');
|
|
if (subBtn) {
|
|
event.preventDefault();
|
|
var followPanel = subBtn.closest('.dashboard-panel');
|
|
if (!followPanel) return;
|
|
var sub = subBtn.getAttribute('data-uh-sub') === 'following' ? 'following' : 'followers';
|
|
loadPanelBody(followPanel, 'follow', 1, sub).catch(notifyLoadError);
|
|
return;
|
|
}
|
|
|
|
var pageBtn = target.closest('[data-uh-page]');
|
|
if (!pageBtn) return;
|
|
event.preventDefault();
|
|
var panel = pageBtn.closest('.dashboard-panel');
|
|
if (!panel) return;
|
|
var name = panelNameOf(panel);
|
|
if (!name) return;
|
|
var page = Math.max(1, parseInt(pageBtn.getAttribute('data-uh-page') || '1', 10) || 1);
|
|
loadPanelBody(panel, name, page, panel.dataset.uhSub || 'followers').catch(notifyLoadError);
|
|
});
|
|
|
|
switchTo(defaultTab);
|
|
});
|
|
}
|
|
|
|
function initCoverUpload() {
|
|
var cfg = window.aksrtAuthorPage || {};
|
|
var upload = cfg.coverUpload || null;
|
|
if (!upload || !upload.enabled || !upload.restUrl) return;
|
|
|
|
var btn = document.getElementById(upload.buttonId || 'aksrt-upload-bg-btn');
|
|
var input = document.getElementById(upload.inputId || 'aksrt-bg-file-input');
|
|
if (!btn || !input || btn.dataset.aksrtCoverBound === '1') return;
|
|
|
|
btn.dataset.aksrtCoverBound = '1';
|
|
|
|
var label = btn.querySelector('span');
|
|
var idleText = upload.idleText || (label ? label.textContent : '');
|
|
var loadingText = upload.loadingText || idleText;
|
|
|
|
function setBusy(busy) {
|
|
btn.disabled = !!busy;
|
|
if (label) {
|
|
label.textContent = busy ? loadingText : idleText;
|
|
}
|
|
}
|
|
|
|
function resetImage(previousUrl) {
|
|
var image = document.getElementById(upload.imageId || 'aksrt-cover-img');
|
|
if (!image) return;
|
|
if (previousUrl) {
|
|
image.src = previousUrl;
|
|
return;
|
|
}
|
|
if (upload.fallbackImage) {
|
|
image.src = upload.fallbackImage;
|
|
}
|
|
}
|
|
|
|
function applyImage(nextUrl) {
|
|
var image = document.getElementById(upload.imageId || 'aksrt-cover-img');
|
|
if (image && nextUrl) {
|
|
image.src = nextUrl;
|
|
}
|
|
}
|
|
|
|
btn.addEventListener('click', function () {
|
|
input.click();
|
|
});
|
|
|
|
input.addEventListener('change', function () {
|
|
var file = input.files && input.files[0];
|
|
if (!file) return;
|
|
|
|
if (upload.maxSize && file.size > upload.maxSize) {
|
|
notyf(upload.tooLargeText || '图片大小不能超过 2MB', 'warning');
|
|
input.value = '';
|
|
return;
|
|
}
|
|
|
|
setBusy(true);
|
|
|
|
var formData = new FormData();
|
|
formData.append(upload.fieldName || 'background', file);
|
|
|
|
fetch(upload.restUrl, {
|
|
method: 'POST',
|
|
credentials: 'same-origin',
|
|
headers: {
|
|
'X-WP-Nonce': upload.restNonce || ''
|
|
},
|
|
body: formData
|
|
})
|
|
.then(function (response) {
|
|
return response.json().catch(function () {
|
|
return null;
|
|
}).then(function (payload) {
|
|
if (!response.ok) {
|
|
throw payload || { message: upload.errorText || '上传失败' };
|
|
}
|
|
return payload;
|
|
});
|
|
})
|
|
.then(function (payload) {
|
|
if (payload && payload.success && payload.url) {
|
|
applyImage(payload.url);
|
|
notyf(upload.successText || '背景图已更新', 'success');
|
|
return;
|
|
}
|
|
throw payload || { message: upload.errorText || '上传失败' };
|
|
})
|
|
.catch(function (error) {
|
|
var previousUrl = error && error.data && error.data.previous_url !== undefined
|
|
? error.data.previous_url
|
|
: '';
|
|
resetImage(previousUrl);
|
|
notyf((error && error.message) || upload.networkErrorText || '网络错误,请重试', 'danger');
|
|
})
|
|
.finally(function () {
|
|
setBusy(false);
|
|
input.value = '';
|
|
});
|
|
});
|
|
}
|
|
|
|
function initAvatarUpload() {
|
|
var upload = (window.aksrtUserCenter || {}).avatarUpload;
|
|
if (!upload || !upload.enabled || !upload.restUrl) return;
|
|
|
|
var button = document.getElementById(upload.buttonId || 'aksrt-upload-avatar-btn');
|
|
var input = document.getElementById(upload.inputId || 'aksrt-avatar-file-input');
|
|
var image = document.getElementById(upload.imageId || 'aksrt-user-avatar-image');
|
|
if (!button || !input || button.dataset.aksrtAvatarBound === '1') return;
|
|
button.dataset.aksrtAvatarBound = '1';
|
|
|
|
var label = button.querySelector('span');
|
|
var idleText = upload.idleText || (label ? label.textContent : '更换头像');
|
|
|
|
function setBusy(busy) {
|
|
button.disabled = !!busy;
|
|
if (label) label.textContent = busy ? (upload.loadingText || '上传中…') : idleText;
|
|
}
|
|
|
|
button.addEventListener('click', function () { input.click(); });
|
|
input.addEventListener('change', function () {
|
|
var file = input.files && input.files[0];
|
|
if (!file) return;
|
|
if (upload.maxSize && file.size > upload.maxSize) {
|
|
notyf(upload.tooLargeText || '图片大小不能超过 2MB', 'warning');
|
|
input.value = '';
|
|
return;
|
|
}
|
|
|
|
var body = new FormData();
|
|
body.append(upload.fieldName || 'avatar', file);
|
|
setBusy(true);
|
|
fetch(upload.restUrl, {
|
|
method: 'POST',
|
|
credentials: 'same-origin',
|
|
headers: { 'X-WP-Nonce': upload.restNonce || '' },
|
|
body: body
|
|
})
|
|
.then(function (response) {
|
|
return response.json().then(function (payload) {
|
|
if (!response.ok || !payload || !payload.success) throw payload || {};
|
|
return payload;
|
|
});
|
|
})
|
|
.then(function (payload) {
|
|
var url = payload.url || (payload.data && payload.data.url) || '';
|
|
if (!url) throw payload;
|
|
if (image) image.src = url;
|
|
notyf(payload.message || upload.successText || '头像已更新', 'success');
|
|
})
|
|
.catch(function (error) {
|
|
notyf((error && error.message) || upload.errorText || '上传失败', 'danger');
|
|
})
|
|
.finally(function () {
|
|
setBusy(false);
|
|
input.value = '';
|
|
});
|
|
});
|
|
}
|
|
|
|
function initCoverMenus() {
|
|
document.querySelectorAll('[data-aksrt-menu]').forEach(function (menu) {
|
|
if (menu.dataset.aksrtMenuBound === '1') return;
|
|
menu.dataset.aksrtMenuBound = '1';
|
|
var trigger = menu.querySelector('.aksrt-cover-menu__trigger');
|
|
var panel = menu.querySelector('.aksrt-cover-menu__panel');
|
|
if (!trigger || !panel) return;
|
|
function close() { panel.hidden = true; trigger.setAttribute('aria-expanded', 'false'); }
|
|
function open() {
|
|
document.querySelectorAll('[data-aksrt-menu]').forEach(function (otherMenu) {
|
|
if (otherMenu === menu) return;
|
|
var otherPanel = otherMenu.querySelector('.aksrt-cover-menu__panel');
|
|
var otherTrigger = otherMenu.querySelector('.aksrt-cover-menu__trigger');
|
|
if (otherPanel) otherPanel.hidden = true;
|
|
if (otherTrigger) otherTrigger.setAttribute('aria-expanded', 'false');
|
|
});
|
|
panel.hidden = false;
|
|
trigger.setAttribute('aria-expanded', 'true');
|
|
var first = panel.querySelector('[role="menuitem"]');
|
|
if (first) first.focus();
|
|
}
|
|
trigger.addEventListener('click', function () { panel.hidden ? open() : close(); });
|
|
menu.addEventListener('keydown', function (event) { if (event.key === 'Escape') { close(); trigger.focus(); } });
|
|
document.addEventListener('pointerdown', function (event) { if (!menu.contains(event.target)) close(); });
|
|
});
|
|
}
|
|
|
|
function initProfileDetailsModal() {
|
|
var modal = document.getElementById('aksrt-profile-details-modal');
|
|
if (!modal) return;
|
|
var dialog = modal.querySelector('.aksrt-profile-details-modal__dialog');
|
|
var returnFocus = null;
|
|
function close() { modal.hidden = true; if (returnFocus) returnFocus.focus(); }
|
|
document.querySelectorAll('[data-aksrt-profile-details]').forEach(function (button) {
|
|
button.addEventListener('click', function () { returnFocus = button; modal.hidden = false; if (dialog) dialog.focus(); });
|
|
});
|
|
modal.querySelectorAll('[data-aksrt-modal-close]').forEach(function (button) { button.addEventListener('click', close); });
|
|
document.addEventListener('keydown', function (event) { if (event.key === 'Escape' && !modal.hidden) close(); });
|
|
}
|
|
|
|
function init() {
|
|
initPublicDashboards();
|
|
initCoverMenus();
|
|
initProfileDetailsModal();
|
|
initCoverUpload();
|
|
initAvatarUpload();
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
})();
|