Skip to content

Commit 843dfbb

Browse files
Initial commit
1 parent e34bfc5 commit 843dfbb

File tree

2 files changed

+91
-1
lines changed

2 files changed

+91
-1
lines changed

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ export * from './token_manager';
2222
export * from './types';
2323
export * from './channel_manager';
2424
export * from './custom_types';
25-
export { isOwnUser, chatCodes, logChatPromiseExecution, formatMessage, promoteChannel } from './utils';
25+
export { isOwnUser, chatCodes, logChatPromiseExecution, formatMessage, promoteChannel, sortChannels } from './utils';

src/utils.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,3 +1089,93 @@ export const promoteChannel = ({
10891089

10901090
return newChannels;
10911091
};
1092+
1093+
1094+
export const sortChannels = (array: Channel[], criteria: ChannelSort) => {
1095+
const temporaryCache: Record<string, ReturnType<typeof mapToSortable>> = {};
1096+
1097+
// format criteria to always be an iterable array
1098+
let arrayCriteria: ChannelSortBase[];
1099+
if (!Array.isArray(criteria)) {
1100+
const remappedCriteria: ChannelSortBase[] = [];
1101+
for (let key in criteria) {
1102+
const typeSafeKey = key as keyof ChannelSortBase;
1103+
remappedCriteria.push({ [typeSafeKey]: criteria[typeSafeKey] });
1104+
}
1105+
arrayCriteria = remappedCriteria;
1106+
} else {
1107+
arrayCriteria = criteria;
1108+
}
1109+
1110+
const getSortable = (c: Channel) => {
1111+
if (!temporaryCache[c.cid]) {
1112+
temporaryCache[c.cid] = mapToSortable(c);
1113+
}
1114+
1115+
return temporaryCache[c.cid];
1116+
};
1117+
1118+
const arrayCopy = [...array];
1119+
1120+
arrayCopy.sort((a, b) => {
1121+
for (const criterion of arrayCriteria) {
1122+
const [[key, order]] = Object.entries(criterion);
1123+
1124+
const typeSafeKey = key as keyof ChannelSortBase;
1125+
const sortableA = getSortable(a);
1126+
const sortableB = getSortable(b);
1127+
1128+
const aValue = sortableA[typeSafeKey];
1129+
const bValue = sortableB[typeSafeKey];
1130+
1131+
if (aValue === null || bValue === null) {
1132+
if (aValue === null && bValue !== null) return 1;
1133+
if (aValue !== null && bValue === null) return -1;
1134+
1135+
continue;
1136+
}
1137+
1138+
// ascending
1139+
if (order === 1) {
1140+
if (aValue > bValue) return 1;
1141+
if (aValue < bValue) return -1;
1142+
}
1143+
1144+
// descending
1145+
if (order === -1) {
1146+
if (aValue > bValue) return -1;
1147+
if (aValue < bValue) return 1;
1148+
}
1149+
}
1150+
1151+
return 0;
1152+
});
1153+
1154+
return arrayCopy;
1155+
};
1156+
1157+
/**
1158+
* Certain criteria properties rely on data which live across Channel.
1159+
* For example property `pinned_at` maps to `Channel.membership.pinned_at` that's
1160+
* why we need a simpler mapped object upon which we can apply criteria.
1161+
* Date objects are mapped to integers through `getTime`.
1162+
*/
1163+
const mapToSortable = (channel: Channel) => {
1164+
return {
1165+
pinned_at:
1166+
typeof channel.state.membership.pinned_at === 'string'
1167+
? new Date(channel.state.membership.pinned_at).getTime()
1168+
: null,
1169+
created_at: typeof channel.data?.created_at === 'string' ? new Date(channel.data.created_at).getTime() : null,
1170+
has_unread: channel.countUnread() > 0 ? 1 : 0,
1171+
last_message_at: channel.state.last_message_at?.getTime() ?? null,
1172+
updated_at: typeof channel.data?.updated_at === 'string' ? new Date(channel.data?.updated_at).getTime() : null,
1173+
member_count: channel.data?.member_count ?? null,
1174+
unread_count: channel.countUnread(),
1175+
last_updated: null, // not sure what to map this one to
1176+
} satisfies Record<keyof ChannelSortBase, number | null>;
1177+
};
1178+
1179+
const mapToFilterable = (channel: Channel) => {
1180+
// TODO: https://github.com/GetStream/stream-video-js/blob/main/packages/react-sdk/src/utilities/filter.ts
1181+
};

0 commit comments

Comments
 (0)