From f2a7d89c170222de59cc6042763284ff9a6ef348 Mon Sep 17 00:00:00 2001 From: Kaspar Poland Date: Wed, 13 Jul 2022 11:58:59 -0400 Subject: [PATCH 1/9] Finish UI --- components/sidebar/CourseView.tsx | 180 +++++++++++++----- .../sidebar/SearchView/SearchView.test.tsx | 13 +- components/sidebar/SearchView/SearchView.tsx | 28 ++- components/sidebar/Sidebar.tsx | 8 +- src/Course.ts | 4 +- src/useCourses.tsx | 2 +- src/utils/course.ts | 22 +++ 7 files changed, 192 insertions(+), 65 deletions(-) diff --git a/components/sidebar/CourseView.tsx b/components/sidebar/CourseView.tsx index 4d472ff..994e71d 100644 --- a/components/sidebar/CourseView.tsx +++ b/components/sidebar/CourseView.tsx @@ -23,13 +23,9 @@ import { Tooltip, useClipboard, useToast, + VStack, } from "@chakra-ui/react" -import React, { - Fragment, - useEffect, - useRef, - useState, -} from "react" +import React, { Dispatch, Fragment, useEffect, useRef, useState } from "react" import reactStringReplace from "react-string-replace" import { useAppContext } from "../../src/SqrlContext" import { MeetingCategoryType } from "../timetable/Meeting" @@ -44,8 +40,20 @@ import useSections from "../../src/useSections" import { motion } from "framer-motion" import useTimetable from "../../src/useTimetable" import { SearchIcon } from "@chakra-ui/icons" +import { + getCourseLetterFromTerm, + computeSiblingCourseId, +} from "../../src/utils/course" +import { useLazyQuery } from "@apollo/client" +import { CHECK_COURSE_EXISTS } from "../../operations/queries/checkCourseExists" -const CourseView = ({ setSearchQuery }: { setSearchQuery: Function }) => { +const CourseView = ({ + setSearchQuery, + setChosenCourse, +}: { + setSearchQuery: Dispatch> + setChosenCourse: Dispatch> +}) => { const { state: { // courses, @@ -61,9 +69,50 @@ const CourseView = ({ setSearchQuery }: { setSearchQuery: Function }) => { sections, }) const { allowedToEdit } = useTimetable({ id: router.query.id as string }) + const [siblingCourseId, setSiblingCourseId] = useState(null) const course = courses[identifier] + const [checkCourseExists] = useLazyQuery(CHECK_COURSE_EXISTS, { + errorPolicy: "all", + }) + + useEffect(() => { + // Cannot put async callback to useEffect, so wrap + // Check for the sibling course if it exists, and if it does, + // add the ID of that course to the state. If not, it's null + + // Set it to null initially, so that if a sibling course from another course existed, + // say CSC108F and CSC108S, and then the user clicks on CSC304F which has no S sibling, + // the sibling does not erroneously display CSC108S + setSiblingCourseId(null) + + // Run the query + ;(async () => { + if (!course) return + + const siblingCourseId = computeSiblingCourseId(course) + if (siblingCourseId === null) return + + const result = await checkCourseExists({ + variables: { + id: siblingCourseId, + }, + }) + + console.log(result) + console.log(course) + + if (result.error) { + // I don't know how to show errors in sqrl :/ so return for now + return + } else if (result.data.courseById) { + // courseById is null when there is course with matching id + setSiblingCourseId(result.data.courseById.id) + } + })() + }, [course, checkCourseExists]) + // TODO: meetings out of the timetable's display bounds are hidden without warning. Warn them. const boxRef = useRef(null) @@ -97,7 +146,7 @@ const CourseView = ({ setSearchQuery }: { setSearchQuery: Function }) => { if (missing.length !== 0) return toast.close("warn-missing-section") - }, [course, userMeetings, identifier]) + }, [toast, course, userMeetings, identifier]) useEffect(() => { if (!hasCopied) return @@ -108,7 +157,7 @@ const CourseView = ({ setSearchQuery }: { setSearchQuery: Function }) => { duration: 9000, isClosable: true, }) - }, [hasCopied]) + }, [toast, hasCopied]) if (!identifier) { return ( @@ -225,11 +274,7 @@ const CourseView = ({ setSearchQuery }: { setSearchQuery: Function }) => { {suffix} - {(() => { - if (course.term === "FIRST_SEMESTER") return "F" - if (course.term === "SECOND_SEMESTER") return "S" - return "Y" - })()} + {getCourseLetterFromTerm(course)} @@ -316,46 +361,77 @@ const CourseView = ({ setSearchQuery }: { setSearchQuery: Function }) => { )} - - + + )} + - - {t("sidebar:remove")} - + - - + + + {Object.values(MeetingCategoryType).map((method) => ( { - {}} /> + {}} + searchOffset={0} + setSearchOffset={() => {}} + chosenCourse="" + setChosenCourse={() => {}} + /> @@ -29,6 +36,10 @@ describe("Search view", () => { {}} + searchOffset={0} + setSearchOffset={() => {}} + chosenCourse="" + setChosenCourse={() => {}} /> diff --git a/components/sidebar/SearchView/SearchView.tsx b/components/sidebar/SearchView/SearchView.tsx index f4c2c1f..4bd38fc 100644 --- a/components/sidebar/SearchView/SearchView.tsx +++ b/components/sidebar/SearchView/SearchView.tsx @@ -34,20 +34,25 @@ import SearchViewHints from "./SearchViewHints" const MotionFlex = motion(Flex) const MotionButton = motion(Button) +interface SearchViewProps { + searchQuery: string + setSearchQuery: Dispatch> + searchOffset: number + setSearchOffset: Dispatch> + chosenCourse: string + setChosenCourse: Dispatch> +} + const SearchView = ({ searchQuery, setSearchQuery, searchOffset, setSearchOffset, -}: { - searchQuery: string - setSearchQuery: Dispatch> - searchOffset: number - setSearchOffset: Dispatch> -}) => { + chosenCourse, + setChosenCourse, +}: SearchViewProps) => { const searchRef = useRef() as MutableRefObject const [searchLimit, setSearchLimit] = useState(7) - const [chosenCourse, setChosenCourse] = useState("") const [search, { loading, data, error, fetchMore }] = useLazyQuery(SEARCH_COURSES) @@ -69,8 +74,15 @@ const SearchView = ({ }) useEffect(() => { + if (!searchQuery) return + + debouncedZero(searchQuery) + }, []) + + useEffect(() => { + setSearchOffset(0) debounced(searchQuery) - }, [debounced, searchQuery]) + }, [debounced, setSearchOffset, searchQuery]) useEffect(() => { if (searchOffset === 0) return diff --git a/components/sidebar/Sidebar.tsx b/components/sidebar/Sidebar.tsx index 57b1ab4..cd95fb5 100644 --- a/components/sidebar/Sidebar.tsx +++ b/components/sidebar/Sidebar.tsx @@ -48,6 +48,7 @@ const Sidebar = () => { const boxBackground = useColorModeValue("gray.75", "gray.700") const [searchQuery, setSearchQuery] = useState("") const [searchOffset, setSearchOffset] = useState(0) + const [chosenCourse, setChosenCourse] = useState("") const router = useRouter() @@ -150,10 +151,15 @@ const Sidebar = () => { setSearchQuery={setSearchQuery} searchOffset={searchOffset} setSearchOffset={setSearchOffset} + chosenCourse={chosenCourse} + setChosenCourse={setChosenCourse} /> - + diff --git a/src/Course.ts b/src/Course.ts index 4113671..bed8855 100644 --- a/src/Course.ts +++ b/src/Course.ts @@ -111,8 +111,8 @@ export interface Course { prerequisites: string recommendedPreparation: string sections: Array
- term: string - sessionCode: string + term: string // Why is this not a FIRST_SEMESTER, SECOND_SEMESTER, YEAR enum? + sessionCode: string // Why is this not a LEC, PRA, TUT enum? title: string webTimetableInstructions: string | null } diff --git a/src/useCourses.tsx b/src/useCourses.tsx index cedd5ab..d4858fd 100644 --- a/src/useCourses.tsx +++ b/src/useCourses.tsx @@ -39,7 +39,7 @@ const useCourses = ({ sections }: Props) => { ids: coursesToGet, }, }) - }, [sections, sidebarCourse]) + }, [getCoursesById, sections, sidebarCourse]) useEffect(() => { if (!data || !data.coursesById) return diff --git a/src/utils/course.ts b/src/utils/course.ts index dd0da10..b038d1d 100644 --- a/src/utils/course.ts +++ b/src/utils/course.ts @@ -2,6 +2,28 @@ import { MeetingCategoryType } from "../../components/timetable/Meeting" import { Course } from "../Course" import { UserMeeting } from "../SqrlContext" +export const getCourseLetterFromTerm = (course: Course): "F" | "S" | "Y" => { + switch (course.term) { + case "FIRST_SEMESTER": + return "F" + case "SECOND_SEMESTER": + return "S" + default: + return "Y" + } +} + +export const computeSiblingCourseId = (course: Course): string | null => { + // This is SUPER fragile. If the ID of the courses changes at all, like for supporting UTM, this will break! + const { department, numeral, suffix, term, terminal } = + breakdownCourseIdentifier(course.id) + if (term === "Y") return null + + const siblingTerm = term === "S" ? "F" : "S" + const siblingCourseId = `${department}${numeral}${suffix}-${siblingTerm}-${terminal}` + return siblingCourseId +} + export const breakdownCourseCode = (title: string) => { const firstDigitContent = title.match(/\d{3,}/g) let firstDigit = 0 From 8b06fea1ef77884aeccaf4c55d31039d17047c46 Mon Sep 17 00:00:00 2001 From: Kaspar Poland Date: Wed, 13 Jul 2022 11:59:19 -0400 Subject: [PATCH 2/9] Add t() definitions --- public/locales/de/sidebar.json | 2 ++ public/locales/en/sidebar.json | 2 ++ public/locales/fr/sidebar.json | 2 ++ public/locales/zh/sidebar.json | 2 ++ 4 files changed, 8 insertions(+) diff --git a/public/locales/de/sidebar.json b/public/locales/de/sidebar.json index 06222c6..d02ba1c 100644 --- a/public/locales/de/sidebar.json +++ b/public/locales/de/sidebar.json @@ -16,6 +16,8 @@ "second": "Zweite(n)", "year": "Jahr", "help": "Hilfe", + "see-in-second-semester": "Siehe Kurs in den zweiten Semester", + "see-in-first-semester": "Siehe Kurs in den ersten Semester", "remove": "Löschen", "missing-a": "Es fehlt einen", "section": "Sektion", diff --git a/public/locales/en/sidebar.json b/public/locales/en/sidebar.json index 9b2b950..37bfba6 100644 --- a/public/locales/en/sidebar.json +++ b/public/locales/en/sidebar.json @@ -16,6 +16,8 @@ "second": "Second", "year": "Year", "help": "Help", + "see-in-second-semester": "See course in second semester", + "see-in-first-semester": "See course in first semester", "remove": "Remove", "missing-a": "Missing a", "section": "Section", diff --git a/public/locales/fr/sidebar.json b/public/locales/fr/sidebar.json index 1e94a8f..ddd25d6 100644 --- a/public/locales/fr/sidebar.json +++ b/public/locales/fr/sidebar.json @@ -15,6 +15,8 @@ "second": "Second", "year": "Année", "help": "Help", + "see-in-second-semester": "See course in second semester", + "see-in-first-semester": "See course in first semester", "remove": "Remove", "missing-a": "Missing a", "section": "Section", diff --git a/public/locales/zh/sidebar.json b/public/locales/zh/sidebar.json index d862dc0..673fd4a 100644 --- a/public/locales/zh/sidebar.json +++ b/public/locales/zh/sidebar.json @@ -14,6 +14,8 @@ "second": "下学期", "year": "全年", "help": "Help", + "see-in-second-semester": "See course in second semester", + "see-in-first-semester": "See course in first semester", "remove": "Remove", "missing-a": "Missing a", "section": "Section", From 2eb1513856505723cf09627184c6a3e918135fba Mon Sep 17 00:00:00 2001 From: Kaspar Poland Date: Wed, 13 Jul 2022 11:59:43 -0400 Subject: [PATCH 3/9] Add checkCourseExists query --- operations/queries/checkCourseExists.tsx | 9 +++++++++ src/utils/misc.ts | 2 ++ 2 files changed, 11 insertions(+) create mode 100644 operations/queries/checkCourseExists.tsx diff --git a/operations/queries/checkCourseExists.tsx b/operations/queries/checkCourseExists.tsx new file mode 100644 index 0000000..2b853ed --- /dev/null +++ b/operations/queries/checkCourseExists.tsx @@ -0,0 +1,9 @@ +import { gql } from "@apollo/client" + +export const CHECK_COURSE_EXISTS = gql` + query CourseById($id: String!) { + courseById(id: $id) { + id + } + } +` diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 5de7d8d..2627fd4 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -1 +1,3 @@ +export type Optional = T | undefined // I'm pretty sure this exists somewhere and can't find it + export const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1) From d7f8d4a782d4d4c202c5c27c0c50bf8fd9615e16 Mon Sep 17 00:00:00 2001 From: Kaspar Poland Date: Wed, 13 Jul 2022 12:48:20 -0400 Subject: [PATCH 4/9] Remove logs --- components/sidebar/CourseView.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/sidebar/CourseView.tsx b/components/sidebar/CourseView.tsx index 994e71d..949c89b 100644 --- a/components/sidebar/CourseView.tsx +++ b/components/sidebar/CourseView.tsx @@ -100,9 +100,6 @@ const CourseView = ({ }, }) - console.log(result) - console.log(course) - if (result.error) { // I don't know how to show errors in sqrl :/ so return for now return From d4c70181fd9fbbf5634ea311aeacfa2457f93240 Mon Sep 17 00:00:00 2001 From: Kaspar Poland Date: Wed, 13 Jul 2022 13:46:12 -0400 Subject: [PATCH 5/9] Implement early return and props formatting --- components/sidebar/CourseView.tsx | 8 ++++---- components/sidebar/SearchView/SearchView.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/sidebar/CourseView.tsx b/components/sidebar/CourseView.tsx index 949c89b..37a579e 100644 --- a/components/sidebar/CourseView.tsx +++ b/components/sidebar/CourseView.tsx @@ -100,10 +100,10 @@ const CourseView = ({ }, }) - if (result.error) { - // I don't know how to show errors in sqrl :/ so return for now - return - } else if (result.data.courseById) { + // I don't know how to show errors in sqrl :/ so return for now + if (result.error) return + + if (result.data.courseById) { // courseById is null when there is course with matching id setSiblingCourseId(result.data.courseById.id) } diff --git a/components/sidebar/SearchView/SearchView.tsx b/components/sidebar/SearchView/SearchView.tsx index 4bd38fc..4e65e52 100644 --- a/components/sidebar/SearchView/SearchView.tsx +++ b/components/sidebar/SearchView/SearchView.tsx @@ -34,7 +34,7 @@ import SearchViewHints from "./SearchViewHints" const MotionFlex = motion(Flex) const MotionButton = motion(Button) -interface SearchViewProps { +type Props = { searchQuery: string setSearchQuery: Dispatch> searchOffset: number @@ -50,7 +50,7 @@ const SearchView = ({ setSearchOffset, chosenCourse, setChosenCourse, -}: SearchViewProps) => { +}: Props) => { const searchRef = useRef() as MutableRefObject const [searchLimit, setSearchLimit] = useState(7) From f66197b9d11c395077606a833e102d053ec67f6c Mon Sep 17 00:00:00 2001 From: Kaspar Poland Date: Wed, 13 Jul 2022 13:47:02 -0400 Subject: [PATCH 6/9] Remove unused Optional monadic type --- src/utils/misc.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 2627fd4..5de7d8d 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -1,3 +1 @@ -export type Optional = T | undefined // I'm pretty sure this exists somewhere and can't find it - export const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1) From 2e78e0691cdc7e34701ac30a98bc5d6a1a2b75fb Mon Sep 17 00:00:00 2001 From: hisbaan Date: Thu, 14 Jul 2022 12:25:21 -0400 Subject: [PATCH 7/9] Fix call to locale file causing incorrect text --- components/preferences/PreferencesMeeting.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/preferences/PreferencesMeeting.tsx b/components/preferences/PreferencesMeeting.tsx index 9f19472..0e5bbfb 100644 --- a/components/preferences/PreferencesMeeting.tsx +++ b/components/preferences/PreferencesMeeting.tsx @@ -210,7 +210,7 @@ const PreferencesMeeting = () => { fontSize="sm" fontWeight={500} > - {t("cosmic ")} + {t("cosmic")} From 79b2a40481086ef7cbbd9f2e9a4b5b7ba3e6e3a9 Mon Sep 17 00:00:00 2001 From: Kaspar Poland Date: Tue, 19 Jul 2022 13:32:09 -0400 Subject: [PATCH 8/9] Remove lifted state in favor of context for sidebar course --- components/Header.tsx | 21 +++++------------- components/ShareModal.tsx | 12 +++------- components/TitleMeta.tsx | 22 +++++++++++++++++++ components/sidebar/CourseView.tsx | 16 ++++---------- .../sidebar/SearchView/SearchResults.tsx | 8 ++----- .../sidebar/SearchView/SearchView.test.tsx | 4 ---- components/sidebar/SearchView/SearchView.tsx | 10 +-------- components/sidebar/Sidebar.tsx | 8 +------ pages/timetable/[id].tsx | 5 +++++ src/useSharePrefix.ts | 18 +++++++++++++++ 10 files changed, 62 insertions(+), 62 deletions(-) create mode 100644 components/TitleMeta.tsx create mode 100644 src/useSharePrefix.ts diff --git a/components/Header.tsx b/components/Header.tsx index 0be0f78..8364c15 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -17,7 +17,6 @@ import { Badge, useToast, } from "@chakra-ui/react" -import Head from "next/head" import { EditIcon, Icon, InfoIcon, SettingsIcon } from "@chakra-ui/icons" import { FaShareSquare } from "react-icons/fa" import { useAppContext } from "../src/SqrlContext" @@ -29,6 +28,8 @@ import useSections from "../src/useSections" import { DUPLICATE_TIMETABLE } from "../operations/mutations/duplicateTimetable" import { useMutation } from "@apollo/client" import { useTranslation } from "next-i18next" +import TitleMeta from "./TitleMeta" +import useSharePrefix from "../src/useSharePrefix" const HeaderComponent = styled(chakra.header)` /* display: grid; */ @@ -78,7 +79,8 @@ const Header = ({ setSidebarOpen }: { setSidebarOpen: any }) => { const [osModifier, setOsModifier] = useState("") useEffect(() => { - if (navigator.userAgent.indexOf("Mac OS X") !== -1) return setOsModifier("⌘") + if (navigator.userAgent.indexOf("Mac OS X") !== -1) + return setOsModifier("⌘") setOsModifier("Ctrl + ") }, []) @@ -149,13 +151,7 @@ const Header = ({ setSidebarOpen }: { setSidebarOpen: any }) => { const [duplicateTimetable] = useMutation(DUPLICATE_TIMETABLE) const [loading, setLoading] = useState(false) - const [sharePrefix, setSharePrefix] = useState("") - - useEffect(() => { - setSharePrefix( - `${window.location.protocol}//${window.location.host}/timetable/` - ) - }, []) + const [sharePrefix] = useSharePrefix() const id = router.query.id @@ -163,12 +159,7 @@ const Header = ({ setSidebarOpen }: { setSidebarOpen: any }) => { return ( - - Sqrl Planner | {name} - - - - + diff --git a/components/ShareModal.tsx b/components/ShareModal.tsx index f9528d9..efe5770 100644 --- a/components/ShareModal.tsx +++ b/components/ShareModal.tsx @@ -28,6 +28,7 @@ import { useMutation } from "@apollo/client" import { DUPLICATE_TIMETABLE } from "../operations/mutations/duplicateTimetable" import { CopyIcon, ExternalLinkIcon } from "@chakra-ui/icons" import { useTranslation } from "next-i18next" +import useSharePrefix from "../src/useSharePrefix" type props = { isOpen: boolean @@ -39,14 +40,7 @@ const ShareModal = ({ isOpen, onClose }: props) => { const id = router.query.id - const [sharePrefix, setSharePrefix] = useState("") - - useEffect(() => { - setSharePrefix( - `${window.location.protocol}//${window.location.host}/timetable/` - ) - }, []) - + const [sharePrefix] = useSharePrefix() const shareUrl = `${sharePrefix}${id}` const { onCopy, hasCopied } = useClipboard(shareUrl) @@ -61,7 +55,7 @@ const ShareModal = ({ isOpen, onClose }: props) => { duration: 9000, isClosable: true, }) - }, [hasCopied]) + }, [toast, hasCopied]) const [duplicateTimetable] = useMutation(DUPLICATE_TIMETABLE) diff --git a/components/TitleMeta.tsx b/components/TitleMeta.tsx new file mode 100644 index 0000000..b16eb0e --- /dev/null +++ b/components/TitleMeta.tsx @@ -0,0 +1,22 @@ +import Head from "next/head" + +type Props = { + name: string + sharePrefix: string + id: string | string[] | undefined +} + +function TitleMeta(props: Props) { + const { name, sharePrefix, id } = props + + return ( + + Sqrl Planner | {name} + + + + + ) +} + +export default TitleMeta diff --git a/components/sidebar/CourseView.tsx b/components/sidebar/CourseView.tsx index 37a579e..c10cf80 100644 --- a/components/sidebar/CourseView.tsx +++ b/components/sidebar/CourseView.tsx @@ -47,13 +47,11 @@ import { import { useLazyQuery } from "@apollo/client" import { CHECK_COURSE_EXISTS } from "../../operations/queries/checkCourseExists" -const CourseView = ({ - setSearchQuery, - setChosenCourse, -}: { +type Props = { setSearchQuery: Dispatch> - setChosenCourse: Dispatch> -}) => { +} + +const CourseView = ({ setSearchQuery }: Props) => { const { state: { // courses, @@ -82,11 +80,6 @@ const CourseView = ({ // Check for the sibling course if it exists, and if it does, // add the ID of that course to the state. If not, it's null - // Set it to null initially, so that if a sibling course from another course existed, - // say CSC108F and CSC108S, and then the user clicks on CSC304F which has no S sibling, - // the sibling does not erroneously display CSC108S - setSiblingCourseId(null) - // Run the query ;(async () => { if (!course) return @@ -371,7 +364,6 @@ const CourseView = ({ alignItems="center" gap={2} onClick={() => { - setChosenCourse(siblingCourseId) dispatch({ type: "SET_SIDEBAR", payload: 1, diff --git a/components/sidebar/SearchView/SearchResults.tsx b/components/sidebar/SearchView/SearchResults.tsx index c78bb2c..d17126c 100644 --- a/components/sidebar/SearchView/SearchResults.tsx +++ b/components/sidebar/SearchView/SearchResults.tsx @@ -8,21 +8,18 @@ import { VStack, } from "@chakra-ui/react" import { motion } from "framer-motion" -import React, { SetStateAction } from "react" -import { Dispatch } from "react" +import React from "react" import { Course } from "../../../src/Course" import { useAppContext } from "../../../src/SqrlContext" import { breakdownCourseCode } from "../../../src/utils/course" type Props = { courses: Array - setChosenCourse: Dispatch> - chosenCourse: string } const MotionFlex = motion(Flex) -const SearchResults = ({ courses, setChosenCourse, chosenCourse }: Props) => { +const SearchResults = ({ courses }: Props) => { const { dispatch } = useAppContext() const hoverBackground = useColorModeValue("gray.100", "gray.600") @@ -65,7 +62,6 @@ const SearchResults = ({ courses, setChosenCourse, chosenCourse }: Props) => { }} tabIndex={0} onClick={() => { - setChosenCourse(course.id) dispatch({ type: "SET_SIDEBAR", payload: 1, diff --git a/components/sidebar/SearchView/SearchView.test.tsx b/components/sidebar/SearchView/SearchView.test.tsx index acf965e..c3b548e 100644 --- a/components/sidebar/SearchView/SearchView.test.tsx +++ b/components/sidebar/SearchView/SearchView.test.tsx @@ -17,8 +17,6 @@ describe("Search view", () => { setSearchQuery={() => {}} searchOffset={0} setSearchOffset={() => {}} - chosenCourse="" - setChosenCourse={() => {}} /> @@ -38,8 +36,6 @@ describe("Search view", () => { setSearchQuery={() => {}} searchOffset={0} setSearchOffset={() => {}} - chosenCourse="" - setChosenCourse={() => {}} /> diff --git a/components/sidebar/SearchView/SearchView.tsx b/components/sidebar/SearchView/SearchView.tsx index 4e65e52..a7766f5 100644 --- a/components/sidebar/SearchView/SearchView.tsx +++ b/components/sidebar/SearchView/SearchView.tsx @@ -39,8 +39,6 @@ type Props = { setSearchQuery: Dispatch> searchOffset: number setSearchOffset: Dispatch> - chosenCourse: string - setChosenCourse: Dispatch> } const SearchView = ({ @@ -48,8 +46,6 @@ const SearchView = ({ setSearchQuery, searchOffset, setSearchOffset, - chosenCourse, - setChosenCourse, }: Props) => { const searchRef = useRef() as MutableRefObject const [searchLimit, setSearchLimit] = useState(7) @@ -169,11 +165,7 @@ const SearchView = ({ {!error && !!data && searchQuery && ( {!!data.searchCourses.length && ( - + )} {!loading && data.searchCourses.length === 0 && ( { const boxBackground = useColorModeValue("gray.75", "gray.700") const [searchQuery, setSearchQuery] = useState("") const [searchOffset, setSearchOffset] = useState(0) - const [chosenCourse, setChosenCourse] = useState("") const router = useRouter() @@ -151,15 +150,10 @@ const Sidebar = () => { setSearchQuery={setSearchQuery} searchOffset={searchOffset} setSearchOffset={setSearchOffset} - chosenCourse={chosenCourse} - setChosenCourse={setChosenCourse} /> - + diff --git a/pages/timetable/[id].tsx b/pages/timetable/[id].tsx index 9f1a2d1..b8557e4 100644 --- a/pages/timetable/[id].tsx +++ b/pages/timetable/[id].tsx @@ -3,16 +3,21 @@ import type { NextPage } from "next" import { serverSideTranslations } from "next-i18next/serverSideTranslations" import React from "react" import Layout from "../../components/Layout" +import TitleMeta from "../../components/TitleMeta" import { GET_TIMETABLE_BY_ID } from "../../operations/queries/getTimetableById" import { apolloClientParams } from "../../src/apollo-client" import { PreferencesProvider } from "../../src/PreferencesContext" import Sqrl from "../../src/Sqrl" import { AppContextProvider } from "../../src/SqrlContext" import { SectionsProvider } from "../../src/useSections" +import useSharePrefix from "../../src/useSharePrefix" export const TimetableView: NextPage = (props: any) => { + const [sharePrefix] = useSharePrefix() + return ( + diff --git a/src/useSharePrefix.ts b/src/useSharePrefix.ts new file mode 100644 index 0000000..d182e49 --- /dev/null +++ b/src/useSharePrefix.ts @@ -0,0 +1,18 @@ +import React, { useEffect, useState } from "react" + +const useSharePrefix = (): [ + string, + React.Dispatch> +] => { + const [sharePrefix, setSharePrefix] = useState("") + + useEffect(() => { + setSharePrefix( + `${window.location.protocol}//${window.location.host}/timetable/` + ) + }, [setSharePrefix]) + + return [sharePrefix, setSharePrefix] +} + +export default useSharePrefix From 88c5197fde826e54967f118e89eee74ac45e0718 Mon Sep 17 00:00:00 2001 From: Kaspar Poland Date: Tue, 19 Jul 2022 13:54:37 -0400 Subject: [PATCH 9/9] Override server with context driven <title> on render --- components/Header.tsx | 15 ++++++++++++++- components/TitleMeta.tsx | 1 + src/useSections.tsx | 21 +++++++++++---------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/components/Header.tsx b/components/Header.tsx index 8364c15..5ab2dab 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -92,6 +92,17 @@ const Header = ({ setSidebarOpen }: { setSidebarOpen: any }) => { const { name, updateName } = useSections() + // Handles overriding the server-side title. When wget or discord embed searches for the title, + // it will find the one in [id].tsx, and never gives the client a chance to load. When the client loads + // in a browser, this <TitleMeta> overrides that one, making it react to name changes appropriately. + const [showReactiveTitle, setShowReactiveTitle] = useState<boolean>(false) + + // The first time updateName is called, override the server-shown + // title with the client-side context-driven one. + useEffect(() => { + setShowReactiveTitle(true) + }, [setShowReactiveTitle]) + const keydownListener = useCallback( (e: KeyboardEvent) => { if (!allowedToEdit) return @@ -159,7 +170,9 @@ const Header = ({ setSidebarOpen }: { setSidebarOpen: any }) => { return ( <HeaderComponent bg={useColorModeValue("gray.75", "gray.700")}> - <TitleMeta name={name} sharePrefix={sharePrefix} id={id} /> + {showReactiveTitle && ( + <TitleMeta name={name} sharePrefix={sharePrefix} id={id} /> + )} <AboutModal isOpen={isAboutOpen} onClose={onCloseAbout} /> <ShareModal isOpen={isShareOpen} onClose={onCloseShare} /> <Flex flex="1" alignItems="center" pl="11rem"> diff --git a/components/TitleMeta.tsx b/components/TitleMeta.tsx index b16eb0e..144623c 100644 --- a/components/TitleMeta.tsx +++ b/components/TitleMeta.tsx @@ -1,3 +1,4 @@ +import React from "react" import Head from "next/head" type Props = { diff --git a/src/useSections.tsx b/src/useSections.tsx index 919f0a3..eef31b7 100644 --- a/src/useSections.tsx +++ b/src/useSections.tsx @@ -22,16 +22,17 @@ type RemoveTimetableCourseProps = { courseId: string } -const SectionsContext = createContext< - | { - sections: { [key: string]: Array<string> } - name: string - updateName: Function - setSections: Function - removeCourse: Function - } - | undefined ->(undefined) +type SectionsContextContent = { + sections: { [key: string]: Array<string> } + name: string + updateName: Function + setSections: Function + removeCourse: Function +} + +const SectionsContext = createContext<SectionsContextContent | undefined>( + undefined +) export const SectionsProvider = ({ children,