Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 521x 521x 521x 521x 521x 1012x 1012x 20x 1012x 1012x 991x 21x 21x 521x 1019x 1012x 7x 15x 18x 2x 16x 3x 13x 2x 11x 2x 9x 1x 8x 521x 4069x 4069x 4434x 567x 3867x 4069x | import { Dayjs } from "dayjs";
import {
AgeGroup,
Language,
ProgramItem,
Tag,
} from "shared/types/models/programItem";
import { isPreConventionWeekProgramItem } from "shared/utils/isPreConventionWeekProgramItem";
import {
getUpcomingProgramItems,
isMainEventProgramVisible,
} from "client/utils/getUpcomingProgramItems";
export enum StartingTimeOption {
UPCOMING = "upcoming",
ALL = "all",
REVOLVING_DOOR = "revolvingDoor",
}
export const getVisibleProgramItems = (
programItems: readonly ProgramItem[],
selectedView: StartingTimeOption,
selectedTags: readonly string[],
hideFull: boolean,
fullProgramItemIds: ReadonlySet<string>,
timeNow: Dayjs,
): readonly ProgramItem[] => {
const tagFilteredProgramItems = getTagFilteredProgramItems(
programItems,
selectedTags,
);
const fullnessFiltered = hideFull
? tagFilteredProgramItems.filter(
(item) => !fullProgramItemIds.has(item.programItemId),
)
: tagFilteredProgramItems;
// Before the main event program is visible, only show pre-convention week program.
// After it is visible, pre-convention week program is in the past so it drops out of
// the upcoming list on its own
const phaseFilteredProgramItems = isMainEventProgramVisible(timeNow)
? fullnessFiltered
: fullnessFiltered.filter((programItem) =>
isPreConventionWeekProgramItem(programItem),
);
if (selectedView === StartingTimeOption.UPCOMING) {
return getUpcomingProgramItems(phaseFilteredProgramItems, timeNow);
} else Iif (selectedView === StartingTimeOption.REVOLVING_DOOR) {
return getUpcomingProgramItems(phaseFilteredProgramItems, timeNow).filter(
(programItem) => programItem.revolvingDoor,
);
}
return phaseFilteredProgramItems;
};
export const getTagFilteredProgramItems = (
programItems: readonly ProgramItem[],
selectedTags: readonly string[],
): readonly ProgramItem[] => {
if (selectedTags.length === 0) {
return programItems;
}
// Show items matching all of the selected tags (AND)
return programItems.filter((programItem) =>
selectedTags.every((selectedTag) => {
if (programItem.programType.includes(selectedTag)) {
return true;
}
if (programItem.tags.includes(selectedTag as Tag)) {
return true;
}
if (programItem.ageGroups.includes(selectedTag as AgeGroup)) {
return true;
}
if (programItem.languages.includes(selectedTag as Language)) {
return true;
}
if (
programItem.languages.includes(Language.LANGUAGE_FREE) &&
Object.values(Language).includes(selectedTag as Language)
) {
return true;
}
return false;
}),
);
};
// The header of the group currently at (or scrolled past) the top of the visible
// range — pinned to the top while its group scrolls. `stickyHeaderIndexes` is
// ascending, so the last one at or before `rangeStartIndex` is the active header
export const getActiveStickyHeaderIndex = (
stickyHeaderIndexes: readonly number[],
rangeStartIndex: number,
): number => {
let activeIndex = 0;
for (const headerIndex of stickyHeaderIndexes) {
if (headerIndex > rangeStartIndex) {
break;
}
activeIndex = headerIndex;
}
return activeIndex;
};
|