summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/explore-page.js
blob: bb47487c5df3895d22d69f6bd056b9e6af77a62d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import {
  html, useState, useEffect, useCallback,
} from './vendor/htm-preact.js';
import { t } from './i18n.js';
import { hubFetch, navigate } from './hub-client.js';
import * as platform from './platform.js';
import { GroupName } from './group-name.js';
import { Icon } from './icon.js';

export function ExplorePage({ token, myGroupIds, allowPublicGroups = true }) {
  const [groups, setGroups] = useState([]);
  const [loading, setLoading] = useState(true);
  const [search, setSearch] = useState('');
  const [joining, setJoining] = useState(null);

  const doSearch = useCallback((q) => {
    setLoading(true);
    const url = q ? `/v1/groups?q=${encodeURIComponent(q)}` : '/v1/groups';
    hubFetch(url, { token })
      .then(data => setGroups(data.groups || []))
      .catch(() => {})
      .finally(() => setLoading(false));
  }, [token]);

  useEffect(() => { doSearch(''); }, [token]);

  const onSearch = useCallback((e) => {
    const q = e.target.value;
    setSearch(q);
    doSearch(q);
  }, [doSearch]);

  const joinGroup = useCallback(async (gid) => {
    setJoining(gid);
    try {
      await hubFetch(`/v1/groups/${gid}/join`, { method: 'POST', token });
      navigate(`/group/${gid}`);
      setTimeout(() => window.location.reload(), 100);
    } catch (err) {
      if (err.message.includes('Already a member')) {
        navigate(`/group/${gid}`);
      } else {
        alert(err.message);
      }
    } finally {
      setJoining(null);
    }
  }, [token]);

  const isMember = (gid) => myGroupIds && myGroupIds.includes(gid);

  if (!allowPublicGroups) {
    return html`<div><p class="page-message">${t('explore.disabled')}</p></div>`;
  }

  return html`
    <div>
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px">
        <h2 style="margin:0">${t('explore.title')}</h2>
        ${platform.node.available && html`
          <a class="admin-btn" href="#/create-group">${t('explore.create_group')}</a>
        `}
      </div>
      <div class="file-toolbar" style="margin-bottom:16px">
        <input type="text" class="admin-search" placeholder="${t('explore.search')}"
          value=${search} onInput=${onSearch} />
      </div>
      ${loading
        ? html`<p class="page-message">${t('explore.loading')}</p>`
        : groups.length === 0
          ? html`<p class="page-message">${t('explore.empty')}</p>`
          : html`
            <div class="group-grid">
              ${groups.map(g => html`
                <div key=${g.id} class="group-card">
                  <a href="#/group/${g.id}" style="text-decoration:none;color:inherit">
                    <h3><${GroupName} name=${g.name}
                      owner=${g.source && g.source !== 'local' ? g.source : g.owner_username} /></h3>
                    ${g.description && html`<p class="group-card-desc">${g.description}</p>`}
                  </a>
                  ${/* No join_policy badge: on a hub whose groups are all
                        invite-only it read "invite" on every card. An open
                        group still announces itself — with the Join button
                        below, which is the useful half. The @host is already
                        beside the name, via GroupName. */''}
                  ${isMember(g.id)
                    ? html`<span class="badge">${t('explore.member')}</span>`
                    : g.join_policy === 'open' && html`
                      <button class="admin-btn" style="margin-top:8px"
                        disabled=${joining === g.id}
                        onClick=${() => joinGroup(g.id)}>
                        ${joining === g.id ? '...' : t('explore.join')}
                      </button>
                    `
                  }
                </div>
              `)}
            </div>
          `
      }
    </div>
  `;
}