|
15 | 15 | * @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon |
16 | 16 | * @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661 |
17 | 17 | * @author Miroslav Pejic - miroslav.pejic.85@gmail.com |
18 | | - * @version 1.7.92 |
| 18 | + * @version 1.7.93 |
19 | 19 | * |
20 | 20 | */ |
21 | 21 |
|
@@ -1339,7 +1339,8 @@ function initCursorLightEffect() { |
1339 | 1339 | /** |
1340 | 1340 | * On body load Get started |
1341 | 1341 | */ |
1342 | | -function initClientPeer() { |
| 1342 | +async function initClientPeer() { |
| 1343 | + await getThemes(); |
1343 | 1344 | setTheme(); |
1344 | 1345 |
|
1345 | 1346 | if (!isWebRTCSupported) { |
@@ -1711,6 +1712,99 @@ async function getButtons() { |
1711 | 1712 | } |
1712 | 1713 | } |
1713 | 1714 |
|
| 1715 | +/** |
| 1716 | + * Get Themes config from server side and merge with built-in defaults |
| 1717 | + */ |
| 1718 | +async function getThemes() { |
| 1719 | + try { |
| 1720 | + const response = await axios.get('/themes', { |
| 1721 | + timeout: 5000, |
| 1722 | + }); |
| 1723 | + const serverThemes = response.data.message; |
| 1724 | + if (serverThemes) { |
| 1725 | + // Deep merge each theme: server overrides take precedence |
| 1726 | + for (const [name, vars] of Object.entries(serverThemes)) { |
| 1727 | + themeMap[name] = themeMap[name] ? { ...themeMap[name], ...vars } : vars; |
| 1728 | + } |
| 1729 | + renderDynamicThemeCards(); |
| 1730 | + console.log('AXIOS ROOM THEMES SETTINGS', { |
| 1731 | + serverThemes: serverThemes, |
| 1732 | + clientThemes: Object.keys(themeMap), |
| 1733 | + }); |
| 1734 | + } |
| 1735 | + } catch (error) { |
| 1736 | + console.error('AXIOS GET THEMES ERROR', error.message); |
| 1737 | + } |
| 1738 | +} |
| 1739 | + |
| 1740 | +/** |
| 1741 | + * Dynamically add theme cards & dropdown options for server-defined themes |
| 1742 | + * that are not part of the built-in defaults. |
| 1743 | + */ |
| 1744 | +function renderDynamicThemeCards() { |
| 1745 | + const grid = getId('themeCardsGrid'); |
| 1746 | + if (!grid) return; |
| 1747 | + |
| 1748 | + const builtInThemes = new Set(Array.from(themeSelect.options).map((opt) => opt.value)); |
| 1749 | + |
| 1750 | + const iconPool = [ |
| 1751 | + 'fa-solid fa-wand-magic-sparkles', |
| 1752 | + 'fa-solid fa-palette', |
| 1753 | + 'fa-solid fa-paint-roller', |
| 1754 | + 'fa-solid fa-swatchbook', |
| 1755 | + 'fa-solid fa-brush', |
| 1756 | + 'fa-solid fa-eye-dropper', |
| 1757 | + 'fa-solid fa-fill-drip', |
| 1758 | + 'fa-solid fa-circle-half-stroke', |
| 1759 | + ]; |
| 1760 | + let iconIndex = 0; |
| 1761 | + |
| 1762 | + for (const [name, vars] of Object.entries(themeMap)) { |
| 1763 | + if (builtInThemes.has(name)) continue; |
| 1764 | + |
| 1765 | + // Add <option> to the hidden select |
| 1766 | + const option = document.createElement('option'); |
| 1767 | + option.value = name; |
| 1768 | + option.textContent = name.charAt(0).toUpperCase() + name.slice(1); |
| 1769 | + themeSelect.appendChild(option); |
| 1770 | + |
| 1771 | + const index = themeSelect.options.length - 1; |
| 1772 | + |
| 1773 | + // Pick an icon and a color from the theme's --dd-color |
| 1774 | + const iconClass = iconPool[iconIndex % iconPool.length]; |
| 1775 | + iconIndex++; |
| 1776 | + const iconColor = vars['--dd-color'] || '#c0c0c0'; |
| 1777 | + |
| 1778 | + // Create the card |
| 1779 | + const card = document.createElement('div'); |
| 1780 | + card.className = 'theme-card'; |
| 1781 | + card.dataset.theme = name; |
| 1782 | + card.dataset.index = index; |
| 1783 | + card.innerHTML = `<i class="${iconClass}"></i><span>${option.textContent}</span>`; |
| 1784 | + |
| 1785 | + // Apply dynamic icon color via inline style |
| 1786 | + const icon = card.querySelector('i'); |
| 1787 | + icon.style.color = iconColor; |
| 1788 | + |
| 1789 | + // Set dynamic active border color |
| 1790 | + card.style.setProperty('--dynamic-theme-color', iconColor); |
| 1791 | + |
| 1792 | + // Click handler (same logic as built-in cards) |
| 1793 | + card.onclick = () => { |
| 1794 | + if (card.classList.contains('disabled')) return; |
| 1795 | + themeSelect.selectedIndex = index; |
| 1796 | + updateThemeCardsActive(); |
| 1797 | + if (themeCardDebounce) clearTimeout(themeCardDebounce); |
| 1798 | + themeCardDebounce = setTimeout(() => { |
| 1799 | + themeCardDebounce = null; |
| 1800 | + themeSelect.dispatchEvent(new Event('change')); |
| 1801 | + }, 200); |
| 1802 | + }; |
| 1803 | + |
| 1804 | + grid.appendChild(card); |
| 1805 | + } |
| 1806 | +} |
| 1807 | + |
1714 | 1808 | /** |
1715 | 1809 | * Deep merge two objects |
1716 | 1810 | * @param {object} target target object |
@@ -3116,7 +3210,7 @@ function handleRemovePeer(config) { |
3116 | 3210 | /** |
3117 | 3211 | * Theme definitions — each key maps to the CSS custom properties for that theme |
3118 | 3212 | */ |
3119 | | -const themeMap = { |
| 3213 | +let themeMap = { |
3120 | 3214 | dark: { |
3121 | 3215 | '--body-bg': 'radial-gradient(#2a2a2e, #121214)', |
3122 | 3216 | '--msger-bg': 'radial-gradient(#2a2a2e, #121214)', |
@@ -3328,7 +3422,14 @@ function setTheme() { |
3328 | 3422 | function updateThemeCardsActive() { |
3329 | 3423 | const cards = document.querySelectorAll('.theme-card'); |
3330 | 3424 | cards.forEach((card) => { |
3331 | | - card.classList.toggle('active', parseInt(card.dataset.index) === themeSelect.selectedIndex); |
| 3425 | + const isActive = parseInt(card.dataset.index) === themeSelect.selectedIndex; |
| 3426 | + card.classList.toggle('active', isActive); |
| 3427 | + // For dynamic (server-added) cards, apply active border/shadow via inline style |
| 3428 | + const dynamicColor = card.style.getPropertyValue('--dynamic-theme-color'); |
| 3429 | + if (dynamicColor) { |
| 3430 | + card.style.borderColor = isActive ? dynamicColor : ''; |
| 3431 | + card.style.boxShadow = isActive ? `0 0 12px ${dynamicColor}33` : ''; |
| 3432 | + } |
3332 | 3433 | }); |
3333 | 3434 | } |
3334 | 3435 |
|
@@ -14836,7 +14937,7 @@ function showAbout() { |
14836 | 14937 | Swal.fire({ |
14837 | 14938 | background: swBg, |
14838 | 14939 | position: 'center', |
14839 | | - title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.7.92', |
| 14940 | + title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.7.93', |
14840 | 14941 | imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about, |
14841 | 14942 | customClass: { image: 'img-about' }, |
14842 | 14943 | html: ` |
|
0 commit comments