Skip to content

Repository files navigation

Important

This project was created by @mvtbh. My only contribution was the backend, which is now open source. Aside from that, I was not involved in the development of this project.

The project has been discontinued and is no longer being worked on. It is also important to note that the project was never fully completed and remains unfinished. A significant amount of work would still be required to bring it to a complete and production-ready state.

As the project has been abandoned, I decided to release my contribution as open source.

The back-end source code can be found here: HERE

PixelMessenger (Frontend)

Modern React/Next.js messenger-style UI. This README explains how to connect the frontend to a backend API, which endpoints the app expects, where to integrate calls, and how to run the project in both demo and real modes.

Quick start

  • Node.js 18+ and pnpm/npm installed
  • Clone repo, then install and run:
pnpm install
pnpm dev

The app runs in demo mode by default (mocked data) until you provide real API/WS URLs.

Environment configuration

Create a .env.local in the project root:

NEXT_PUBLIC_API_URL=https://api.your-domain.com
NEXT_PUBLIC_WS_URL=wss://ws.your-domain.com

Notes:

  • Demo mode is used when these variables are missing or point to localhost/demo (see lib/api.ts isDemoMode()).
  • To use a real backend, set both variables to non-localhost URLs and restart the dev server.

Backend systems the frontend needs

  • Authentication: register, login (optionally 2FA), JWT token issuance/validation
  • Users: current user profile, user list, profiles, presence/online status
  • Messages: global channel, direct messages, reactions, read receipts, pagination
  • Groups: create group, list groups, group membership, group messages
  • Presence & typing: real-time status via WebSocket (connect with JWT)
  • File uploads: receive file uploads or issue pre-signed URLs; return downloadable URL + expiry
  • Settings & privacy: read/write user preferences (optional; some settings are client-only today)

API endpoints required by this frontend

The current code calls a subset already. You can keep these paths and payloads to minimize changes.

Authentication

  • POST /user/register

Request:

{ "username": "alice", "email": "alice@example.com", "password": "StrongPass1!" }

Response 200:

{
  "text": "Account Created!",
  "token": "<jwt>",
  "user": {
    "id": "123",
    "username": "alice",
    "email": "alice@example.com",
    "createdAt": "2024-01-01T00:00:00.000Z"
  }
}
  • POST /user/login

Request:

{ "email": "alice@example.com", "password": "StrongPass1!", "twoFactorCode": "123456", "twoFactorToken": "optional" }

Response 200 (normal):

{ "text": "Login successful", "token": "<jwt>", "user": { "id": "123", "username": "alice", "email": "alice@example.com", "createdAt": "..." } }

Response 200 (2FA required):

{ "requiresTwoFactor": true, "twoFactorToken": "temp-2fa-token" }

Optional 2FA endpoints (wire later to UI):

  • POST /user/2fa/enable { code }
  • POST /user/2fa/disable { code }

Users

  • GET /user/list

Response 200:

[
  { "id": "2", "username": "Alice", "email": "alice@pixel.com", "createdAt": "..." },
  { "id": "3", "username": "Bob",   "email": "bob@pixel.com",   "createdAt": "..." }
]

Recommended (not yet called by code):

  • GET /user/me → current user
  • GET /user/:id → profile
  • PATCH /user/profile → update profile
  • GET /presence or WS events for presence

Messages (global + DMs)

  • GET /messages?page=0&limit=50

Response 200:

[
  {
    "id": "1",
    "sendedById": 2,
    "sendedByUsername": "Alice",
    "message": "Hello world",
    "receiverId": null,
    "inGlobalChat": true,
    "timestamp": "1710000000000"
  }
]
  • POST /messages/:dmId

Request:

{ "userId": 1 }

Response 200: same array shape as above (DM history).

Suggested send endpoints (wire from ChatContext.sendMessage when you build it):

  • POST /messages (global) body: { message }
  • POST /dms/:userId/messages body: { message }

Reactions

  • POST /messages/:id/reactions body: { emoji }
  • DELETE /messages/:id/reactions body: { emoji }

Response shape for a reaction the UI can merge:

{ "id": "rx1", "messageId": "1", "userId": 123, "username": "alice", "emoji": "👍", "timestamp": "1710000001000" }

Friends / DMs management

  • POST /friends/:userId
  • DELETE /friends/:userId
  • POST /blocks/:userId
  • DELETE /blocks/:userId
  • POST /dms/:userId/mute
  • DELETE /dms/:userId/mute
  • POST /dms/:userId/pin
  • DELETE /dms/:userId/pin
  • GET /dms → return DM inbox summaries the UI can show as DMInfo[]

Example DMInfo item:

{ "userId": 2, "username": "Alice", "isOnline": true, "unreadCount": 3, "isPinned": false, "isMuted": false, "isBlocked": false, "pinnedMessages": [] }

Groups

  • POST /groups body: { name, description?, members: string[] }
  • GET /groups
  • GET /groups/:id/messages
  • POST /groups/:id/messages body: { message }

Files

  • POST /files (multipart/form-data) → { url, expiresAt, fileName, size, contentType } Or implement pre-signed uploads:
  • POST /files/presign → { uploadUrl, url, expiresAt } then PUT the file to uploadUrl.

News channel (optional)

  • GET /news?page=0&limit=50 → array of items { id, title, content, author, timestamp, isPinned, tags }

WebSocket events

Connect with wss://.../socket?token=<jwt> and send/receive JSON messages like:

{ "type": "message", "data": { "id": "...", "sendedById": 1, "sendedByUsername": "alice", "message": "...", "receiverId": null, "inGlobalChat": true, "timestamp": "..." } }

Other event types to support:

  • typing { userId, username, chatId, isTyping }
  • presence { userId, status }
  • reaction { messageId, ...reaction }
  • read-receipt { messageId, readerId, timestamp }

Where to integrate API calls in this codebase

  • lib/api.ts

    • Primary HTTP client. Replace demo handlers by implementing real requests.
    • Already implements: register, login, getMessages, getDMMessages, getUsers.
    • Needs backend wiring: updateProfile, addReaction, removeReaction, 2FA methods, friends/block/mute/pin, DM inbox: getDMInfos, getNewsMessages.
  • context/auth-context.tsx

    • Uses apiClient.login/register, persists token/user to localStorage.
    • If your /user/login returns a richer user, ensure it matches types/User.
  • context/chat-context.tsx

    • Loads users/DM info/messages via apiClient.
    • sendMessage is optimistic only; wire it to POST endpoints and merge the server echo/ack.
    • Friend/block/mute/pin functions call apiClient.* placeholders—implement these.
  • lib/socket.ts

    • Contains a commented WebSocket client showing the expected event shapes.
    • Uncomment and adapt once your WS server is ready.
  • components/ui/file-upload.tsx and lib/file-manager.ts

    • Currently simulate uploads with URL.createObjectURL.
    • Replace fileManager.uploadFile(file) with a call to your /files or presign flow.
  • components/chat/create-group-modal.tsx

    • Calls useChat().createGroup(...)—add createGroup to ChatContext and ApiClient when backend is ready.

Example API client (fetch-based)

You already have lib/api.ts. If you prefer a minimal services layer, this example shows token storage, error handling, and a couple endpoints.

// services/apiClient.ts
const API_URL = process.env.NEXT_PUBLIC_API_URL!;

let authToken: string | null = null;
export function setAuthToken(token: string | null) { authToken = token; }

async function http(path: string, init: RequestInit = {}) {
  const headers: HeadersInit = { 'Content-Type': 'application/json', ...(init.headers || {}) };
  if (authToken) headers['Authorization'] = `Bearer ${authToken}`;
  const res = await fetch(`${API_URL}${path}`, { ...init, headers });
  const text = await res.text();
  const data = text ? JSON.parse(text) : null;
  if (!res.ok) throw new Error(data?.message || `HTTP ${res.status}`);
  return data;
}

export const api = {
  register: (username: string, email: string, password: string) =>
    http('/user/register', { method: 'POST', body: JSON.stringify({ username, email, password }) }),
  login: async (email: string, password: string, twoFactorCode?: string, twoFactorToken?: string) => {
    const data = await http('/user/login', { method: 'POST', body: JSON.stringify({ email, password, twoFactorCode, twoFactorToken }) });
    if (data.token) setAuthToken(data.token);
    return data;
  },
  getUsers: () => http('/user/list'),
  getMessages: (page = 0, limit = 50) => http(`/messages?page=${page}&limit=${limit}`),
  getDMMessages: (dmId: number, userId: number) => http(`/messages/${dmId}`, { method: 'POST', body: JSON.stringify({ userId }) }),
  sendMessage: (payload: { message: string; receiverId?: number | null }) => http('/messages', { method: 'POST', body: JSON.stringify(payload) }),
  addReaction: (messageId: string, emoji: string) => http(`/messages/${messageId}/reactions`, { method: 'POST', body: JSON.stringify({ emoji }) }),
  removeReaction: (messageId: string, emoji: string) => http(`/messages/${messageId}/reactions`, { method: 'DELETE', body: JSON.stringify({ emoji }) }),
};

To use it, replace calls in lib/api.ts or import api in contexts/components and wire it similarly to the existing apiClient.

Example WebSocket client

// services/socketClient.ts
type Message = { id: string; sendedById: number; sendedByUsername: string; message: string; receiverId?: number|null; inGlobalChat: boolean; timestamp: string };

export class SocketClient {
  private socket: WebSocket | null = null;
  connect(token: string) {
    const WS_URL = process.env.NEXT_PUBLIC_WS_URL!;
    this.socket = new WebSocket(`${WS_URL}?token=${encodeURIComponent(token)}`);
  }
  sendMessage(data: Message) {
    this.socket?.send(JSON.stringify({ type: 'message', data }));
  }
}

Wire into context/chat-context.tsx after login and clean up on logout.

Backend implementation notes

  • Auth

    • Use JWT (RS256/HS256). Return token and user from /user/login and /user/register.
    • Protect all non-auth endpoints with Authorization: Bearer <token>.
  • CORS

    • Allow the frontend origin and Authorization, Content-Type headers. Include credentials if needed.
  • Pagination

    • /messages accepts page and limit. Return arrays the UI maps directly to types/MessageInterface.
  • Message fields

    • The UI expects: id, sendedById, sendedByUsername, message, receiverId (null for global), inGlobalChat, timestamp (ms as string). Include these in responses or adjust lib/api.ts mappers.
  • Presence/typing

    • Prefer WS. Broadcast typing and presence updates to relevant subscribers.
  • Files

    • Enforce size/type limits. Return downloadable url and expiresAt. If using object storage, prefer a presign flow.
  • Security

    • Validate/sanitize inputs server-side. Rate-limit auth/message endpoints. Consider audit logging for admin.

How this frontend maps server responses

See the mappers in lib/api.ts:

if (endpoint === "/user/login") {
  const body = JSON.parse(options.body as string);
  return this.demoLogin(body.email, body.password);
}
// Map backend message to frontend MessageInterface
private mapBackendMessage(backendMsg: any): MessageInterface {
  return {
    id: backendMsg.id,
    sendedById: backendMsg.sendedById,
    sendedByUsername: backendMsg.sendedByUsername,
    message: backendMsg.message,
    receiverId: backendMsg.receiverId,
    inGlobalChat: backendMsg.inGlobalChat,
    timestamp: backendMsg.timestamp,
    encrypted: false,
    reactions: [],
  };
}

If your backend uses different names, adjust these mappers.

Developer checklist (backend + integration)

  • Set .env.local with real API/WS URLs
  • Implement endpoints listed above; return fields expected by types/*
  • Ensure JWT auth on all protected routes and WS connection
  • Update lib/api.ts to use real endpoints and remove demo guards
  • Wire sendMessage in context/chat-context.tsx to POST/WS
  • Replace lib/file-manager.ts with actual upload calls
  • Uncomment and adapt lib/socket.ts once your WS is ready
  • Validate CORS and test via pnpm dev

Running the app

pnpm install
cp .env.local.example .env.local # or create one as shown above
pnpm dev

Open http://localhost:3000/. With valid env URLs and a running backend, you’ll see real data. Without them, you’ll see demo data.

About

A simple front-end for a messager called PixelMessager.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages