Skip to main content
Channel member management provides comprehensive group member management capabilities, including member information retrieval, member status listening, permission management, and other core features.
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

// Get all members under a specific channel
let members = WKIM.shared.channelMemberManager().getMembers(channelId, channelType);

Paginated query of channel members

// Get channel members with pagination
WKIM.shared.channelMemberManager().getWithPageOrSearch(channelId, channelType, option);
option description:
export class SyncChannelMemberOptions {
  searchKey: string = '';  // Search keyword
  page: number = 0;        // Page number
  limit: number = 20;      // Items per page
}

Complete member retrieval example

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

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

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

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

FieldTypeDescription
channelIdstringChannel ID
channelTypenumberChannel type
memberUIDstringMember UID
memberNamestringMember name
memberRemarkstringMember remark
memberAvatarstringMember avatar URL
rolenumberMember role (0=normal member, 1=admin, 2=owner)
statusnumberMember status (1=normal, 2=blacklist)
isDeletednumberWhether deleted (0=no, 1=yes)
versionnumberVersion number
robotnumberWhether robot (0=no, 1=yes)
forbiddenExpirationTimenumberMute expiration timestamp

Member Role Description

Role ValueDescription
0Normal member
1Admin
2Owner

Member Status Description

Status ValueDescription
1Normal
2Blacklist

HarmonyOS Component Integration Example

Member List Component

@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);
  }
}

Next Steps