diff --git a/api_v1_channels.php b/api_v1_channels.php new file mode 100644 index 0000000..133af40 --- /dev/null +++ b/api_v1_channels.php @@ -0,0 +1,31 @@ +prepare("SELECT 1 FROM server_members WHERE server_id = ? AND user_id = ?"); + $stmt->execute([$server_id, $user_id]); + + if ($stmt->fetch() && $name) { + try { + // Basic sanitization for channel name + $name = strtolower(preg_replace('/[^a-zA-Z0-3\-]/', '-', $name)); + + $stmt = db()->prepare("INSERT INTO channels (server_id, name, type) VALUES (?, ?, ?)"); + $stmt->execute([$server_id, $name, $type]); + $channel_id = db()->lastInsertId(); + + header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id); + exit; + } catch (Exception $e) { + die("Error creating channel: " . $e->getMessage()); + } + } +} +header('Location: index.php'); diff --git a/api_v1_servers.php b/api_v1_servers.php new file mode 100644 index 0000000..3d450bb --- /dev/null +++ b/api_v1_servers.php @@ -0,0 +1,55 @@ +prepare("SELECT id FROM servers WHERE invite_code = ?"); + $stmt->execute([$invite_code]); + $server = $stmt->fetch(); + + if ($server) { + $stmt = db()->prepare("INSERT IGNORE INTO server_members (server_id, user_id) VALUES (?, ?)"); + $stmt->execute([$server['id'], $user_id]); + header('Location: index.php?server_id=' . $server['id']); + exit; + } else { + die("Invalid invite code."); + } + } + + $name = $_POST['name'] ?? ''; + + if ($name) { + try { + $db = db(); + $db->beginTransaction(); + + // Create server + $invite_code = substr(strtoupper(md5(uniqid())), 0, 8); + $stmt = $db->prepare("INSERT INTO servers (name, owner_id, invite_code) VALUES (?, ?, ?)"); + $stmt->execute([$name, $user_id, $invite_code]); + $server_id = $db->lastInsertId(); + + // Add owner as member + $stmt = $db->prepare("INSERT INTO server_members (server_id, user_id) VALUES (?, ?)"); + $stmt->execute([$server_id, $user_id]); + + // Create default channel + $stmt = $db->prepare("INSERT INTO channels (server_id, name, type) VALUES (?, 'general', 'text')"); + $stmt->execute([$server_id]); + + $db->commit(); + header('Location: index.php?server_id=' . $server_id); + exit; + } catch (Exception $e) { + $db->rollBack(); + die("Error creating server: " . $e->getMessage()); + } + } +} +header('Location: index.php'); diff --git a/assets/css/discord.css b/assets/css/discord.css index ac5dfee..9e9d1f0 100644 --- a/assets/css/discord.css +++ b/assets/css/discord.css @@ -120,6 +120,15 @@ body { font-size: 0.9em; } +.server-icon.add-btn { + color: #23a559; +} + +.server-icon.add-btn:hover { + background-color: #23a559; + color: white; +} + .channel-category { color: var(--text-muted); font-size: 0.75em; @@ -127,6 +136,46 @@ body { font-weight: bold; margin-bottom: 8px; padding-left: 8px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.add-channel-btn { + cursor: pointer; + font-size: 1.2em; + margin-right: 8px; + line-height: 1; +} + +.add-channel-btn:hover { + color: var(--text-primary); +} + +/* Modals */ +.modal-content { + background-color: var(--bg-channels); + color: var(--text-primary); +} + +.modal-header { + border-bottom: 1px solid var(--bg-servers); +} + +.modal-footer { + border-top: 1px solid var(--bg-servers); +} + +.form-control { + background-color: var(--bg-servers); + border: none; + color: var(--text-primary); +} + +.form-control:focus { + background-color: var(--bg-servers); + color: var(--text-primary); + box-shadow: 0 0 0 0.25rem rgba(88, 101, 242, 0.25); } /* User Panel */ diff --git a/assets/js/main.js b/assets/js/main.js index edc58cf..8564681 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -43,6 +43,32 @@ document.addEventListener('DOMContentLoaded', () => { content: content, channel_id: channel_id }) + }); + + const result = await response.json(); + if (result.success) { + // If WS is connected, we might want to let WS handle the UI update + // But for simplicity, we append here and also send to WS + if (ws && ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ + type: 'message', + data: JSON.stringify({ + ...result.message, + channel_id: channel_id + }) + })); + } else { + appendMessage(result.message); + messagesList.scrollTop = messagesList.scrollHeight; + } + } else { + alert('Error: ' + result.error); + } + } catch (err) { + console.error('Failed to send message:', err); + } + }); + // Voice const voiceHandler = new VoiceChannel(ws); document.querySelectorAll('.voice-item').forEach(item => { @@ -57,6 +83,28 @@ document.addEventListener('DOMContentLoaded', () => { }); }); +function appendMessage(msg) { + const messagesList = document.getElementById('messages-list'); + const div = document.createElement('div'); + div.className = 'message-item'; + div.innerHTML = ` +
+
+
+ ${msg.username} + ${msg.time} +
+
+ ${msg.content.replace(/\n/g, '
')} +
+
+ `; + messagesList.appendChild(div); +} + + }); +}); + const result = await response.json(); if (result.success) { diff --git a/auth/register.php b/auth/register.php index a5599e8..df194f0 100644 --- a/auth/register.php +++ b/auth/register.php @@ -12,7 +12,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { try { $stmt = db()->prepare("INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)"); $stmt->execute([$username, $email, $hash]); - $_SESSION['user_id'] = db()->lastInsertId(); + $userId = db()->lastInsertId(); + + // Add to default server + $stmt = db()->prepare("INSERT IGNORE INTO server_members (server_id, user_id) VALUES (1, ?)"); + $stmt->execute([$userId]); + + $_SESSION['user_id'] = $userId; header('Location: ../index.php'); exit; } catch (Exception $e) { diff --git a/database/schema.sql b/database/schema.sql index fd495fd..4b64023 100644 --- a/database/schema.sql +++ b/database/schema.sql @@ -67,6 +67,15 @@ CREATE TABLE IF NOT EXISTS user_roles ( FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE ); +CREATE TABLE IF NOT EXISTS server_members ( + server_id INT NOT NULL, + user_id INT NOT NULL, + joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (server_id, user_id), + FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); + -- Seed initial data INSERT IGNORE INTO users (id, username, email, password_hash, status) VALUES (1, 'System', 'system@local', '$2y$10$xyz', 'online'); @@ -75,6 +84,8 @@ INSERT IGNORE INTO servers (id, name, owner_id, invite_code) VALUES (1, 'General Community', 1, 'GEN-123'), (2, 'Flatlogic Devs', 1, 'DEV-456'); +INSERT IGNORE INTO server_members (server_id, user_id) VALUES (1, 1), (2, 1); + INSERT IGNORE INTO channels (id, server_id, name, type) VALUES (1, 1, 'general', 'text'), (2, 1, 'random', 'text'), diff --git a/index.php b/index.php index 30479a0..51c3e3a 100644 --- a/index.php +++ b/index.php @@ -5,10 +5,20 @@ requireLogin(); $user = getCurrentUser(); $current_user_id = $user['id']; -// Fetch servers -$servers = db()->query("SELECT * FROM servers LIMIT 10")->fetchAll(); +// Fetch servers user is member of +$stmt = db()->prepare(" + SELECT s.* FROM servers s + JOIN server_members sm ON s.id = sm.server_id + WHERE sm.user_id = ? + LIMIT 20 +"); +$stmt->execute([$current_user_id]); +$servers = $stmt->fetchAll(); $active_server_id = $_GET['server_id'] ?? ($servers[0]['id'] ?? 1); +// If no servers found, we might want to show a default or an empty state +// For now, let's assume the seed data or the first server works + // Fetch channels $stmt = db()->prepare("SELECT * FROM channels WHERE server_id = ?"); $stmt->execute([$active_server_id]); @@ -30,6 +40,16 @@ $messages = $stmt->fetchAll(); $current_channel_name = 'general'; foreach($channels as $c) if($c['id'] == $active_channel_id) $current_channel_name = $c['name']; +// Fetch members +$stmt = db()->prepare(" + SELECT u.username, u.avatar_url, u.status + FROM users u + JOIN server_members sm ON u.id = sm.user_id + WHERE sm.server_id = ? +"); +$stmt->execute([$active_server_id]); +$members = $stmt->fetchAll(); + // SEO & Env tags $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Discord-like messaging app built with PHP'; $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; @@ -69,15 +89,25 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; + + +
- +
-
Text Channels
+
+ Text Channels + + +
@@ -85,7 +115,10 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; -
Voice Channels
+
+ Voice Channels + + +
@@ -150,17 +183,110 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
-
- Online — 1 +
+ Members —
-
-
- System + +
+
+ +
+ +
+ + + +
+ +
+
+ + + + + + + +