Список пользователей

1
Админ
Постов: 232
2
VIP
Постов: 72
3
Элита
Постов: 51
4
Проверенные
Постов: 37
5
VIP
Постов: 35
6
Проверенные
Постов: 32
7
Пользователи
Постов: 31
8
Проверенные
Постов: 29

  • Страница 1 из 1
  • 1
Модуль ВК «Блок друзей» для Ucoz v 12.1 светлая
Дата: Вторник, 31.03.2026, 04:10 | Сообщение # 1 | | Написал: Узнаваемый
Автор темы
Мурчанн не в сети
        Сообщений:232
         Регистрация:20.10.2016

На этот раз форма была переработана более глубоко и осмысленно. Скрипт был немного модифицирован: внесены изменения в CSS-стили, добавлены более высокие приоритеты, чтобы всплывающее окно отображалось поверх всех элементов сайта.

В какой-то момент пришло понимание, что всё лишнее только портит внешний вид. Нужна концентрация на том, к чему мы привыкли , без всякого визуального обмана.



Как оказалось, я недостаточно хорошо протестировал скрипт и заметил проблему: он не создавал категорию «Коллеги». Записи, конечно, сохранялись на сервере, но при формировании списка возникали ошибки, которые я упустил.

В этот раз я снова пытался реализовать стекловидный эффект для всплывающих окон. Пока результат не идеален, но в этом есть свои плюсы: каждая попытка приближает меня к более совершенному виду модуля друзей. Он становится лучше, легче и богаче на эффекты. Всё зависит от фантазии , если появляются идеи, я сразу стараюсь их реализовать. Думаю, модуль уже делает даже больше, чем аналогичные функции в социальной сети ВК.

Скрипт подгружает данные всего один раз , он не делает это постоянно, а лишь проверяет, изменилось ли что-нибудь. Если изменения есть, он обновляет кэш и загружает только необходимые данные.



Исходной код:

Код
<!-- MINI BLOCK ДРУЗЬЯ -->
<div class="vk-old-block">
<div class="vk-old-header">Друзья</div>
<div class="vk-old-footer" id="friends-count">0 друзей</div>

<div class="vk-old-content">
<div class="vk-friends"></div>
</div>

<div class="vk-old-footer" id="friends-footer" title="Показать всех друзей">
Показать всех друзей →
</div>
</div>

<link rel="stylesheet" href="/css/friends03.css" media="all">

<div id="uc-online-list" style="display:none;">
$ONLINE_USERS_LIST$
</div>

