This commit is contained in:
2025-10-06 15:39:54 -07:00
parent cff7f0e835
commit 9a39e8ec13
3 changed files with 182 additions and 2 deletions
+180
View File
@@ -107,6 +107,7 @@
let shopModalObserver = null;
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
function findButtonContainer() {
@@ -401,6 +402,184 @@
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) {
try {
const contentContainer = modalBox.querySelector('div[class*="flex"]');
@@ -511,6 +690,7 @@
});
rebuildInfoModalSocialLinks(modalBox);
rebuildInfoModalFooter(modalBox);
}
// Watch for info modal opening
+1 -1
View File
@@ -91,7 +91,7 @@
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "FurryPlace",
"url": "https://github.com/FurryPlaceteam/FurryPlace"
"url": "https://gitea.goocat.gay/zack3d/my_openplace"
}
</script>
+1 -1
View File
@@ -236,7 +236,7 @@ app.use(async (req, res, next) => {
if (ext === ".html") {
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:; " +
"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; " +
"connect-src 'self' https://*.hcaptcha.com https://www.google.com https://www.gstatic.com https://tiles.openfreemap.org;"
);