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
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
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
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'; ?>