<script>
(function(){
  const USER_ID = window.UCOZ_DATA?.userId || "0";
  if(USER_ID === "0") return;

  const $container = $('.vk-friends');
  const $counter = $('#friends-count');
  const $footer = $('#friends-footer');
  const DEFAULT_AVA = '/.s/src/profile/img/profile_photo_thumbnail.png';
  const LS_FRIENDS = 'friends_' + USER_ID;
  let allFriends = [];

  /* =========================
   РЕНДЕР МИНИ-БЛОКА
  ========================= */
  function renderMini(friends){
    const onlineHtml = $('#uc-online-list').text().toLowerCase();
    if(!friends || friends.length === 0){
      const empty = document.createElement('div');
      empty.className = 'vk-empty-wrap';
      empty.innerHTML = `
        <div class="vk-empty-icon">
          <svg width="58" height="58" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M16 11c1.66 0 3-1.34 3-3S17.66 5 16 5s-3 1.34-3 3 1.34 3 3 3zM8 11c1.66 0 3-1.34 3-3S9.66 5 8 5 5 6.34 5 8s1.34 3 3 3z" fill="#99a2ad"/>
            <path d="M8 13c-2.67 0-8 1.34-8 4v2h10v-2c0-1.2.6-2.3 1.6-3.2C10.3 13.3 9 13 8 13zm8 0c-.9 0-2.3.2-3.6.8 1 .9 1.6 2 1.6 3.2v2h10v-2c0-2.66-5.33-4-8-4z" fill="#99a2ad"/>
          </svg>
        </div>
        <div class="vk-empty-title">У вас нет друзей</div>
        <div class="vk-empty-sub">Добавляйте людей, чтобы видеть их здесь</div>
        <div class="vk-empty-action">
          <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
            <line x1="12" y1="5" x2="12" y2="19"></line>
            <line x1="5" y1="12" x2="19" y2="12"></line>
          </svg>
          Добавить друзей
        </div>
      `;
      empty.querySelector('.vk-empty-action').onclick = () => location.href = '/index/15-2';
      $container[0].replaceChildren(empty);
      $counter.html('0 друзей');
      return;
    }

    $container.empty();
    friends.slice(0, 6).forEach(f => {
      const isOnline = onlineHtml.includes(f.nick.toLowerCase());
      const statusDot = `<span class="statusDot ${isOnline ? 'onlineDot' : 'offlineDot'}"></span>`;
      const card = $(`
        <div class="vk-friend" title="${f.nick}">
          <img src="${f.ava}">
          <span>${f.nick}</span>
          ${statusDot}
        </div>
      `);
      card.on('click', () => location.href = f.profile);
      $container.append(card);
    });
    $counter.html(`${friends.length} друзей`);
  }

  /* =========================
   POPUP
  ========================= */
  function showPopup(){
    if(!allFriends.length) return;

    $('.vk-ui-overlay').remove();

    const overlay = $('<div class="vk-ui-overlay"></div>');
    const popup = $(`
      <div class="vk-ui-popup">
        <div class="vk-ui-header">
          <div class="vk-ui-title">Друзья <span class="vk-ui-title-count">${allFriends.length}</span></div>
          <div class="vk-ui-search"><input type="text" placeholder="Поиск друзей" id="friend-search"></div>
          <button class="vk-ui-close" title="Закрыть">×</button>
        </div>
        <div class="vk-ui-list"></div>
      </div>
    `);

    const list = popup.find('.vk-ui-list');
    const onlineHtml = $('#uc-online-list').text().toLowerCase();

    function renderList(filter = ''){
      list.empty();
      allFriends
        .filter(f => f.nick.toLowerCase().includes(filter.toLowerCase()))
        .forEach(f => {
          let badge = 'Без статуса', cls = 'nostatus', letter = '';

          const g = (f.group || '').toLowerCase().trim();

          if (g.includes('кумир'))      { badge = 'Кумир';     cls = 'idol';       letter = 'К'; }
          else if (g.includes('друг'))   { badge = 'Друг';      cls = 'friend';     letter = 'Д'; }
          else if (g.includes('семья') || g.includes('сем')) { badge = 'Семья'; cls = 'family'; letter = 'С'; }
          else if (g.includes('знаком')) { badge = 'Знакомый';  cls = 'acquaintance'; letter = 'З'; }
          else if (g.includes('прият'))  { badge = 'Приятель';  cls = 'buddy';      letter = 'П'; }
          else if (g.includes('коллег')) { badge = 'Коллега';   cls = 'colleague';   letter = 'К'; }

          const isOnline = onlineHtml.includes(f.nick.toLowerCase());

          const row = $(`
            <div class="vk-ui-row ${cls}">
              <div class="vk-ui-ava">
                <img src="${f.ava}">
                ${letter ? `<span class="vk-ui-letter ${cls}">${letter}</span>` : ''}
                <span class="statusDot ${isOnline ? 'onlineDot' : 'offlineDot'}"></span>
              </div>
              <div class="vk-ui-info">
                <div class="vk-ui-name">${f.nick}</div>
                <div class="vk-ui-badge ${cls}">${badge}</div>
              </div>
              <div class="vk-ui-actions">
                <button class="vk-del-btn" title="Удалить">✕</button>
                <div class="vk-ui-arrow">›</div>
              </div>
            </div>
          `);

          row.on('click', function(e){
            if(!$(e.target).closest('.vk-del-btn').length) location.href = f.profile;
          });

          row.find('.vk-del-btn').on('click', function(e){
            e.stopPropagation();
            const btn = $(this);
            if(btn.data('loading')) return;
            if(!f.del) return alert('Ошибка: нет ссылки удаления');

            const originalText = btn.text();

            openDeleteModal(f.nick, f.ava, () => {
              btn.data('loading', true);
              btn.text('...').css({'pointer-events':'none', 'opacity':'0.6'});

              $.get(f.del).done(() => {
                allFriends = allFriends.filter(x => x.nick !== f.nick);

                row.css({
                  transition: 'all 0.55s cubic-bezier(.2,.9,.2,1)',
                  transform: 'translateX(160px) translateY(100px) scale(0.3) rotate(25deg)',
                  opacity: '0',
                  filter: 'blur(10px)'
                });

                setTimeout(() => row.remove(), 420);

                renderMini(allFriends);
                $counter.html(`${allFriends.length} друзей`);
                localStorage.setItem(LS_FRIENDS, JSON.stringify(allFriends));
              }).fail(() => {
                alert('Ошибка удаления');
                btn.text(originalText).css({'pointer-events':'auto', 'opacity':'1'});
                btn.data('loading', false);
              });
            });
          });

          list.append(row);
        });
    }

    renderList();

    popup.find('#friend-search').on('input', function(){
      renderList($(this).val());
    });

    popup.find('.vk-ui-close').on('click', () => overlay.remove());
    overlay.on('click', e => { if(e.target === overlay[0]) overlay.remove(); });

    overlay.css({ zIndex: '2147483647', position: 'fixed', isolation: 'isolate' });
    popup.css({ zIndex: '2147483647' });

    overlay.append(popup);
    $('body').append(overlay);
  }

  /* =========================
   МОДАЛКА УДАЛЕНИЯ
  ========================= */
  function ensureModal(){
    if ($('.vk-modal-overlay').length) return;
    $('body').append(`
      <div class="vk-modal-overlay">
        <div class="vk-modal">
          <div class="vk-modal-user">
            <div class="vk-modal-avatar"><img src="" alt=""></div>
            <div>
              <div class="vk-modal-user-name"></div>
              <div class="vk-modal-user-sub">будет удалён из друзей</div>
            </div>
          </div>
          <div class="vk-modal-title">Удаление друга</div>
          <div class="vk-modal-text"></div>
          <div class="vk-modal-warning">Это действие нельзя отменить</div>
          <div class="vk-modal-actions">
            <button class="vk-modal-cancel">Отмена</button>
            <button class="vk-modal-confirm">Удалить</button>
          </div>
        </div>
      </div>
    `);
  }

  function openDeleteModal(name, ava, onConfirm){
    ensureModal();
    const modal = $('.vk-modal-overlay');
    modal.find('.vk-modal-text').text(`Удалить "${name}" из друзей?`);
    modal.find('.vk-modal-avatar img').attr('src', ava || DEFAULT_AVA);
    modal.find('.vk-modal-user-name').text(name);

    modal.addClass('active');
    $('body').addClass('vk-modal-open');

    function closeModal(){
      modal.removeClass('active');
      $('body').removeClass('vk-modal-open');
    }

    let locked = false;

    modal.find('.vk-modal-cancel').off('click').on('click', () => { if(!locked) closeModal(); });

    modal.find('.vk-modal-confirm').off('click').on('click', function(){
      if(locked) return;
      locked = true;
      const btn = $(this);
      btn.text('Удаление...').css({'pointer-events':'none', 'opacity':'0.7'});

      setTimeout(() => {
        closeModal();
        if(typeof onConfirm === 'function') onConfirm();
        btn.text('Удалить').css({'pointer-events':'auto', 'opacity':'1'});
        locked = false;
      }, 180);
    });

    modal.off('click').on('click', e => { if(e.target === this && !locked) closeModal(); });
    $(document).off('keydown.vkmodal').on('keydown.vkmodal', e => { if(e.key === 'Escape' && !locked) closeModal(); });
  }

  /* =========================
   ЗАГРУЗКА ДАННЫХ — ИСПРАВЛЕНО!
  ========================= */
  function loadFriends(){
    $.get(`/blog/0-0-1-0-17-${USER_ID}`, html => {
      const $tmp = $('<div>').html(html);
      const updated = [];

      $tmp.find('.friend').each(function(){
        const $el = $(this);

        // Более точное извлечение группы
        let group = $el.find('.gr').text().trim();
        if (!group) {
          // Если .gr пустой — ищем в тексте записи
          const text = $el.text();
          const match = text.match(/(Кумир|Друг|Семья|Приятель|Знакомый|Коллега)/i);
          group = match ? match[0] : 'Без статуса';
        }

        const delLink = $el.find('.del').text().trim();

        updated.push({
          nick: $el.find('.nick').text().trim(),
          ava: $el.find('.ava').text().trim() || DEFAULT_AVA,
          profile: $el.find('.url').text().trim(),
          group: group,                    // Теперь сохраняем точно то, что пришло
          del: delLink || null
        });
      });

      if(JSON.stringify(updated) !== JSON.stringify(allFriends)){
        allFriends = updated;
        localStorage.setItem(LS_FRIENDS, JSON.stringify(allFriends));
        renderMini(allFriends);
      }
    }).fail(() => console.warn('Не удалось загрузить список друзей'));
  }

  /* Инициализация */
  $counter.add($footer)
    .css('cursor','pointer')
    .attr('title','Показать всех друзей')
    .on('click', showPopup);

  try {
    const cached = JSON.parse(localStorage.getItem(LS_FRIENDS) || "[]");
    allFriends = cached;
    renderMini(allFriends);
  } catch(e){ renderMini([]); }

  setTimeout(loadFriends, 800);
  setInterval(loadFriends, 30000);

})();
</script>


Мурчанн

Признаюсь, не знаю почему, но глядя на звезды мне всегда хочется мечтать.
  • Страница 1 из 1
  • 1
Поиск: