Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,22 @@ quickSocket.editMessage('room-001', 'message-id', 'Updated text')
quickSocket.deleteMessage('room-001', 'message-id')
```

### Get Room Messages

```javascript
const result = quickSocket.getRoomMessages('room-001', 1, 20)

console.log(result)
// {
// roomId: 'room-001',
// page: 1,
// limit: 20,
// total: 42,
// totalPages: 3,
// messages: [...]
// }
```

### Notifications

```javascript
Expand Down
3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { init, getIO } = require('./server')
const { authMiddleware } = require('./middleware')
const { notifyUser, broadcast, notifyRoom } = require('./notifications')
const { createRoom, joinRoom, leaveRoom, getRoomParticipants, closeRoom } = require('./rooms')
const { sendMessage, editMessage, deleteMessage, sendTyping, MESSAGE_TYPES } = require('./messages')
const { sendMessage, editMessage, deleteMessage, sendTyping, getRoomMessages, MESSAGE_TYPES } = require('./messages')
const { markDelivered, markRead, markAllRead, STATUS } = require('./status')

module.exports = {
Expand All @@ -21,6 +21,7 @@ module.exports = {
editMessage,
deleteMessage,
sendTyping,
getRoomMessages,
MESSAGE_TYPES,
markDelivered,
markRead,
Expand Down
47 changes: 46 additions & 1 deletion src/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const MESSAGE_TYPES = {
SYSTEM: 'system'
}

const roomMessages = new Map()

function sendMessage(roomId, message) {
if (!roomId) throw new Error(
'[quick-socket] sendMessage() requires a roomId as the first argument. Received: ' + roomId
Expand All @@ -31,11 +33,21 @@ function sendMessage(roomId, message) {
type: message.type || MESSAGE_TYPES.TEXT,
createdAt: new Date()
}
if (!roomMessages.has(roomId)) {
roomMessages.set(roomId, [])
}
roomMessages.get(roomId).push(payload)
getIO().to(roomId).emit('message:new', payload)
return payload
}

function editMessage(roomId, messageId, newContent) {
const messages = roomMessages.get(roomId) || []
const message = messages.find(msg => msg.id === messageId)
if (message) {
message.content = newContent
message.editedAt = new Date()
}
getIO().to(roomId).emit('message:edited', {
messageId,
content: newContent,
Expand All @@ -44,6 +56,8 @@ function editMessage(roomId, messageId, newContent) {
}

function deleteMessage(roomId, messageId) {
const messages = roomMessages.get(roomId) || []
roomMessages.set(roomId, messages.filter(msg => msg.id !== messageId))
getIO().to(roomId).emit('message:deleted', {
messageId,
deletedAt: new Date()
Expand All @@ -54,4 +68,35 @@ function sendTyping(roomId, userId, isTyping) {
getIO().to(roomId).emit('message:typing', { userId, isTyping })
}

module.exports = { sendMessage, editMessage, deleteMessage, sendTyping, MESSAGE_TYPES }
function getRoomMessages(roomId, page = 1, limit = 20) {
const messages = roomMessages.get(roomId) || []
const currentPage = Math.max(Number(page) || 1, 1)
const pageSize = Math.max(Number(limit) || 20, 1)
const total = messages.length
const totalPages = total === 0 ? 0 : Math.ceil(total / pageSize)
const end = total - (currentPage - 1) * pageSize
const start = Math.max(0, end - pageSize)

return {
roomId,
page: currentPage,
limit: pageSize,
total,
totalPages,
messages: end > 0 ? messages.slice(start, end) : []
}
}

function clearRoomMessages(roomId) {
roomMessages.delete(roomId)
}

module.exports = {
sendMessage,
editMessage,
deleteMessage,
sendTyping,
getRoomMessages,
clearRoomMessages,
MESSAGE_TYPES
}
2 changes: 2 additions & 0 deletions src/rooms.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { getIO } = require('./server')
const { clearRoomMessages } = require('./messages')

const rooms = new Map()

Expand Down Expand Up @@ -66,6 +67,7 @@ function getRoomParticipants(roomId) {

function closeRoom(roomId) {
getIO().to(roomId).emit('room:closed', { roomId })
clearRoomMessages(roomId)
rooms.delete(roomId)
}

Expand Down
50 changes: 25 additions & 25 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const http = require('http')
const { io: Client } = require('socket.io-client')
const quickSocket = require('../src/index')
const { authMiddleware } = quickSocket

const server = http.createServer()
const PORT = 4501
Expand Down Expand Up @@ -76,6 +77,10 @@ async function runTests() {
log('sendMessage has correct type', msgResult?.type === 'text')
log('sendMessage has generated id', !!msgResult?.id)

const initialMessages = quickSocket.getRoomMessages('room-1', 1, 20)
log('getRoomMessages stores sent messages', initialMessages.total === 1)
log('getRoomMessages returns the sent message', initialMessages.messages[0]?.id === msgResult?.id)

// ── Test 5: sendTyping ──
const typingResult = await new Promise((resolve) => {
client2.once('message:typing', (data) => resolve(data))
Expand Down Expand Up @@ -141,6 +146,24 @@ async function runTests() {
})
log('notifyRoom delivers event', notifyRoomResult?.status === 'active')

quickSocket.sendMessage('room-1', {
senderId: 'u1',
content: 'Second',
type: quickSocket.MESSAGE_TYPES.TEXT
})
quickSocket.sendMessage('room-1', {
senderId: 'u2',
content: 'Third',
type: quickSocket.MESSAGE_TYPES.TEXT
})

const pageOne = quickSocket.getRoomMessages('room-1', 1, 2)
log('getRoomMessages paginates latest messages', pageOne.messages.length === 2)
log('getRoomMessages page 1 returns latest slice', pageOne.messages[0]?.content === 'Second' && pageOne.messages[1]?.content === 'Third')

const pageTwo = quickSocket.getRoomMessages('room-1', 2, 2)
log('getRoomMessages page 2 returns older messages', pageTwo.messages.length === 1 && pageTwo.messages[0]?.content === 'Hello!')

// ── Test 13: closeRoom ──
const closeResult = await new Promise((resolve) => {
client2.once('room:closed', (data) => resolve(data))
Expand All @@ -149,31 +172,8 @@ async function runTests() {
})
log('closeRoom emits room:closed', closeResult?.roomId === 'room-1')

// ── Validation Tests ──
let validationPassed = true

try {
quickSocket.sendMessage(undefined, {})
validationPassed = false
} catch (err) {
if (!err.message.includes('requires a roomId')) validationPassed = false
}

try {
quickSocket.sendMessage('room-1', { senderId: 'u1', content: '' })
validationPassed = false
} catch (err) {
if (!err.message.includes('cannot be empty')) validationPassed = false
}

try {
quickSocket.createRoom('')
validationPassed = false
} catch (err) {
if (!err.message.includes('non-empty string roomId')) validationPassed = false
}

log('validation throws correct errors for invalid inputs', validationPassed)
const messagesAfterClose = quickSocket.getRoomMessages('room-1', 1, 20)
log('closeRoom clears stored room messages', messagesAfterClose.total === 0)

// ── Test 14: MESSAGE_TYPES constants ──
log('MESSAGE_TYPES.TEXT is "text"', quickSocket.MESSAGE_TYPES.TEXT === 'text')
Expand Down