wee
This commit is contained in:
@@ -107,6 +107,7 @@
|
|||||||
let shopModalObserver = null;
|
let shopModalObserver = null;
|
||||||
|
|
||||||
const DEFAULT_SOCIAL_ORDER = ['discord', 'twitter', 'bluesky', 'instagram', 'youtube', 'tiktok', 'reddit', 'github'];
|
const DEFAULT_SOCIAL_ORDER = ['discord', 'twitter', 'bluesky', 'instagram', 'youtube', 'tiktok', 'reddit', 'github'];
|
||||||
|
const DEFAULT_FOOTER_LINK_ORDER = ['terms', 'privacy', 'refund', 'ban-appeal', 'suggestions', 'bug-report'];
|
||||||
|
|
||||||
// Find the button container in the DOM
|
// Find the button container in the DOM
|
||||||
function findButtonContainer() {
|
function findButtonContainer() {
|
||||||
@@ -401,6 +402,184 @@
|
|||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDefaultFooterOrder(id) {
|
||||||
|
const index = DEFAULT_FOOTER_LINK_ORDER.indexOf(id);
|
||||||
|
return index === -1 ? DEFAULT_FOOTER_LINK_ORDER.length + 10 : index;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureFooterEntry(map, id) {
|
||||||
|
if (!map[id]) {
|
||||||
|
map[id] = { id, order: getDefaultFooterOrder(id) };
|
||||||
|
}
|
||||||
|
return map[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFooterLabel(id) {
|
||||||
|
if (!id) return '';
|
||||||
|
return id
|
||||||
|
.split('-')
|
||||||
|
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFooterEntriesFromContent(content) {
|
||||||
|
const data = content || {};
|
||||||
|
const entries = {};
|
||||||
|
|
||||||
|
Object.entries(data).forEach(([key, value]) => {
|
||||||
|
if (!value) return;
|
||||||
|
const match = key.match(/^modal\.footer\.links\.([^.]+)\.(url|text|order|target)$/);
|
||||||
|
if (!match) return;
|
||||||
|
|
||||||
|
const [, id, field] = match;
|
||||||
|
const entry = ensureFooterEntry(entries, id);
|
||||||
|
|
||||||
|
if (field === 'order') {
|
||||||
|
const numericOrder = Number(value);
|
||||||
|
if (!Number.isNaN(numericOrder)) {
|
||||||
|
entry.order = numericOrder;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
entry[field] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const legacyFooterLinks = [
|
||||||
|
['terms', 'modal.footer.terms'],
|
||||||
|
['privacy', 'modal.footer.privacy'],
|
||||||
|
['refund', 'modal.footer.refund'],
|
||||||
|
['ban-appeal', 'modal.footer.banAppeal'],
|
||||||
|
['suggestions', 'modal.footer.suggestions'],
|
||||||
|
['bug-report', 'modal.footer.bugReport']
|
||||||
|
];
|
||||||
|
|
||||||
|
legacyFooterLinks.forEach(([id, baseKey]) => {
|
||||||
|
const url = data[`${baseKey}.url`] || data[baseKey];
|
||||||
|
if (!url) return;
|
||||||
|
|
||||||
|
const entry = ensureFooterEntry(entries, id);
|
||||||
|
entry.url = url;
|
||||||
|
|
||||||
|
const textValue = data[`${baseKey}.text`];
|
||||||
|
if (!entry.text && textValue) {
|
||||||
|
entry.text = textValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderValue = data[`${baseKey}.order`];
|
||||||
|
if (orderValue !== undefined) {
|
||||||
|
const numericOrder = Number(orderValue);
|
||||||
|
if (!Number.isNaN(numericOrder)) {
|
||||||
|
entry.order = numericOrder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetValue = data[`${baseKey}.target`];
|
||||||
|
if (targetValue) {
|
||||||
|
entry.target = targetValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.values(entries)
|
||||||
|
.filter(entry => entry.url)
|
||||||
|
.map(entry => {
|
||||||
|
if (!entry.text) {
|
||||||
|
entry.text = formatFooterLabel(entry.id);
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a.order !== b.order) {
|
||||||
|
return a.order - b.order;
|
||||||
|
}
|
||||||
|
return a.id.localeCompare(b.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFooterLinkElement(entry) {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = entry.url;
|
||||||
|
link.className = 'link';
|
||||||
|
|
||||||
|
if (entry.target) {
|
||||||
|
link.target = entry.target;
|
||||||
|
} else if (!entry.url.startsWith('mailto:')) {
|
||||||
|
link.target = '_blank';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link.target === '_blank') {
|
||||||
|
link.rel = 'noopener noreferrer';
|
||||||
|
}
|
||||||
|
|
||||||
|
link.textContent = entry.text || formatFooterLabel(entry.id);
|
||||||
|
link.setAttribute('data-footer-id', entry.id);
|
||||||
|
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function rebuildInfoModalFooter(modalBox) {
|
||||||
|
try {
|
||||||
|
const footerSection = Array.from(modalBox.querySelectorAll('section')).find(section =>
|
||||||
|
section.classList.contains('text-base-content/80') && section.classList.contains('text-sm')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!footerSection) return;
|
||||||
|
|
||||||
|
const locale = getPreferredLocale();
|
||||||
|
if (
|
||||||
|
footerSection.dataset.fpFooterLocale === locale &&
|
||||||
|
footerSection.dataset.fpFooterLoaded === 'true'
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = await getSiteContent(locale);
|
||||||
|
if (!content) return;
|
||||||
|
|
||||||
|
const email = content['modal.footer.email'];
|
||||||
|
const footerEntries = buildFooterEntriesFromContent(content);
|
||||||
|
|
||||||
|
footerSection.innerHTML = '';
|
||||||
|
|
||||||
|
const items = [];
|
||||||
|
if (email) {
|
||||||
|
const emailSpan = document.createElement('span');
|
||||||
|
emailSpan.className = 'inline-flex items-center gap-1';
|
||||||
|
const emailLink = document.createElement('a');
|
||||||
|
emailLink.href = `mailto:${email}`;
|
||||||
|
emailLink.className = 'link';
|
||||||
|
emailLink.textContent = email;
|
||||||
|
emailSpan.append('Email: ', emailLink);
|
||||||
|
items.push(emailSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
footerEntries.forEach(entry => {
|
||||||
|
const link = createFooterLinkElement(entry);
|
||||||
|
items.push(link);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
footerSection.dataset.fpFooterLocale = locale;
|
||||||
|
footerSection.dataset.fpFooterLoaded = 'false';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
items.forEach((node, index) => {
|
||||||
|
footerSection.appendChild(node);
|
||||||
|
if (index < items.length - 1) {
|
||||||
|
const separator = document.createElement('span');
|
||||||
|
separator.className = 'mx-1 text-base-content/60';
|
||||||
|
separator.textContent = ' · ';
|
||||||
|
footerSection.appendChild(separator);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
footerSection.dataset.fpFooterLocale = locale;
|
||||||
|
footerSection.dataset.fpFooterLoaded = 'true';
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[FurryPlace SDK] Failed to rebuild info modal footer:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function rebuildInfoModalSocialLinks(modalBox) {
|
async function rebuildInfoModalSocialLinks(modalBox) {
|
||||||
try {
|
try {
|
||||||
const contentContainer = modalBox.querySelector('div[class*="flex"]');
|
const contentContainer = modalBox.querySelector('div[class*="flex"]');
|
||||||
@@ -511,6 +690,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
rebuildInfoModalSocialLinks(modalBox);
|
rebuildInfoModalSocialLinks(modalBox);
|
||||||
|
rebuildInfoModalFooter(modalBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch for info modal opening
|
// Watch for info modal opening
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
"@context": "https://schema.org",
|
"@context": "https://schema.org",
|
||||||
"@type": "WebApplication",
|
"@type": "WebApplication",
|
||||||
"name": "FurryPlace",
|
"name": "FurryPlace",
|
||||||
"url": "https://github.com/FurryPlaceteam/FurryPlace"
|
"url": "https://gitea.goocat.gay/zack3d/my_openplace"
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -236,7 +236,7 @@ app.use(async (req, res, next) => {
|
|||||||
if (ext === ".html") {
|
if (ext === ".html") {
|
||||||
res.setHeader("Content-Security-Policy",
|
res.setHeader("Content-Security-Policy",
|
||||||
"script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://challenges.cloudflare.com https://js.hcaptcha.com https://*.hcaptcha.com https://www.google.com https://www.gstatic.com blob:; " +
|
"script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://challenges.cloudflare.com https://js.hcaptcha.com https://*.hcaptcha.com https://www.google.com https://www.gstatic.com blob:; " +
|
||||||
"frame-src 'self' https://challenges.cloudflare.com https://*.hcaptcha.com https://www.google.com https://www.gstatic.com; " +
|
"frame-src 'self' https://challenges.cloudflare.com https://*.hcaptcha.com https://www.google.com https://www.gstatic.com https://www.youtube.com; " +
|
||||||
"style-src 'self' 'unsafe-inline' https://*.hcaptcha.com https://www.gstatic.com; " +
|
"style-src 'self' 'unsafe-inline' https://*.hcaptcha.com https://www.gstatic.com; " +
|
||||||
"connect-src 'self' https://*.hcaptcha.com https://www.google.com https://www.gstatic.com https://tiles.openfreemap.org;"
|
"connect-src 'self' https://*.hcaptcha.com https://www.google.com https://www.gstatic.com https://tiles.openfreemap.org;"
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user