Скрипт не является идеальным, однако он работает достаточно стабильно.
Он корректно очищает старый кэш и заменяет его новым. Обновление происходит не мгновенно, но достаточно быстро, чтобы пользователю не приходилось долго ждать , это первый важный момент.
Есть недостатки.
Когда пользователи дарят одинаковые подарки, платформа объединяет их в одну карточку, при этом под подарком отображаются разные записи от разных отправителей. Скрипт пока не способен корректно разделять такие случаи, поскольку его логика устроена иначе. Для решения этой задачи требуется более сложная и продвинутая обработка данных, а сам скрипт уже достаточно объёмный.
Я постарался максимально оптимизировать его, чтобы снизить нагрузку на системные ресурсы, но любая дополнительная логика неизбежно влияет на производительность. Возможно, в будущем я найду способ улучшить обработку объединённых подарков. А пока, чтобы избежать подобных неточностей, рекомендуется по возможности не отправлять одинаковые подарки.
Этот модуль не просто декоративный блок, а самостоятельная система, которая незаметно для пользователя управляет загрузкой и отображением подарков. Его работа построена по принципу «минимум действий максимум результата, где каждая операция выполняется только тогда, когда это действительно необходимо.
Сразу после открытия страницы скрипт не спешит обращаться к серверу. В первую очередь он проверяет локальный кэш браузера. Если данные уже были сохранены ранее, блок подарков мгновенно заполняется — пользователь сразу видит количество подарков и последние из них, без ожидания загрузки. Это создаёт ощущение моментальной работы страницы.
Далее скрипт запускает тихую фоновую проверку , он обращается к системе и запрашивает только одно значение: общее количество подарков. Никакие изображения, профили или комментарии в этот момент не загружаются. Это ключевой момент оптимизации: если число подарков не изменилось, скрипт полностью прекращает активность и «засыпает», не создавая нагрузки ни на браузер, ни на систему.
Если же обнаружено изменение — например, появился новый подарок — скрипт автоматически очищает старый кэш и начинает полное обновление данных. Он заново собирает информацию о подарках, аккуратно обогащает их сведениями об отправителях, комментариях и датах, сортирует по времени и сохраняет обновлённый кэш. После этого интерфейс мгновенно перерисовывается, а пользователь видит актуальное состояние без ручного обновления страницы.
Особое внимание уделено производительности. Обработка подарков происходит с микропаузами, чтобы не перегружать браузер, а повторные запросы полностью исключены, если данные не изменились. Благодаря этому скрипт работает стабильно даже при большом количестве подарков и практически не влияет на системные ресурсы.
Таким образом, модуль действует как фоновый наблюдатель: большую часть времени он бездействует, не расходуя ресурсы, и активируется только тогда, когда действительно происходят изменения. Именно эта логика делает его быстрым, экономичным и незаметным для пользователя.
Если вы хотите придать скрипту привычный для себя вид, нет необходимости менять его внутреннюю логику или функционал. Если у вас используется более старая версия скрипта с привычными стилями. Всё, что нужно это перенести стили: взять CSS из старой версии и вставить в новый скрипт.
Внутренний механизм останется современным и обновлённым, а визуальное оформление будет соответствовать вашему привычному восприятию. Таким образом, функциональность сохраняется, а дизайн остаётся знакомым и комфортным.
.show-all-btn { position: absolute; bottom: -20px; /* сильно ниже видимой области */ right: 16px; background: #4a76a8; color: white; font-size: 12px; font-weight: 600; padding: 8px 16px; border-radius: 8px 8px 0 0; border: none; box-shadow: 0 -3px 10px rgba(0,0,0,0.25); cursor: pointer; transition: all 0.38s cubic-bezier(0.25, 0.8, 0.25, 1); z-index: 10; /* выше всех элементов внутри контейнера */ opacity: 0.85; white-space: nowrap; }
#gifts-container { position: relative; overflow: visible !important; /* критично — чтобы hover-зона не обрезалась */ padding-bottom: 20px; /* запас места снизу для вылезания кнопки */ min-height: 140px; /* чтобы даже при 0 подарках была зона наведения */ }
/* hover — поднимаем вверх и делаем полностью видимой */ .show-all-btn:hover, .show-all-btn:focus-visible { bottom: 0; /* прижимаем к нижнему краю */ opacity: 1; box-shadow: 0 -6px 16px rgba(0,0,0,0.35); transform: none; /* убираем translateY, используем bottom */ }
let total = 0; temp.querySelectorAll('td[onclick*="_uWnd.reload"]').forEach(td => { const b = td.querySelector('b'); total += b ? (+b.textContent.trim() || 1) : 1; }); return total; } catch (e) { console.error(e); return -1; } }
// ====================== ПОЛНОЕ ОБНОВЛЕНИЕ (только при изменении числа) ====================== async function fullRefresh() { console.log('[Gifts] Количество изменилось → полное обновление кэша');
try { const res = await fetch(`/index/54-${userId}-`); const text = await res.text(); const xml = new DOMParser().parseFromString(text, 'application/xml'); const cmd = xml.querySelector('cmd[p="content"]'); if (!cmd) return;
const tds = [...temp.querySelectorAll('td[onclick*="_uWnd.reload"]')]; let total = 0; const newGifts = [];
for (const td of tds) { const img = td.querySelector('img'); if (!img) continue; const count = td.querySelector('b') ? +td.querySelector('b').textContent.trim() || 1 : 1; total += count; const onclick = td.getAttribute('onclick');
for (let i = 0; i < count; i++) { newGifts.push({ img: img.src, onclick, nick: 'Загрузка...', avatar: '/.s/src/profile/img/profile_photo_thumbnail.png', date: '', comment: '', profile: '#', recipientId: null }); } }
cachedCount = total; allGifts = newGifts;
// Обогащаем все подарки for (let i = 0; i < Math.min(allGifts.length, 40); i++) { allGifts[i] = await enrichGift(allGifts[i]); // небольшая задержка, чтобы не убивать браузер if (i % 4 === 3) await new Promise(r => setTimeout(r, 350)); }
allGifts.sort((a, b) => new Date(b.date || 0) - new Date(a.date || 0));
// Глобальная переменная со списком онлайн (должна быть определена раньше в скрипте!) const onlineListText = (document.getElementById('uc-online-list')?.textContent || '').toLowerCase();
// ====================== ПОПАП С ОНЛАЙН-СТАТУСОМ ====================== function showPopup() { if (!allGifts?.length) { alert('Нет подарков или данные ещё не загружены'); return; }
// Закрытие по крестику popup.querySelector('span').onclick = () => overlay.remove();
// Закрытие по клику вне попапа overlay.addEventListener('click', function closeHandler(e) { if (e.target === overlay) { overlay.remove(); overlay.removeEventListener('click', closeHandler); } });
// 1. Мгновенно показываем данные из кэша (если есть) const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); if (saved.gifts && typeof saved.count === 'number') { allGifts = saved.gifts; cachedCount = saved.count; renderCounter(); renderPreview(); updateShowAllButton(); console.log('[Gifts] Моментально показали кэш — страница готова'); } else { // Если кэша нет — показываем 0 renderCounter(0); console.log('[Gifts] Кэша нет — ждём загрузку'); }
// 2. Тихо, в фоне, проверяем актуальное количество (async () => { const liveCount = await getGiftCount();
// если не удалось получить число — НИЧЕГО не делаем if (liveCount < 0) { console.warn('[Gifts] Не удалось проверить количество — спим'); return; }
// если кэш есть и число НЕ изменилось — СПИМ if (cachedCount !== -1 && liveCount === cachedCount) { console.log(`[Gifts] Число не изменилось (${liveCount}) — ничего не грузим`); return; }
// если кэша нет (первый запуск) if (cachedCount === -1) { console.log('[Gifts] Кэша нет — первая загрузка'); await fullRefresh(); return; }
// если число изменилось — обновляем кэш console.log(`[Gifts] Число изменилось: ${cachedCount} → ${liveCount} — обновляем кэш`); await fullRefresh(); })(); })(); </script>
На видео проверяем работу модуля подарков в действии.
Тестируем функционал в реальном времени с разных аккаунтов: дарим подарки друг другу и наблюдаем, как происходят обновления и изменения. Смотрим, анализируем процесс и делаем выводы о работе системы прямо на глазах.
С их помощью можно создавать эффект стекла, придавать элементам различные выпуклости, менять ингредиенты и цветовые решения. Фундаментальная часть уже заложена, однако многие параметры по умолчанию обнулены. Их нужно настраивать самостоятельно, ориентируясь на свои предпочтения и вкус.
Например, я закруглил обводку вокруг числа и увеличил интенсивность эффекта стекла вокруг самого всплывающего окна. При этом остаётся ещё огромное количество настроек, которые вы можете адаптировать под себя и свои цели.
Стандартное окно с обнулёнными настройками я подготовил специально для вас. Для себя же я создаю совершенно другие стили, более индивидуальные и продуманные.
.show-all-btn { position: absolute; bottom: -20px; /* сильно ниже видимой области */ right: 16px; background: #4a76a8; color: white; font-size: 12px; font-weight: 600; padding: 8px 16px; border-radius: 8px 8px 0 0; border: none; box-shadow: 0 -3px 10px rgba(0,0,0,0.25); cursor: pointer; transition: all 0.38s cubic-bezier(0.25, 0.8, 0.25, 1); z-index: 10; /* выше всех элементов внутри контейнера */ opacity: 0.85; white-space: nowrap; }
#gifts-container { position: relative; overflow: visible !important; /* критично — чтобы hover-зона не обрезалась */ padding-bottom: 20px; /* запас места снизу для вылезания кнопки */ min-height: 140px; /* чтобы даже при 0 подарках была зона наведения */ }
/* hover — поднимаем вверх и делаем полностью видимой */ .show-all-btn:hover, .show-all-btn:focus-visible { bottom: 0; /* прижимаем к нижнему краю */ opacity: 1; box-shadow: 0 -6px 16px rgba(0,0,0,0.35); transform: none; /* убираем translateY, используем bottom */ }
let total = 0; temp.querySelectorAll('td[onclick*="_uWnd.reload"]').forEach(td => { const b = td.querySelector('b'); total += b ? (+b.textContent.trim() || 1) : 1; }); return total; } catch (e) { console.error(e); return -1; } }
// ====================== ПОЛНОЕ ОБНОВЛЕНИЕ (только при изменении числа) ====================== async function fullRefresh() { console.log('[Gifts] Количество изменилось → полное обновление кэша');
try { const res = await fetch(`/index/54-${userId}-`); const text = await res.text(); const xml = new DOMParser().parseFromString(text, 'application/xml'); const cmd = xml.querySelector('cmd[p="content"]'); if (!cmd) return;
const tds = [...temp.querySelectorAll('td[onclick*="_uWnd.reload"]')]; let total = 0; const newGifts = [];
for (const td of tds) { const img = td.querySelector('img'); if (!img) continue; const count = td.querySelector('b') ? +td.querySelector('b').textContent.trim() || 1 : 1; total += count; const onclick = td.getAttribute('onclick');
for (let i = 0; i < count; i++) { newGifts.push({ img: img.src, onclick, nick: 'Загрузка...', avatar: '/.s/src/profile/img/profile_photo_thumbnail.png', date: '', comment: '', profile: '#', recipientId: null }); } }
cachedCount = total; allGifts = newGifts;
// Обогащаем все подарки for (let i = 0; i < Math.min(allGifts.length, 40); i++) { allGifts[i] = await enrichGift(allGifts[i]); // небольшая задержка, чтобы не убивать браузер if (i % 4 === 3) await new Promise(r => setTimeout(r, 350)); }
allGifts.sort((a, b) => new Date(b.date || 0) - new Date(a.date || 0));
// Глобальная переменная со списком онлайн (должна быть определена раньше в скрипте!) const onlineListText = (document.getElementById('uc-online-list')?.textContent || '').toLowerCase();
// ====================== ПОПАП С ОНЛАЙН-СТАТУСОМ ====================== function showPopup() { if (!allGifts?.length) { alert('Нет подарков или данные ещё не загружены'); return; }
// Закрытие по крестику popup.querySelector('span').onclick = () => overlay.remove();
// Закрытие по клику вне попапа overlay.addEventListener('click', function closeHandler(e) { if (e.target === overlay) { overlay.remove(); overlay.removeEventListener('click', closeHandler); } });
// 1. Мгновенно показываем данные из кэша (если есть) const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); if (saved.gifts && typeof saved.count === 'number') { allGifts = saved.gifts; cachedCount = saved.count; renderCounter(); renderPreview(); updateShowAllButton(); console.log('[Gifts] Моментально показали кэш — страница готова'); } else { // Если кэша нет — показываем 0 renderCounter(0); console.log('[Gifts] Кэша нет — ждём загрузку'); }
// 2. Тихо, в фоне, проверяем актуальное количество (async () => { const liveCount = await getGiftCount();
// если не удалось получить число — НИЧЕГО не делаем if (liveCount < 0) { console.warn('[Gifts] Не удалось проверить количество — спим'); return; }
// если кэш есть и число НЕ изменилось — СПИМ if (cachedCount !== -1 && liveCount === cachedCount) { console.log(`[Gifts] Число не изменилось (${liveCount}) — ничего не грузим`); return; }
// если кэша нет (первый запуск) if (cachedCount === -1) { console.log('[Gifts] Кэша нет — первая загрузка'); await fullRefresh(); return; }
// если число изменилось — обновляем кэш console.log(`[Gifts] Число изменилось: ${cachedCount} → ${liveCount} — обновляем кэш`); await fullRefresh(); })(); })(); </script>