You need to implement the data source for paginated channel member profiles: Get Paginated Channel Member Profile Data Source
Get Channel Members
Get all members of a specific channel
Copy
// Get all members under a specific channel
let members = WKIM.shared.channelMemberManager().getMembers(channelId, channelType);
Paginated query of channel members
Copy
// Get channel members with pagination
WKIM.shared.channelMemberManager().getWithPageOrSearch(channelId, channelType, option);
option description:
Copy
export class SyncChannelMemberOptions {
searchKey: string = ''; // Search keyword
page: number = 0; // Page number
limit: number = 20; // Items per page
}
Complete member retrieval example
Copy
import { WKIM, WKChannelMember, WKChannelType, SyncChannelMemberOptions } from '@wukong/wkim';
class ChannelMemberManager {
// Get all members
static getAllMembers(channelId: string, channelType: number): WKChannelMember[] {
try {
const members = WKIM.shared.channelMemberManager().getMembers(channelId, channelType);
console.log(`Retrieved ${members.length} members`);
return members;
} catch (error) {
console.error('Failed to get member list:', error);
return [];
}
}
// Get members with pagination
static async getPagedMembers(
channelId: string,
channelType: number,
page: number = 0,
limit: number = 20,
searchKey: string = ''
): Promise<WKChannelMember[]> {
try {
const option = new SyncChannelMemberOptions();
option.page = page;
option.limit = limit;
option.searchKey = searchKey;
const members = await WKIM.shared.channelMemberManager().getWithPageOrSearch(channelId, channelType, option);
console.log(`Paginated members retrieved successfully, page ${page}, ${members.length} members`);
return members;
} catch (error) {
console.error('Failed to get paginated members:', error);
return [];
}
}
// Search members
static async searchMembers(channelId: string, channelType: number, keyword: string): Promise<WKChannelMember[]> {
if (!keyword.trim()) {
return [];
}
return await this.getPagedMembers(channelId, channelType, 0, 50, keyword);
}
// Filter members by role
static getMembersByRole(channelId: string, channelType: number, role: number): WKChannelMember[] {
const allMembers = this.getAllMembers(channelId, channelType);
return allMembers.filter(member => member.role === role);
}
// Get admin member list
static getAdminMembers(channelId: string, channelType: number): WKChannelMember[] {
return this.getMembersByRole(channelId, channelType, 1); // Assuming 1 is admin role
}
// Get normal member list
static getNormalMembers(channelId: string, channelType: number): WKChannelMember[] {
return this.getMembersByRole(channelId, channelType, 0); // Assuming 0 is normal member role
}
// Get online members (needs to be combined with channel information)
static getOnlineMembers(channelId: string, channelType: number): WKChannelMember[] {
const allMembers = this.getAllMembers(channelId, channelType);
// Here you need to combine with channel manager to get online status
return allMembers.filter(member => {
const channel = WKIM.shared.channelManager().getChannel(member.memberUID, WKChannelType.personal);
return channel?.online === 1;
});
}
// Get member display name
static getMemberDisplayName(member: WKChannelMember): string {
if (member.memberRemark) {
return member.memberRemark;
}
if (member.memberName) {
return member.memberName;
}
return member.memberUID;
}
// Check if member is admin
static isAdmin(member: WKChannelMember): boolean {
return member.role === 1; // Assuming 1 is admin role
}
// Check if member is muted
static isMuted(member: WKChannelMember): boolean {
if (member.forbiddenExpirationTime === 0) {
return false;
}
const now = Math.floor(Date.now() / 1000);
return member.forbiddenExpirationTime > now;
}
// Check if member is blacklisted
static isBlacklisted(member: WKChannelMember): boolean {
return member.status === 2; // 2 indicates blacklist status
}
// Get member statistics
static getMemberStats(channelId: string, channelType: number): {
total: number;
admins: number;
normal: number;
muted: number;
online: number;
} {
const allMembers = this.getAllMembers(channelId, channelType);
return {
total: allMembers.length,
admins: allMembers.filter(m => this.isAdmin(m)).length,
normal: allMembers.filter(m => !this.isAdmin(m)).length,
muted: allMembers.filter(m => this.isMuted(m)).length,
online: this.getOnlineMembers(channelId, channelType).length,
};
}
}
Event Listening
Listen for channel member refresh
Copy
refreshChannelMemberListener = (members: WKChannelMember[]) => {
// Refresh
};
// Add channel member refresh listener
WKIM.shared.channelMemberManager().addRefreshListener(this.refreshChannelMemberListener);
// Remove listener when exiting page
WKIM.shared.channelMemberManager().removeRefreshListener(this.refreshChannelMemberListener);
Complete event listening management
Copy
interface ChannelMemberUpdateEvent {
members: WKChannelMember[];
channelId: string;
channelType: number;
timestamp: number;
}
class ChannelMemberListener {
private static refreshListeners: Set<(members: WKChannelMember[]) => void> = new Set();
private static updateCallbacks: Array<(event: ChannelMemberUpdateEvent) => void> = [];
// Add member refresh listener
static addRefreshMemberListener(callback: (members: WKChannelMember[]) => void): void {
this.refreshListeners.add(callback);
WKIM.shared.channelMemberManager().addRefreshListener((members: WKChannelMember[]) => {
// Call callback
callback(members);
// Send to update callbacks
if (members.length > 0) {
const event: ChannelMemberUpdateEvent = {
members,
channelId: members[0].channelId,
channelType: members[0].channelType,
timestamp: Date.now()
};
this.updateCallbacks.forEach(cb => cb(event));
}
console.log('Channel members updated:', members.length);
});
}
// Remove member refresh listener
static removeRefreshMemberListener(callback: (members: WKChannelMember[]) => void): void {
this.refreshListeners.delete(callback);
WKIM.shared.channelMemberManager().removeRefreshListener(callback);
}
// Add member update callback
static addMemberUpdateCallback(callback: (event: ChannelMemberUpdateEvent) => void): void {
this.updateCallbacks.push(callback);
}
// Remove member update callback
static removeMemberUpdateCallback(callback: (event: ChannelMemberUpdateEvent) => void): void {
const index = this.updateCallbacks.indexOf(callback);
if (index > -1) {
this.updateCallbacks.splice(index, 1);
}
}
// Remove all listeners
static removeAllListeners(): void {
this.refreshListeners.forEach(callback => {
WKIM.shared.channelMemberManager().removeRefreshListener(callback);
});
this.refreshListeners.clear();
this.updateCallbacks = [];
}
// Add listener for specific channel
static addChannelMemberListener(channelId: string, channelType: number, callback: (members: WKChannelMember[]) => void): void {
const wrappedCallback = (members: WKChannelMember[]) => {
// Filter members of current channel
const channelMembers = members.filter(member =>
member.channelId === channelId && member.channelType === channelType
);
if (channelMembers.length > 0) {
callback(channelMembers);
}
};
this.addRefreshMemberListener(wrappedCallback);
}
// Dispose
static dispose(): void {
this.removeAllListeners();
}
}
Data Structure Description
WKChannelMember Channel Member Object
Copy
export class WKChannelMember {
channelId: string = ''; // Channel ID
channelType: number = WKChannelType.personal; // Channel type
memberUID: string = ''; // Member UID
memberName: string = ''; // Member name
memberRemark: string = ''; // Member remark
memberAvatar: string = ''; // Member avatar
role = 0; // Member role
status = 0; // Member status
isDeleted = 0; // Whether deleted
createdAt: string = ''; // Creation time
updatedAt: string = ''; // Update time
version = 0; // Version number
robot = 0; // Whether robot
extra?: Record<string, object>; // Extended fields
memberInviteUID: string = ''; // Inviter UID
forbiddenExpirationTime = 0; // Mute expiration time
memberAvatarCacheKey: string = ''; // Avatar cache key
}
Field Description
| Field | Type | Description |
|---|---|---|
channelId | string | Channel ID |
channelType | number | Channel type |
memberUID | string | Member UID |
memberName | string | Member name |
memberRemark | string | Member remark |
memberAvatar | string | Member avatar URL |
role | number | Member role (0=normal member, 1=admin, 2=owner) |
status | number | Member status (1=normal, 2=blacklist) |
isDeleted | number | Whether deleted (0=no, 1=yes) |
version | number | Version number |
robot | number | Whether robot (0=no, 1=yes) |
forbiddenExpirationTime | number | Mute expiration timestamp |
Member Role Description
| Role Value | Description |
|---|---|
0 | Normal member |
1 | Admin |
2 | Owner |
Member Status Description
| Status Value | Description |
|---|---|
1 | Normal |
2 | Blacklist |
HarmonyOS Component Integration Example
Member List Component
Copy
@Component
export struct ChannelMemberListComponent {
@State private members: WKChannelMember[] = [];
@State private filteredMembers: WKChannelMember[] = [];
@State private loading: boolean = true;
@State private searchKeyword: string = '';
private channelId: string;
private channelType: number;
private refreshListener?: (members: WKChannelMember[]) => void;
constructor(channelId: string, channelType: number) {
this.channelId = channelId;
this.channelType = channelType;
}
aboutToAppear(): void {
this.loadMembers();
this.setupListener();
}
aboutToDisappear(): void {
if (this.refreshListener) {
ChannelMemberListener.removeRefreshMemberListener(this.refreshListener);
}
}
private loadMembers(): void {
this.loading = true;
const members = ChannelMemberManager.getAllMembers(this.channelId, this.channelType);
this.members = members;
this.filteredMembers = members;
this.loading = false;
}
private setupListener(): void {
this.refreshListener = (members: WKChannelMember[]) => {
// Filter members of current channel
const channelMembers = members.filter(member =>
member.channelId === this.channelId && member.channelType === this.channelType
);
if (channelMembers.length > 0) {
this.loadMembers();
}
};
ChannelMemberListener.addRefreshMemberListener(this.refreshListener);
}
private searchMembers(keyword: string): void {
this.searchKeyword = keyword;
if (!keyword.trim()) {
this.filteredMembers = this.members;
} else {
this.filteredMembers = this.members.filter(member => {
const name = member.memberName.toLowerCase();
const remark = member.memberRemark.toLowerCase();
const uid = member.memberUID.toLowerCase();
const searchKey = keyword.toLowerCase();
return name.includes(searchKey) ||
remark.includes(searchKey) ||
uid.includes(searchKey);
});
}
}
build() {
Column() {
// Search bar
Row() {
TextInput({ placeholder: 'Search members' })
.layoutWeight(1)
.onChange((value: string) => {
this.searchMembers(value);
})
Button('Search')
.margin({ left: 8 })
}
.padding(16)
// Member statistics
if (!this.loading) {
Row() {
Text(`Total ${this.filteredMembers.length} members`)
.fontSize(14)
.fontColor(Color.Grey)
}
.padding({ left: 16, right: 16, bottom: 8 })
}
// Member list
if (this.loading) {
Column() {
LoadingProgress()
.width(40)
.height(40)
Text('Loading...')
.margin({ top: 8 })
}
.justifyContent(FlexAlign.Center)
.layoutWeight(1)
} else if (this.filteredMembers.length === 0) {
Column() {
Image($r('app.media.empty_member'))
.width(64)
.height(64)
.margin({ bottom: 16 })
Text('No members')
.fontSize(16)
.fontColor(Color.Grey)
}
.justifyContent(FlexAlign.Center)
.layoutWeight(1)
} else {
List() {
ForEach(this.filteredMembers, (member: WKChannelMember) => {
ListItem() {
this.buildMemberItem(member)
}
})
}
.layoutWeight(1)
}
}
.width('100%')
.height('100%')
}
@Builder
private buildMemberItem(member: WKChannelMember) {
Row() {
// Avatar
Image(member.memberAvatar || this.getDefaultAvatar(member.memberUID))
.width(50)
.height(50)
.borderRadius(25)
.margin({ right: 12 })
Column() {
// Member name
Text(ChannelMemberManager.getMemberDisplayName(member))
.fontSize(16)
.fontWeight(FontWeight.Medium)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
// Member ID
Text(`ID: ${member.memberUID}`)
.fontSize(12)
.fontColor(Color.Grey)
.margin({ top: 2 })
// Status tags
Row() {
if (ChannelMemberManager.isAdmin(member)) {
this.buildStatusTag('Admin', Color.Orange)
}
if (member.robot === 1) {
this.buildStatusTag('Bot', Color.Blue)
}
if (ChannelMemberManager.isMuted(member)) {
this.buildStatusTag('Muted', Color.Red)
}
}
.margin({ top: 4 })
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
// Online status
Circle({ width: 8, height: 8 })
.fill(this.isOnline(member) ? Color.Green : Color.Grey)
.margin({ left: 8 })
}
.width('100%')
.padding(12)
.alignItems(VerticalAlign.Top)
.onClick(() => {
this.showMemberDetails(member);
})
}
@Builder
private buildStatusTag(text: string, color: Color) {
Text(text)
.fontSize(10)
.fontColor(Color.White)
.backgroundColor(color)
.padding({ left: 4, right: 4, top: 2, bottom: 2 })
.borderRadius(4)
.margin({ right: 4 })
}
private getDefaultAvatar(uid: string): string {
return `https://ui-avatars.com/api/?name=${uid}&background=random`;
}
private isOnline(member: WKChannelMember): boolean {
const channel = WKIM.shared.channelManager().getChannel(member.memberUID, WKChannelType.personal);
return channel?.online === 1;
}
private showMemberDetails(member: WKChannelMember): void {
// Show member details
console.log('Show member details:', member.memberUID);
}
}

