Skip to content

Commit

Permalink
feat: add option for custom paginator
Browse files Browse the repository at this point in the history
  • Loading branch information
szuperaz committed Nov 21, 2023
1 parent c655e6e commit cf73e5d
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 11 deletions.
40 changes: 37 additions & 3 deletions projects/stream-chat-angular/src/lib/channel.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,36 @@ describe('ChannelService', () => {
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
});

it('should set pagination options correctly if #customPaginator is provided', async () => {
service.customPaginator = (
channelQueryResult: Channel<DefaultStreamChatGenerics>[]
) => {
const lastChannel = channelQueryResult[channelQueryResult.length - 1];
if (!lastChannel) {
return {
type: 'filter',
paginationFilter: {},
};
} else {
return {
type: 'filter',
paginationFilter: {
cid: { $gte: lastChannel.cid },
},
};
}
};

await init();

expect(service['nextPageConfiguration']).toEqual({
type: 'filter',
paginationFilter: {
cid: { $gte: jasmine.any(String) },
},
});
});

it('should not set active channel if #shouldSetActiveChannel is false', async () => {
const activeChannelSpy = jasmine.createSpy();
service.activeChannel$.subscribe(activeChannelSpy);
Expand Down Expand Up @@ -375,7 +405,10 @@ describe('ChannelService', () => {
await init();

// Check that offset is set properly after query
expect(service['options']?.offset).toEqual(service.channels.length);
expect(service['nextPageConfiguration']).toEqual({
type: 'offset',
offset: service.channels.length,
});

mockChatClient.queryChannels.calls.reset();
const existingChannel = service.channels[0];
Expand All @@ -390,6 +423,7 @@ describe('ChannelService', () => {
jasmine.any(Object),
jasmine.any(Object)
);

expect(service.channels.length).toEqual(prevChannelCount + 1);
});

Expand Down Expand Up @@ -1952,14 +1986,14 @@ describe('ChannelService', () => {
});

it('should reset pagination options after reconnect', async () => {
await init(undefined, undefined, { offset: 20 });
await init(undefined, undefined, { limit: 20 });
mockChatClient.queryChannels.calls.reset();
events$.next({ eventType: 'connection.recovered' } as ClientEvent);

expect(mockChatClient.queryChannels).toHaveBeenCalledWith(
jasmine.any(Object),
jasmine.any(Object),
jasmine.objectContaining({ offset: 0 })
{ limit: 20, state: true, presence: true, watch: true, message_limit: 25 }
);
});

Expand Down
59 changes: 51 additions & 8 deletions projects/stream-chat-angular/src/lib/channel.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
DefaultStreamChatGenerics,
MessageInput,
MessageReactionType,
NextPageConfiguration,
StreamMessage,
} from './types';

Expand Down Expand Up @@ -293,6 +294,10 @@ export class ChannelService<
beforeUpdateMessage?: (
message: StreamMessage<T>
) => StreamMessage<T> | Promise<StreamMessage<T>>;
/**
* By default the SDK uses an offset based pagination, you can change/extend this by providing your own custom paginator method. It will be called with the result of the latest channel query.
*/
customPaginator?: (channelQueryResult: Channel<T>[]) => NextPageConfiguration;
private channelsSubject = new BehaviorSubject<Channel<T>[] | undefined>(
undefined
);
Expand Down Expand Up @@ -374,6 +379,7 @@ export class ChannelService<
this.activeParentMessageIdSubject.next(message?.id);
};
private dismissErrorNotification?: Function;
private nextPageConfiguration?: NextPageConfiguration;

constructor(
private chatClientService: ChatClientService<T>,
Expand Down Expand Up @@ -650,15 +656,14 @@ export class ChannelService<
) {
this.filters = filters;
this.options = {
offset: 0,
limit: 25,
state: true,
presence: true,
watch: true,
message_limit: this.messagePageSize,
...options,
};
this.sort = sort || { last_message_at: -1, updated_at: -1 };
this.sort = sort || { last_message_at: -1 };
this.shouldSetActiveChannel = shouldSetActiveChannel;
this.clientEventsSubscription = this.chatClientService.events$.subscribe(
(notification) => void this.handleNotification(notification)
Expand Down Expand Up @@ -1107,9 +1112,7 @@ export class ChannelService<
}
this.isStateRecoveryInProgress = true;
try {
if (this.options) {
this.options.offset = 0;
}
this.nextPageConfiguration = undefined;
// If channel list is not inited, we set the active channel
const shoulSetActiveChannel =
this.shouldSetActiveChannel &&
Expand Down Expand Up @@ -1377,10 +1380,16 @@ export class ChannelService<
await activeChannel?.stopTyping(parentId);
}

/**
* The current list of channels
*/
get channels() {
return this.channelsSubject.getValue() || [];
}

/**
* The current active channel
*/
get activeChannel() {
return this.activeChannelSubject.getValue() || undefined;
}
Expand Down Expand Up @@ -1480,12 +1489,32 @@ export class ChannelService<
) {
try {
this.channelQueryStateSubject.next({ state: 'in-progress' });
let filters: ChannelFilters<T>;
let options: ChannelOptions;
if (this.nextPageConfiguration) {
if (this.nextPageConfiguration.type === 'filter') {
filters = {
...this.filters!,
...this.nextPageConfiguration.paginationFilter,
};
options = this.options as ChannelOptions;
} else {
options = {
...this.options,
offset: this.nextPageConfiguration.offset,
};
filters = this.filters!;
}
} else {
filters = this.filters!;
options = this.options as ChannelOptions;
}
const channels = await this.chatClientService.chatClient.queryChannels(
this.filters!,
filters,
this.sort || {},
this.options
options
);
this.options!.offset = channels.length!;
this.setNextPageConfiguration(channels);
channels.forEach((c) => this.watchForChannelEvents(c));
const prevChannels = recoverState
? []
Expand Down Expand Up @@ -1898,4 +1927,18 @@ export class ChannelService<
void channel.markRead();
}
}

private setNextPageConfiguration(channelQueryResult: Channel<T>[]) {
if (this.customPaginator) {
this.nextPageConfiguration = this.customPaginator(channelQueryResult);
} else {
this.nextPageConfiguration = {
type: 'offset',
offset:
(this.nextPageConfiguration?.type === 'offset'
? this.nextPageConfiguration.offset
: 0) + channelQueryResult.length,
};
}
}
}
17 changes: 17 additions & 0 deletions projects/stream-chat-angular/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Observable, Subject } from 'rxjs';
import type {
Attachment,
Channel,
ChannelFilters,
ChannelMemberResponse,
CommandResponse,
Event,
Expand Down Expand Up @@ -360,3 +361,19 @@ export type MessageInput<
quotedMessageId: string | undefined;
customData: undefined | Partial<T['messageType']>;
};

export type OffsetNextPageConfiguration = {
type: 'offset';
offset: number;
};

export type FiltertNextPageConfiguration<
T extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
> = {
type: 'filter';
paginationFilter: ChannelFilters<T>;
};

export type NextPageConfiguration =
| OffsetNextPageConfiguration
| FiltertNextPageConfiguration;

0 comments on commit cf73e5d

Please sign in to comment.