Skip to content

Commit f937438

Browse files
Initial commit
1 parent 6887cd2 commit f937438

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,6 @@ export {
5151
localMessageToNewMessagePayload,
5252
formatMessage,
5353
promoteChannel,
54+
sortChannels,
5455
} from './utils';
5556
export { FixedSizeQueueCache } from './utils/FixedSizeQueueCache';

src/utils.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,3 +1232,98 @@ export const runDetached = <T>(
12321232

12331233
promise.catch(onError);
12341234
};
1235+
1236+
export const sortChannels = (array: Channel[], criteria: ChannelSort) => {
1237+
const temporaryCache: Record<string, ReturnType<typeof mapToSortable>> = {};
1238+
1239+
// format criteria to always be an iterable array
1240+
let arrayCriteria: ChannelSortBase[];
1241+
if (!Array.isArray(criteria)) {
1242+
const remappedCriteria: ChannelSortBase[] = [];
1243+
for (let key in criteria) {
1244+
const typeSafeKey = key as keyof ChannelSortBase;
1245+
remappedCriteria.push({ [typeSafeKey]: criteria[typeSafeKey] });
1246+
}
1247+
arrayCriteria = remappedCriteria;
1248+
} else {
1249+
arrayCriteria = criteria;
1250+
}
1251+
1252+
const getSortable = (c: Channel) => {
1253+
if (!temporaryCache[c.cid]) {
1254+
temporaryCache[c.cid] = mapToSortable(c);
1255+
}
1256+
1257+
return temporaryCache[c.cid];
1258+
};
1259+
1260+
const arrayCopy = [...array];
1261+
1262+
arrayCopy.sort((a, b) => {
1263+
for (const criterion of arrayCriteria) {
1264+
const [[key, order]] = Object.entries(criterion);
1265+
1266+
const typeSafeKey = key as keyof ChannelSortBase;
1267+
const sortableA = getSortable(a);
1268+
const sortableB = getSortable(b);
1269+
1270+
const aValue = sortableA[typeSafeKey];
1271+
const bValue = sortableB[typeSafeKey];
1272+
1273+
if (aValue === null || bValue === null) {
1274+
if (aValue === null && bValue !== null) return 1;
1275+
if (aValue !== null && bValue === null) return -1;
1276+
1277+
continue;
1278+
}
1279+
1280+
// ascending
1281+
if (order === 1) {
1282+
if (aValue > bValue) return 1;
1283+
if (aValue < bValue) return -1;
1284+
}
1285+
1286+
// descending
1287+
if (order === -1) {
1288+
if (aValue > bValue) return -1;
1289+
if (aValue < bValue) return 1;
1290+
}
1291+
}
1292+
1293+
return 0;
1294+
});
1295+
1296+
return arrayCopy;
1297+
};
1298+
1299+
/**
1300+
* Certain criteria properties rely on data which live across Channel.
1301+
* For example property `pinned_at` maps to `Channel.membership.pinned_at` that's
1302+
* why we need a simpler mapped object upon which we can apply criteria.
1303+
* Date objects are mapped to integers through `getTime`.
1304+
*/
1305+
const mapToSortable = (channel: Channel) => {
1306+
return {
1307+
pinned_at:
1308+
typeof channel.state.membership.pinned_at === 'string'
1309+
? new Date(channel.state.membership.pinned_at).getTime()
1310+
: null,
1311+
created_at:
1312+
typeof channel.data?.created_at === 'string'
1313+
? new Date(channel.data.created_at).getTime()
1314+
: null,
1315+
has_unread: channel.countUnread() > 0 ? 1 : 0,
1316+
last_message_at: channel.state.last_message_at?.getTime() ?? null,
1317+
updated_at:
1318+
typeof channel.data?.updated_at === 'string'
1319+
? new Date(channel.data?.updated_at).getTime()
1320+
: null,
1321+
member_count: channel.data?.member_count ?? null,
1322+
unread_count: channel.countUnread(),
1323+
last_updated: null, // not sure what to map this one to
1324+
} satisfies Record<keyof ChannelSortBase, number | null>;
1325+
};
1326+
1327+
const mapToFilterable = (channel: Channel) => {
1328+
// TODO: https://github.com/GetStream/stream-video-js/blob/main/packages/react-sdk/src/utilities/filter.ts
1329+
};

0 commit comments

Comments
 (0)