feat(sdk): add localized social links to info modal
Build and render social links in the info modal using localized site content. Detect the preferred locale (localStorage or document lang, fallback to 'en') and fetch content via getSiteContent(locale). Parse keys social.<id>.(url|text|icon|order), apply defaults (labels and ordering via DEFAULT_SOCIAL_ORDER), support icons, and render with separators. Cache per-locale rendering using data attributes to avoid unnecessary rebuilds, and invoke it from injectInfoSections so links appear when the modal opens.
This commit is contained in:
@@ -106,6 +106,8 @@
|
||||
let infoModalObserver = null;
|
||||
let shopModalObserver = null;
|
||||
|
||||
const DEFAULT_SOCIAL_ORDER = ['discord', 'twitter', 'bluesky', 'instagram', 'youtube', 'tiktok', 'reddit', 'github'];
|
||||
|
||||
// Find the button container in the DOM
|
||||
function findButtonContainer() {
|
||||
// Look for the container with class "flex flex-col items-center gap-3"
|
||||
@@ -301,6 +303,166 @@
|
||||
return section;
|
||||
}
|
||||
|
||||
function getPreferredLocale() {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
const storedLocale = localStorage.getItem('locale');
|
||||
if (storedLocale) {
|
||||
return storedLocale;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined' && document.documentElement && document.documentElement.lang) {
|
||||
return document.documentElement.lang;
|
||||
}
|
||||
|
||||
return 'en';
|
||||
}
|
||||
|
||||
function getDefaultSocialOrder(id) {
|
||||
const index = DEFAULT_SOCIAL_ORDER.indexOf(id);
|
||||
return index === -1 ? DEFAULT_SOCIAL_ORDER.length + 10 : index;
|
||||
}
|
||||
|
||||
function ensureSocialEntry(map, id) {
|
||||
if (!map[id]) {
|
||||
map[id] = { id, order: getDefaultSocialOrder(id) };
|
||||
}
|
||||
return map[id];
|
||||
}
|
||||
|
||||
function formatSocialLabel(id) {
|
||||
if (!id) return '';
|
||||
return id.charAt(0).toUpperCase() + id.slice(1);
|
||||
}
|
||||
|
||||
function buildSocialEntriesFromContent(content) {
|
||||
const data = content || {};
|
||||
const entries = {};
|
||||
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
if (!value) return;
|
||||
const match = key.match(/^social\.([^.]+)\.(url|text|icon|order)$/);
|
||||
if (!match) return;
|
||||
|
||||
const [, id, field] = match;
|
||||
const entry = ensureSocialEntry(entries, id);
|
||||
|
||||
if (field === 'order') {
|
||||
const numericOrder = Number(value);
|
||||
if (!Number.isNaN(numericOrder)) {
|
||||
entry.order = numericOrder;
|
||||
}
|
||||
} else {
|
||||
entry[field] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return Object.values(entries)
|
||||
.filter(entry => entry.url)
|
||||
.map(entry => {
|
||||
if (!entry.text) {
|
||||
entry.text = formatSocialLabel(entry.id);
|
||||
}
|
||||
return entry;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
if (a.order !== b.order) {
|
||||
return a.order - b.order;
|
||||
}
|
||||
return a.id.localeCompare(b.id);
|
||||
});
|
||||
}
|
||||
|
||||
function createSocialLinkElement(entry) {
|
||||
const link = document.createElement('a');
|
||||
link.href = entry.url;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
link.className = 'link inline-flex items-center gap-1 text-nowrap';
|
||||
link.setAttribute('data-social-id', entry.id);
|
||||
|
||||
if (entry.icon) {
|
||||
const iconWrapper = document.createElement('span');
|
||||
iconWrapper.innerHTML = entry.icon;
|
||||
const iconElement = iconWrapper.firstElementChild;
|
||||
if (iconElement) {
|
||||
iconElement.classList.add('inline');
|
||||
if (!iconElement.classList.contains('size-4')) {
|
||||
iconElement.classList.add('size-4');
|
||||
}
|
||||
link.insertBefore(iconElement, link.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.textContent = entry.text || entry.id;
|
||||
link.appendChild(label);
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
async function rebuildInfoModalSocialLinks(modalBox) {
|
||||
try {
|
||||
const contentContainer = modalBox.querySelector('div[class*="flex"]');
|
||||
if (!contentContainer) return;
|
||||
|
||||
const firstSection = contentContainer.querySelector('section');
|
||||
if (!firstSection) return;
|
||||
|
||||
let socialContainer = firstSection.querySelector('[data-furryplace-socials]');
|
||||
if (!socialContainer) {
|
||||
socialContainer = firstSection.querySelector('div.w-full.text-center.text-sm');
|
||||
if (socialContainer) {
|
||||
socialContainer.setAttribute('data-furryplace-socials', 'true');
|
||||
} else {
|
||||
socialContainer = document.createElement('div');
|
||||
socialContainer.className = 'w-full text-center text-sm';
|
||||
socialContainer.setAttribute('data-furryplace-socials', 'true');
|
||||
firstSection.appendChild(socialContainer);
|
||||
}
|
||||
}
|
||||
|
||||
const locale = getPreferredLocale();
|
||||
if (socialContainer.dataset.fpSocialLocale === locale && socialContainer.dataset.fpSocialLoaded === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
const content = await getSiteContent(locale);
|
||||
if (!content) return;
|
||||
|
||||
const socialEntries = buildSocialEntriesFromContent(content);
|
||||
if (socialEntries.length === 0) {
|
||||
socialContainer.innerHTML = '';
|
||||
socialContainer.dataset.fpSocialLocale = locale;
|
||||
socialContainer.dataset.fpSocialLoaded = 'false';
|
||||
return;
|
||||
}
|
||||
|
||||
socialContainer.innerHTML = '';
|
||||
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.className = 'flex flex-wrap items-center justify-center gap-2';
|
||||
|
||||
socialEntries.forEach((entry, index) => {
|
||||
const link = createSocialLinkElement(entry);
|
||||
paragraph.appendChild(link);
|
||||
|
||||
if (index < socialEntries.length - 1) {
|
||||
const separator = document.createElement('span');
|
||||
separator.className = 'text-base-content/50';
|
||||
separator.textContent = '|';
|
||||
paragraph.appendChild(separator);
|
||||
}
|
||||
});
|
||||
|
||||
socialContainer.appendChild(paragraph);
|
||||
socialContainer.dataset.fpSocialLocale = locale;
|
||||
socialContainer.dataset.fpSocialLoaded = 'true';
|
||||
} catch (error) {
|
||||
console.warn('[FurryPlace SDK] Failed to rebuild info modal social links:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Inject custom sections into info modal
|
||||
function injectInfoSections() {
|
||||
const modalBox = findInfoModal();
|
||||
@@ -347,6 +509,8 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rebuildInfoModalSocialLinks(modalBox);
|
||||
}
|
||||
|
||||
// Watch for info modal opening
|
||||
|
||||
Reference in New Issue
Block a user