@@ -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