Sending Messages
Description
Method for sending messages
/**
* Send message
* @param model Message content
* @param channel Channel object (personal channel, group channel)
*/
WKIM.shared.messageManager().send(model: WKMessageContent, channel: WKChannel);
Text Messages
// Text message
let textModel: WKTextContent = new WKTextContent('Hello, WuKong')
// Send to user A
WKIM.shared.messageManager().send(textModel, new WKChannel('A', WKChannelType.personal));
Image Messages
// Image message
let imageModel: WKImageContent = new WKImageContent(localPath)
imageModel.width = 100
imageModel.height = 100
// Send to user A
WKIM.shared.messageManager().send(imageModel, new WKChannel('A', WKChannelType.personal));
Custom Messages
Reference custom messages: Custom Messages
Message Storage Callback (Not Message Send Status)
When sending messages, the SDK will trigger a storage callback after saving the message to the local database. At this point, the message has not been sent yet, and you can display the message in the UI in this listener.
// Listen for message storage
WKIM.shared.messageManager().addInsertedListener((msg) => {
// Display message in UI
})
New Messages
Listen for new message events
// New message listener
newMsgsListener = (msgs: WKMsg[]) => {
// Handle new messages
}
// Listen for new messages
WKIM.shared.messageManager().addNewMsgListener(this.newMsgsListener)
// Remove new message listener
WKIM.shared.messageManager().removeNewMsgListener(this.newMsgsListener)
Message Refresh
When the SDK updates messages, such as: message send status, someone likes a message, message read receipt, message recall, message editing, etc., the SDK will callback the following event. The UI can determine which specific message has changed through the clientMsgNO of the message object WKMsg.
// Refresh message listener
refreshMsgListener = (msg: WKMsg) => {
// Handle refresh message
}
// Listen for refresh messages
WKIM.shared.messageManager().addRefreshListener(this.refreshMsgListener)
// Remove refresh message listener
WKIM.shared.messageManager().removeRefreshListener(this.refreshMsgListener)
let option = new ChannelMsgOptions(() => {
// Syncing - show loading as needed
}, (list) => {
// Message data
})
option.oldestOrderSeq = 0 // Last message's large orderSeq, pass 0 for first entry into chat
option.contain = false // Whether to include the oldestOrderSeq message
option.pullMode = 1 // Pull mode 0: pull down 1: pull up
option.limit = 20 // Number to get each time
option.aroundMsgOrderSeq = 0 // Query messages around this message, e.g. aroundMsgOrderSeq=20 returns [16,17,19,20,21,22,23,24,25]
// View chat information for a channel
WKIM.shared.messageManager().getOrSyncHistoryMessages(channel, option)
Getting history messages is not a synchronous method, as there may be non-continuous data that needs to be synced from the server
Complete Message Management Example
import { WKIM, WKMsg, WKMessageContent, WKTextContent, WKImageContent, WKChannel, WKChannelType, ChannelMsgOptions } from '@wukong/wkim';
@Component
export struct MessageManager {
@State private messages: WKMsg[] = [];
@State private isLoading: boolean = false;
private newMsgsListener = (msgs: WKMsg[]) => {
this.handleNewMessages(msgs);
}
private refreshMsgListener = (msg: WKMsg) => {
this.handleMessageRefresh(msg);
}
private insertedListener = (msg: WKMsg) => {
this.handleMessageInserted(msg);
}
aboutToAppear(): void {
this.setupMessageListeners();
}
aboutToDisappear(): void {
this.removeMessageListeners();
}
private setupMessageListeners(): void {
// Listen for message storage
WKIM.shared.messageManager().addInsertedListener(this.insertedListener);
// Listen for new messages
WKIM.shared.messageManager().addNewMsgListener(this.newMsgsListener);
// Listen for message refresh
WKIM.shared.messageManager().addRefreshListener(this.refreshMsgListener);
}
private removeMessageListeners(): void {
WKIM.shared.messageManager().removeInsertedListener(this.insertedListener);
WKIM.shared.messageManager().removeNewMsgListener(this.newMsgsListener);
WKIM.shared.messageManager().removeRefreshListener(this.refreshMsgListener);
}
private handleMessageInserted(msg: WKMsg): void {
// Message saved to database, update UI immediately
console.log('Message inserted:', msg.clientMsgNo);
this.addMessageToUI(msg);
}
private handleNewMessages(msgs: WKMsg[]): void {
console.log('Received new messages:', msgs.length);
msgs.forEach(msg => {
this.addMessageToUI(msg);
});
}
private handleMessageRefresh(msg: WKMsg): void {
console.log('Message refreshed:', msg.clientMsgNo);
this.updateMessageInUI(msg);
}
private addMessageToUI(msg: WKMsg): void {
// Check if message already exists
const existingIndex = this.messages.findIndex(m => m.clientMsgNo === msg.clientMsgNo);
if (existingIndex >= 0) {
// Update existing message
this.messages[existingIndex] = msg;
} else {
// Add new message
this.messages.push(msg);
}
// Sort messages by timestamp
this.messages.sort((a, b) => a.timestamp - b.timestamp);
}
private updateMessageInUI(msg: WKMsg): void {
const index = this.messages.findIndex(m => m.clientMsgNo === msg.clientMsgNo);
if (index >= 0) {
this.messages[index] = msg;
}
}
// Send text message
public sendTextMessage(text: string, channel: WKChannel): void {
try {
const textModel = new WKTextContent(text);
WKIM.shared.messageManager().send(textModel, channel);
} catch (error) {
console.error('Failed to send text message:', error);
}
}
// Send image message
public sendImageMessage(localPath: string, width: number, height: number, channel: WKChannel): void {
try {
const imageModel = new WKImageContent(localPath);
imageModel.width = width;
imageModel.height = height;
WKIM.shared.messageManager().send(imageModel, channel);
} catch (error) {
console.error('Failed to send image message:', error);
}
}
// Load history messages
public loadHistoryMessages(channel: WKChannel, oldestOrderSeq: number = 0): void {
this.isLoading = true;
const option = new ChannelMsgOptions(
() => {
// Syncing callback
console.log('Syncing messages...');
},
(list: WKMsg[]) => {
// Message data callback
this.isLoading = false;
this.handleHistoryMessages(list);
}
);
option.oldestOrderSeq = oldestOrderSeq;
option.contain = false;
option.pullMode = 0; // Pull down
option.limit = 20;
option.aroundMsgOrderSeq = 0;
WKIM.shared.messageManager().getOrSyncHistoryMessages(channel, option);
}
private handleHistoryMessages(messages: WKMsg[]): void {
console.log('Loaded history messages:', messages.length);
// Merge with existing messages
messages.forEach(msg => {
const existingIndex = this.messages.findIndex(m => m.clientMsgNo === msg.clientMsgNo);
if (existingIndex < 0) {
this.messages.push(msg);
}
});
// Sort messages by timestamp
this.messages.sort((a, b) => a.timestamp - b.timestamp);
}
// Get messages for specific channel
public getMessagesForChannel(channelId: string, channelType: number): WKMsg[] {
return this.messages.filter(msg =>
msg.channelId === channelId && msg.channelType === channelType
);
}
// Clear messages
public clearMessages(): void {
this.messages = [];
}
build() {
Column() {
Text('Message Manager')
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
if (this.isLoading) {
Row() {
LoadingProgress()
.width(20)
.height(20)
.margin({ right: 10 })
Text('Loading messages...')
}
.margin({ bottom: 20 })
}
Text(`Total Messages: ${this.messages.length}`)
.fontSize(16)
.margin({ bottom: 20 })
List() {
ForEach(this.messages, (message: WKMsg) => {
ListItem() {
this.buildMessageItem(message)
}
})
}
.layoutWeight(1)
Row() {
TextInput({ placeholder: 'Enter message...' })
.layoutWeight(1)
.onSubmit((value: string) => {
const channel = new WKChannel('test_user', WKChannelType.personal);
this.sendTextMessage(value, channel);
})
Button('Send')
.onClick(() => {
// Send message logic
})
}
.padding(10)
}
.width('100%')
.height('100%')
.padding(20)
}
@Builder
buildMessageItem(message: WKMsg) {
Column() {
Row() {
Text(message.fromUID)
.fontSize(14)
.fontColor(Color.Gray)
.margin({ right: 10 })
Text(this.formatTimestamp(message.timestamp))
.fontSize(12)
.fontColor(Color.Gray)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 5 })
Text(message.content)
.fontSize(16)
.textAlign(TextAlign.Start)
.width('100%')
if (message.status === 0) {
Text('Sending...')
.fontSize(12)
.fontColor(Color.Orange)
.margin({ top: 5 })
}
}
.alignItems(HorizontalAlign.Start)
.padding(10)
.margin({ bottom: 10 })
.backgroundColor(Color.White)
.borderRadius(8)
}
private formatTimestamp(timestamp: number): string {
const date = new Date(timestamp * 1000);
return date.toLocaleTimeString();
}
}
Offline Messages
Need to implement sync channel message data source Channel Message Data Source
Because WuKongIM supports permanent message storage, it will generate massive offline messages. For this, we adopt an on-demand pull mechanism. For example, with 10 conversations each having 100,000 messages, WuKongIM will not pull all 10*100,000=1 million messages to local storage. Instead, it pulls information for these 10 conversations and the corresponding latest 20 messages, which means actually only 200 messages are pulled. Compared to 1 million messages, this greatly improves offline pull speed. Users will only pull messages for a specific conversation when they enter that conversation. These mechanisms are already encapsulated within the SDK, so users don’t need to worry about them. Users only need to focus on recent conversation changes and listen for data retrieval callbacks.
Data Structure Description
Message Structure
export class WKMsg {
// Server unique message ID
messageId = "";
// Server message sequence number
messageSeq = 0;
// Client message sequence number
clientSeq = 0;
// Message timestamp
timestamp = 0;
// Expiration time
expireTime = 0;
// Expiration timestamp
expireTimestamp = 0;
// Client unique number
clientMsgNo = "";
// Sender uid
fromUID = "";
// Channel ID
channelId = "";
// Channel type
channelType = WKChannelType.personal;
// Message type
contentType = 0;
// Message content string
content = "";
// Message status 1.sent successfully 0.sending
status = 0;
voiceStatus = 0;
// Is deleted 1.yes
isDeleted = 0;
// Search keyword
searchableWord = "";
// Message sender profile
private from?: WKChannel
// Message channel profile
private channelInfo?: WKChannel
// Message sender profile in channel (only for group messages)
private memberOfFrom?: WKChannelMember
// Sort number
orderSeq = 0;
// Is read
viewed = 0;
// Read time
viewedAt = 0;
// Topic ID
topicId = "";
// Local extensions
localExtraMap?: Record<string, object>
// Remote extensions
wkMsgExtra?: WKMsgExtra
// Message reaction data
reactionList?: WKMsgReaction[]
// Message content
messageContent?: WKMessageContent
}
Message Content Structure
export class WKMessageContent {
// Message type
contentType: number = 0
// Message content
content: string = ""
// Render message content, e.g., need to render @xxx text when @someone
entities?: ArrayList<WKMsgEntity>
// Message reply
reply?: WKReply
// Mention information
mentionInfo?: WKMentionInfo
}
Next Steps