Skip to content

Commit

Permalink
Add my starred classes
Browse files Browse the repository at this point in the history
  • Loading branch information
snowmonkey18 committed Jan 22, 2025
1 parent 1bd32e8 commit da69eff
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 18 deletions.
1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 52 additions & 17 deletions src/components/ClassTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { Box, Group, Flex, Image, Input } from "@chakra-ui/react";

Check failure on line 9 in src/components/ClassTable.tsx

View workflow job for this annotation

GitHub Actions / Build

'Image' is defined but never used. Allowed unused vars must match /^_/u
import React, { useEffect, useMemo, useRef, useState } from "react";

import { LuPlus, LuMinus, LuSearch } from "react-icons/lu";
import { LuPlus, LuMinus, LuSearch, LuStar } from "react-icons/lu";

import { InputGroup } from "./ui/input-group";
import { Button, LabelledButton } from "./ui/button";

Check failure on line 15 in src/components/ClassTable.tsx

View workflow job for this annotation

GitHub Actions / Build

'LabelledButton' is defined but never used. Allowed unused vars must match /^_/u
Expand Down Expand Up @@ -201,10 +201,11 @@ function ClassInput(props: {
);
}

type FilterGroup = Array<[keyof Flags | "fits", string, string?]>;
type FilterGroup = Array<[keyof Flags | "fits" | "starred", string, React.ReactNode?]>;

/** List of top filter IDs and their displayed names. */
const CLASS_FLAGS_1: FilterGroup = [
["starred", "My starred", <LuStar fill="currentColor" />],
["hass", "HASS"],
["cih", "CI-H"],
["fits", "Fits schedule"],
Expand Down Expand Up @@ -245,7 +246,7 @@ function ClassFlags(props: {
const { setFlagsFilter, state, updateFilter } = props;

// Map from flag to whether it's on.
const [flags, setFlags] = useState<Map<keyof Flags | "fits", boolean>>(() => {
const [flags, setFlags] = useState<Map<keyof Flags | "fits" | "starred", boolean>>(() => {
const result = new Map();
for (const flag of CLASS_FLAGS) {
result.set(flag, false);
Expand All @@ -262,7 +263,7 @@ function ClassFlags(props: {
state.fitsScheduleCallback = () => flags.get("fits") && updateFilter();
}, [state, flags, updateFilter]);

const onChange = (flag: keyof Flags | "fits", value: boolean) => {
const onChange = (flag: keyof Flags | "fits" | "starred", value: boolean) => {
const newFlags = new Map(flags);
newFlags.set(flag, value);
setFlags(newFlags);
Expand All @@ -275,7 +276,9 @@ function ClassFlags(props: {
newFlags.forEach((value, flag) => {
if (value && flag === "fits" && !state.fitsSchedule(cls)) {
result = false;
} else if (value && flag !== "fits" && !cls.flags[flag]) {
} else if (value && flag === "starred" && !state.isClassStarred(cls.number)) {
result = false;
} else if (value && flag !== "fits" && flag !== "starred" && !cls.flags[flag]) {
result = false;
}
});
Expand All @@ -291,23 +294,14 @@ function ClassFlags(props: {
{group.map(([flag, label, image]) => {
const checked = flags.get(flag);
return image ? (
<LabelledButton
<Button
key={flag}
onClick={() => onChange(flag, !checked)}
title={label}
variant={checked ? "solid" : "outline"}
portalled
>
<Image
src={image}
alt={label}
filter={
colorMode === "dark" && DARK_IMAGES.includes(flag ?? "")
? "invert()"
: ""
}
/>
</LabelledButton>
{image}
</Button>
) : (
<Button
key={flag}
Expand Down Expand Up @@ -342,6 +336,28 @@ function ClassFlags(props: {
);
}

const StarButton = ({ classNumber, state, onStarToggle }: {
classNumber: string;
state: State;
onStarToggle?: () => void;
}) => {
const isStarred = state.isClassStarred(classNumber);
return (
<Button
onClick={(e) => {
e.stopPropagation();
state.toggleStarClass(classNumber);
onStarToggle?.();
}}
variant="ghost"
size="sm"
aria-label={isStarred ? "Unstar class" : "Star class"}
>
<LuStar fill={isStarred ? "currentColor" : "none"} />
</Button>
);
};

/** The table of all classes, along with searching and filtering with flags. */
export function ClassTable(props: {
classes: Map<string, Class>;
Expand Down Expand Up @@ -373,6 +389,25 @@ export function ClassTable(props: {
...sortProps,
};
return [
{
headerName: "",
field: "number",
maxWidth: 49,
cellRenderer: (params: any) => (

Check failure on line 396 in src/components/ClassTable.tsx

View workflow job for this annotation

GitHub Actions / Build

Unexpected any. Specify a different type
<StarButton
classNumber={params.value}
state={state}
onStarToggle={() => {
gridRef.current?.api?.refreshCells({
force: true,
columns: ["number"],
});
}}
/>
),
sortable: false,
cellStyle: { padding: 0 },
},
{
field: "number",
headerName: "Class",
Expand Down
31 changes: 31 additions & 0 deletions src/lib/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export class State {
conflicts: number = 0;
/** Browser-specific saved state. */
store: Store;
/** Set of starred class numbers */
private starredClasses: Set<string> = new Set();

// The following are React state, so should be private. Even if we pass the
// State object to React components, they shouldn't be looking at these
Expand Down Expand Up @@ -68,6 +70,12 @@ export class State {
this.classes.set(number, new Class(cls, this.colorScheme));
});
this.initState();

// Load starred classes from storage
const storedStarred = this.store.get("starredClasses");
if (storedStarred) {
this.starredClasses = new Set(storedStarred);
}
}

/** All activities. */
Expand Down Expand Up @@ -401,4 +409,27 @@ export class State {
this.loadSave(this.saves[0]!.id);
}
}

/** Star or unstar a class */
toggleStarClass(classNumber: string): void {
if (this.starredClasses.has(classNumber)) {
this.starredClasses.delete(classNumber);
} else {
this.starredClasses.add(classNumber);
}
this.store.set("starredClasses", Array.from(this.starredClasses));
this.updateState();
}

/** Check if a class is starred */
isClassStarred(classNumber: string): boolean {
return this.starredClasses.has(classNumber);
}

/** Get all starred classes */
getStarredClasses(): Class[] {
return Array.from(this.starredClasses)
.map(number => this.classes.get(number))
.filter((cls): cls is Class => cls !== undefined);
}
}
1 change: 1 addition & 0 deletions src/lib/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Preferences, Save } from "./schema";

export type TermStore = {
saves: Save[];
starredClasses: string[]; // Array of class numbers that are starred
[saveId: string]: unknown[];
};

Expand Down

0 comments on commit da69eff

Please sign in to comment.