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