xgit simple git

nexus

nexus

clone git clone https://kb.hax.al/nexus

messages/index.php


1<?php
2require_once __DIR__ . '/../includes/bootstrap.php';
3must_login();
4 
5$box = get('box', 'inbox');
6if (!in_array($box, ['inbox', 'sent'])) $box = 'inbox';
7 
8/* ── Fetch messages ─────────────────────────────────── */
9if ($box === 'inbox') {
10    $msgs = DB::rows("
11        SELECT m.*, u.username AS other_name, u.avatar AS other_av,
12               u.last_seen, u.role
13        FROM messages m
14        JOIN users u ON u.id = m.sender_id
15        WHERE m.receiver_id = ? AND m.deleted_by_receiver = 0
16        ORDER BY m.created_at DESC
17    ", [$USER['id']]);
18} else {
19    $msgs = DB::rows("
20        SELECT m.*, u.username AS other_name, u.avatar AS other_av,
21               u.last_seen, u.role
22        FROM messages m
23        JOIN users u ON u.id = m.receiver_id
24        WHERE m.sender_id = ? AND m.deleted_by_sender = 0
25        ORDER BY m.created_at DESC
26    ", [$USER['id']]);
27}
28 
29/* ── Online users (last seen within 15 min) ─────────── */
30$since    = DB::sinceSeconds(900);
31$onlineUsers = DB::rows(
32    "SELECT id, username, avatar, role FROM users
33     WHERE last_seen >= $since AND id != ? AND suspended = 0
34     ORDER BY last_seen DESC LIMIT 20",
35    [$USER['id']]
36);
37 
38$unread    = unread_messages();
39$sentCount = (int)DB::val("SELECT COUNT(*) FROM messages WHERE sender_id=? AND deleted_by_sender=0", [$USER['id']]);
40 
41$PAGE_TITLE = 'Messages';
42include __DIR__ . '/../views/partials/layout.php';
43?>
44 
45<div class="mx-layout">
46 
47  <!-- ── Left: Inbox ───────────────────────────── -->
48  <div class="mx-main">
49 
50    <div class="mx-topbar">
51      <div>
52        <h1 class="mx-title">
53          <?= $box === 'inbox' ? '📥 Inbox' : '📤 Sent' ?>
54          <?php if ($box === 'inbox' && $unread > 0): ?>
55            <span class="mx-unread-badge"><?= $unread ?> unread</span>
56          <?php endif; ?>
57        </h1>
58      </div>
59      <a href="<?= u('messages/compose.php') ?>" class="btn-primary">
60        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 5v14M5 12h14"/></svg>
61        Compose
62      </a>
63    </div>
64 
65    <!-- Tabs -->
66    <div class="mx-tabs">
67      <a href="?box=inbox" class="mx-tab <?= $box === 'inbox' ? 'active' : '' ?>">
68        📥 Inbox
69        <?php if ($unread > 0): ?>
70          <span class="mx-tab-badge"><?= $unread ?></span>
71        <?php endif; ?>
72      </a>
73      <a href="?box=sent" class="mx-tab <?= $box === 'sent' ? 'active' : '' ?>">
74        📤 Sent
75        <?php if ($sentCount > 0): ?>
76          <span class="mx-tab-badge muted"><?= $sentCount ?></span>
77        <?php endif; ?>
78      </a>
79    </div>
80 
81    <?php if (isset($_GET['sent'])): ?>
82      <div class="alert ok" style="margin-bottom:16px">✓ Message sent successfully.</div>
83    <?php endif; ?>
84 
85    <?php if (empty($msgs)): ?>
86      <div class="mx-empty">
87        <div class="mx-empty-icon"><?= $box === 'inbox' ? '📭' : '📤' ?></div>
88        <p><?= $box === 'inbox' ? 'Your inbox is empty.' : 'No sent messages yet.' ?></p>
89        <a href="<?= u('messages/compose.php') ?>" class="btn-primary">Send a message</a>
90      </div>
91    <?php else: ?>
92      <div class="mx-list">
93        <?php foreach ($msgs as $m):
94          $isUnread = !$m['is_read'] && $box === 'inbox';
95          $isOnline = strtotime($m['last_seen']) >= (time() - 900);
96          $name     = $m['other_name'];
97          $av       = $m['other_av'];
98        ?>
99          <a href="<?= u('messages/view.php?id=' . $m['id']) ?>"
100             class="mx-row <?= $isUnread ? 'unread' : '' ?>">
101 
102            <!-- Avatar + online dot -->
103            <div class="mx-row-av">
104              <?php if ($av): ?>
105                <img src="<?= e($av) ?>" class="av-md" alt="">
106              <?php else: ?>
107                <span class="av-md av-init"><?= strtoupper($name[0]) ?></span>
108              <?php endif; ?>
109              <?php if ($isOnline): ?>
110                <span class="mx-online-dot" title="Online now"></span>
111              <?php endif; ?>
112            </div>
113 
114            <!-- Content -->
115            <div class="mx-row-body">
116              <div class="mx-row-top">
117                <span class="mx-row-from">
118                  <?= $box === 'inbox' ? 'From' : 'To' ?>
119                  <strong>@<?= e($name) ?></strong>
120                  <span class="role-tag role-<?= e($m['role']) ?>"><?= e($m['role']) ?></span>
121                  <?php if ($isOnline): ?>
122                    <span class="mx-online-label">● Online</span>
123                  <?php endif; ?>
124                </span>
125                <span class="mx-row-time">
126                  <?php if ($isUnread): ?>
127                    <span class="mx-new-badge">NEW</span>
128                  <?php endif; ?>
129                  <span class="ago" data-ts="<?= e($m['created_at']) ?>"></span>
130                </span>
131              </div>
132              <?php if ($m['subject']): ?>
133                <div class="mx-row-subject"><?= e($m['subject']) ?></div>
134              <?php endif; ?>
135              <div class="mx-row-preview"><?= e(mb_substr($m['body'], 0, 100)) ?><?= mb_strlen($m['body']) > 100 ? '…' : '' ?></div>
136            </div>
137 
138            <!-- Arrow -->
139            <div class="mx-row-arrow">›</div>
140          </a>
141        <?php endforeach; ?>
142      </div>
143    <?php endif; ?>
144  </div>
145 
146  <!-- ── Right: Online users + quick compose ───── -->
147  <aside class="mx-aside">
148 
149    <!-- Online users -->
150    <div class="mx-widget">
151      <div class="mx-widget-head">
152        <span class="mx-online-dot" style="position:static;margin-right:4px"></span>
153        Online Now
154        <span class="mx-widget-count"><?= count($onlineUsers) ?></span>
155      </div>
156      <?php if (empty($onlineUsers)): ?>
157        <div class="mx-widget-empty">No one online right now</div>
158      <?php else: ?>
159        <div class="mx-online-list">
160          <?php foreach ($onlineUsers as $ou): ?>
161            <div class="mx-online-row">
162              <div style="position:relative;flex-shrink:0">
163                <?php if ($ou['avatar']): ?>
164                  <img src="<?= e($ou['avatar']) ?>" class="av-sm" alt="">
165                <?php else: ?>
166                  <span class="av-sm av-init"><?= strtoupper($ou['username'][0]) ?></span>
167                <?php endif; ?>
168                <span class="mx-online-dot mx-online-dot-sm"></span>
169              </div>
170              <div class="mx-online-info">
171                <a href="<?= u('users/profile.php?u=' . urlencode($ou['username'])) ?>"
172                   class="mx-online-name">@<?= e($ou['username']) ?></a>
173                <span class="role-tag role-<?= e($ou['role']) ?>"><?= e($ou['role']) ?></span>
174              </div>
175              <a href="<?= u('messages/compose.php?to=' . urlencode($ou['username'])) ?>"
176                 class="mx-msg-quick" title="Send message">
177                <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
178              </a>
179            </div>
180          <?php endforeach; ?>
181        </div>
182      <?php endif; ?>
183    </div>
184 
185    <!-- Quick compose -->
186    <div class="mx-widget" style="margin-top:16px">
187      <div class="mx-widget-head">✏️ Quick Message</div>
188      <div style="padding:14px">
189        <div style="position:relative;margin-bottom:10px">
190          <input type="text" id="quickTo" class="fi" placeholder="@username" autocomplete="off"
191                 style="padding-left:26px">
192          <span style="position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--faint);font-size:13px;pointer-events:none">@</span>
193          <div id="quickSuggestions" class="to-suggestions"></div>
194        </div>
195        <a id="quickComposeBtn" href="<?= u('messages/compose.php') ?>" class="btn-primary btn-block">
196          Compose →
197        </a>
198      </div>
199    </div>
200 
201  </aside>
202</div>
203 
204<script>
205/* Quick compose — update link as user types */
206var qTo  = document.getElementById('quickTo');
207var qBtn = document.getElementById('quickComposeBtn');
208var qSug = document.getElementById('quickSuggestions');
209var qTimer;
210 
211if (qTo) {
212  qTo.addEventListener('input', function() {
213    var val = this.value.trim().replace(/^@/, '');
214    qBtn.href = NX.base + '/messages/compose.php' + (val ? '?to=' + encodeURIComponent(val) : '');
215    clearTimeout(qTimer);
216    if (val.length < 1) { qSug.style.display = 'none'; return; }
217    qTimer = setTimeout(function() {
218      fetch(NX.base + '/api/search_users.php?q=' + encodeURIComponent(val))
219        .then(function(r) { return r.json(); })
220        .then(function(rows) {
221          if (!rows.length) { qSug.style.display = 'none'; return; }
222          qSug.innerHTML = rows.slice(0, 5).map(function(u) {
223            return '<div class="to-sug-item" onclick="pickQuick(\'' + u.username + '\')">@' + u.username + '</div>';
224          }).join('');
225          qSug.style.display = 'block';
226        });
227    }, 220);
228  });
229  document.addEventListener('click', function(e) {
230    if (!qTo.contains(e.target)) qSug.style.display = 'none';
231  });
232}
233 
234function pickQuick(uname) {
235  qTo.value = uname;
236  qBtn.href = NX.base + '/messages/compose.php?to=' + encodeURIComponent(uname);
237  qSug.style.display = 'none';
238  qBtn.click();
239}
240</script>
241 
242<?php include __DIR__ . '/../views/partials/layout_end.php'; ?>