Features Messaging - nself-org/nchat GitHub Wiki
Complete guide to all messaging features in nself-chat.
nself-chat provides 14 messaging features covering everything from basic text to voice messages.
| Feature | Flag | Default |
|---|---|---|
| Edit Messages | messages.edit |
Enabled |
| Delete Messages | messages.delete |
Enabled |
| Reactions | messages.reactions |
Enabled |
| Threads | messages.threads |
Enabled |
| Pins | messages.pins |
Enabled |
| Bookmarks | messages.bookmarks |
Enabled |
| Forward | messages.forward |
Enabled |
| Schedule | messages.schedule |
Enabled |
| Voice Messages | messages.voice |
Enabled |
| Code Blocks | messages.codeBlocks |
Enabled |
| Markdown | messages.markdown |
Enabled |
| Link Previews | messages.linkPreviews |
Enabled |
| Mentions | messages.mentions |
Enabled |
| Quotes | messages.quotes |
Enabled |
Allow users to modify sent messages within a configurable time window.
const editConfig = {
enabled: true,
timeWindow: 3600, // seconds (1 hour default)
showEditHistory: true,
markAsEdited: true,
}import { useMessages } from '@/hooks/use-messages'
function MessageEditor({ messageId, content }) {
const { editMessage } = useMessages()
const handleEdit = async (newContent: string) => {
await editMessage(messageId, newContent)
}
return <EditForm onSubmit={handleEdit} initialContent={content} />
}View previous versions of edited messages:
import { useMessageVersions } from '@/hooks/use-message-versions'
function EditHistory({ messageId }) {
const { versions } = useMessageVersions(messageId)
return (
<ul>
{versions.map((v) => (
<li key={v.version}>
Version {v.version}: {v.content} ({v.editedAt})
</li>
))}
</ul>
)
}Allow users to remove their own messages.
const deleteConfig = {
enabled: true,
timeWindow: 86400, // seconds (24 hours)
softDelete: true, // Keep in database, hide from UI
adminCanDeleteAny: true,
}- Soft Delete: Message hidden but retained for compliance
- Hard Delete: Permanently removed from database
Emoji reactions on messages.
import { useReactions } from '@/hooks/use-reactions'
function ReactionButton({ messageId }) {
const { addReaction, removeReaction } = useReactions(messageId)
return (
<EmojiPicker
onSelect={(emoji) => addReaction(emoji)}
/>
)
}<MessageReactions reactions={message.reactions} onReact={handleReact} currentUserId={user.id} />const reactionConfig = {
enabled: true,
maxPerMessage: 20,
maxPerUser: 5,
allowCustomEmoji: true,
}Threaded replies to messages.
Message (parent)
├── Reply 1
├── Reply 2
│ └── (threads can be nested or flat)
└── Reply 3
import { useThreads } from '@/hooks/use-threads'
function ThreadReply({ parentMessageId }) {
const { createReply } = useThreads(parentMessageId)
return (
<MessageComposer
onSend={(content) => createReply(content)}
placeholder="Reply in thread..."
/>
)
}<ThreadPanel messageId={selectedMessage.id} onClose={() => setSelectedMessage(null)} />Pin important messages to channel.
import { usePins } from '@/hooks/use-pins'
function PinButton({ messageId, channelId }) {
const { pinMessage, unpinMessage, isPinned } = usePins(channelId)
return (
<Button
onClick={() => isPinned(messageId)
? unpinMessage(messageId)
: pinMessage(messageId)
}
>
{isPinned(messageId) ? 'Unpin' : 'Pin'}
</Button>
)
}<PinnedMessagesPanel channelId={channel.id} />Save messages for personal reference.
import { useBookmarks } from '@/lib/bookmarks'
function BookmarkButton({ messageId }) {
const { addBookmark, removeBookmark, isBookmarked } = useBookmarks()
return (
<Button onClick={() =>
isBookmarked(messageId)
? removeBookmark(messageId)
: addBookmark(messageId)
}>
{isBookmarked(messageId) ? 'Saved' : 'Save'}
</Button>
)
}Access at /saved - shows all bookmarked messages with organization options.
Forward messages to other channels or users.
<ForwardDialog
messageId={message.id}
onForward={(targetId, type) => forwardMessage(message.id, targetId, type)}
/>- Forward to channel
- Forward to direct message
- Forward with comment
- Forward multiple messages
Schedule messages to send later.
import { useScheduled } from '@/lib/scheduled'
function ScheduleButton({ content, channelId }) {
const { scheduleMessage } = useScheduled()
return (
<DateTimePicker
onSelect={(date) => scheduleMessage({
content,
channelId,
sendAt: date
})}
/>
)
}Access at /drafts - view and manage all scheduled messages.
Record and send audio messages.
import { useVoiceRecorder } from '@/lib/voice'
function VoiceRecorder({ onSend }) {
const {
isRecording,
duration,
start,
stop,
cancel
} = useVoiceRecorder()
return (
<div>
{isRecording ? (
<>
<span>{duration}s</span>
<Button onClick={stop}>Send</Button>
<Button onClick={cancel}>Cancel</Button>
</>
) : (
<Button onClick={start}>Record</Button>
)}
</div>
)
}<VoiceMessagePlayer
src={message.audioUrl}
duration={message.audioDuration}
waveform={message.waveform}
/>Syntax-highlighted code snippets.
```javascript
const greeting = 'Hello, World!'
console.log(greeting)
```50+ languages including:
- JavaScript/TypeScript
- Python
- Go
- Rust
- Java
- C/C++
- SQL
- And more...
const codeBlockConfig = {
enabled: true,
lineNumbers: true,
maxLines: 100,
copyButton: true,
theme: 'github-dark',
}Rich text formatting using Markdown.
| Syntax | Result |
|---|---|
**bold** |
bold |
*italic* |
italic |
~~strike~~ |
|
`code` |
code |
[link](url) |
link |
> quote |
blockquote |
- item |
bullet list |
1. item |
numbered list |
const markdownConfig = {
enabled: true,
allowHtml: false,
sanitize: true,
linkify: true,
}Automatic URL unfurling with preview cards.
- General websites (Open Graph)
- YouTube videos
- Twitter/X posts
- GitHub repos/issues
- And more...
const linkPreviewConfig = {
enabled: true,
timeout: 5000,
maxWidth: 400,
showImage: true,
showDescription: true,
}@mention users and channels.
| Type | Syntax | Description |
|---|---|---|
| User | @username |
Mention specific user |
| Channel | #channel |
Link to channel |
| Everyone | @everyone |
Notify all members |
| Here | @here |
Notify online members |
<MentionAutocomplete onSelect={(mention) => insertMention(mention)} filter={searchText} />const mentionConfig = {
enabled: true,
allowEveryone: 'admin', // 'all' | 'admin' | 'none'
allowHere: 'admin',
highlightStyle: 'background', // 'background' | 'bold' | 'color'
}Quote previous messages in replies.
> Original message content
> - @author
My reply to this
<QuotedMessage originalMessage={quotedMessage} onClick={() => scrollToMessage(quotedMessage.id)} />// Full message configuration
const messageConfig = {
maxLength: 10000,
edit: {
enabled: true,
timeWindow: 3600,
showHistory: true,
},
delete: {
enabled: true,
timeWindow: 86400,
softDelete: true,
},
reactions: {
enabled: true,
maxPerMessage: 20,
},
threads: {
enabled: true,
maxDepth: 1,
},
pins: {
enabled: true,
maxPerChannel: 50,
},
bookmarks: {
enabled: true,
maxPerUser: 1000,
},
voice: {
enabled: true,
maxDuration: 300,
format: 'webm',
},
codeBlocks: {
enabled: true,
lineNumbers: true,
},
markdown: {
enabled: true,
sanitize: true,
},
linkPreviews: {
enabled: true,
timeout: 5000,
},
mentions: {
enabled: true,
allowEveryone: 'admin',
},
}