From 6fcb5aa0a848f53846aab87472d7598b81f8d64f Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Fri, 20 Jan 2023 00:08:28 +0100 Subject: [PATCH 01/15] RoomDetails Page --- assets/freeClassrooms/clock.svg | 3 + assets/freeClassrooms/tick.svg | 3 + src/MainContainer.tsx | 13 +- .../FreeClass/ClassDetails/PageWrapper.tsx | 55 ++ src/navigation/MainStackNavigator.tsx | 5 + src/navigation/NavigationTypes.ts | 2 + src/pages/FreeClass/RoomDetails.tsx | 479 ++++++++++++++++++ src/utils/rooms.ts | 71 +++ 8 files changed, 630 insertions(+), 1 deletion(-) create mode 100644 assets/freeClassrooms/clock.svg create mode 100644 assets/freeClassrooms/tick.svg create mode 100644 src/components/FreeClass/ClassDetails/PageWrapper.tsx create mode 100644 src/pages/FreeClass/RoomDetails.tsx create mode 100644 src/utils/rooms.ts diff --git a/assets/freeClassrooms/clock.svg b/assets/freeClassrooms/clock.svg new file mode 100644 index 00000000..075e8225 --- /dev/null +++ b/assets/freeClassrooms/clock.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/freeClassrooms/tick.svg b/assets/freeClassrooms/tick.svg new file mode 100644 index 00000000..bae8dd33 --- /dev/null +++ b/assets/freeClassrooms/tick.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/MainContainer.tsx b/src/MainContainer.tsx index 64ba0d4d..c7e37861 100644 --- a/src/MainContainer.tsx +++ b/src/MainContainer.tsx @@ -28,8 +28,19 @@ export const MainContainer: FC = () => { onDownloads={() => { console.log("downloads") }} + /* eslint-disable @typescript-eslint/naming-convention */ onNotifications={() => { - console.log("notifications") + navigate("RoomDetails", { + room: { + name: "2.5", + building: "2", + power: true, + link: "https://www7.ceda.polimi.it/spazi/spazi/controller/Aula.do?evn_init=espandi&idaula=2328", + room_id: 2328, + lat: 45.47889998740511, + long: 9.227246568702538, + }, + }) }} onSettings={() => { navigate("SettingsNav", { diff --git a/src/components/FreeClass/ClassDetails/PageWrapper.tsx b/src/components/FreeClass/ClassDetails/PageWrapper.tsx new file mode 100644 index 00000000..03274d2a --- /dev/null +++ b/src/components/FreeClass/ClassDetails/PageWrapper.tsx @@ -0,0 +1,55 @@ +import React, { FC } from "react" +import { ScrollView, View, ViewStyle } from "react-native" +import { NavBar, NavbarProps } from "components/NavBar" +import { usePalette } from "utils/colors" + +/** + * Page Wrapper for Class Details page, maybe unify this with Settings Wrapper or any other wrapper + * somewhere in the app. + */ +export const PageWrapper: FC<{ + hideNavbar?: boolean + /** + * Props for the navbar, see {@link NavBar} + */ + navbarOptions?: NavbarProps + + style?: ViewStyle + + children: React.ReactNode +}> = props => { + const { background, isLight, primary } = usePalette() + + const navbar = !props.hideNavbar + + return ( + + + + {props.children} + + + + {navbar ? : null} + + ) +} diff --git a/src/navigation/MainStackNavigator.tsx b/src/navigation/MainStackNavigator.tsx index 19ad9c70..3cab5064 100644 --- a/src/navigation/MainStackNavigator.tsx +++ b/src/navigation/MainStackNavigator.tsx @@ -15,6 +15,7 @@ import { CampusChoice } from "pages/FreeClass/CampusChoice" import { PositionChoice } from "pages/FreeClass/PositionChoice" import { BuildingChoice } from "pages/FreeClass/BuildingChoice" import { ClassChoice } from "pages/FreeClass/ClassChoice" +import { RoomDetails } from "pages/FreeClass/RoomDetails" // eslint-disable-next-line @typescript-eslint/naming-convention const MainStackNavigator = createStackNavigator() @@ -45,6 +46,10 @@ export const MainStack: FC = () => { name="ClassChoice" component={ClassChoice} /> + ) diff --git a/src/navigation/NavigationTypes.ts b/src/navigation/NavigationTypes.ts index 86c0baaa..28585914 100644 --- a/src/navigation/NavigationTypes.ts +++ b/src/navigation/NavigationTypes.ts @@ -24,6 +24,7 @@ import { import { Article } from "api/articles" import { CampusItem } from "pages/FreeClass/CampusChoice" import { NavigatorScreenParams } from "@react-navigation/native" +import { MockedClass } from "pages/FreeClass/RoomDetails" /** * interface containing the info about the params for each page of the stack navigator @@ -50,6 +51,7 @@ export type MainStackNavigatorParams = { PositionChoice: undefined BuildingChoice: { campus: CampusItem } ClassChoice: { building: string } + RoomDetails: { room: MockedClass } } export type SettingsStackNavigatorParams = { diff --git a/src/pages/FreeClass/RoomDetails.tsx b/src/pages/FreeClass/RoomDetails.tsx new file mode 100644 index 00000000..194c41ea --- /dev/null +++ b/src/pages/FreeClass/RoomDetails.tsx @@ -0,0 +1,479 @@ +import { MainStackScreen } from "navigation/NavigationTypes" +import React, { useMemo } from "react" +import { View } from "react-native" +import { usePalette } from "utils/colors" +import { PageWrapper } from "components/FreeClass/ClassDetails/PageWrapper" +import { BodyText, Text } from "components/Text" +import { + useSVG, + Canvas, + ImageSVG, + Skia, + BlendMode, + Group, +} from "@shopify/react-native-skia" +import clock from "assets/freeClassrooms/clock.svg" +import tick from "assets/freeClassrooms/tick.svg" +/* eslint-disable @typescript-eslint/naming-convention */ +export interface MockedClass { + name: string + building: string + power: boolean + link: string + room_id: number + lat: number + long: number +} + +export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { + const clockSvg = useSVG(clock) + const tickSvg = useSVG(tick) + + const { isLight, primary } = usePalette() + + // funny how renaming it class breaks everything, because class is also a keyword + const { room } = props.route.params + + const paint = useMemo(() => Skia.Paint(), []) + paint.setColorFilter( + Skia.ColorFilter.MakeBlend( + Skia.Color(isLight ? "#414867" : "#fff"), + BlendMode.SrcIn + ) + ) + const paintTick = useMemo(() => Skia.Paint(), []) + paintTick.setColorFilter( + Skia.ColorFilter.MakeBlend( + Skia.Color(isLight ? "#424967" : "#8791BD"), + BlendMode.SrcIn + ) + ) + return ( + + + + + + {room.building}. + + + {room.name} + + + + + Indirizzo : + + + bho + + + + + Capienza + + + bho + + + + + + + + + + consulta la{" "} + + + mappa + + + + + + + Libera: + + + + + + + Da + + + + 08 : 00 + + + + + + A + + + + 08 : 00 + + + + + + + + {clock && clockSvg && ( + + + + + + + + )} + + + Mancano: + + + 2 h 23`'` + + + + + + + + + Seziona affolamento + + + + + Info Utili: + + + {tick && tickSvg && ( + + + + + + + + )} + + ribaltine / banco piccolo + + + + {tick && tickSvg && ( + + + + + + + + )} + + prese per ogni postazione + + + + {tick && tickSvg && ( + + + + + + + + )} + + aula informatizzata + + + + + ) +} diff --git a/src/utils/rooms.ts b/src/utils/rooms.ts new file mode 100644 index 00000000..ec1813f9 --- /dev/null +++ b/src/utils/rooms.ts @@ -0,0 +1,71 @@ +/** + * BL.27.04 --> {"27.04" , false} + * + * "F.LLI CASTIGLIONI" ---> {"F.LLI CASTIGLIONI", true} + * + * B8 0.3 ---> {"0.3" , false} + * + * there are some pathological cases, for example: + * + * "ATRIO P.T. - ED. BL.27" + * "CORRIDOIO SX 1� P. - ED. BL.27" + * B8 0.10 (EX B8.0.8) + * + * what should I show? + * + * probably I need to find another way, maybe ask aliases from backend? + */ + +function extractRoom(val: string) { + const len = val.length + const hasNumbers = containsNumber(val) + if (!hasNumbers) { + return { room: val, isOnlyText: true } + } else { + val = val.trimEnd() + + let separatorIndex: number | undefined = undefined + + let dotsFound = 0 + for (let n = len - 1; n > 0 && separatorIndex === undefined; n--) { + if (val[n] === "." || val[n] === " ") { + dotsFound++ + } + if (dotsFound === 2) { + separatorIndex = n + } + } + if (separatorIndex) { + return { + room: val.substring(separatorIndex + 1, len), + isOnlyText: false, + } + } else { + //something went wrong, display all string ? + return { room: val, isOnlyText: true } + } + } + return +} + +function extractBuilding(val: string) { + const len = val.length + let separatorIndex: number | undefined = undefined + for (let n = len; n > 0 && separatorIndex === undefined; n--) { + if (val === " ") { + separatorIndex = n + } + } + if (separatorIndex) { + return { building: val.substring(separatorIndex + 1, len) } + } else { + //something went wrong, display only room + return { building: undefined } + } +} + +function containsNumber(val: string) { + // if has digits + const regExp = /\d/g + return regExp.test(val) +} From ba3366214734ad02007f3f6cac2afe026f5ffc09 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Sun, 5 Feb 2023 17:45:47 +0100 Subject: [PATCH 02/15] Added RoomDetails to flow added Map and actual infos --- assets/freeClassrooms/expand.svg | 3 + src/MainContainer.tsx | 12 +- src/api/Room.ts | 2 +- .../FreeClass/ClassDetails/InfoMapTile.tsx | 195 +++++++++++ .../FreeClass/ClassDetails/PageWrapper.tsx | 11 +- .../FreeClass/ClassDetails/TimeLeftTile.tsx | 213 ++++++++++++ src/components/FreeClass/FreeClassList.tsx | 18 ++ src/navigation/NavigationTypes.ts | 4 +- src/pages/FreeClass/ClassChoice.tsx | 2 +- src/pages/FreeClass/PositionChoice.tsx | 2 +- src/pages/FreeClass/RoomDetails.tsx | 302 +----------------- src/utils/rooms.ts | 65 ++-- 12 files changed, 480 insertions(+), 349 deletions(-) create mode 100644 assets/freeClassrooms/expand.svg create mode 100644 src/components/FreeClass/ClassDetails/InfoMapTile.tsx create mode 100644 src/components/FreeClass/ClassDetails/TimeLeftTile.tsx diff --git a/assets/freeClassrooms/expand.svg b/assets/freeClassrooms/expand.svg new file mode 100644 index 00000000..a8064ab6 --- /dev/null +++ b/assets/freeClassrooms/expand.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/MainContainer.tsx b/src/MainContainer.tsx index c7e37861..0f3cf32a 100644 --- a/src/MainContainer.tsx +++ b/src/MainContainer.tsx @@ -30,17 +30,7 @@ export const MainContainer: FC = () => { }} /* eslint-disable @typescript-eslint/naming-convention */ onNotifications={() => { - navigate("RoomDetails", { - room: { - name: "2.5", - building: "2", - power: true, - link: "https://www7.ceda.polimi.it/spazi/spazi/controller/Aula.do?evn_init=espandi&idaula=2328", - room_id: 2328, - lat: 45.47889998740511, - long: 9.227246568702538, - }, - }) + console.log("notifications") }} onSettings={() => { navigate("SettingsNav", { diff --git a/src/api/Room.ts b/src/api/Room.ts index 677e9ae9..6877f61d 100644 --- a/src/api/Room.ts +++ b/src/api/Room.ts @@ -50,7 +50,7 @@ export const rooms = { }, async getRoomInfo(roomId: number, options?: RequestOptions) { - const response = await client.poliNetworkInstance.get( + const response = await client.poliNetworkInstance.get( "/v1/rooms/" + roomId, { ...options, diff --git a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx new file mode 100644 index 00000000..b3fe1ee6 --- /dev/null +++ b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx @@ -0,0 +1,195 @@ +import React, { FC } from "react" +import { View } from "react-native" +import { usePalette } from "utils/colors" +import { BodyText, Text } from "components/Text" +import { extractBuilding, extractRoom } from "utils/rooms" +import MapView from "react-native-maps" +import expand from "assets/freeClassrooms/expand.svg" +import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" + +interface InfoMapTileProps { + roomName: string + building: string + address: string + capacity: string +} + +export const InfoMapTile: FC = props => { + const { isLight, primary } = usePalette() + + const building = extractBuilding(props.building) + + const roomName = extractRoom(props.roomName) + + const expandSvg = useSVG(expand) + return ( + + + + + {building && roomName ? `${building}.` : undefined} + + + {building && roomName ? roomName : props.roomName} + + + + + Indirizzo : + + + {props.address} + + + + + Capienza:{" "} + + + {props.capacity} + + + + + + + {expand && expandSvg && ( + + + + + + )} + + + + + + consulta la{" "} + + + mappa + + + + + ) +} diff --git a/src/components/FreeClass/ClassDetails/PageWrapper.tsx b/src/components/FreeClass/ClassDetails/PageWrapper.tsx index 03274d2a..5ef7ab79 100644 --- a/src/components/FreeClass/ClassDetails/PageWrapper.tsx +++ b/src/components/FreeClass/ClassDetails/PageWrapper.tsx @@ -44,8 +44,15 @@ export const PageWrapper: FC<{ overflow: "hidden", }} > - - {props.children} + + {props.children} diff --git a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx new file mode 100644 index 00000000..a1d2026a --- /dev/null +++ b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx @@ -0,0 +1,213 @@ +import React, { FC, useMemo } from "react" +import { View } from "react-native" +import { usePalette } from "utils/colors" +import { BodyText } from "components/Text" + +import { + BlendMode, + Canvas, + Group, + ImageSVG, + Skia, + useSVG, +} from "@shopify/react-native-skia" +import clock from "assets/freeClassrooms/clock.svg" +import { extractTimeLeft } from "utils/rooms" + +interface TimeLeftTileProps { + startDate: string +} + +export const TimeLeftTile: FC = props => { + const { isLight } = usePalette() + + const clockSvg = useSVG(clock) + + const paint = useMemo(() => Skia.Paint(), []) + paint.setColorFilter( + Skia.ColorFilter.MakeBlend( + Skia.Color(isLight ? "#414867" : "#fff"), + BlendMode.SrcIn + ) + ) + const paintClock = useMemo(() => Skia.Paint(), []) + paintClock.setColorFilter( + Skia.ColorFilter.MakeBlend( + Skia.Color(isLight ? "#424967" : "#fff"), + BlendMode.SrcIn + ) + ) + + const startDate = new Date(props.startDate) + const endDate = new Date(startDate.getTime() + 8 * 60 * 60 * 1000) + const startHour = startDate.getHours().toString().padStart(2, "0") + const endhour = endDate.getHours().toString().padStart(2, "0") + + const { hoursLeft, minutesLeft, isPositive } = extractTimeLeft(startDate) + + return ( + + + Libera: + + + + + + + Da + + + + {startHour} : 00 + + + + + + A + + + + {endhour} : 00 + + + + + + + + {clock && clockSvg && ( + + + + + + + + )} + + + Mancano: + + + {isPositive + ? `${hoursLeft} h ${minutesLeft}'` + : "-- h -- '"} + + + + + + + ) +} diff --git a/src/components/FreeClass/FreeClassList.tsx b/src/components/FreeClass/FreeClassList.tsx index 84c78c72..97a72e5e 100644 --- a/src/components/FreeClass/FreeClassList.tsx +++ b/src/components/FreeClass/FreeClassList.tsx @@ -7,11 +7,14 @@ import timerIcon from "assets/freeClassrooms/timer.svg" import overcrowdingIcon from "assets/freeClassrooms/overcrowding.svg" import fireIcon from "assets/freeClassrooms/fire.svg" import { ScrollView } from "react-native-gesture-handler" +import { useNavigation } from "navigation/NavigationTypes" +import { api } from "api" const { width } = Dimensions.get("window") interface FreeClassListProps { data: number[] + date: Date } export const FreeClassList: FC = props => { @@ -20,6 +23,8 @@ export const FreeClassList: FC = props => { const overcrowdingSVG = useSVG(overcrowdingIcon) const fireSVG = useSVG(fireIcon) + const { navigate } = useNavigation() + return ( = props => { marginBottom: 34, borderRadius: 12, }} + onPress={async () => { + try { + const selectedRoom = await api.rooms.getRoomInfo( + room + ) + navigate("RoomDetails", { + room: selectedRoom, + startDate : props.date.toISOString() + }) + } catch (err) { + console.log(err) + } + }} > = props => { setDate={(date: Date) => setDate(date)} /> - + diff --git a/src/pages/FreeClass/PositionChoice.tsx b/src/pages/FreeClass/PositionChoice.tsx index a3c898c1..2a268a12 100644 --- a/src/pages/FreeClass/PositionChoice.tsx +++ b/src/pages/FreeClass/PositionChoice.tsx @@ -270,7 +270,7 @@ export const PositionChoice: MainStackScreen<"PositionChoice"> = () => { paddingBottom: 100, }} > - + ) : ( = props => { - const clockSvg = useSVG(clock) const tickSvg = useSVG(tick) - const { isLight, primary } = usePalette() + const { isLight } = usePalette() // funny how renaming it class breaks everything, because class is also a keyword - const { room } = props.route.params + const { room, startDate } = props.route.params const paint = useMemo(() => Skia.Paint(), []) paint.setColorFilter( @@ -49,281 +40,14 @@ export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { ) ) return ( - - - - - - {room.building}. - - - {room.name} - - - - - Indirizzo : - - - bho - - - - - Capienza - - - bho - - - - - - - - - - consulta la{" "} - - - mappa - - - - - - - Libera: - - - - - - - Da - - - - 08 : 00 - - - - - - A - - - - 08 : 00 - - - - - - - - {clock && clockSvg && ( - - - - - - - - )} - - - Mancano: - - - 2 h 23`'` - - - - - - + + + 2) { + return arr.slice(1).join(".") } else { - val = val.trimEnd() - - let separatorIndex: number | undefined = undefined - - let dotsFound = 0 - for (let n = len - 1; n > 0 && separatorIndex === undefined; n--) { - if (val[n] === "." || val[n] === " ") { - dotsFound++ - } - if (dotsFound === 2) { - separatorIndex = n - } - } - if (separatorIndex) { - return { - room: val.substring(separatorIndex + 1, len), - isOnlyText: false, - } - } else { - //something went wrong, display all string ? - return { room: val, isOnlyText: true } - } + return undefined } - return } -function extractBuilding(val: string) { - const len = val.length - let separatorIndex: number | undefined = undefined - for (let n = len; n > 0 && separatorIndex === undefined; n--) { - if (val === " ") { - separatorIndex = n - } - } - if (separatorIndex) { - return { building: val.substring(separatorIndex + 1, len) } - } else { - //something went wrong, display only room - return { building: undefined } +export function extractBuilding(val: string) { + const arr = val.split(" ") + if (arr.length === 2) { + return arr[1] } + return undefined } -function containsNumber(val: string) { +/* function containsNumber(val: string) { // if has digits const regExp = /\d/g return regExp.test(val) +} */ + +export function extractTimeLeft(startDate: Date) { + const deltaMilliseconds = startDate.getTime() - Date.now() + const hours = Math.floor(deltaMilliseconds / 3.6e6) + const minutes = Math.floor( + (deltaMilliseconds - hours * 60 * 60 * 1000) / 60000 + ) + const hoursLeft = hours.toString() + const minutesLeft = minutes.toString() + const isPositive = hours >= 0 && minutes >= 0 + return { hoursLeft, minutesLeft, isPositive } } From 833b80ccd94478dfd4d4a4f5c66798e8d64268ce Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Sun, 5 Feb 2023 18:13:32 +0100 Subject: [PATCH 03/15] Sezione Info Utili --- src/MainContainer.tsx | 1 - .../ClassDetails/RoomUtilsSection.tsx | 42 +++++ .../FreeClass/ClassDetails/RoomUtilsTile.tsx | 88 +++++++++ src/pages/FreeClass/RoomDetails.tsx | 169 +----------------- 4 files changed, 133 insertions(+), 167 deletions(-) create mode 100644 src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx create mode 100644 src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx diff --git a/src/MainContainer.tsx b/src/MainContainer.tsx index 0f3cf32a..64ba0d4d 100644 --- a/src/MainContainer.tsx +++ b/src/MainContainer.tsx @@ -28,7 +28,6 @@ export const MainContainer: FC = () => { onDownloads={() => { console.log("downloads") }} - /* eslint-disable @typescript-eslint/naming-convention */ onNotifications={() => { console.log("notifications") }} diff --git a/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx b/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx new file mode 100644 index 00000000..3529f041 --- /dev/null +++ b/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx @@ -0,0 +1,42 @@ +import React, { FC, useMemo } from "react" +import { View } from "react-native" +import { usePalette } from "utils/colors" +import { BodyText } from "components/Text" +import { RoomUtilsTile } from "./RoomUtilsTile" + + +interface RoomUtilsSectionProps { + power?: boolean + computers?: boolean + ribaltine?: boolean +} + +export const RoomUtilsSection: FC = props => { + const { isLight } = usePalette() + + return ( + + + Info Utili: + + + + + + ) +} diff --git a/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx b/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx new file mode 100644 index 00000000..cee7b5d8 --- /dev/null +++ b/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx @@ -0,0 +1,88 @@ +import React, { FC, useMemo } from "react" +import { View } from "react-native" +import { usePalette } from "utils/colors" +import { BodyText } from "components/Text" + +import { + BlendMode, + Canvas, + Group, + ImageSVG, + Skia, + useSVG, +} from "@shopify/react-native-skia" +import tick from "assets/freeClassrooms/tick.svg" + +interface RoomUtilsTileProps { + name: string + status?: boolean +} + +export const RoomUtilsTile: FC = props => { + const { isLight } = usePalette() + + const tickSvg = useSVG(tick) + + const paintTick = useMemo(() => Skia.Paint(), []) + paintTick.setColorFilter( + Skia.ColorFilter.MakeBlend( + Skia.Color( + props.status + ? isLight + ? "#424967" + : "#8791BD" + : isLight + ? "#8791BD" + : "#424967" + ), + BlendMode.SrcIn + ) + ) + + return ( + + {tick && tickSvg && ( + + + + + + + + )} + + {props.name} + + + ) +} diff --git a/src/pages/FreeClass/RoomDetails.tsx b/src/pages/FreeClass/RoomDetails.tsx index 88ec319d..83d74eae 100644 --- a/src/pages/FreeClass/RoomDetails.tsx +++ b/src/pages/FreeClass/RoomDetails.tsx @@ -1,44 +1,17 @@ import { MainStackScreen } from "navigation/NavigationTypes" -import React, { useMemo } from "react" +import React from "react" import { View } from "react-native" -import { usePalette } from "utils/colors" import { PageWrapper } from "components/FreeClass/ClassDetails/PageWrapper" import { BodyText } from "components/Text" -import { - useSVG, - Canvas, - ImageSVG, - Skia, - BlendMode, - Group, -} from "@shopify/react-native-skia" -import tick from "assets/freeClassrooms/tick.svg" import { InfoMapTile } from "components/FreeClass/ClassDetails/InfoMapTile" import { TimeLeftTile } from "components/FreeClass/ClassDetails/TimeLeftTile" +import { RoomUtilsSection } from "components/FreeClass/ClassDetails/RoomUtilsSection" /* eslint-disable @typescript-eslint/naming-convention */ export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { - const tickSvg = useSVG(tick) - - const { isLight } = usePalette() - // funny how renaming it class breaks everything, because class is also a keyword const { room, startDate } = props.route.params - const paint = useMemo(() => Skia.Paint(), []) - paint.setColorFilter( - Skia.ColorFilter.MakeBlend( - Skia.Color(isLight ? "#414867" : "#fff"), - BlendMode.SrcIn - ) - ) - const paintTick = useMemo(() => Skia.Paint(), []) - paintTick.setColorFilter( - Skia.ColorFilter.MakeBlend( - Skia.Color(isLight ? "#424967" : "#8791BD"), - BlendMode.SrcIn - ) - ) return ( = props => { Seziona affolamento - - - Info Utili: - - - {tick && tickSvg && ( - - - - - - - - )} - - ribaltine / banco piccolo - - - - {tick && tickSvg && ( - - - - - - - - )} - - prese per ogni postazione - - - - {tick && tickSvg && ( - - - - - - - - )} - - aula informatizzata - - - + ) } From 73ef00a7725ca97246f252cd8b9534fcfd2f43fe Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Mon, 13 Feb 2023 00:12:40 +0100 Subject: [PATCH 04/15] Affollamento UI --- App.tsx | 1 + .../CrowdSlider/CrowdSliderDynamic.tsx | 67 +++++++++++ .../CrowdSlider/CrowdSliderLabels.tsx | 49 ++++++++ .../CrowdSlider/CrowdSliderStatic.tsx | 52 +++++++++ .../ClassDetails/CrowdingSection.tsx | 109 ++++++++++++++++++ .../FreeClass/ClassDetails/InfoMapTile.tsx | 15 +-- src/components/Home/MainMenu.tsx | 39 ++++++- src/components/Modal.tsx | 35 ++---- src/pages/FreeClass/RoomDetails.tsx | 17 +-- 9 files changed, 333 insertions(+), 51 deletions(-) create mode 100644 src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx create mode 100644 src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx create mode 100644 src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx create mode 100644 src/components/FreeClass/ClassDetails/CrowdingSection.tsx diff --git a/App.tsx b/App.tsx index 8663da89..25c3c9f9 100644 --- a/App.tsx +++ b/App.tsx @@ -125,6 +125,7 @@ export default function App() { if (loginState.loggedIn) { console.log(await api.user.getPoliNetworkMe()) console.log(await api.user.getPolimiUserInfo()) + } }) } diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx new file mode 100644 index 00000000..51f5b97e --- /dev/null +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx @@ -0,0 +1,67 @@ +import React, { FC } from "react" +import { View } from "react-native" +import { usePalette } from "utils/colors" +import Animated, { + useAnimatedStyle, + useSharedValue, +} from "react-native-reanimated" +import { + Gesture, + GestureDetector, + GestureHandlerRootView, +} from "react-native-gesture-handler" + +interface CrowdSliderDynamicProps { + isSlidable?: boolean + onSlided?: () => void +} + +export const CrowdSliderDynamic: FC = props => { + const { isLight } = usePalette() + + const position = useSharedValue(0) + const lastPosition = useSharedValue(0) + const panGesture = Gesture.Pan() + .onUpdate(e => { + console.log("update") + position.value = e.translationX + lastPosition.value + }) + .onEnd(() => { + lastPosition.value = position.value + }) + + const animatedPos = useAnimatedStyle(() => ({ + transform: [{ translateX: position.value }], + })) + return ( + + + + + + + + + + ) +} diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx new file mode 100644 index 00000000..0fee17b6 --- /dev/null +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx @@ -0,0 +1,49 @@ +import React, { FC } from "react" +import { usePalette } from "utils/colors" +import { BodyText } from "components/Text" +import { View } from "react-native" + +export const CrowdSliderLabels: FC = () => { + const { isLight } = usePalette() + + return ( + + + Basso + + + Medio + + + Alto + + + ) +} diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx new file mode 100644 index 00000000..1b0828cc --- /dev/null +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx @@ -0,0 +1,52 @@ +import React, { FC } from "react" +import { FlexAlignType, View } from "react-native" +import { usePalette } from "utils/colors" +import { BodyText } from "components/Text" +import Animated from "react-native-reanimated" + +interface CrowdSliderStaticProps { + position?: "basso" | "medio" | "alto" +} + +export const CrowdSliderStatic: FC = props => { + const { isLight } = usePalette() + + const getPos = ( + pos?: "basso" | "medio" | "alto" + ): FlexAlignType | undefined => { + if (pos === "basso") { + return "flex-start" + } else if (pos === "medio") { + return "center" + } else if (pos === "alto") { + return "flex-end" + } + return undefined + } + + return ( + + + + + + ) +} diff --git a/src/components/FreeClass/ClassDetails/CrowdingSection.tsx b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx new file mode 100644 index 00000000..9416da54 --- /dev/null +++ b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx @@ -0,0 +1,109 @@ +import React, { FC, useMemo, useState } from "react" +import { Pressable, View } from "react-native" +import { usePalette } from "utils/colors" +import { BodyText } from "components/Text" +import { CrowdSliderStatic } from "./CrowdSlider/CrowdSliderStatic" +import { ModalCustom } from "components/Modal" +import { CrowdSliderDynamic } from "./CrowdSlider/CrowdSliderDynamic" +import { CrowdSliderLabels } from "./CrowdSlider/CrowdSliderLabels" +import { ButtonCustom } from "components/Settings" + +interface CrowdingSectionProps { + isSlidable?: boolean + onSlided?: () => void +} + +export const CrowdingSection: FC = props => { + const { isLight } = usePalette() + + const [isModalVisible, setIsModalVisible] = useState(false) + return ( + + + Affollamento: + + + + + + Se il dato sull'affollamento non è corretto + + setIsModalVisible(true)}> + + esprimi opinione + + + + setIsModalVisible(false)} + centerText={true} + > + + + Esprimi{"\n"}Opinione + + + Indica il livello di affollamento {"\n"} dell'aula + + + + + + + console.log("press")} + /> + + + + ) +} diff --git a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx index b3fe1ee6..430c337b 100644 --- a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx +++ b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx @@ -8,18 +8,19 @@ import expand from "assets/freeClassrooms/expand.svg" import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" interface InfoMapTileProps { - roomName: string - building: string - address: string - capacity: string + roomName?: string + building?: string + address?: string + capacity?: string } export const InfoMapTile: FC = props => { const { isLight, primary } = usePalette() - const building = extractBuilding(props.building) - - const roomName = extractRoom(props.roomName) + console.log(props.building) + const building = extractBuilding(props.building ?? "Edificio 50") + console.log(props.roomName) + const roomName = extractRoom(props.roomName ?? "50.1.1") const expandSvg = useSVG(expand) return ( diff --git a/src/components/Home/MainMenu.tsx b/src/components/Home/MainMenu.tsx index 0cfbe004..dfc61029 100644 --- a/src/components/Home/MainMenu.tsx +++ b/src/components/Home/MainMenu.tsx @@ -16,9 +16,10 @@ import grading_book from "assets/menu/grading_book.svg" import tests from "assets/menu/tests.svg" import add from "assets/menu/add.svg" import { ModalCustom } from "components/Modal" - +import { Text } from "components/Text" import AsyncStorage from "@react-native-async-storage/async-storage" import { useOutsideClick } from "utils/outsideClick" +import { usePalette } from "utils/colors" /** * the buttons and their features @@ -59,6 +60,8 @@ export const MainMenu: FC<{ filter?: string }> = ({ filter }) => { scrollView.current?.scrollTo({ x: 0, y: 0, animated: true }) }, [filter, scrollView]) + const { homeBackground, isLight } = usePalette() + useEffect(() => { AsyncStorage.getItem("menu:icons") .then(iconJSON => { @@ -109,6 +112,34 @@ export const MainMenu: FC<{ filter?: string }> = ({ filter }) => { isShowing={isModalVisible} onClose={() => setModalVisible(false)} > + + Aggiungi features + + + Personalizza la tua bacheca + = ({ filter }) => { if (isDeleting) setIsDeleting(false) if (buttonIcon.id === 9) setModalVisible(true) // TODO: actual navigation - + if (!isDeleting && buttonIcon.id !== 9) { if (buttonIcon.id == 3) { navigate("FreeClassrooms") } else if (buttonIcon.id === 5) { navigate("Groups") - } - else { + } else { navigate("Error404") } } - }} onLongPress={() => { if (buttonIcon.id !== 9) setIsDeleting(!isDeleting) diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 921edec3..a273aad5 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -49,7 +49,13 @@ export const ModalCustom: FC = props => { onPress={props.onClose} style={[styles.pageWrapper, { backgroundColor: modalBarrier }]} > - + props.onClose()} @@ -81,27 +87,9 @@ export const ModalCustom: FC = props => { { backgroundColor: backgroundSecondary }, ]} > - - {props.title} - - - {props.subTitle} - - {props.children} + + {props.children} + @@ -116,10 +104,7 @@ const styles = StyleSheet.create({ alignItems: "center", }, contentWrapper: { - width: 320, - height: 420, borderRadius: 12, - marginHorizontal: 15, shadowColor: "#000", shadowOffset: { diff --git a/src/pages/FreeClass/RoomDetails.tsx b/src/pages/FreeClass/RoomDetails.tsx index 83d74eae..b3aa1a9e 100644 --- a/src/pages/FreeClass/RoomDetails.tsx +++ b/src/pages/FreeClass/RoomDetails.tsx @@ -6,6 +6,7 @@ import { BodyText } from "components/Text" import { InfoMapTile } from "components/FreeClass/ClassDetails/InfoMapTile" import { TimeLeftTile } from "components/FreeClass/ClassDetails/TimeLeftTile" import { RoomUtilsSection } from "components/FreeClass/ClassDetails/RoomUtilsSection" +import { CrowdingSection } from "components/FreeClass/ClassDetails/CrowdingSection" /* eslint-disable @typescript-eslint/naming-convention */ export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { @@ -21,20 +22,8 @@ export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { roomName={room.name} /> - - - Seziona affolamento - - - + + ) } From 9a5220c7b007e15fb1a44290d4c3c019f84a1f57 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Tue, 14 Feb 2023 22:58:30 +0100 Subject: [PATCH 05/15] Sezione affollamento completata --- App.tsx | 1 - babel.config.js | 2 +- src/api/Room.ts | 33 ++++- .../CrowdSlider/CrowdSliderDynamic.tsx | 75 ++++++---- .../CrowdSlider/CrowdSliderStatic.tsx | 25 ++-- .../ClassDetails/CrowdingSection.tsx | 67 +++++++-- .../ClassDetails/RoomUtilsSection.tsx | 3 +- src/components/FreeClass/FreeClassList.tsx | 3 +- .../FreeClass/ModalWithGestures.tsx | 128 ++++++++++++++++++ src/components/Modal.tsx | 24 +--- src/navigation/NavigationTypes.ts | 2 +- src/pages/FreeClass/RoomDetails.tsx | 8 +- src/utils/rooms.ts | 39 +++--- 13 files changed, 303 insertions(+), 107 deletions(-) create mode 100644 src/components/FreeClass/ModalWithGestures.tsx diff --git a/App.tsx b/App.tsx index 25c3c9f9..8663da89 100644 --- a/App.tsx +++ b/App.tsx @@ -125,7 +125,6 @@ export default function App() { if (loginState.loggedIn) { console.log(await api.user.getPoliNetworkMe()) console.log(await api.user.getPolimiUserInfo()) - } }) } diff --git a/babel.config.js b/babel.config.js index cc4d51fe..d1ff5d2e 100644 --- a/babel.config.js +++ b/babel.config.js @@ -3,7 +3,6 @@ module.exports = function (api) { return { presets: ["babel-preset-expo"], plugins: [ - "react-native-reanimated/plugin", [ "module-resolver", { @@ -17,6 +16,7 @@ module.exports = function (api) { }, }, ], + "react-native-reanimated/plugin", ], } } diff --git a/src/api/Room.ts b/src/api/Room.ts index 6877f61d..12819af7 100644 --- a/src/api/Room.ts +++ b/src/api/Room.ts @@ -1,4 +1,5 @@ -import { HttpClient, RequestOptions } from "./HttpClient" +import { ValidCrowdStatus } from "components/FreeClass/ClassDetails/CrowdingSection" +import { AuthType, HttpClient, RequestOptions } from "./HttpClient" /* eslint-disable @typescript-eslint/naming-convention */ export interface Rooms { @@ -20,6 +21,11 @@ export interface RoomDetails { power: boolean } +export interface OccupancyInfo { + room_id: number + occupancy_rate: null | ValidCrowdStatus +} + const client = HttpClient.getInstance() /** @@ -58,4 +64,29 @@ export const rooms = { ) return response.data }, + + async getOccupancyRate(roomId: number, options?: RequestOptions) { + const response = await client.poliNetworkInstance.get( + "/v1/rooms/" + roomId + "/occupancy", + { + ...options, + } + ) + return response.data + }, + + async postOccupancyRate( + roomId: number, + rate: number, + options?: RequestOptions + ) { + const res = await client.poliNetworkInstance.post( + "/v1/rooms/" + roomId + "/occupancy", + { + ...options, + }, + { params: { rate: rate }, authType: AuthType.POLINETWORK } + ) + return res + }, } diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx index 51f5b97e..792fd26a 100644 --- a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx @@ -2,6 +2,7 @@ import React, { FC } from "react" import { View } from "react-native" import { usePalette } from "utils/colors" import Animated, { + runOnJS, useAnimatedStyle, useSharedValue, } from "react-native-reanimated" @@ -10,58 +11,80 @@ import { GestureDetector, GestureHandlerRootView, } from "react-native-gesture-handler" +import { ValidCrowdStatus } from "../CrowdingSection" +import { getCrowdStatus } from "utils/rooms" interface CrowdSliderDynamicProps { - isSlidable?: boolean - onSlided?: () => void + usableWidth?: number + startingPos: ValidCrowdStatus + onSlideEnd: (pos: ValidCrowdStatus) => void } export const CrowdSliderDynamic: FC = props => { const { isLight } = usePalette() - const position = useSharedValue(0) - const lastPosition = useSharedValue(0) + const wrapper = (pos: number, width: number) => { + const crowdStatus = getCrowdStatus(pos, width) + if (props.onSlideEnd) { + props.onSlideEnd(crowdStatus) + } + } + const circleWidth = 28 + + //320 is modal width, 52 is content padding + const usableWidth = props.usableWidth ?? 320 - 52 - circleWidth + + const startingPos = ((props.startingPos - 1) / 4) * usableWidth + + const position = useSharedValue(startingPos) + const lastPosition = useSharedValue(startingPos) const panGesture = Gesture.Pan() .onUpdate(e => { - console.log("update") - position.value = e.translationX + lastPosition.value + const newPos = e.translationX + lastPosition.value + position.value = newPos + if (newPos < 0) { + position.value = 0 + } else if (newPos > usableWidth) { + position.value = usableWidth + } }) .onEnd(() => { lastPosition.value = position.value + runOnJS(wrapper)(position.value, usableWidth) }) const animatedPos = useAnimatedStyle(() => ({ transform: [{ translateX: position.value }], })) + return ( - + - - - - - - + /> + + + + + + ) } diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx index 1b0828cc..a875f59c 100644 --- a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx @@ -1,28 +1,19 @@ import React, { FC } from "react" -import { FlexAlignType, View } from "react-native" +import { Dimensions, View } from "react-native" import { usePalette } from "utils/colors" -import { BodyText } from "components/Text" -import Animated from "react-native-reanimated" +import { ValidCrowdStatus } from "../CrowdingSection" interface CrowdSliderStaticProps { - position?: "basso" | "medio" | "alto" + position: ValidCrowdStatus } export const CrowdSliderStatic: FC = props => { const { isLight } = usePalette() - const getPos = ( - pos?: "basso" | "medio" | "alto" - ): FlexAlignType | undefined => { - if (pos === "basso") { - return "flex-start" - } else if (pos === "medio") { - return "center" - } else if (pos === "alto") { - return "flex-end" - } - return undefined - } + //56 is padding, 28 is circle diameter + const usableWidth = Dimensions.get("screen").width - 56 - 28 + + const startingPos = ((props.position - 1) / 4) * usableWidth return ( @@ -43,7 +34,7 @@ export const CrowdSliderStatic: FC = props => { backgroundColor: "#D9D9D9", borderRadius: 14, top: -14, - alignSelf: getPos(props.position), + transform: [{ translateX: startingPos }], }} /> diff --git a/src/components/FreeClass/ClassDetails/CrowdingSection.tsx b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx index 9416da54..ebec374d 100644 --- a/src/components/FreeClass/ClassDetails/CrowdingSection.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx @@ -1,22 +1,56 @@ -import React, { FC, useMemo, useState } from "react" +import React, { FC, useEffect, useState } from "react" import { Pressable, View } from "react-native" import { usePalette } from "utils/colors" import { BodyText } from "components/Text" import { CrowdSliderStatic } from "./CrowdSlider/CrowdSliderStatic" -import { ModalCustom } from "components/Modal" import { CrowdSliderDynamic } from "./CrowdSlider/CrowdSliderDynamic" import { CrowdSliderLabels } from "./CrowdSlider/CrowdSliderLabels" import { ButtonCustom } from "components/Settings" +import { ModalWithGestures } from "../ModalWithGestures" +import { api, RetryType } from "api" interface CrowdingSectionProps { + roomId: number isSlidable?: boolean onSlided?: () => void } +export type ValidCrowdStatus = 1 | 2 | 3 | 4 | 5 + export const CrowdingSection: FC = props => { const { isLight } = usePalette() const [isModalVisible, setIsModalVisible] = useState(false) + + const [occupancyRate, setOccupancyRate] = useState(1) + + let occupancyRateUser: ValidCrowdStatus = 3 + + const getOccupancyRate = async () => { + try { + const res = await api.rooms.getOccupancyRate(props.roomId) + if (res.occupancy_rate !== null) { + setOccupancyRate(res.occupancy_rate) + } + } catch (err) { + console.log(err) + } + } + + useEffect(() => { + void getOccupancyRate() + }, []) + + const postOccupancyRate = async () => { + try { + await api.rooms.postOccupancyRate(props.roomId, occupancyRateUser, { + retryType: RetryType.RETRY_N_TIMES, + maxRetries: 3, + }) + } catch (err) { + console.log(err) + } + } return ( = props => { > Affollamento: - + + = props => { color: isLight ? "#454773" : "#fff", }} > - Se il dato sull'affollamento non è corretto + Se il dato sull'affollamento non è corretto setIsModalVisible(true)}> = props => { - setIsModalVisible(false)} - centerText={true} > = props => { textAlign: "center", }} > - Indica il livello di affollamento {"\n"} dell'aula + Indica il livello di affollamento {"\n"} dell'aula - - + + + (occupancyRateUser = pos) + } + /> @@ -100,10 +138,13 @@ export const CrowdingSection: FC = props => { text="Conferma" light={true} style={{ alignSelf: "center" }} - onPress={() => console.log("press")} + onPress={() => { + void postOccupancyRate() + setIsModalVisible(false) + }} /> - + ) } diff --git a/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx b/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx index 3529f041..aac3ed55 100644 --- a/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx +++ b/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx @@ -1,10 +1,9 @@ -import React, { FC, useMemo } from "react" +import React, { FC } from "react" import { View } from "react-native" import { usePalette } from "utils/colors" import { BodyText } from "components/Text" import { RoomUtilsTile } from "./RoomUtilsTile" - interface RoomUtilsSectionProps { power?: boolean computers?: boolean diff --git a/src/components/FreeClass/FreeClassList.tsx b/src/components/FreeClass/FreeClassList.tsx index 97a72e5e..9867b0e2 100644 --- a/src/components/FreeClass/FreeClassList.tsx +++ b/src/components/FreeClass/FreeClassList.tsx @@ -50,7 +50,8 @@ export const FreeClassList: FC = props => { ) navigate("RoomDetails", { room: selectedRoom, - startDate : props.date.toISOString() + startDate: props.date.toISOString(), + roomId: room, }) } catch (err) { console.log(err) diff --git a/src/components/FreeClass/ModalWithGestures.tsx b/src/components/FreeClass/ModalWithGestures.tsx new file mode 100644 index 00000000..75ad1f41 --- /dev/null +++ b/src/components/FreeClass/ModalWithGestures.tsx @@ -0,0 +1,128 @@ +import React, { FC } from "react" +import { View, Modal, StyleSheet, Pressable } from "react-native" +import { usePalette } from "utils/colors" +import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" +import { deleteSvg as icon } from "assets/modal" +import { gestureHandlerRootHOC } from "react-native-gesture-handler" +export interface ModalWithGesturesProps { + /** + * content of the modal + */ + children: React.ReactNode + + /** + * whether ot not to show the modal + */ + isShowing: boolean + /** + * this function hides the modal by changing the state in the parent component + */ + onClose: () => void +} + +/** + * custom modal component that can use reanimated gestures!! The problem with standard modals is + * that gestures wrapped inside Pressable dont work. The content needs to use `gestureHandlerRootHOC` + * as implemented below. + * + */ +export const ModalWithGestures: FC = props => { + const { backgroundSecondary, modalBarrier } = usePalette() + + const deleteSvg = useSVG(icon.svg) + + // eslint-disable-next-line @typescript-eslint/naming-convention + const ContentRootHOC = gestureHandlerRootHOC(() => ( + + + props.onClose()} + > + + + {deleteSvg && ( + + )} + + + + + + {props.children} + + + + + )) + + return ( + //TODO: animationType fade or slide? + + + + ) +} + +const styles = StyleSheet.create({ + pageWrapper: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + contentWrapper: { + borderRadius: 12, + + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 3, + }, + shadowOpacity: 0.27, + shadowRadius: 4.65, + elevation: 6, + }, + circle: { + width: 30, + height: 30, + backgroundColor: "#ffffff", + borderRadius: 15, + marginTop: 96, + marginBottom: 8, + justifyContent: "center", + alignItems: "center", + }, +}) diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index a273aad5..6273b11e 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,6 +1,5 @@ import React, { FC } from "react" import { View, Modal, StyleSheet, Pressable } from "react-native" -import { Text } from "components/Text" import { usePalette } from "utils/colors" import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" import { deleteSvg as icon } from "assets/modal" @@ -9,8 +8,6 @@ export interface ModalCustomProps { * content of the modal */ children: React.ReactNode - title: string - subTitle?: string /** * whether ot not to show the modal */ @@ -19,12 +16,6 @@ export interface ModalCustomProps { * this function hides the modal by changing the state in the parent component */ onClose: () => void - - /** - * whether ot not to center title and subtitle and apply different margins - * @default false - */ - centerText?: boolean } /** @@ -32,9 +23,8 @@ export interface ModalCustomProps { * */ export const ModalCustom: FC = props => { - const { backgroundSecondary, homeBackground, modalBarrier, isLight } = - usePalette() - const centerText = props.centerText ?? false + const { backgroundSecondary, modalBarrier } = usePalette() + const deleteSvg = useSVG(icon.svg) return ( //TODO: animationType fade or slide? @@ -125,14 +115,4 @@ const styles = StyleSheet.create({ justifyContent: "center", alignItems: "center", }, - title: { - fontSize: 32, - marginHorizontal: 27, - fontWeight: "900", - }, - subTitle: { - fontSize: 13, - marginHorizontal: 27, - fontWeight: "600", - }, }) diff --git a/src/navigation/NavigationTypes.ts b/src/navigation/NavigationTypes.ts index f8f7a717..e4165f7c 100644 --- a/src/navigation/NavigationTypes.ts +++ b/src/navigation/NavigationTypes.ts @@ -50,7 +50,7 @@ export type MainStackNavigatorParams = { FreeClassrooms: undefined CampusChoice: { currentDate: string } PositionChoice: undefined - RoomDetails: { room: RoomDetails; startDate : string } + RoomDetails: { room: RoomDetails; startDate: string; roomId: number } BuildingChoice: { campus: CampusItem; currentDate: string } ClassChoice: { building: BuildingItem; currentDate: string } Groups: undefined diff --git a/src/pages/FreeClass/RoomDetails.tsx b/src/pages/FreeClass/RoomDetails.tsx index b3aa1a9e..1119a962 100644 --- a/src/pages/FreeClass/RoomDetails.tsx +++ b/src/pages/FreeClass/RoomDetails.tsx @@ -1,8 +1,6 @@ import { MainStackScreen } from "navigation/NavigationTypes" import React from "react" -import { View } from "react-native" import { PageWrapper } from "components/FreeClass/ClassDetails/PageWrapper" -import { BodyText } from "components/Text" import { InfoMapTile } from "components/FreeClass/ClassDetails/InfoMapTile" import { TimeLeftTile } from "components/FreeClass/ClassDetails/TimeLeftTile" import { RoomUtilsSection } from "components/FreeClass/ClassDetails/RoomUtilsSection" @@ -11,10 +9,10 @@ import { CrowdingSection } from "components/FreeClass/ClassDetails/CrowdingSecti export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { // funny how renaming it class breaks everything, because class is also a keyword - const { room, startDate } = props.route.params + const { room, startDate, roomId } = props.route.params return ( - + = props => { roomName={room.name} /> - + ) diff --git a/src/utils/rooms.ts b/src/utils/rooms.ts index 22090801..e97a761d 100644 --- a/src/utils/rooms.ts +++ b/src/utils/rooms.ts @@ -1,20 +1,4 @@ -/** - * BL.27.04 --> {"27.04" , false} - * - * "F.LLI CASTIGLIONI" ---> {"F.LLI CASTIGLIONI", true} - * - * B8 0.3 ---> {"0.3" , false} - * - * there are some pathological cases, for example: - * - * "ATRIO P.T. - ED. BL.27" - * "CORRIDOIO SX 1� P. - ED. BL.27" - * B8 0.10 (EX B8.0.8) - * - * what should I show? - * - * probably I need to find another way, maybe ask aliases from backend? - */ +import { ValidCrowdStatus } from "components/FreeClass/ClassDetails/CrowdingSection" export function extractRoom(val: string) { const arr = val.split(".") @@ -50,3 +34,24 @@ export function extractTimeLeft(startDate: Date) { const isPositive = hours >= 0 && minutes >= 0 return { hoursLeft, minutesLeft, isPositive } } + +export function getCrowdStatus(pos: number, width: number): ValidCrowdStatus { + if (pos < 0) { + pos = 0 + } + + const radius = 14 + const percentage = (pos + radius) / (width + 2 * radius) + if (percentage < 0.2) { + return 1 + } else if (percentage < 0.4) { + return 2 + } else if (percentage < 0.6) { + return 3 + } else if (percentage < 0.8) { + return 4 + } else { + return 5 + } +} + From e5e60478ebe1c7b0da6a7ecd7a9b1ac47456f642 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Tue, 14 Feb 2023 23:04:40 +0100 Subject: [PATCH 06/15] fix styling and lint --- src/components/FreeClass/ClassDetails/CrowdingSection.tsx | 4 ++-- src/components/Home/MainMenu.tsx | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/FreeClass/ClassDetails/CrowdingSection.tsx b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx index ebec374d..90909eb1 100644 --- a/src/components/FreeClass/ClassDetails/CrowdingSection.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx @@ -136,8 +136,8 @@ export const CrowdingSection: FC = props => { { void postOccupancyRate() setIsModalVisible(false) diff --git a/src/components/Home/MainMenu.tsx b/src/components/Home/MainMenu.tsx index dfc61029..2f38fd85 100644 --- a/src/components/Home/MainMenu.tsx +++ b/src/components/Home/MainMenu.tsx @@ -106,9 +106,6 @@ export const MainMenu: FC<{ filter?: string }> = ({ filter }) => { showsHorizontalScrollIndicator={false} > setModalVisible(false)} > From ea30f16b3cd6a0d1dcd70c021b817477bbdd4564 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Tue, 14 Feb 2023 23:37:14 +0100 Subject: [PATCH 07/15] open map --- .../FreeClass/ClassDetails/InfoMapTile.tsx | 202 ++++++++++-------- src/utils/rooms.ts | 2 +- 2 files changed, 117 insertions(+), 87 deletions(-) diff --git a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx index 430c337b..70f52864 100644 --- a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx +++ b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx @@ -1,5 +1,5 @@ import React, { FC } from "react" -import { View } from "react-native" +import { Linking, Platform, Pressable, View } from "react-native" import { usePalette } from "utils/colors" import { BodyText, Text } from "components/Text" import { extractBuilding, extractRoom } from "utils/rooms" @@ -8,8 +8,8 @@ import expand from "assets/freeClassrooms/expand.svg" import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" interface InfoMapTileProps { - roomName?: string - building?: string + roomName: string + building: string address?: string capacity?: string } @@ -18,11 +18,29 @@ export const InfoMapTile: FC = props => { const { isLight, primary } = usePalette() console.log(props.building) - const building = extractBuilding(props.building ?? "Edificio 50") + const building = extractBuilding(props.building) console.log(props.roomName) - const roomName = extractRoom(props.roomName ?? "50.1.1") + const roomName = extractRoom(props.roomName) const expandSvg = useSVG(expand) + + /*from https://stackoverflow.com/questions/73653813/how-to-open-google-map-with-latitude-and-longitude*/ + const openAddressOnMap = (label: string, lat: string, lng: string) => { + const scheme = Platform.select({ + ios: "maps:0,0?q=", + android: "geo:0,0?q=", + }) + const latLng = `${lat},${lng}` + if (scheme) { + const url = Platform.select({ + ios: `${scheme}${label}@${latLng}`, + android: `${scheme}${latLng}(${label})`, + }) + if (url) { + void Linking.openURL(url) + } + } + } return ( = props => { - - + openAddressOnMap( + props.building ? building + roomName : roomName, + "45.478053", + "9.228061" + ) + } + > + + - {expand && expandSvg && ( - - + {expand && expandSvg && ( + - - - - )} - - + + + + + )} + + - - - consulta la{" "} - - - mappa - + + consulta la{" "} + + + mappa + + - + ) } diff --git a/src/utils/rooms.ts b/src/utils/rooms.ts index e97a761d..fd8c124c 100644 --- a/src/utils/rooms.ts +++ b/src/utils/rooms.ts @@ -5,7 +5,7 @@ export function extractRoom(val: string) { if (arr.length > 2) { return arr.slice(1).join(".") } else { - return undefined + return val } } From 8e8c1cf9fa95e78425b085a7381c53906fb177e9 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Wed, 15 Feb 2023 12:28:15 +0100 Subject: [PATCH 08/15] small fix --- .../FreeClass/ClassDetails/InfoMapTile.tsx | 192 ++++++++++-------- src/pages/FreeClass/RoomDetails.tsx | 2 - 2 files changed, 102 insertions(+), 92 deletions(-) diff --git a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx index 70f52864..5d5fdce8 100644 --- a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx +++ b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx @@ -118,109 +118,121 @@ export const InfoMapTile: FC = props => { - - - openAddressOnMap( - props.building ? building + roomName : roomName, - "45.478053", - "9.228061" - ) - } + - - + + openAddressOnMap( + props.building + ? building + "." + roomName + : roomName, + "45.478053", + "9.228061" + ) + } > - {expand && expandSvg && ( - - + {expand && expandSvg && ( + - - - - )} - - - - - - consulta la{" "} - - + + + + )} + + + - mappa - - + + consulta la{" "} + + + mappa + + + - + ) } diff --git a/src/pages/FreeClass/RoomDetails.tsx b/src/pages/FreeClass/RoomDetails.tsx index 1119a962..1b815575 100644 --- a/src/pages/FreeClass/RoomDetails.tsx +++ b/src/pages/FreeClass/RoomDetails.tsx @@ -5,10 +5,8 @@ import { InfoMapTile } from "components/FreeClass/ClassDetails/InfoMapTile" import { TimeLeftTile } from "components/FreeClass/ClassDetails/TimeLeftTile" import { RoomUtilsSection } from "components/FreeClass/ClassDetails/RoomUtilsSection" import { CrowdingSection } from "components/FreeClass/ClassDetails/CrowdingSection" -/* eslint-disable @typescript-eslint/naming-convention */ export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { - // funny how renaming it class breaks everything, because class is also a keyword const { room, startDate, roomId } = props.route.params return ( From 9fe5aa2b658941488a2e8683eade66b1e5f7d07a Mon Sep 17 00:00:00 2001 From: Tommaso Morganti Date: Mon, 20 Feb 2023 11:15:37 +0100 Subject: [PATCH 09/15] Merge remote-tracking branch 'origin/feature/CU-861m3wffu' into feature/CU-861m77zun --- .eslintrc.cjs | 142 +- .github/ISSUE_TEMPLATE/bug_report.md | 31 + .github/ISSUE_TEMPLATE/feature_request.md | 21 + .github/workflows/check_file_length.yml | 18 +- .github/workflows/clickup_integration.yml | 18 + .github/workflows/clickup_issues.yml | 32 - .github/workflows/clickuptask.yml | 63 - .github/workflows/prune-branches.yml | 50 +- .github/workflows/publish.yml | 38 +- .github/workflows/test.yml | 26 +- .prettierrc | 10 +- .vscode/extensions.json | 10 +- .vscode/launch.json | 26 +- App.tsx | 254 +- README.md | 34 +- app.json | 6 +- assets/asset-declarations.d.ts | 12 +- assets/groups/index.ts | 38 +- assets/modal/index.ts | 12 +- assets/navbar/index.ts | 56 +- assets/settings/index.ts | 63 +- assets/settings/privacy.svg | 12 + assets/tray/index.ts | 36 +- babel.config.js | 41 +- package.json | 165 +- scripts/prune-branches.js | 72 +- src/AppContainer.tsx | 22 +- src/MainContainer.tsx | 88 +- src/SettingsContainer.tsx | 22 +- src/api/HttpClient.ts | 610 ++--- src/api/Room.ts | 92 - src/api/articles.ts | 134 +- src/api/auth.ts | 36 +- src/api/event.ts | 54 + src/api/groups.ts | 84 +- src/api/index.ts | 18 +- src/api/rooms.ts | 107 + src/api/tags.ts | 26 +- src/api/timetable.ts | 76 +- src/api/user.ts | 135 +- src/components/Button.tsx | 54 + src/components/CardWithGradient.tsx | 121 +- src/components/ContentWrapperScroll.tsx | 129 +- src/components/Divider.tsx | 32 +- src/components/FreeClass/AddressText.tsx | 133 +- .../CrowdSlider/CrowdSliderDynamic.tsx | 134 +- .../CrowdSlider/CrowdSliderLabels.tsx | 82 +- .../CrowdSlider/CrowdSliderStatic.tsx | 60 +- .../ClassDetails/CrowdingSection.tsx | 240 +- .../FreeClass/ClassDetails/InfoMapTile.tsx | 412 ++- .../FreeClass/ClassDetails/PageWrapper.tsx | 85 +- .../ClassDetails/RoomUtilsSection.tsx | 49 +- .../FreeClass/ClassDetails/RoomUtilsTile.tsx | 126 +- .../FreeClass/ClassDetails/TimeLeftTile.tsx | 364 ++- .../FreeClass/DateTimePicker/DateTimeBox.tsx | 52 +- .../DateTimePicker/DateTimePicker.tsx | 201 +- src/components/FreeClass/FreeClassList.tsx | 391 ++- src/components/FreeClass/Map.tsx | 198 +- .../FreeClass/ModalWithGestures.tsx | 194 +- src/components/FreeClass/PositionModality.tsx | 137 + .../FreeClass/PositionSearchBar.tsx | 132 + src/components/FreeClass/buildingCoords.json | 1165 +++++++++ src/components/Groups/AnimatedLine.tsx | 84 +- .../Groups/AnimatedPoliSearchBar.tsx | 38 +- src/components/Groups/Filters.tsx | 324 ++- src/components/Groups/GroupTile.tsx | 170 +- src/components/Groups/ModalGroup.tsx | 250 +- src/components/Groups/ModalGroupItem.tsx | 160 +- src/components/Groups/OutlinedButton.tsx | 108 +- src/components/Groups/PageWrapper.tsx | 84 +- .../Home/Highlights/CustomFlatlist.tsx | 158 ++ .../Home/Highlights/HighlightsManager.tsx | 190 +- .../Home/Highlights/PaginationCarousel.tsx | 139 +- .../Home/Highlights/PoliCarousel.tsx | 211 +- src/components/Home/Highlights/lectures.json | 674 ----- src/components/Home/MainMenu.tsx | 335 ++- src/components/Home/MainTitle.tsx | 82 +- src/components/Home/MenuButton.tsx | 224 +- src/components/Home/News/NewsBottomSheet.tsx | 216 ++ src/components/Home/News/NewsManager.tsx | 136 + src/components/Home/News/NewsTagsGrid.tsx | 63 + src/components/Home/News/index.tsx | 1 + src/components/Home/NewsBottomSheet.tsx | 137 - src/components/Home/NewsCategoriesGrid.tsx | 143 -- src/components/Home/PoliSearchBar.tsx | 220 +- src/components/Home/StickyHeader.tsx | 86 +- src/components/Home/index.tsx | 1 - src/components/Modal.tsx | 230 +- src/components/ModalWithButtons.tsx | 144 ++ src/components/NavBar.tsx | 395 +-- src/components/Page.tsx | 237 -- src/components/ScrollPage.tsx | 258 +- src/components/ScrollPageInfinite.tsx | 258 ++ src/components/Settings/ButtonCustom.tsx | 54 - src/components/Settings/CareerColumn.tsx | 52 +- src/components/Settings/CareerTile.tsx | 50 +- src/components/Settings/Description.tsx | 26 + src/components/Settings/ModalPicker.tsx | 110 + src/components/Settings/ModalSelection.tsx | 137 - src/components/Settings/RadioButtonCustom.tsx | 176 +- src/components/Settings/SelectTile.tsx | 95 +- src/components/Settings/SettingTile.tsx | 198 +- src/components/Settings/UserAnonymousTile.tsx | 118 +- src/components/Settings/UserDetailsTile.tsx | 114 +- src/components/Settings/index.ts | 2 - src/components/Text/BodyText.tsx | 56 +- src/components/Text/CardTitle.tsx | 34 +- src/components/Text/HyperLink.tsx | 28 + src/components/Text/Subtitle.tsx | 36 +- src/components/Text/Text.tsx | 36 +- src/components/Text/Title.tsx | 34 +- src/components/Text/index.tsx | 1 + src/components/TouchableRipple.tsx | 62 +- src/components/Tray/TrayButton.tsx | 64 +- src/components/Tray/index.tsx | 44 +- src/contexts/login.ts | 44 + src/contexts/newsPreferences.ts | 32 + src/{utils => contexts}/settings.ts | 20 +- src/navigation/MainStackNavigator.tsx | 67 +- src/navigation/NavigationTypes.ts | 81 +- src/navigation/RootStackNavigator.tsx | 23 +- src/navigation/SettingsNavigator.tsx | 22 +- src/pages/ArticleDetails.tsx | 107 - src/pages/Error404.tsx | 160 +- src/pages/FreeClass/BuildingChoice.tsx | 356 ++- src/pages/FreeClass/CampusChoice.tsx | 282 +- src/pages/FreeClass/ClassChoice.tsx | 122 +- src/pages/FreeClass/FreeClassrooms.tsx | 321 +-- src/pages/FreeClass/PositionChoice.tsx | 445 ++-- src/pages/FreeClass/RoomDetails.tsx | 28 +- src/pages/Groups.tsx | 240 +- src/pages/Home.tsx | 184 +- src/pages/IntroSlider.tsx | 222 +- src/pages/Login.tsx | 286 ++- src/pages/NewsList.tsx | 74 - src/pages/news/ArticleDetails.tsx | 125 + src/pages/news/ArticlesList.tsx | 119 + src/pages/news/OtherCategories.tsx | 61 + src/pages/settings/Help.tsx | 49 +- src/pages/settings/Privacy.tsx | 235 ++ src/pages/settings/Settings.tsx | 278 +- src/utils/cardsPatterns.ts | 120 +- src/utils/carousel.ts | 113 + src/utils/colors.ts | 312 ++- src/utils/functions.ts | 40 +- src/utils/groups.ts | 141 +- src/utils/height.ts | 32 +- src/utils/loadTokens.ts | 10 +- src/utils/login.ts | 45 - src/utils/outsideClick.tsx | 205 +- src/utils/rooms.ts | 73 +- src/utils/strings.ts | 18 + src/utils/useMounted.ts | 10 +- tsconfig.json | 33 +- yarn.lock | 2258 ++++++++--------- 155 files changed, 11574 insertions(+), 10383 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/clickup_integration.yml delete mode 100644 .github/workflows/clickup_issues.yml delete mode 100644 .github/workflows/clickuptask.yml create mode 100644 assets/settings/privacy.svg delete mode 100644 src/api/Room.ts create mode 100644 src/api/event.ts create mode 100644 src/api/rooms.ts create mode 100644 src/components/Button.tsx create mode 100644 src/components/FreeClass/PositionModality.tsx create mode 100644 src/components/FreeClass/PositionSearchBar.tsx create mode 100644 src/components/FreeClass/buildingCoords.json create mode 100644 src/components/Home/Highlights/CustomFlatlist.tsx delete mode 100644 src/components/Home/Highlights/lectures.json create mode 100644 src/components/Home/News/NewsBottomSheet.tsx create mode 100644 src/components/Home/News/NewsManager.tsx create mode 100644 src/components/Home/News/NewsTagsGrid.tsx create mode 100644 src/components/Home/News/index.tsx delete mode 100644 src/components/Home/NewsBottomSheet.tsx delete mode 100644 src/components/Home/NewsCategoriesGrid.tsx create mode 100644 src/components/ModalWithButtons.tsx delete mode 100644 src/components/Page.tsx create mode 100644 src/components/ScrollPageInfinite.tsx delete mode 100644 src/components/Settings/ButtonCustom.tsx create mode 100644 src/components/Settings/Description.tsx create mode 100644 src/components/Settings/ModalPicker.tsx delete mode 100644 src/components/Settings/ModalSelection.tsx create mode 100644 src/components/Text/HyperLink.tsx create mode 100644 src/contexts/login.ts create mode 100644 src/contexts/newsPreferences.ts rename src/{utils => contexts}/settings.ts (50%) delete mode 100644 src/pages/ArticleDetails.tsx delete mode 100644 src/pages/NewsList.tsx create mode 100644 src/pages/news/ArticleDetails.tsx create mode 100644 src/pages/news/ArticlesList.tsx create mode 100644 src/pages/news/OtherCategories.tsx create mode 100644 src/pages/settings/Privacy.tsx create mode 100644 src/utils/carousel.ts delete mode 100644 src/utils/login.ts create mode 100644 src/utils/strings.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 6485d0d4..1b0727bc 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,75 +1,81 @@ -/* eslint-disable indent, no-undef */ -/* eslint-disable prettier/prettier */ -// eslint-disable-next-line no-undef module.exports = { - settings: { - react: { - version: "detect", - }, + settings: { + react: { + version: "detect", }, - env: { - browser: true, - es2021: true, + }, + env: { + browser: true, + es2021: true, + }, + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "prettier", + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaFeatures: { + jsx: true, }, - extends: [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - "prettier", + ecmaVersion: "latest", + sourceType: "module", + tsconfigRootDir: __dirname, + }, + plugins: ["react", "@typescript-eslint", "prettier"], + rules: { + "prettier/prettier": "error", + "no-extra-parens": ["off"], + "linebreak-style": ["error", "unix"], + quotes: ["error", "double"], + "react/prop-types": ["off"], + "react/no-multi-comp": ["error"], + "react/self-closing-comp": ["error"], + "@typescript-eslint/naming-convention": [ + "error", + { + selector: "default", + format: ["camelCase"], + leadingUnderscore: "allow", + trailingUnderscore: "allow", + }, + { + selector: "variable", + types: ["function"], + format: ["camelCase", "PascalCase"], + }, + { + selector: "variable", + format: ["camelCase", "UPPER_CASE"], + leadingUnderscore: "allow", + trailingUnderscore: "allow", + }, + + { + selector: "typeLike", + format: ["PascalCase"], + }, + { + selector: "enumMember", + format: ["UPPER_CASE"], + }, + ], + "@typescript-eslint/restrict-plus-operands": "off", + "@typescript-eslint/no-misused-promises": [ + "error", + { + checksVoidReturn: false, + }, ], - parser: "@typescript-eslint/parser", - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - ecmaVersion: "latest", - sourceType: "module", - tsconfigRootDir: __dirname, + }, + overrides: [ + { + files: ["*.ts", "*.tsx"], + parserOptions: { project: ["./tsconfig.json"], + }, }, - plugins: ["react", "@typescript-eslint", "prettier"], - rules: { - "no-extra-parens": ["off"], - "linebreak-style": ["error", "unix"], - quotes: ["error", "double"], - "react/prop-types": ["off"], - "react/no-multi-comp": ["error"], - "@typescript-eslint/naming-convention": [ - "error", - { - selector: "default", - format: ["camelCase"], - leadingUnderscore: "allow", - trailingUnderscore: "allow", - }, - { - selector: "variable", - types: ["function"], - format: ["camelCase", "PascalCase"], - }, - { - selector: "variable", - format: ["camelCase", "UPPER_CASE"], - leadingUnderscore: "allow", - trailingUnderscore: "allow", - }, - - { - selector: "typeLike", - format: ["PascalCase"], - }, - { - selector: "enumMember", - format: ["UPPER_CASE"], - }, - ], - "@typescript-eslint/restrict-plus-operands": "off", - "@typescript-eslint/no-misused-promises": [ - "error", - { - checksVoidReturn: false, - }, - ], - }, + ], } diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..d3715840 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Report something that isn't working as expected, a weird behavior, a crash + or a bug +title: "[BUG] Bug report title" +labels: 'type: bug' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**Steps to reproduce** +Explain steps to reproduce the bug + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Describe your environment:** +(You can find this information in Settings > About this app) + - Device: [e.g. iPhone 14, Android Emulator] + - OS: [e.g. iOS 16.3.1, Android 11] + - Client [e.g. Expo Go, Standalone App] + - Version [e.g. 1.0.0] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..197c239b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,21 @@ +--- +name: Feature request +about: Suggest an idea that you think will improve the app, a suggestion on better + behavior that doesn't exist yet or a whole new feature you think should be added +title: "[FEATURE] New feature request" +labels: 'type: suggestion' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered, explain pros and cons where applicable. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/check_file_length.yml b/.github/workflows/check_file_length.yml index 53bd7088..444da9c7 100644 --- a/.github/workflows/check_file_length.yml +++ b/.github/workflows/check_file_length.yml @@ -8,12 +8,12 @@ jobs: check_file_length: runs-on: ubuntu-20.04 steps: - - name: Install dependencies - run: | - sudo apt-get install bash curl git python3 python3-pip - - name: Pull repository - uses: actions/checkout@v2 - - name: Get check_file_length - run: curl -Lo check_file_length.py https://raw.githubusercontent.com/PoliNetworkOrg/CheckFilesLength/master/main.py - - name: Run check_file_length - run: python3 ./check_file_length.py + - name: Install dependencies + run: | + sudo apt-get install bash curl git python3 python3-pip + - name: Pull repository + uses: actions/checkout@v2 + - name: Get check_file_length + run: curl -Lo check_file_length.py https://raw.githubusercontent.com/PoliNetworkOrg/CheckFilesLength/master/main.py + - name: Run check_file_length + run: python3 ./check_file_length.py diff --git a/.github/workflows/clickup_integration.yml b/.github/workflows/clickup_integration.yml new file mode 100644 index 00000000..4cd810aa --- /dev/null +++ b/.github/workflows/clickup_integration.yml @@ -0,0 +1,18 @@ +name: ClickUp integration + +on: + pull_request: + types: [opened, reopened, ready_for_review, closed] + issues: + types: [opened, edited, reopened, closed, labeled] + +jobs: + automate: + name: Automate ClickUp integration + runs-on: ubuntu-latest + steps: + - uses: polinetworkorg/clickup-integration-action@main + with: + problem_list_id: 210431256 + feature_list_id: 900400324463 + clickup_api_key: ${{ secrets.CLICKUP_TOKEN }} diff --git a/.github/workflows/clickup_issues.yml b/.github/workflows/clickup_issues.yml deleted file mode 100644 index b56ed61d..00000000 --- a/.github/workflows/clickup_issues.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Create Problem Task on ClickUp - -on: - issues: - types: [opened] - -jobs: - problem: - name: Create Problem Task on ClickUp - runs-on: ubuntu-latest - - steps: - - name: Prepare Request Body - id: prepare - uses: actions/github-script@v3 - with: - result-encoding: string - script: | - const body = { - name: context.payload.issue.title, - description: context.payload.issue.body, - status: "to do" - } - return JSON.stringify(body) - - - name: Calling Clickup API via cURL call - run: | - curl -i -X POST \ - 'https://api.clickup.com/api/v2//list/210431256/task' \ - -H 'Authorization: ${{secrets.CLICKUP_TOKEN}}' \ - -H 'Content-Type: application/json' \ - -d ${{ toJSON(steps.prepare.outputs.result) }} diff --git a/.github/workflows/clickuptask.yml b/.github/workflows/clickuptask.yml deleted file mode 100644 index 386397a2..00000000 --- a/.github/workflows/clickuptask.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Update ClickUp task status - -on: - pull_request: - types: [opened, reopened, closed] - -jobs: - update: - name: Update ClickUp task status - if: github.event.action == 'opened' || github.event.action == 'reopened' || (github.event.action == 'closed' && github.event.pull_request.merged == true) - runs-on: ubuntu-latest - - steps: - - name: Get taskID - uses: actions/github-script@v6 - id: taskID - with: - result-encoding: string - script: | - const branch_name = context.payload.pull_request.head.ref; - console.log(`Branch name: ${branch_name}`); - if (!branch_name) return false; - const matches = branch_name.match(/feature\/CU-([a-z0-9]+)/i) - if (!matches) return false; - const taskID = matches[1] - console.log(`Task ID: ${taskID}`); - if (!taskID) return false; - return taskID; - - - name: Get new status - if: steps.taskID.outputs.result != 'false' - uses: actions/github-script@v6 - id: new-status - with: - result-encoding: string - script: | - const OPENED = context.payload.action === 'opened' || context.payload.action === 'reopened'; - return OPENED ? "in review" : "completed" - - - name: Change status with cURL call - if: steps.taskID.outputs.result != 'false' - run: | - curl -i -X PUT \ - 'https://api.clickup.com/api/v2/task/${{steps.taskID.outputs.result}}' \ - -H 'Authorization: ${{secrets.CLICKUP_TOKEN}}' \ - -H 'Content-Type: application/json' \ - -d '{ - "status": "${{steps.new-status.outputs.result}}" - }' - - - name: Comment on PR - if: steps.taskID.outputs.result != 'false' - uses: actions/github-script@v6 - id: my-script - with: - retries: 3 - script: | - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: `Status task aggiornato: CU-${{steps.taskid.outputs.result}} - ${{steps.new-status.outputs.result}}` - }) diff --git a/.github/workflows/prune-branches.yml b/.github/workflows/prune-branches.yml index c5f7e3a8..63bfeb7f 100644 --- a/.github/workflows/prune-branches.yml +++ b/.github/workflows/prune-branches.yml @@ -1,34 +1,34 @@ name: Prune EAS branches on: - workflow_dispatch: - schedule: - - cron: "42 0 * * *" # ahah funny number + workflow_dispatch: + schedule: + - cron: "42 0 * * *" # ahah funny number jobs: - prune: - name: Prune EAS branches - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 # fetch all branches to get the remote ones - - uses: actions/setup-node@v3 - with: - node-version: 16 + prune: + name: Prune EAS branches + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # fetch all branches to get the remote ones + - uses: actions/setup-node@v3 + with: + node-version: 16 - - name: Log remote branches - run: git branch -r + - name: Log remote branches + run: git branch -r - - name: Install dependencies - run: yarn install --immutable + - name: Install dependencies + run: yarn install --immutable - - name: Setup Expo - uses: expo/expo-github-action@v7 - with: - expo-version: latest - eas-version: latest - token: ${{ secrets.EXPO_TOKEN }} + - name: Setup Expo + uses: expo/expo-github-action@v7 + with: + expo-version: latest + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} - - name: Prune EAS branches - run: yarn run prune-branches + - name: Prune EAS branches + run: yarn run prune-branches diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6a8da593..ca195f68 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,25 +1,25 @@ name: Publish on: - push: + push: jobs: - publish: - name: Publish via EAS updates - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: 16 - cache: "yarn" + publish: + name: Publish via EAS updates + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 16 + cache: "yarn" - - name: Setup Expo - uses: expo/expo-github-action@v7 - with: - expo-version: latest - eas-version: latest - token: ${{ secrets.EXPO_TOKEN }} + - name: Setup Expo + uses: expo/expo-github-action@v7 + with: + expo-version: latest + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} - - run: yarn install --immutable - - name: Publish via EAS updates - run: eas update --auto + - run: yarn install --immutable + - name: Publish via EAS updates + run: eas update --auto diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d280c5bf..be64d895 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,17 +1,17 @@ name: Test on: - push: + push: jobs: - lint: - name: Run linters and tsc - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: 16 - cache: "yarn" - - run: yarn install --frozen-lockfile --ignore-scripts - - run: yarn run lint - - run: yarn run type-check + lint: + name: Run linters and tsc + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 16 + cache: "yarn" + - run: yarn install --frozen-lockfile --ignore-scripts + - run: yarn run lint + - run: yarn run type-check diff --git a/.prettierrc b/.prettierrc index 6673750d..9702e43c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,6 @@ { - "tabWidth": 4, - "useTabs": false, - "arrowParens": "avoid", - "semi": false -} \ No newline at end of file + "tabWidth": 2, + "useTabs": false, + "arrowParens": "avoid", + "semi": false +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 41366352..76495afc 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,7 @@ { - "recommendations": [ - "msjsdiag.vscode-react-native", - "esbenp.prettier-vscode", - "dbaeumer.vscode-eslint" - ] + "recommendations": [ + "msjsdiag.vscode-react-native", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint" + ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 0ef6b3e9..f3ce4579 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,15 +1,15 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Debug in Exponent", - "cwd": "${workspaceFolder}", - "type": "reactnative", - "request": "launch", - "platform": "exponent" - } - ] + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Debug in Exponent", + "cwd": "${workspaceFolder}", + "type": "reactnative", + "request": "launch", + "platform": "exponent" + } + ] } diff --git a/App.tsx b/App.tsx index 8663da89..828b0d43 100644 --- a/App.tsx +++ b/App.tsx @@ -4,20 +4,20 @@ import { NavigationContainer, DefaultTheme } from "@react-navigation/native" import { hideAsync } from "expo-splash-screen" import { useFonts } from "@expo-google-fonts/roboto" import { - Roboto_300Light, - Roboto_400Regular, - Roboto_500Medium_Italic, - Roboto_700Bold, - Roboto_900Black, + Roboto_300Light, + Roboto_400Regular, + Roboto_500Medium_Italic, + Roboto_700Bold, + Roboto_900Black, } from "@expo-google-fonts/roboto" import { AppContainer } from "./src/AppContainer" import { OutsideClickProvider } from "utils/outsideClick" -import { LoginContext, LoginState } from "utils/login" import { api } from "api" import AsyncStorage from "@react-native-async-storage/async-storage" -import { SettingsContext, Settings } from "utils/settings" +import { LoginContext, LoginState } from "contexts/login" +import { SettingsContext, Settings } from "contexts/settings" import { useLoadTokens } from "utils/loadTokens" import { HttpClient } from "api/HttpClient" import { usePalette } from "utils/colors" @@ -26,138 +26,136 @@ import { StatusBar } from "react-native" const client = HttpClient.getInstance() export default function App() { - const { homeBackground } = usePalette() - const [settingsReady, setSettingsReady] = useState(false) - const [settings, setSettings] = useState({ - theme: "predefined", - }) + const { homeBackground } = usePalette() + const [settingsReady, setSettingsReady] = useState(false) + const [settings, setSettings] = useState({ + theme: "predefined", + }) - //tracking first render - const firstRender = useRef(true) + //tracking first render + const firstRender = useRef(true) - // docs: https://docs.expo.dev/versions/latest/sdk/splash-screen/ - useEffect(() => { - async function prepare() { - try { - const settingsJSON = await AsyncStorage.getItem("settings") - if (settingsJSON) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const parsedSettings: Settings = JSON.parse(settingsJSON) - console.log("loaded theme: " + parsedSettings.theme) - setSettings(parsedSettings) - } - } catch (e) { - console.warn(e) - } finally { - setSettingsReady(true) - } + // docs: https://docs.expo.dev/versions/latest/sdk/splash-screen/ + useEffect(() => { + async function prepare() { + try { + const settingsJSON = await AsyncStorage.getItem("settings") + if (settingsJSON) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const parsedSettings: Settings = JSON.parse(settingsJSON) + console.log("loaded theme: " + parsedSettings.theme) + setSettings(parsedSettings) } + } catch (e) { + console.warn(e) + } finally { + setSettingsReady(true) + } + } - void prepare() - }, []) + void prepare() + }, []) - //Update storage as a side effect of settings change - useEffect(() => { - //skip first render - if (firstRender.current) { - firstRender.current = false - } else { - AsyncStorage.setItem("settings", JSON.stringify(settings)).catch( - err => console.log(err) - ) - console.log("Set theme " + settings.theme) - } - }, [settings]) - const [fontsLoaded] = useFonts({ - Roboto_300Light, - Roboto_400Regular, - Roboto_500Medium_Italic, - Roboto_700Bold, - Roboto_900Black, - }) - const tokensLoaded = useLoadTokens() + //Update storage as a side effect of settings change + useEffect(() => { + //skip first render + if (firstRender.current) { + firstRender.current = false + } else { + AsyncStorage.setItem("settings", JSON.stringify(settings)).catch(err => + console.log(err) + ) + console.log("Set theme " + settings.theme) + } + }, [settings]) + const [fontsLoaded] = useFonts({ + Roboto_300Light, + Roboto_400Regular, + Roboto_500Medium_Italic, + Roboto_700Bold, + Roboto_900Black, + }) + const tokensLoaded = useLoadTokens() - const [loginState, setLoginState] = useState({ - loggedIn: false, - }) + const [loginState, setLoginState] = useState({ + loggedIn: false, + }) - useEffect(() => { - // subscribe to the API login events to manage the login state - const handleLoginEvent = async (loggedIn: boolean) => { - if (loggedIn) { - const inf = await api.user.getPolimiUserInfo() - setLoginState({ - loggedIn, - userInfo: { - firstname: inf.nome, - lastname: inf.cognome, - careers: [ - { - matricola: inf.matricola, - type: "Studente TEMP - " + inf.classeCarriera, - }, - { matricola: "222222", type: "Visitatore" }, - { - matricola: "333333", - type: "Studente - Titolo Conseguito", - }, - { - matricola: "444444", - type: "Studente - Magistrale", - }, - ], - codPersona: inf.codicePersona, - profilePic: inf.fotoURL, - }, - }) - } else setLoginState({ loggedIn }) - } + useEffect(() => { + // subscribe to the API login events to manage the login state + const handleLoginEvent = async (loggedIn: boolean) => { + if (loggedIn) { + const inf = await api.user.getPolimiUserInfo() + setLoginState({ + loggedIn, + userInfo: { + firstname: inf.nome, + lastname: inf.cognome, + careers: [ + { + matricola: inf.matricola, + type: "Studente TEMP - " + inf.classeCarriera, + }, + { matricola: "222222", type: "Visitatore" }, + { + matricola: "333333", + type: "Studente - Titolo Conseguito", + }, + { + matricola: "444444", + type: "Studente - Magistrale", + }, + ], + codPersona: inf.codicePersona, + profilePic: inf.fotoURL, + }, + }) + } else setLoginState({ loggedIn }) + } - client.on("login_event", handleLoginEvent) - return () => { - client.removeListener("login_event", handleLoginEvent) - } - }, []) + client.on("login_event", handleLoginEvent) + return () => { + client.removeListener("login_event", handleLoginEvent) + } + }, []) - useEffect(() => { - if (settingsReady && fontsLoaded && tokensLoaded) { - void hideAsync().then(async () => { - if (loginState.loggedIn) { - console.log(await api.user.getPoliNetworkMe()) - console.log(await api.user.getPolimiUserInfo()) - } - }) + useEffect(() => { + if (settingsReady && fontsLoaded && tokensLoaded) { + void hideAsync().then(async () => { + if (loginState.loggedIn) { + console.log(await api.user.getPoliNetworkMe()) + console.log(await api.user.getPolimiUserInfo()) } - }, [settingsReady, fontsLoaded, tokensLoaded]) + }) + } + }, [settingsReady, fontsLoaded, tokensLoaded]) - if (!settingsReady || !fontsLoaded || !tokensLoaded) return null + if (!settingsReady || !fontsLoaded || !tokensLoaded) return null - return ( - + + + - - - - - - - - - - ) + + + + + + + ) } diff --git a/README.md b/README.md index 4cd0c532..d6a14efc 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,17 @@ Prerequisiti: -- [NodeJS](http://nodejs.org/) versione lts 16.14.2 (che su linux/mac consiglio di installare - tramite [nvm](https://github.com/nvm-sh/nvm)) -- [Yarn](https://yarnpkg.com) che una volta installato Node si può installare con - ```sh - corepack enable - ``` -- La CLI Expo che si può installare con il comando - ```sh - npm install --global expo-cli - ``` -- L'applicazione Expo GO installata sul proprio telefono (https://expo.dev/client) +- [NodeJS](http://nodejs.org/) versione lts 16.14.2 (che su linux/mac consiglio di installare + tramite [nvm](https://github.com/nvm-sh/nvm)) +- [Yarn](https://yarnpkg.com) che una volta installato Node si può installare con + ```sh + corepack enable + ``` +- La CLI Expo che si può installare con il comando + ```sh + npm install --global expo-cli + ``` +- L'applicazione Expo GO installata sul proprio telefono (https://expo.dev/client) Una volta clonata la repo la prima cosa da fare è installare le dependencies con @@ -83,9 +83,9 @@ Molte risorse utili sono linkate nei commenti in [App.tsx](App.tsx) In generale è sono scritte molto bene le documentazioni di -- [React](https://it.reactjs.org/docs/getting-started.html) - per la sintassi e i paradigmi utilizzati per la UI -- [React Native](https://reactnative.dev/docs/getting-started) - specificatamente su come funziona su mobile e -- [Expo](https://docs.expo.dev) per le molti moduli nativi - (tipo fotocamera, gps, ecc.) +- [React](https://it.reactjs.org/docs/getting-started.html) + per la sintassi e i paradigmi utilizzati per la UI +- [React Native](https://reactnative.dev/docs/getting-started) + specificatamente su come funziona su mobile e +- [Expo](https://docs.expo.dev) per le molti moduli nativi + (tipo fotocamera, gps, ecc.) diff --git a/app.json b/app.json index ed0c5b29..d1113041 100644 --- a/app.json +++ b/app.json @@ -16,9 +16,7 @@ "fallbackToCacheTimeout": 0, "url": "https://u.expo.dev/3279e65d-78fe-4db0-9d4f-368a501f4912" }, - "assetBundlePatterns": [ - "**/*" - ], + "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true }, @@ -43,4 +41,4 @@ } }, "name": "polifemo" -} \ No newline at end of file +} diff --git a/assets/asset-declarations.d.ts b/assets/asset-declarations.d.ts index e98fccbb..696e8531 100644 --- a/assets/asset-declarations.d.ts +++ b/assets/asset-declarations.d.ts @@ -1,11 +1,11 @@ declare module "*.png" { - import { ImageSourcePropType } from "react-native" - const value: ImageSourcePropType - export default value + import { ImageSourcePropType } from "react-native" + const value: ImageSourcePropType + export default value } declare module "*.svg" { - import { DataSourceParam } from "@shopify/react-native-skia" - const value: DataSourceParam - export default value + import { DataSourceParam } from "@shopify/react-native-skia" + const value: DataSourceParam + export default value } diff --git a/assets/groups/index.ts b/assets/groups/index.ts index fd402523..58e902a2 100644 --- a/assets/groups/index.ts +++ b/assets/groups/index.ts @@ -8,25 +8,25 @@ import telegram from "./telegram.svg" */ export const platformIconList = ["telegram", "whatsapp", "facebook"] as const -export type PlatformIcon = typeof platformIconList[number] +export type PlatformIcon = (typeof platformIconList)[number] export const platformIcons: Record< - PlatformIcon, - { svg: DataSourceParam; width: number; heigth: number } + PlatformIcon, + { svg: DataSourceParam; width: number; heigth: number } > = { - whatsapp: { - svg: whatsapp, - width: 24, - heigth: 24, - }, - telegram: { - svg: telegram, - width: 24, - heigth: 24, - }, - facebook: { - svg: facebook, - width: 25, - heigth: 24, - }, -} \ No newline at end of file + whatsapp: { + svg: whatsapp, + width: 24, + heigth: 24, + }, + telegram: { + svg: telegram, + width: 24, + heigth: 24, + }, + facebook: { + svg: facebook, + width: 25, + heigth: 24, + }, +} diff --git a/assets/modal/index.ts b/assets/modal/index.ts index 8054e725..77b0361a 100644 --- a/assets/modal/index.ts +++ b/assets/modal/index.ts @@ -2,11 +2,11 @@ import { DataSourceParam } from "@shopify/react-native-skia" import _delete from "./delete.svg" export const deleteSvg: { - svg: DataSourceParam - width: number - heigth: number + svg: DataSourceParam + width: number + heigth: number } = { - svg: _delete, - width: 18, - heigth: 18, + svg: _delete, + width: 18, + heigth: 18, } diff --git a/assets/navbar/index.ts b/assets/navbar/index.ts index a2155b8f..5f7d7228 100644 --- a/assets/navbar/index.ts +++ b/assets/navbar/index.ts @@ -13,37 +13,37 @@ import down from "./down.svg" */ export const navbarIconList = ["home", "back", "add", "sync", "down"] as const -export type NavbarIcon = typeof navbarIconList[number] +export type NavbarIcon = (typeof navbarIconList)[number] export const navbarIcons: Record< - NavbarIcon, - { svg: DataSourceParam; width: number; heigth: number } + NavbarIcon, + { svg: DataSourceParam; width: number; heigth: number } > = { - home: { - svg: home, - width: 18, - heigth: 19, - }, - back: { - svg: back, - width: 12, - heigth: 11, - }, - add: { - svg: add, - width: 17, - heigth: 17, - }, - sync: { - svg: sync, - width: 18, - heigth: 19, - }, - down: { - svg: down, - width: 12, - heigth: 13, - }, + home: { + svg: home, + width: 18, + heigth: 19, + }, + back: { + svg: back, + width: 12, + heigth: 11, + }, + add: { + svg: add, + width: 17, + heigth: 17, + }, + sync: { + svg: sync, + width: 18, + heigth: 19, + }, + down: { + svg: down, + width: 12, + heigth: 13, + }, } /** diff --git a/assets/settings/index.ts b/assets/settings/index.ts index 256ebde1..c3b7d2c4 100644 --- a/assets/settings/index.ts +++ b/assets/settings/index.ts @@ -1,43 +1,50 @@ import { DataSourceParam } from "@shopify/react-native-skia" import help from "./help.svg" import modify from "./modify.svg" +import privacy from "./privacy.svg" import disconnect from "./disconnect.svg" import notifiche from "./notifiche.svg" export const settingsIconList = [ - "notifiche", - "modify", - "help", - "disconnect", + "notifiche", + "privacy", + "modify", + "help", + "disconnect", ] as const -export type SettingIconNames = typeof settingsIconList[number] +export type SettingIconNames = (typeof settingsIconList)[number] export interface IconProps { - svg: DataSourceParam - width: number - heigth: number + svg: DataSourceParam + width: number + heigth: number } export const settingsIcons: Record = { - notifiche: { - svg: notifiche, - width: 20, - heigth: 24, - }, - modify: { - svg: modify, - width: 20, - heigth: 20, - }, - help: { - svg: help, - width: 24, - heigth: 24, - }, - disconnect: { - svg: disconnect, - width: 24, - heigth: 24, - }, + notifiche: { + svg: notifiche, + width: 20, + heigth: 24, + }, + privacy: { + svg: privacy, + width: 18, + heigth: 24, + }, + modify: { + svg: modify, + width: 20, + heigth: 20, + }, + help: { + svg: help, + width: 24, + heigth: 24, + }, + disconnect: { + svg: disconnect, + width: 24, + heigth: 24, + }, } diff --git a/assets/settings/privacy.svg b/assets/settings/privacy.svg new file mode 100644 index 00000000..06812a05 --- /dev/null +++ b/assets/settings/privacy.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/tray/index.ts b/assets/tray/index.ts index 46fb043d..06c85d39 100644 --- a/assets/tray/index.ts +++ b/assets/tray/index.ts @@ -11,25 +11,25 @@ import settings from "./settings.svg" */ export const trayIconList = ["downloads", "notifications", "settings"] as const -export type TrayIcon = typeof trayIconList[number] +export type TrayIcon = (typeof trayIconList)[number] export const trayIcons: Record< - TrayIcon, - { svg: DataSourceParam; width: number; heigth: number } + TrayIcon, + { svg: DataSourceParam; width: number; heigth: number } > = { - downloads: { - svg: downloads, - width: 22, - heigth: 23, - }, - notifications: { - svg: notifications, - width: 31, - heigth: 39, - }, - settings: { - svg: settings, - width: 28, - heigth: 29, - }, + downloads: { + svg: downloads, + width: 22, + heigth: 23, + }, + notifications: { + svg: notifications, + width: 31, + heigth: 39, + }, + settings: { + svg: settings, + width: 28, + heigth: 29, + }, } diff --git a/babel.config.js b/babel.config.js index d1ff5d2e..781a8039 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,22 +1,23 @@ module.exports = function (api) { - api.cache(true) - return { - presets: ["babel-preset-expo"], - plugins: [ - [ - "module-resolver", - { - alias: { - assets: "./assets", - components: "./src/components", - navigation: "./src/navigation", - pages: "./src/pages", - utils: "./src/utils", - api: "./src/api", - }, - }, - ], - "react-native-reanimated/plugin", - ], - } + api.cache(true) + return { + presets: ["babel-preset-expo"], + plugins: [ + "react-native-reanimated/plugin", + [ + "module-resolver", + { + alias: { + assets: "./assets", + components: "./src/components", + navigation: "./src/navigation", + pages: "./src/pages", + utils: "./src/utils", + contexts: "./src/contexts", + api: "./src/api", + }, + }, + ], + ], + } } diff --git a/package.json b/package.json index d81a08df..69c01a00 100644 --- a/package.json +++ b/package.json @@ -1,81 +1,86 @@ { - "name": "polifemo", - "version": "1.0.0", - "main": "node_modules/expo/AppEntry.js", - "scripts": { - "start": "expo start", - "android": "expo start --android", - "ios": "expo start --ios", - "web": "expo start --web", - "eject": "expo eject", - "lint": "eslint src", - "lint:fix": "eslint src --fix", - "type-check": "tsc --noEmit", - "prune-branches": "node scripts/prune-branches.js" - }, - "dependencies": { - "@ant-design/icons-react-native": "^2.3.2", - "@expo-google-fonts/roboto": "^0.2.2", - "@expo/webpack-config": "^0.17.2", - "@fortawesome/fontawesome-svg-core": "^6.1.1", - "@fortawesome/free-solid-svg-icons": "^6.1.1", - "@fortawesome/react-native-fontawesome": "^0.2.7", - "@freakycoder/react-native-header-view": "^1.2.0", - "@gorhom/bottom-sheet": "^4.4.3", - "@react-native-async-storage/async-storage": "~1.17.3", - "@react-native-community/datetimepicker": "6.5.2", - "@react-native-masked-view/masked-view": "0.2.8", - "@react-navigation/native": "^6.0.13", - "@react-navigation/stack": "^6.3.2", - "@shopify/react-native-skia": "0.1.157", - "axios": "^1.1.3", - "expo": "^47.0.0", - "expo-asset": "~8.7.0", - "expo-linear-gradient": "~12.0.1", - "expo-location": "~15.0.1", - "expo-splash-screen": "~0.17.5", - "expo-status-bar": "~1.4.2", - "expo-updates": "~0.15.6", - "geolib": "^3.3.3", - "react": "18.1.0", - "react-dom": "18.1.0", - "react-native": "0.70.5", - "react-native-app-intro-slider": "^4.0.4", - "react-native-gesture-handler": "~2.8.0", - "react-native-imaged-card-view": "^0.0.11", - "react-native-maps": "1.3.2", - "react-native-modal-datetime-picker": "^14.0.1", - "react-native-reanimated": "~2.12.0", - "react-native-safe-area-context": "4.4.1", - "react-native-safe-area-view": "^1.1.1", - "react-native-screens": "~3.18.0", - "react-native-status-bar-height": "^2.6.0", - "react-native-svg": "13.4.0", - "react-native-vector-icons": "^9.1.0", - "react-native-web": "~0.18.7", - "react-native-webview": "11.23.1", - "rn-slick-bottom-tabs": "^1.1.1", - "styled-components": "^5.3.6", - "yarn": "^1.22.19" - }, - "devDependencies": { - "@babel/core": "^7.19.3", - "@types/react": "~18.0.24", - "@types/react-dom": "~18.0.8", - "@types/react-native": "~0.70.6", - "@types/react-native-vector-icons": "^6.4.10", - "@typescript-eslint/eslint-plugin": "^5.18.0", - "@typescript-eslint/parser": "^5.18.0", - "eslint": "^8.13.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-react": "^7.31.8", - "prettier": "^2.6.2", - "typescript": "^4.6.3" - }, - "resolutions": { - "@types/react": "~18.0.20", - "@react-navigation/core": "~6.2.2" - }, - "private": true -} + "name": "polifemo", + "version": "1.0.0", + "main": "node_modules/expo/AppEntry.js", + "scripts": { + "start": "expo start", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web", + "eject": "expo eject", + "lint": "eslint src", + "lint:fix": "eslint src --fix", + "type-check": "tsc --noEmit", + "prune-branches": "node scripts/prune-branches.js" + }, + "dependencies": { + "@ant-design/icons-react-native": "^2.3.2", + "@expo-google-fonts/roboto": "^0.2.2", + "@expo/webpack-config": "^0.17.2", + "@fortawesome/fontawesome-svg-core": "^6.1.1", + "@fortawesome/free-solid-svg-icons": "^6.1.1", + "@fortawesome/react-native-fontawesome": "^0.2.7", + "@freakycoder/react-native-header-view": "^1.2.0", + "@gorhom/bottom-sheet": "^4.4.3", + "@react-native-async-storage/async-storage": "~1.17.3", + "@react-native-community/datetimepicker": "6.5.2", + "@react-native-masked-view/masked-view": "0.2.8", + "@react-native-picker/picker": "2.4.8", + "@react-navigation/native": "^6.0.13", + "@react-navigation/stack": "^6.3.2", + "@shopify/react-native-skia": "0.1.157", + "axios": "1.2.1", + "expo": "^47.0.0", + "expo-asset": "~8.7.0", + "expo-clipboard": "^4.0.1", + "expo-file-system": "~15.1.1", + "expo-linear-gradient": "~12.0.1", + "expo-location": "~15.0.1", + "expo-sharing": "~11.0.1", + "expo-splash-screen": "~0.17.5", + "expo-status-bar": "~1.4.2", + "expo-updates": "~0.15.6", + "geolib": "^3.3.3", + "react": "18.1.0", + "react-dom": "18.1.0", + "react-native": "0.70.5", + "react-native-app-intro-slider": "^4.0.4", + "react-native-gesture-handler": "~2.8.0", + "react-native-imaged-card-view": "^0.0.11", + "react-native-maps": "1.3.2", + "react-native-modal-datetime-picker": "^14.0.1", + "react-native-reanimated": "~2.12.0", + "react-native-safe-area-context": "4.4.1", + "react-native-safe-area-view": "^1.1.1", + "react-native-screens": "~3.18.0", + "react-native-status-bar-height": "^2.6.0", + "react-native-svg": "13.4.0", + "react-native-switch": "^1.5.1", + "react-native-vector-icons": "^9.1.0", + "react-native-web": "~0.18.7", + "react-native-webview": "11.23.1", + "rn-slick-bottom-tabs": "^1.1.1", + "styled-components": "^5.3.6", + "yarn": "^1.22.19" + }, + "devDependencies": { + "@babel/core": "^7.19.3", + "@types/react": "~18.0.24", + "@types/react-dom": "~18.0.8", + "@types/react-native": "~0.70.6", + "@types/react-native-vector-icons": "^6.4.10", + "@typescript-eslint/eslint-plugin": "^5.18.0", + "@typescript-eslint/parser": "^5.18.0", + "eslint": "^8.13.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "^7.31.8", + "prettier": "^2.6.2", + "typescript": "^4.6.3" + }, + "resolutions": { + "@types/react": "~18.0.20", + "@react-navigation/core": "~6.2.2" + }, + "private": true +} \ No newline at end of file diff --git a/scripts/prune-branches.js b/scripts/prune-branches.js index bfd48451..b3de1e94 100644 --- a/scripts/prune-branches.js +++ b/scripts/prune-branches.js @@ -3,44 +3,44 @@ const { promisify } = require("util") const execAsync = promisify(exec) ;(async () => { - const { stdout } = await execAsync("git branch -r") - const remoteBranches = stdout.split("\n").filter((b, i) => i > 0) - const currentBranchNames = remoteBranches - .map(branch => { - const m = branch.match(/origin\/(.*)/) - return m ? m[1] : null - }) - .filter(b => b !== null) + const { stdout } = await execAsync("git branch -r") + const remoteBranches = stdout.split("\n").filter((b, i) => i > 0) + const currentBranchNames = remoteBranches + .map(branch => { + const m = branch.match(/origin\/(.*)/) + return m ? m[1] : null + }) + .filter(b => b !== null) - // get all branches from eas - const { stdout: json } = await execAsync( - "eas branch:list --json --non-interactive", - { - maxBuffer: 1024 * 1024 * 10, // 10mb, required for lots of branches - } - ) - const easBranches = JSON.parse(json) - const easBranchNames = easBranches.map(b => b.name) - const branchesToDelete = easBranchNames - .filter(b => !currentBranchNames.includes(b)) - .filter(b => b !== "main") - - console.log("\nCurrent git branches:\n" + currentBranchNames.join("\n - ")) - console.log("\nCurrent EAS branches:\n" + easBranchNames.join("\n - ")) - console.log("\nBranches to be deleted:\n" + branchesToDelete.join("\n - ")) + // get all branches from eas + const { stdout: json } = await execAsync( + "eas branch:list --json --non-interactive", + { + maxBuffer: 1024 * 1024 * 10, // 10mb, required for lots of branches + } + ) + const easBranches = JSON.parse(json) + const easBranchNames = easBranches.map(b => b.name) + const branchesToDelete = easBranchNames + .filter(b => !currentBranchNames.includes(b)) + .filter(b => b !== "main") - // delete branches from eas - for (const branch of branchesToDelete) { - try { - // delete channel first - await execAsync(`eas channel:delete ${branch} --non-interactive`) - } catch (e) { - // apparently, it could happen that the channel is already deleted but the branch is not - console.error(`unable to delete channel for branch ${branch}`) - } + console.log("\nCurrent git branches:\n" + currentBranchNames.join("\n - ")) + console.log("\nCurrent EAS branches:\n" + easBranchNames.join("\n - ")) + console.log("\nBranches to be deleted:\n" + branchesToDelete.join("\n - ")) - // delete branch - await execAsync(`eas branch:delete ${branch} --non-interactive`) - console.log(`Deleted branch ${branch} from EAS`) + // delete branches from eas + for (const branch of branchesToDelete) { + try { + // delete channel first + await execAsync(`eas channel:delete ${branch} --non-interactive`) + } catch (e) { + // apparently, it could happen that the channel is already deleted but the branch is not + console.error(`unable to delete channel for branch ${branch}`) } + + // delete branch + await execAsync(`eas branch:delete ${branch} --non-interactive`) + console.log(`Deleted branch ${branch} from EAS`) + } })() diff --git a/src/AppContainer.tsx b/src/AppContainer.tsx index 7b63297c..1d81af0a 100644 --- a/src/AppContainer.tsx +++ b/src/AppContainer.tsx @@ -10,16 +10,16 @@ import { usePalette } from "utils/colors" * structural components. */ export const AppContainer: FC = () => { - const { homeBackground } = usePalette() + const { homeBackground } = usePalette() - return ( - - - - ) + return ( + + + + ) } diff --git a/src/MainContainer.tsx b/src/MainContainer.tsx index 64ba0d4d..da440944 100644 --- a/src/MainContainer.tsx +++ b/src/MainContainer.tsx @@ -1,9 +1,11 @@ -import React, { FC } from "react" +import React, { FC, useEffect, useState } from "react" import { View } from "react-native" import { Tray } from "components/Tray" import { usePalette } from "utils/colors" import { useNavigation } from "navigation/NavigationTypes" import { MainStack } from "navigation/MainStackNavigator" +import { NewsPreferencesContext, Preference } from "contexts/newsPreferences" +import AsyncStorage from "@react-native-async-storage/async-storage" /** * The Main Container. @@ -12,31 +14,65 @@ import { MainStack } from "navigation/MainStackNavigator" */ export const MainContainer: FC = () => { - const { homeBackground } = usePalette() + const { homeBackground } = usePalette() - const { navigate } = useNavigation() - return ( - - + const { navigate } = useNavigation() - { - console.log("downloads") - }} - onNotifications={() => { - console.log("notifications") - }} - onSettings={() => { - navigate("SettingsNav", { - screen: "Settings", - }) - }} - /> - - ) + const [preferences, setPreferences] = useState>({}) + + useEffect(() => { + console.log("Loading tags preferences from storage") + AsyncStorage.getItem("newstags:preferences") + .then(preferencesJSON => { + if (preferencesJSON) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const data: Record = JSON.parse(preferencesJSON) + console.log(data) + setPreferences(data) + } + }) + .catch(err => console.log(err)) + }, []) + + useEffect(() => { + console.log("Saving tags preferences to storage") + console.log(preferences) + AsyncStorage.setItem( + "newstags:preferences", + JSON.stringify(preferences) + ).catch(err => console.log(err)) + }, [preferences]) + + return ( + + { + setPreferences(pref.preferences) + }, + }} + > + + + { + console.log("downloads") + }} + onNotifications={() => { + console.log("notifications") + }} + onSettings={() => { + navigate("SettingsNav", { + screen: "Settings", + }) + }} + /> + + ) } diff --git a/src/SettingsContainer.tsx b/src/SettingsContainer.tsx index 476d842f..91b139bc 100644 --- a/src/SettingsContainer.tsx +++ b/src/SettingsContainer.tsx @@ -9,16 +9,16 @@ import { SettingsStack } from "navigation/SettingsNavigator" * It's a view that wraps pages of the Settings Navigator */ export const SettingsContainer: FC = () => { - const { homeBackground } = usePalette() + const { homeBackground } = usePalette() - return ( - - - - ) + return ( + + + + ) } diff --git a/src/api/HttpClient.ts b/src/api/HttpClient.ts index 4b38f910..3ae4c24f 100644 --- a/src/api/HttpClient.ts +++ b/src/api/HttpClient.ts @@ -1,13 +1,14 @@ import { EventEmitter } from "events" import axios, { - AxiosError, - AxiosInstance, - AxiosRequestConfig, - AxiosResponse, + AxiosError, + AxiosInstance, + AxiosRequestConfig, + AxiosResponse, } from "axios" -import { PolimiToken, PoliNetworkToken, Tokens } from "utils/login" +import { PolimiToken, PoliNetworkToken, Tokens } from "contexts/login" import AsyncStorage from "@react-native-async-storage/async-storage" import { wait } from "utils/functions" +import { Alert } from "react-native" /*Docs used to make this: Singleton: @@ -18,44 +19,44 @@ https://stackblitz.com/edit/retry-api-call-axios-interceptor?file=index.ts */ export enum AuthType { - NONE, - POLIMI, - POLINETWORK, + NONE, + POLIMI, + POLINETWORK, } export enum RetryType { - RETRY_INDEFINETELY, - RETRY_N_TIMES, - NO_RETRY, + RETRY_INDEFINETELY, + RETRY_N_TIMES, + NO_RETRY, } /** * Request options interface */ export interface RequestOptions { - retryType?: RetryType - maxRetries?: number - waitingTime?: number + retryType?: RetryType + maxRetries?: number + waitingTime?: number } /** * Default request options for api requests */ export const defaultOptions = { - retryType: RetryType.RETRY_INDEFINETELY, - maxRetries: 5, - waitingTime: 3000, + retryType: RetryType.RETRY_INDEFINETELY, + maxRetries: 5, + waitingTime: 3000, } //in this way we can add fields to AxiosRequestConfig for more control in case of errors declare module "axios" { - export interface AxiosRequestConfig { - retryType?: RetryType - maxRetries?: number - waitingTime?: number //seconds - readonly retryCount?: number - authType?: AuthType - } + export interface AxiosRequestConfig { + retryType?: RetryType + maxRetries?: number + waitingTime?: number //seconds + readonly retryCount?: number + authType?: AuthType + } } /** @@ -68,293 +69,326 @@ declare module "axios" { * This object also manages auth tokens. */ export declare interface HttpClient { - /** - * fired when login status changes, either because of a login or a logout - * */ - on(event: "login_event", listener: (loggedIn: boolean) => void): this - /** - * fired when tokens are retrieved and user logs in - */ - on(event: "login", listener: () => void): this - /** - * fired when tokens get destroyed and user logs out - */ - on(event: "logout", listener: () => void): this + /** + * fired when login status changes, either because of a login or a logout + * */ + on(event: "login_event", listener: (loggedIn: boolean) => void): this + /** + * fired when tokens are retrieved and user logs in + */ + on(event: "login", listener: () => void): this + /** + * fired when tokens get destroyed and user logs out + */ + on(event: "logout", listener: () => void): this } export class HttpClient extends EventEmitter { - private static classInstance?: HttpClient - - readonly polimiInstance: AxiosInstance - readonly poliNetworkInstance: AxiosInstance + private static classInstance?: HttpClient - private polimiToken?: PolimiToken - private poliNetworkToken?: PoliNetworkToken + readonly polimiInstance: AxiosInstance + readonly poliNetworkInstance: AxiosInstance - // TODO: await for token to refresh before sending multiple requests + private polimiToken?: PolimiToken + private poliNetworkToken?: PoliNetworkToken - /** - * retrieves singleton instance. - * */ - public static getInstance() { - if (!this.classInstance) { - this.classInstance = new HttpClient( - "https://api.polinetwork.org/staging/", - "https://polimiapp.polimi.it/polimi_app/rest/jaf" - ) - } + // TODO: await for token to refresh before sending multiple requests - return this.classInstance + /** + * retrieves singleton instance. + * */ + public static getInstance() { + if (!this.classInstance) { + this.classInstance = new HttpClient( + "https://api.polinetwork.org/staging/", + "https://polimiapp.polimi.it/polimi_app" + ) } - private constructor(baseUrlPoliNetwork: string, baseUrlPolimi: string) { - super() - console.log("HttpClient constructor called") - this.poliNetworkInstance = axios.create({ - baseURL: baseUrlPoliNetwork, - timeout: 2000, - }) - this.polimiInstance = axios.create({ - baseURL: baseUrlPolimi, - timeout: 2000, - }) - this._initializeInterceptors() + return this.classInstance + } + + private constructor(baseUrlPoliNetwork: string, baseUrlPolimi: string) { + super() + console.log("HttpClient constructor called") + this.poliNetworkInstance = axios.create({ + baseURL: baseUrlPoliNetwork, + timeout: 30000, + }) + this.polimiInstance = axios.create({ + baseURL: baseUrlPolimi, + timeout: 30000, + }) + this._initializeInterceptors() + } + + /** + * initialize interceptors functions + * */ + private _initializeInterceptors = () => { + this.poliNetworkInstance.interceptors.request.use(this._handleRequest) + this.poliNetworkInstance.interceptors.response.use( + val => this._handleResponse(val), + err => this._handleError(err as AxiosError, this.poliNetworkInstance) + ) + this.polimiInstance.interceptors.request.use(this._handleRequest) + this.polimiInstance.interceptors.response.use( + val => this._handleResponse(val), + err => this._handleError(err as AxiosError, this.polimiInstance) + ) + } + + private _handleRequest = (config: AxiosRequestConfig) => { + config.headers = config.headers ?? {} + if (config.authType === AuthType.POLIMI && this.polimiToken) { + config.headers["Authorization"] = `Bearer ${this.polimiToken.accessToken}` + } else if ( + config.authType === AuthType.POLINETWORK && + this.poliNetworkToken + ) { + config.headers[ + "Authorization" + ] = `Bearer ${this.poliNetworkToken.access_token}` } - - /** - * initialize interceptors functions - * */ - private _initializeInterceptors = () => { - this.poliNetworkInstance.interceptors.request.use(this._handleRequest) - this.poliNetworkInstance.interceptors.response.use( - val => this._handleResponse(val), - err => - this._handleError(err as AxiosError, this.poliNetworkInstance) - ) - this.polimiInstance.interceptors.request.use(this._handleRequest) - this.polimiInstance.interceptors.response.use( - val => this._handleResponse(val), - err => this._handleError(err as AxiosError, this.polimiInstance) - ) + return config + } + + /** + * intercepts successful responses from server and + * does (or will do) something before `.then` is called + * */ + private _handleResponse = (res: AxiosResponse): AxiosResponse => { + return res + } + + /** + * intercepts unsuccessful responses before `.catch` is called + * and manages retries on proper Axios Instance passed as a parameter + * */ + private _handleError = async (error: AxiosError, instance: AxiosInstance) => { + if (!error.config) throw error + + const config = { + ...defaultOptions, + ...error.config, } - - private _handleRequest = (config: AxiosRequestConfig) => { - config.headers = config.headers ?? {} - if (config.authType === AuthType.POLIMI && this.polimiToken) { - config.headers[ - "Authorization" - ] = `Bearer ${this.polimiToken.accessToken}` - } else if ( - config.authType === AuthType.POLINETWORK && - this.poliNetworkToken - ) { - config.headers[ - "Authorization" - ] = `Bearer ${this.poliNetworkToken.access_token}` + const response = error.response + + if (response?.status === 500) { + if (config.retryType === RetryType.RETRY_INDEFINETELY) { + console.log("Retrying until request is successful") + await wait(config.waitingTime) + return instance(config) + } else if (config.retryType === RetryType.RETRY_N_TIMES) { + const retryCount = (config.retryCount ?? 0) + 1 + if (retryCount <= config.maxRetries) { + console.log(`Try number ${retryCount}/${config.maxRetries}`) + await wait(config.waitingTime) + return instance({ ...config, retryCount }) } - return config - } - - /** - * intercepts successful responses from server and - * does (or will do) something before `.then` is called - * */ - private _handleResponse = (res: AxiosResponse): AxiosResponse => { - return res - } - - /** - * intercepts unsuccessful responses before `.catch` is called - * and manages retries on proper Axios Instance passed as a parameter - * */ - private _handleError = async ( - error: AxiosError, - instance: AxiosInstance - ) => { - if (!error.config) throw error - - const config = { - ...defaultOptions, - ...error.config, + } else { + console.log("You selected NO_RETRY!") + throw error + } + console.log("Maximum numbers of retries reached!") + throw error + } else if (response?.status === 401) { + if (config.authType === AuthType.POLIMI) { + const success = await this.refreshPolimiToken() + if (success) { + return instance(config) + } else { + console.warn("Error: could not refresh Polimi token") + console.warn("Error:") + console.warn(error) + console.warn("Call config:") + console.warn(JSON.stringify(config)) + Alert.alert( + "An error has occurred while refreshing Polimi token", + `The call to ${ + config.url ?? "undefined" + } failed, is the token invalid?\n\nThis is a debug option, if you think this is an error, please report it and try again later, otherwise you can logout`, + [ + { + text: "Cancel", + style: "cancel", + }, + { + text: "Logout", + onPress: () => { + void this.destroyTokens() + }, + }, + ] + ) + throw error } - const response = error.response - - if (response?.status === 500) { - if (config.retryType === RetryType.RETRY_INDEFINETELY) { - console.log("Retrying until request is successful") - await wait(config.waitingTime) - return instance(config) - } else if (config.retryType === RetryType.RETRY_N_TIMES) { - const retryCount = (config.retryCount ?? 0) + 1 - if (retryCount <= config.maxRetries) { - console.log(`Try number ${retryCount}/${config.maxRetries}`) - await wait(config.waitingTime) - return instance({ ...config, retryCount }) - } - } else { - console.log("You selected NO_RETRY!") - throw error - } - console.log("Maximum numbers of retries reached!") - throw error - } else if (response?.status === 401) { - if (config.authType === AuthType.POLIMI) { - const success = await this.refreshPolimiToken() - if (success) { - return instance(config) - } else { - console.warn("Error: could not refresh Polimi token") - console.warn("Should disconnect user") - // void this.destroyTokens() - throw error - } - } else if (config.authType === AuthType.POLINETWORK) { - const success = await this.refreshPoliNetworkToken() - if (success) return instance(config) - else { - console.warn("Error: could not refresh PoliNetwork token") - console.warn("Should disconnect user") - // void this.destroyTokens() - throw error - } - } - throw error + } else if (config.authType === AuthType.POLINETWORK) { + const success = await this.refreshPoliNetworkToken() + if (success) return instance(config) + else { + console.warn("Error: could not refresh PoliNetwork token") + console.warn("Error:") + console.warn(error) + console.warn("Call config:") + console.warn(JSON.stringify(config)) + Alert.alert( + "An error has occurred while refreshing PoliNetwork token", + `The call to ${ + config.url ?? "undefined" + } failed, is the token invalid?\n\nThis is a debug option, if you think this is an error, please report it and try again later, otherwise you can logout`, + [ + { + text: "Cancel", + style: "cancel", + }, + { + text: "Logout", + onPress: () => { + void this.destroyTokens() + }, + }, + ] + ) + + throw error } - throw error + } + throw error + } + throw error + } + + /** + * refresh the polimi token, returns the success value + * @returns true if the token was refreshed, false otherwise + */ + async refreshPolimiToken() { + console.log("Refreshing polimi token") + if (!this.polimiToken || !this.poliNetworkToken) { + console.log("Tokens went missing while trying to refresh Polimi token") + return false } - /** - * refresh the polimi token, returns the success value - * @returns true if the token was refreshed, false otherwise - */ - async refreshPolimiToken() { - console.log("Refreshing polimi token") - if (!this.polimiToken || !this.poliNetworkToken) { - console.log( - "Tokens went missing while trying to refresh Polimi token" - ) - return false + const url = + "/rest/jaf/oauth/token/refresh/" + this.polimiToken?.refreshToken + try { + const response = await this.polimiInstance.get(url, { + retryType: RetryType.RETRY_N_TIMES, + }) + if (typeof response.data.accessToken === "string") { + console.log("Refreshed polimi token") + + this.polimiToken = response.data + const tokens: Tokens = { + poliNetworkToken: this.poliNetworkToken, + polimiToken: this.polimiToken, } + await AsyncStorage.setItem("api:tokens", JSON.stringify(tokens)) - const url = "/oauth/token/refresh/" + this.polimiToken?.refreshToken - try { - const response = await this.polimiInstance.get(url, { - retryType: RetryType.RETRY_N_TIMES, - }) - if (typeof response.data.accessToken === "string") { - console.log("Refreshed polimi token") - - this.polimiToken = response.data - const tokens: Tokens = { - poliNetworkToken: this.poliNetworkToken, - polimiToken: this.polimiToken, - } - await AsyncStorage.setItem("api:tokens", JSON.stringify(tokens)) - - return true - } - console.warn("Invalid response refreshing polimi token") - console.warn(response.status) - console.warn(response.data) - return false - } catch (e) { - console.warn("Error refreshing polimi token") - console.warn(e) - return false - } + return true + } + console.warn("Invalid response refreshing polimi token") + console.warn(response.status) + console.warn(response.data) + return false + } catch (e) { + console.warn("Error refreshing polimi token") + console.warn(e) + return false + } + } + + async refreshPoliNetworkToken() { + console.log("Refreshing polinetwork token") + if (!this.polimiToken || !this.poliNetworkToken) { + console.log( + "Tokens went missing while trying to refresh PoliNetwork token" + ) + return false } - async refreshPoliNetworkToken() { - console.log("Refreshing polinetwork token") - if (!this.polimiToken || !this.poliNetworkToken) { - console.log( - "Tokens went missing while trying to refresh PoliNetwork token" - ) - return false + try { + const response = await this.poliNetworkInstance.get( + "/v1/auth/refresh", + { + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + Token: this.poliNetworkToken?.refresh_token, + }, + retryType: RetryType.RETRY_N_TIMES, + maxRetries: 5, } + ) + if (typeof response.data.access_token === "string") { + console.log("Refreshed polinetwork token") - try { - const response = - await this.poliNetworkInstance.get( - "/v1/auth/refresh", - { - headers: { - // eslint-disable-next-line @typescript-eslint/naming-convention - Token: this.poliNetworkToken?.refresh_token, - }, - retryType: RetryType.RETRY_N_TIMES, - maxRetries: 5, - } - ) - if (typeof response.data.access_token === "string") { - console.log("Refreshed polinetwork token") - - this.poliNetworkToken = response.data - - // save back the tokens - const tokens: Tokens = { - poliNetworkToken: this.poliNetworkToken, - polimiToken: this.polimiToken, - } - await AsyncStorage.setItem("api:tokens", JSON.stringify(tokens)) - - return true - } else { - console.warn("Invalid response refreshing polinetwork token") - console.warn(response.status) - console.warn(response.data) - return false - } - } catch (e) { - console.warn("Error refreshing polinetwork token") - console.warn(e) - return false - } - } - /** - * load tokens from storage on boot, if they aren't present, do nothing - */ - async loadTokens() { - const tokens = await AsyncStorage.getItem("api:tokens") - if (tokens) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const parsedTokens: Tokens = JSON.parse(tokens) - console.log("Loaded tokens from local storage") - this.polimiToken = parsedTokens.polimiToken - this.poliNetworkToken = parsedTokens.poliNetworkToken - this.emit("login") - this.emit("login_event", true) - } else { - console.log("No tokens found in local storage") + this.poliNetworkToken = response.data + + // save back the tokens + const tokens: Tokens = { + poliNetworkToken: this.poliNetworkToken, + polimiToken: this.polimiToken, } - } - /** - * set the tokens and save them to storage - * @param tokens both the polinetwork and polimi tokens - */ - async setTokens(tokens: Tokens) { - this.polimiToken = tokens.polimiToken - this.poliNetworkToken = tokens.poliNetworkToken - - this.emit("login") - this.emit("login_event", true) - - // save the tokens in local storage await AsyncStorage.setItem("api:tokens", JSON.stringify(tokens)) - console.log("Saved tokens in local storage") - } - /** - * remove the tokens from storage, essentially log out - */ - async destroyTokens() { - console.log("Destroying tokens, logging out") - this.polimiToken = undefined - this.poliNetworkToken = undefined - - this.emit("logout") - this.emit("login_event", false) - - // remove the tokens from local storage - await AsyncStorage.removeItem("api:tokens") + return true + } else { + console.warn("Invalid response refreshing polinetwork token") + console.warn(response.status) + console.warn(response.data) + return false + } + } catch (e) { + console.warn("Error refreshing polinetwork token") + console.warn(e) + return false + } + } + /** + * load tokens from storage on boot, if they aren't present, do nothing + */ + async loadTokens() { + const tokens = await AsyncStorage.getItem("api:tokens") + if (tokens) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const parsedTokens: Tokens = JSON.parse(tokens) + console.log("Loaded tokens from local storage") + this.polimiToken = parsedTokens.polimiToken + this.poliNetworkToken = parsedTokens.poliNetworkToken + this.emit("login") + this.emit("login_event", true) + } else { + console.log("No tokens found in local storage") } + } + /** + * set the tokens and save them to storage + * @param tokens both the polinetwork and polimi tokens + */ + async setTokens(tokens: Tokens) { + this.polimiToken = tokens.polimiToken + this.poliNetworkToken = tokens.poliNetworkToken + + this.emit("login") + this.emit("login_event", true) + + // save the tokens in local storage + await AsyncStorage.setItem("api:tokens", JSON.stringify(tokens)) + console.log("Saved tokens in local storage") + } + /** + * remove the tokens from storage, essentially log out + */ + async destroyTokens() { + console.log("Destroying tokens, logging out") + + this.polimiToken = undefined + this.poliNetworkToken = undefined + + this.emit("logout") + this.emit("login_event", false) + + // remove the tokens from local storage + await AsyncStorage.removeItem("api:tokens") + } } diff --git a/src/api/Room.ts b/src/api/Room.ts deleted file mode 100644 index 12819af7..00000000 --- a/src/api/Room.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { ValidCrowdStatus } from "components/FreeClass/ClassDetails/CrowdingSection" -import { AuthType, HttpClient, RequestOptions } from "./HttpClient" - -/* eslint-disable @typescript-eslint/naming-convention */ -export interface Rooms { - freeRooms: Room[] -} -export interface Room { - room_id: number - name: string - building: string - power: boolean - link: string -} - -export interface RoomDetails { - name: string - capacity: string - building: string - address: string - power: boolean -} - -export interface OccupancyInfo { - room_id: number - occupancy_rate: null | ValidCrowdStatus -} - -const client = HttpClient.getInstance() - -/** - * Collection of endpoints related to Rooms. - */ -export const rooms = { - /** - * Retrieves available rooms from PoliNetwork server in a given time range. - */ - async getFreeRoomsTimeRange( - campusAcronym: string, - dateStart: string, - dateEnd: string, - options?: RequestOptions - ) { - const response = await client.poliNetworkInstance.get( - "/v1/rooms/search", - { - ...options, - params: { - sede: campusAcronym, - hourStart: dateStart, - hourStop: dateEnd, - }, - } - ) - return response.data.freeRooms - }, - - async getRoomInfo(roomId: number, options?: RequestOptions) { - const response = await client.poliNetworkInstance.get( - "/v1/rooms/" + roomId, - { - ...options, - } - ) - return response.data - }, - - async getOccupancyRate(roomId: number, options?: RequestOptions) { - const response = await client.poliNetworkInstance.get( - "/v1/rooms/" + roomId + "/occupancy", - { - ...options, - } - ) - return response.data - }, - - async postOccupancyRate( - roomId: number, - rate: number, - options?: RequestOptions - ) { - const res = await client.poliNetworkInstance.post( - "/v1/rooms/" + roomId + "/occupancy", - { - ...options, - }, - { params: { rate: rate }, authType: AuthType.POLINETWORK } - ) - return res - }, -} diff --git a/src/api/articles.ts b/src/api/articles.ts index f7d3bc95..941fb9eb 100644 --- a/src/api/articles.ts +++ b/src/api/articles.ts @@ -1,20 +1,35 @@ -import { getIsoStringFromDaysPassed } from "utils/functions" +/* eslint-disable @typescript-eslint/naming-convention */ import { HttpClient, RequestOptions } from "./HttpClient" -/* eslint-disable @typescript-eslint/naming-convention */ +export interface Tags { + tags: Tag[] +} + +export interface Tag { + name: string + image: string +} + export interface Articles { - results: Article[] + articles: Article[] + start: string | null + end: string | null + tag: string | null + author_id: number | null + title: string | null } export interface Article { - title: string - subtitle?: string - latitude?: number - longitude?: number - publish_time?: string - target_time?: string - content: string - image?: string - author?: { name?: string; link?: string; image?: string } + id: number + tag_id: string + title: string + subtitle?: string + latitude?: number + longitude?: number + publish_time: string + target_time?: string + content: string + image?: string + author?: { name?: string; link?: string; image?: string } } const client = HttpClient.getInstance() @@ -23,38 +38,67 @@ const client = HttpClient.getInstance() * Collection of endpoints related to Articles. */ export const articles = { - /** - * Retrieves articles from PoliNetwork server, from n-days ago till - * ending ISO date. - */ - async getFromDaysAgoTillDate( - days: number, - end: string, - options?: RequestOptions - ) { - const start: string = getIsoStringFromDaysPassed(days) - const response = await client.poliNetworkInstance.get( - "/v1/articles", - { - ...options, - params: { start: start, end: end }, - } - ) - return response.data.results - }, - /** - * Retrieves articles from PoliNetwork server, given a starting and ending ISO date. - */ - async getFromDateTillDate( - start: string, - end: string, - options?: RequestOptions - ) { - const response = await client.poliNetworkInstance.get( - "/v1/articles", - options - ) + /** + * Retrieves at most `limit` articles of a given tag from PoliNetwork server. + * + * If offset is `0` the newest `limit` articles are returned; + * if offset is `1` the next `limit` articles are returned, and so on. + * + * In the last group there might be less than `limit` articles. + * + * @param tag the news category + * @param limit maximum number of articles retrieved + * @param offset the page offset parameter + * + * @param options see {@link RequestOptions} + * + * @example + * ```ts + * api.articles.getFromOffsetByTag("TAG", 10, 2) + * .then(response => { + * const articles: Article[] = response + * //do something + * }) + * .catch(err => console.log(err)) + * } + * ``` + */ + async getFromOffsetByTag( + tag: string, + limit: number, + offset: number, + options?: RequestOptions + ) { + const response = await client.poliNetworkInstance.get( + "/v1/articles", + { + ...options, + params: { + limit: limit, + pageOffset: offset, + tag: tag, + sort: "date", + }, + } + ) + return response.data.articles + }, - return response.data.results - }, + /** + * Retrieves the last article of a given tag from PoliNetwork server. + * + * @param tag the news category + * + * @param options see {@link RequestOptions} + */ + async getLastArticleByTag(tag: string, options?: RequestOptions) { + const response = await client.poliNetworkInstance.get( + "/v1/articles", + { + ...options, + params: { tag: tag, limit: 1, sort: "date" }, + } + ) + return response.data.articles[0] + }, } diff --git a/src/api/auth.ts b/src/api/auth.ts index 2b30fa9f..d0a44d3b 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -1,4 +1,4 @@ -import { PolimiToken } from "utils/login" +import { PolimiToken } from "contexts/login" import { HttpClient, RequestOptions } from "./HttpClient" const client = HttpClient.getInstance() @@ -7,21 +7,21 @@ const client = HttpClient.getInstance() * Collection of endpoints related to authentication */ export const auth = { - /** - * gets the OAuth access token given the polimi authcode from the login flow - * @param code Polimi AuthCode - * @returns polimi accessToken and refreshToken - */ - async getPolimiToken(code: string, options?: RequestOptions) { - const response = await client.polimiInstance.get( - `/oauth/token/get/${code}`, - { - ...options, - headers: { - accept: "application/json", - }, - } - ) - return response.data - }, + /** + * gets the OAuth access token given the polimi authcode from the login flow + * @param code Polimi AuthCode + * @returns polimi accessToken and refreshToken + */ + async getPolimiToken(code: string, options?: RequestOptions) { + const response = await client.polimiInstance.get( + `/rest/jaf/oauth/token/get/${code}`, + { + ...options, + headers: { + accept: "application/json", + }, + } + ) + return response.data + }, } diff --git a/src/api/event.ts b/src/api/event.ts new file mode 100644 index 00000000..6de80a01 --- /dev/null +++ b/src/api/event.ts @@ -0,0 +1,54 @@ +import { AuthType, HttpClient, RequestOptions } from "./HttpClient" + +/* eslint-disable @typescript-eslint/naming-convention */ +export interface Event { + event_id: number + date_start: string + date_end: string + show_agenda: boolean + matricola?: string + title: { + it: string + en: string + } + event_type: { + typeId: number + type_dn: { + it: string + en: string + } + } + event_subtype?: string + calendar: { + calendar_id: number + calendar_dn: { + it: string + en: string + } + } + room?: { + room_id: number + acronym_dn: string + classroom_id: number + room_dn: string + } +} + +const client = HttpClient.getInstance() + +export const events = { + async getEvents( + matricola: string, + start_date: string, + n_events: number, + options?: RequestOptions + ) { + const url = "/agenda/api/me/" + matricola + "/events" + const response = await client.polimiInstance.get(url, { + ...options, + params: { start_date, n_events }, + authType: AuthType.POLIMI, + }) + return response.data + }, +} diff --git a/src/api/groups.ts b/src/api/groups.ts index b2dbc8f7..36b95442 100644 --- a/src/api/groups.ts +++ b/src/api/groups.ts @@ -3,30 +3,30 @@ import { HttpClient, RequestOptions } from "./HttpClient" /* eslint-disable @typescript-eslint/naming-convention */ export interface GroupOptions { - name?: string - year?: string - degree?: string - type?: string - platform?: string - language?: string - office?: string + name?: string + year?: string + degree?: string + type?: string + platform?: string + language?: string + office?: string } export interface Group { - class: string - office: string - id: string - degree?: string - school?: string - link_id: string - language: string - type_?: string - year: string | null //probably I should use | null evreywhere? - platform: string - permanent_id?: number - last_updated?: string - link_is_working?: string - members?: string + class: string + office: string + id: string + degree?: string + school?: string + link_id: string + language: string + type_?: string + year: string | null //probably I should use | null evreywhere? + platform: string + permanent_id?: number + last_updated?: string + link_is_working?: string + members?: string } const client = HttpClient.getInstance() @@ -35,25 +35,25 @@ const client = HttpClient.getInstance() * Collection of endpoints related to Groups. */ export const groups = { - /** - * Retrieves groups from PoliNetwork server. - * Check {@link GroupOptions} for additional parameters. - */ - async get(groupsOptions?: GroupOptions, options?: RequestOptions) { - const response = await client.poliNetworkInstance.get<{ - groups: Group[] - }>("/v1/groups", { - ...options, - params: { - name: groupsOptions?.name, - year: groupsOptions?.year, - degree: groupsOptions?.degree, - type: groupsOptions?.type, - platform: groupsOptions?.platform, - language: groupsOptions?.language, - office: groupsOptions?.office, - }, - }) - return response.data.groups - }, + /** + * Retrieves groups from PoliNetwork server. + * Check {@link GroupOptions} for additional parameters. + */ + async get(groupsOptions?: GroupOptions, options?: RequestOptions) { + const response = await client.poliNetworkInstance.get<{ + groups: Group[] + }>("/v1/groups", { + ...options, + params: { + name: groupsOptions?.name, + year: groupsOptions?.year, + degree: groupsOptions?.degree, + type: groupsOptions?.type, + platform: groupsOptions?.platform, + language: groupsOptions?.language, + office: groupsOptions?.office, + }, + }) + return response.data.groups + }, } diff --git a/src/api/index.ts b/src/api/index.ts index 9035ce26..890b2828 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -4,7 +4,8 @@ import { groups } from "./groups" import { tags } from "./tags" import { timetable } from "./timetable" import { user } from "./user" -import { rooms } from "./Room" +import { rooms } from "./rooms" +import { events } from "./event" export { RetryType, AuthType, RequestOptions } from "./HttpClient" /** * This object groups together all collections of endpoints. @@ -24,11 +25,12 @@ export { RetryType, AuthType, RequestOptions } from "./HttpClient" * like RetryType, etc... */ export const api = { - articles, - auth, - tags, - timetable, - user, - rooms, - groups, + articles, + auth, + events, + groups, + rooms, + tags, + timetable, + user, } diff --git a/src/api/rooms.ts b/src/api/rooms.ts new file mode 100644 index 00000000..3e0763fe --- /dev/null +++ b/src/api/rooms.ts @@ -0,0 +1,107 @@ +import { ValidCrowdStatus } from "components/FreeClass/ClassDetails/CrowdingSection" +import { AuthType, HttpClient, RequestOptions } from "./HttpClient" + +/* eslint-disable @typescript-eslint/naming-convention */ +export interface Rooms { + freeRooms: Room[] +} +export interface Room { + room_id: number + name: string + building: string + power: boolean + link: string +} +export interface RoomSimplified { + roomId: number + name: string +} + +export interface RoomDetails { + name: string + capacity: string + building: string + address: string + power: boolean +} + +export interface OccupancyInfo { + room_id: number + occupancy_rate: null | ValidCrowdStatus +} + +const client = HttpClient.getInstance() + +/** + * Collection of endpoints related to Rooms. + */ +export const rooms = { + /** + * Retrieves available rooms from PoliNetwork server in a given time range. + */ + async getFreeRoomsTimeRange( + campusAcronym: string, + dateStart: string, + dateEnd: string, + options?: RequestOptions + ) { + const response = await client.poliNetworkInstance.get( + "/v1/rooms/search", + { + ...options, + params: { + sede: campusAcronym, + hourStart: dateStart, + hourStop: dateEnd, + }, + } + ) + return response.data.freeRooms + }, + + async getOccupancyRate(roomId: number, options?: RequestOptions) { + const response = await client.poliNetworkInstance.get( + "/v1/rooms/" + roomId + "/occupancy", + { + ...options, + } + ) + return response.data + }, + + async postOccupancyRate( + roomId: number, + rate: number, + options?: RequestOptions + ) { + const res = await client.poliNetworkInstance.post( + "/v1/rooms/" + roomId + "/occupancy", + { + ...options, + }, + { params: { rate: rate }, authType: AuthType.POLINETWORK } + ) + return res + }, + + /** + * Retrieves room details from PoliNetwork Server. + */ + async getRoomInfo(roomId: number, options?: RequestOptions) { + const response = await client.poliNetworkInstance.get( + "/v1/rooms/" + roomId, + { + ...options, + } + ) + return response.data + }, +} +/** + * It simply add an amount of hours to a given date. + */ +export function addHours(dateStart: Date, hours: number) { + const tempDate = new Date(dateStart.getTime()) + tempDate.setHours(tempDate.getHours() + hours) + return tempDate +} diff --git a/src/api/tags.ts b/src/api/tags.ts index 97620057..a7c4ae2a 100644 --- a/src/api/tags.ts +++ b/src/api/tags.ts @@ -1,12 +1,12 @@ import { HttpClient, RequestOptions } from "./HttpClient" export interface Tags { - tags: Tag[] + tags: Tag[] } export interface Tag { - name: string - image: string + name: string + image: string } const client = HttpClient.getInstance() @@ -14,15 +14,15 @@ const client = HttpClient.getInstance() * Collection of endpoints related to Tags. */ export const tags = { - /** - * Retrieves Tags (news categories) from PoliNetwork server. - */ - async getTags(options?: RequestOptions) { - const response = await client.poliNetworkInstance.get( - "/v1/tags", + /** + * Retrieves Tags (news categories) from PoliNetwork server. + */ + async getTags(options?: RequestOptions) { + const response = await client.poliNetworkInstance.get( + "/v1/tags", - options - ) - return response.data.tags - }, + options + ) + return response.data.tags + }, } diff --git a/src/api/timetable.ts b/src/api/timetable.ts index cdd1f9f0..db2d862f 100644 --- a/src/api/timetable.ts +++ b/src/api/timetable.ts @@ -2,36 +2,36 @@ import { HttpClient, RequestOptions } from "./HttpClient" /* eslint-disable @typescript-eslint/naming-convention */ export interface Lecture { - event_id: number - date_start: string - date_end: string - favourite: boolean - show_agenda: boolean - matricola: string - title: { - it: string - en: string + event_id: number + date_start: string + date_end: string + favourite: boolean + show_agenda: boolean + matricola: string + title: { + it: string + en: string + } + event_type: { + typeId: number + type_dn: { + it: string + en: string } - event_type: { - typeId: number - type_dn: { - it: string - en: string - } - } - calendar: { - calendar_id: number - calendar_dn: { - it: string - en: string - } - } - room: { - room_id: number - acronym_dn: string - classroom_id: number - room_dn: string + } + calendar: { + calendar_id: number + calendar_dn: { + it: string + en: string } + } + room: { + room_id: number + acronym_dn: string + classroom_id: number + room_dn: string + } } const client = HttpClient.getInstance() @@ -40,14 +40,14 @@ const client = HttpClient.getInstance() * Collection of endpoints related to Timetable. */ export const timetable = { - /** - * Retrieves mock timetable from PoliNetwork server. - */ - async getTimetable(options?: RequestOptions) { - const response = await client.poliNetworkInstance.get( - "/v1/mock/timetable", - options - ) - return response.data - }, + /** + * Retrieves mock timetable from PoliNetwork server. + */ + async getTimetable(options?: RequestOptions) { + const response = await client.poliNetworkInstance.get( + "/v1/mock/timetable", + options + ) + return response.data + }, } diff --git a/src/api/user.ts b/src/api/user.ts index 4fc5fbe3..1ca9c8ed 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -1,34 +1,39 @@ -import { HttpClient, AuthType } from "./HttpClient" +import { HttpClient, AuthType, RetryType } from "./HttpClient" /** * Interface of UI User Object. */ export interface User { - codPersona: string - careers: Career[] - firstname: string - lastname: string - profilePic?: string + codPersona: string + careers: Career[] + firstname: string + lastname: string + profilePic?: string } export interface Career { - matricola: string - type?: string // Visitatore - Studente - Studente - titolo conseguito + matricola: string + type?: string // Visitatore - Studente - Studente - titolo conseguito } /** * Interface of user data object retrieved from Polimi's server. */ export interface PolimiUserData { - idPersona: number - codicePersona: string - nome: string - cognome: string - matricola: string - classeCarriera: string - description: string - initials: string - email: string - fotoURL: string + idPersona: number + codicePersona: string + nome: string + cognome: string + matricola: string + classeCarriera: string + description: string + initials: string + email: string + fotoURL: string +} + +interface PoliNetworkSettings { + // eslint-disable-next-line @typescript-eslint/naming-convention + expire_in_days: number } const client = HttpClient.getInstance() @@ -37,27 +42,75 @@ const client = HttpClient.getInstance() * Collection of endpoints related to User */ export const user = { - /** - * test PoliNetwork auth call - */ - async getPoliNetworkMe() { - const response = await client.poliNetworkInstance.get<{ - id: string - }>("/v1/accounts/me", { - authType: AuthType.POLINETWORK, - }) - return response.data - }, - /** - * test polimi auth call - */ - async getPolimiUserInfo() { - const response = await client.polimiInstance.get( - "/internal/user", - { - authType: AuthType.POLIMI, - } - ) - return response.data - }, + /** + * Get the user's settings from PoliNetwork + * @returns Settings of the user + */ + async getPoliNetworkSettings() { + const response = await client.poliNetworkInstance.get( + "/v1/accounts/me/settings", + { + authType: AuthType.POLINETWORK, + } + ) + return response.data + }, + + /** + * Update PoliNetwork user settings + * @param settings new settings to be saved + */ + async updatePoliNetworkSettings(settings: Partial) { + await client.poliNetworkInstance.post( + "/v1/accounts/me/settings", + settings, + { + authType: AuthType.POLINETWORK, + retryType: RetryType.NO_RETRY, + } + ) + }, + + /** + * test PoliNetwork auth call + */ + async getPoliNetworkMe() { + const response = await client.poliNetworkInstance.get<{ + id: string + }>("/v1/accounts/me", { + authType: AuthType.POLINETWORK, + }) + return response.data + }, + /** + * test polimi auth call + */ + async getPolimiUserInfo() { + const response = await client.polimiInstance.get( + "/rest/jaf/internal/user", + { + authType: AuthType.POLIMI, + } + ) + return response.data + }, + /** + * Get a file with all of the user's data + */ + async exportPoliNetworkMe() { + const response = await client.poliNetworkInstance.get< + Record + >("/v1/accounts/me/export", { + authType: AuthType.POLINETWORK, + }) + return response.data + }, + /** + * Delete the user's account and data + */ + async deletePoliNetworkMe() { + await client.poliNetworkInstance.delete("/v1/accounts/me", { + authType: AuthType.POLINETWORK, + }) + }, } diff --git a/src/components/Button.tsx b/src/components/Button.tsx new file mode 100644 index 00000000..af82d4bc --- /dev/null +++ b/src/components/Button.tsx @@ -0,0 +1,54 @@ +import React, { FC } from "react" +import { Pressable, StyleSheet, ViewStyle } from "react-native" +import { Text } from "components/Text" +import { usePalette } from "utils/colors" + +export interface ButtonCustomProps { + text?: string + /** + * when true the button is "light type" in Light Mode and "dark type" + * in Dark Mode + */ + light?: boolean + onPress?: () => void + style?: ViewStyle +} +/** + * Custom button component. Specify param `light` to select button type + * + */ +export const ButtonCustom: FC = props => { + const { palette, isLight } = usePalette() + return ( + + {props.text} + + ) +} + +const styles = StyleSheet.create({ + button: { + height: 32, + minWidth: 33, + borderRadius: 16, + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + }, +}) diff --git a/src/components/CardWithGradient.tsx b/src/components/CardWithGradient.tsx index bf69cc62..2469efb4 100644 --- a/src/components/CardWithGradient.tsx +++ b/src/components/CardWithGradient.tsx @@ -5,29 +5,29 @@ import { LinearGradient } from "expo-linear-gradient" import { CardTitle } from "components/Text" export interface CardWithGradientProps { - /** - * Title in the top-left corner of the card - */ - title?: string - /** - * URL of the image in the background of the card - */ - imageURL?: string - /** - * Function called when the card is clicked - * @default null - */ - onClick?: () => void - /** - * whether or not to render the title closer to the top-left corner - * @default false - */ - closerToCorner?: boolean - /** - * Style of the card component - * @default { marginBottom: 17, borderRadius: 12 } - */ - style?: ViewStyle + /** + * Title in the top-left corner of the card + */ + title?: string + /** + * URL of the image in the background of the card + */ + imageURL?: string + /** + * Function called when the card is clicked + * @default null + */ + onClick?: () => void + /** + * whether or not to render the title closer to the top-left corner + * @default false + */ + closerToCorner?: boolean + /** + * Style of the card component + * @default { marginBottom: 17, borderRadius: 12 } + */ + style?: ViewStyle } /** @@ -37,47 +37,42 @@ export interface CardWithGradientProps { * The card can be styled through props. */ export const CardWithGradient: FC = props => { - // border radius given to the background image and linear gradient (it matches the border radius of the card) - const borderRadius = - props.style && props.style.borderRadius !== undefined - ? props.style.borderRadius - : 12 + // border radius given to the background image and linear gradient (it matches the border radius of the card) + const borderRadius = + props.style && props.style.borderRadius !== undefined + ? props.style.borderRadius + : 12 - const closerToCorner = props.closerToCorner ?? false + const closerToCorner = props.closerToCorner ?? false - // TODO: what is the default image? - const imageURL = - props.imageURL || - "https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80" - - return ( - + + - - - - - {props.title} - - - - - - ) + + {props.title} + + + + + ) } diff --git a/src/components/ContentWrapperScroll.tsx b/src/components/ContentWrapperScroll.tsx index b238b91c..a30aab87 100644 --- a/src/components/ContentWrapperScroll.tsx +++ b/src/components/ContentWrapperScroll.tsx @@ -10,71 +10,84 @@ import { usePalette } from "utils/colors" * Default margin Top is 86 (proper margin for Settings Page) */ export const ContentWrapperScroll: FC<{ - children: React.ReactNode + children: React.ReactNode - title?: string - /** - * Remove the navbar from the bottom of the page. - */ - hideNavbar?: boolean - /** - * Props for the navbar, see {@link NavBar} - */ - navbarOptions?: NavbarProps - marginTop?: number + title?: string + /** + * Remove the navbar from the bottom of the page. + */ + hideNavbar?: boolean + /** + * Props for the navbar, see {@link NavBar} + */ + navbarOptions?: NavbarProps + marginTop?: number }> = props => { - const { background, isLight, primary } = usePalette() + const { background, isLight, primary } = usePalette() - const navbar = !props.hideNavbar + const navbar = !props.hideNavbar - return ( + return ( + + {props.title && ( + - {props.title && ( - - - {props.title} - - - )} + > + {props.title} + + + )} + + - - {props.children} - - - {navbar ? : null} + borderTopLeftRadius: 30, + borderTopRightRadius: 30, + + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 0, + }, + shadowOpacity: 0.2, + shadowRadius: 8.3, + elevation: 13, + }} + > + + + {props.children} + - ) + + {navbar ? : null} + + ) } diff --git a/src/components/Divider.tsx b/src/components/Divider.tsx index 3e7c2454..cb4e09c9 100644 --- a/src/components/Divider.tsx +++ b/src/components/Divider.tsx @@ -2,24 +2,24 @@ import React, { FC } from "react" import { View, ViewStyle } from "react-native" export interface DividerProps { - color?: string - height?: number - width?: number - style?: ViewStyle + color?: string + height?: number + width?: number + style?: ViewStyle } export const Divider: FC = props => { - return ( - - ) + props.style, + ]} + /> + ) } diff --git a/src/components/FreeClass/AddressText.tsx b/src/components/FreeClass/AddressText.tsx index 1fccbcbd..b1612c0f 100644 --- a/src/components/FreeClass/AddressText.tsx +++ b/src/components/FreeClass/AddressText.tsx @@ -8,8 +8,8 @@ import { Platform } from "react-native" import { LocationGeocodedAddress, PermissionStatus } from "expo-location" interface AddressTextProps { - currentLocation: LocationGeocodedAddress | undefined - locationStatus: PermissionStatus + currentLocation: LocationGeocodedAddress | undefined + locationStatus: PermissionStatus } /** @@ -19,70 +19,69 @@ interface AddressTextProps { * - GPS not enabled: there is a red text saying that the gps signal is missing */ export const AddressText: FC = props => { - const { background, isDark } = usePalette() - const positionArrowSVG = useSVG(PositionArrowIcon) + const { background, isDark } = usePalette() + const positionArrowSVG = useSVG(PositionArrowIcon) - return ( - - - - {positionArrowSVG && ( - - )} - - - - {props.currentLocation === undefined ? ( - props.locationStatus !== PermissionStatus.GRANTED ? ( - "Segnale GPS mancante" - ) : ( - - ) - ) : Platform.OS === "ios" ? ( - props.currentLocation?.name + - ", " + - props.currentLocation?.city - ) : ( - props.currentLocation?.street + - " " + - props.currentLocation?.streetNumber + - ", " + - props.currentLocation?.city - )} - - - ) + return ( + + + + {positionArrowSVG && ( + + )} + + + + {props.currentLocation === undefined ? ( + props.locationStatus !== PermissionStatus.GRANTED ? ( + "Segnale GPS mancante" + ) : ( + + ) + ) : Platform.OS === "ios" ? ( + props.currentLocation?.name + ", " + props.currentLocation?.city + ) : ( + props.currentLocation?.street + + " " + + props.currentLocation?.streetNumber + + ", " + + props.currentLocation?.city + )} + + + ) } diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx index 792fd26a..c8a6bc5e 100644 --- a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx @@ -2,89 +2,89 @@ import React, { FC } from "react" import { View } from "react-native" import { usePalette } from "utils/colors" import Animated, { - runOnJS, - useAnimatedStyle, - useSharedValue, + runOnJS, + useAnimatedStyle, + useSharedValue, } from "react-native-reanimated" import { - Gesture, - GestureDetector, - GestureHandlerRootView, + Gesture, + GestureDetector, + GestureHandlerRootView, } from "react-native-gesture-handler" import { ValidCrowdStatus } from "../CrowdingSection" import { getCrowdStatus } from "utils/rooms" interface CrowdSliderDynamicProps { - usableWidth?: number - startingPos: ValidCrowdStatus - onSlideEnd: (pos: ValidCrowdStatus) => void + usableWidth?: number + startingPos: ValidCrowdStatus + onSlideEnd: (pos: ValidCrowdStatus) => void } export const CrowdSliderDynamic: FC = props => { - const { isLight } = usePalette() + const { isLight } = usePalette() - const wrapper = (pos: number, width: number) => { - const crowdStatus = getCrowdStatus(pos, width) - if (props.onSlideEnd) { - props.onSlideEnd(crowdStatus) - } + const wrapper = (pos: number, width: number) => { + const crowdStatus = getCrowdStatus(pos, width) + if (props.onSlideEnd) { + props.onSlideEnd(crowdStatus) } - const circleWidth = 28 + } + const circleWidth = 28 - //320 is modal width, 52 is content padding - const usableWidth = props.usableWidth ?? 320 - 52 - circleWidth + //320 is modal width, 52 is content padding + const usableWidth = props.usableWidth ?? 320 - 52 - circleWidth - const startingPos = ((props.startingPos - 1) / 4) * usableWidth + const startingPos = ((props.startingPos - 1) / 4) * usableWidth - const position = useSharedValue(startingPos) - const lastPosition = useSharedValue(startingPos) - const panGesture = Gesture.Pan() - .onUpdate(e => { - const newPos = e.translationX + lastPosition.value - position.value = newPos - if (newPos < 0) { - position.value = 0 - } else if (newPos > usableWidth) { - position.value = usableWidth - } - }) - .onEnd(() => { - lastPosition.value = position.value - runOnJS(wrapper)(position.value, usableWidth) - }) + const position = useSharedValue(startingPos) + const lastPosition = useSharedValue(startingPos) + const panGesture = Gesture.Pan() + .onUpdate(e => { + const newPos = e.translationX + lastPosition.value + position.value = newPos + if (newPos < 0) { + position.value = 0 + } else if (newPos > usableWidth) { + position.value = usableWidth + } + }) + .onEnd(() => { + lastPosition.value = position.value + runOnJS(wrapper)(position.value, usableWidth) + }) - const animatedPos = useAnimatedStyle(() => ({ - transform: [{ translateX: position.value }], - })) + const animatedPos = useAnimatedStyle(() => ({ + transform: [{ translateX: position.value }], + })) - return ( - - + return ( + + - - - - - - - ) + + + + + + + ) } diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx index 0fee17b6..a614bb55 100644 --- a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx @@ -4,46 +4,46 @@ import { BodyText } from "components/Text" import { View } from "react-native" export const CrowdSliderLabels: FC = () => { - const { isLight } = usePalette() + const { isLight } = usePalette() - return ( - - - Basso - - - Medio - - - Alto - - - ) + return ( + + + Basso + + + Medio + + + Alto + + + ) } diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx index a875f59c..9d0fe35b 100644 --- a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx @@ -4,40 +4,40 @@ import { usePalette } from "utils/colors" import { ValidCrowdStatus } from "../CrowdingSection" interface CrowdSliderStaticProps { - position: ValidCrowdStatus + position: ValidCrowdStatus } export const CrowdSliderStatic: FC = props => { - const { isLight } = usePalette() + const { isLight } = usePalette() - //56 is padding, 28 is circle diameter - const usableWidth = Dimensions.get("screen").width - 56 - 28 + //56 is padding, 28 is circle diameter + const usableWidth = Dimensions.get("screen").width - 56 - 28 - const startingPos = ((props.position - 1) / 4) * usableWidth + const startingPos = ((props.position - 1) / 4) * usableWidth - return ( - - - - - - ) + return ( + + + + + + ) } diff --git a/src/components/FreeClass/ClassDetails/CrowdingSection.tsx b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx index 90909eb1..030df5b5 100644 --- a/src/components/FreeClass/ClassDetails/CrowdingSection.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx @@ -5,146 +5,144 @@ import { BodyText } from "components/Text" import { CrowdSliderStatic } from "./CrowdSlider/CrowdSliderStatic" import { CrowdSliderDynamic } from "./CrowdSlider/CrowdSliderDynamic" import { CrowdSliderLabels } from "./CrowdSlider/CrowdSliderLabels" -import { ButtonCustom } from "components/Settings" +import { ButtonCustom } from "components/Button" import { ModalWithGestures } from "../ModalWithGestures" import { api, RetryType } from "api" interface CrowdingSectionProps { - roomId: number - isSlidable?: boolean - onSlided?: () => void + roomId: number + isSlidable?: boolean + onSlided?: () => void } export type ValidCrowdStatus = 1 | 2 | 3 | 4 | 5 export const CrowdingSection: FC = props => { - const { isLight } = usePalette() + const { isLight } = usePalette() - const [isModalVisible, setIsModalVisible] = useState(false) + const [isModalVisible, setIsModalVisible] = useState(false) - const [occupancyRate, setOccupancyRate] = useState(1) + const [occupancyRate, setOccupancyRate] = useState(1) - let occupancyRateUser: ValidCrowdStatus = 3 + let occupancyRateUser: ValidCrowdStatus = 3 - const getOccupancyRate = async () => { - try { - const res = await api.rooms.getOccupancyRate(props.roomId) - if (res.occupancy_rate !== null) { - setOccupancyRate(res.occupancy_rate) - } - } catch (err) { - console.log(err) - } + const getOccupancyRate = async () => { + try { + const res = await api.rooms.getOccupancyRate(props.roomId) + if (res.occupancy_rate !== null) { + setOccupancyRate(res.occupancy_rate) + } + } catch (err) { + console.log(err) } + } - useEffect(() => { - void getOccupancyRate() - }, []) + useEffect(() => { + void getOccupancyRate() + }, []) - const postOccupancyRate = async () => { - try { - await api.rooms.postOccupancyRate(props.roomId, occupancyRateUser, { - retryType: RetryType.RETRY_N_TIMES, - maxRetries: 3, - }) - } catch (err) { - console.log(err) - } + const postOccupancyRate = async () => { + try { + await api.rooms.postOccupancyRate(props.roomId, occupancyRateUser, { + retryType: RetryType.RETRY_N_TIMES, + maxRetries: 3, + }) + } catch (err) { + console.log(err) } - return ( - - - Affollamento: - + } + return ( + + + Affollamento: + - - - - - Se il dato sull'affollamento non è corretto - - setIsModalVisible(true)}> - - esprimi opinione - - - + + + + + Se il dato sull'affollamento non è corretto + + setIsModalVisible(true)}> + + esprimi opinione + + + - setIsModalVisible(false)} - > - - - Esprimi{"\n"}Opinione - - - Indica il livello di affollamento {"\n"} dell'aula - - - - (occupancyRateUser = pos) - } - /> - - + setIsModalVisible(false)} + > + + + Esprimi{"\n"}Opinione + + + Indica il livello di affollamento {"\n"} dell'aula + + + (occupancyRateUser = pos)} + /> + + - { - void postOccupancyRate() - setIsModalVisible(false) - }} - /> - - + { + void postOccupancyRate() + setIsModalVisible(false) + }} + /> - ) + + + ) } diff --git a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx index 5d5fdce8..c3660bd6 100644 --- a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx +++ b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx @@ -8,231 +8,229 @@ import expand from "assets/freeClassrooms/expand.svg" import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" interface InfoMapTileProps { - roomName: string - building: string - address?: string - capacity?: string + roomName: string + building: string + address?: string + capacity?: string } export const InfoMapTile: FC = props => { - const { isLight, primary } = usePalette() + const { isLight, primary } = usePalette() - console.log(props.building) - const building = extractBuilding(props.building) - console.log(props.roomName) - const roomName = extractRoom(props.roomName) + console.log(props.building) + const building = extractBuilding(props.building) + console.log(props.roomName) + const roomName = extractRoom(props.roomName) - const expandSvg = useSVG(expand) + const expandSvg = useSVG(expand) - /*from https://stackoverflow.com/questions/73653813/how-to-open-google-map-with-latitude-and-longitude*/ - const openAddressOnMap = (label: string, lat: string, lng: string) => { - const scheme = Platform.select({ - ios: "maps:0,0?q=", - android: "geo:0,0?q=", - }) - const latLng = `${lat},${lng}` - if (scheme) { - const url = Platform.select({ - ios: `${scheme}${label}@${latLng}`, - android: `${scheme}${latLng}(${label})`, - }) - if (url) { - void Linking.openURL(url) - } - } + /*from https://stackoverflow.com/questions/73653813/how-to-open-google-map-with-latitude-and-longitude*/ + const openAddressOnMap = (label: string, lat: string, lng: string) => { + const scheme = Platform.select({ + ios: "maps:0,0?q=", + android: "geo:0,0?q=", + }) + const latLng = `${lat},${lng}` + if (scheme) { + const url = Platform.select({ + ios: `${scheme}${label}@${latLng}`, + android: `${scheme}${latLng}(${label})`, + }) + if (url) { + void Linking.openURL(url) + } } - return ( + } + return ( + + + - + > + {building && roomName ? `${building}.` : undefined} + + + {building && roomName ? roomName : props.roomName} + + + + + Indirizzo : + + + {props.address} + + + + + Capienza:{" "} + + + {props.capacity} + + + + + + + openAddressOnMap( + props.building ? building + "." + roomName : roomName, + "45.478053", + "9.228061" + ) + } + > + + {expand && expandSvg && ( + - - {building && roomName ? `${building}.` : undefined} - - - {building && roomName ? roomName : props.roomName} - - - - - Indirizzo : - - - {props.address} - - - - - Capienza:{" "} - - - {props.capacity} - + > + + + )} + + - - - openAddressOnMap( - props.building - ? building + "." + roomName - : roomName, - "45.478053", - "9.228061" - ) - } - > - - {expand && expandSvg && ( - - - - - - )} - - - - - consulta la{" "} - - - mappa - - - - + > + consulta la{" "} + + + mappa + + - ) + + + ) } diff --git a/src/components/FreeClass/ClassDetails/PageWrapper.tsx b/src/components/FreeClass/ClassDetails/PageWrapper.tsx index 5ef7ab79..031d392c 100644 --- a/src/components/FreeClass/ClassDetails/PageWrapper.tsx +++ b/src/components/FreeClass/ClassDetails/PageWrapper.tsx @@ -8,55 +8,52 @@ import { usePalette } from "utils/colors" * somewhere in the app. */ export const PageWrapper: FC<{ - hideNavbar?: boolean - /** - * Props for the navbar, see {@link NavBar} - */ - navbarOptions?: NavbarProps + hideNavbar?: boolean + /** + * Props for the navbar, see {@link NavBar} + */ + navbarOptions?: NavbarProps - style?: ViewStyle + style?: ViewStyle - children: React.ReactNode + children: React.ReactNode }> = props => { - const { background, isLight, primary } = usePalette() + const { background, isLight, primary } = usePalette() - const navbar = !props.hideNavbar + const navbar = !props.hideNavbar - return ( - + + - - - {props.children} - - + {props.children} + + - {navbar ? : null} - - ) + {navbar ? : null} + + ) } diff --git a/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx b/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx index aac3ed55..16cefade 100644 --- a/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx +++ b/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx @@ -5,37 +5,28 @@ import { BodyText } from "components/Text" import { RoomUtilsTile } from "./RoomUtilsTile" interface RoomUtilsSectionProps { - power?: boolean - computers?: boolean - ribaltine?: boolean + power?: boolean + computers?: boolean + ribaltine?: boolean } export const RoomUtilsSection: FC = props => { - const { isLight } = usePalette() + const { isLight } = usePalette() - return ( - - - Info Utili: - - - - - - ) + return ( + + + Info Utili: + + + + + + ) } diff --git a/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx b/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx index cee7b5d8..81acea93 100644 --- a/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx +++ b/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx @@ -4,85 +4,79 @@ import { usePalette } from "utils/colors" import { BodyText } from "components/Text" import { - BlendMode, - Canvas, - Group, - ImageSVG, - Skia, - useSVG, + BlendMode, + Canvas, + Group, + ImageSVG, + Skia, + useSVG, } from "@shopify/react-native-skia" import tick from "assets/freeClassrooms/tick.svg" interface RoomUtilsTileProps { - name: string - status?: boolean + name: string + status?: boolean } export const RoomUtilsTile: FC = props => { - const { isLight } = usePalette() + const { isLight } = usePalette() - const tickSvg = useSVG(tick) + const tickSvg = useSVG(tick) - const paintTick = useMemo(() => Skia.Paint(), []) - paintTick.setColorFilter( - Skia.ColorFilter.MakeBlend( - Skia.Color( - props.status - ? isLight - ? "#424967" - : "#8791BD" - : isLight - ? "#8791BD" - : "#424967" - ), - BlendMode.SrcIn - ) + const paintTick = useMemo(() => Skia.Paint(), []) + paintTick.setColorFilter( + Skia.ColorFilter.MakeBlend( + Skia.Color( + props.status + ? isLight + ? "#424967" + : "#8791BD" + : isLight + ? "#8791BD" + : "#424967" + ), + BlendMode.SrcIn ) + ) - return ( + return ( + + {tick && tickSvg && ( + - {tick && tickSvg && ( - - - - - - - - )} - - {props.name} - + > + + + + - ) + )} + + {props.name} + + + ) } diff --git a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx index a1d2026a..6870b77f 100644 --- a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx +++ b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx @@ -4,210 +4,208 @@ import { usePalette } from "utils/colors" import { BodyText } from "components/Text" import { - BlendMode, - Canvas, - Group, - ImageSVG, - Skia, - useSVG, + BlendMode, + Canvas, + Group, + ImageSVG, + Skia, + useSVG, } from "@shopify/react-native-skia" import clock from "assets/freeClassrooms/clock.svg" import { extractTimeLeft } from "utils/rooms" interface TimeLeftTileProps { - startDate: string + startDate: string } export const TimeLeftTile: FC = props => { - const { isLight } = usePalette() + const { isLight } = usePalette() - const clockSvg = useSVG(clock) + const clockSvg = useSVG(clock) - const paint = useMemo(() => Skia.Paint(), []) - paint.setColorFilter( - Skia.ColorFilter.MakeBlend( - Skia.Color(isLight ? "#414867" : "#fff"), - BlendMode.SrcIn - ) + const paint = useMemo(() => Skia.Paint(), []) + paint.setColorFilter( + Skia.ColorFilter.MakeBlend( + Skia.Color(isLight ? "#414867" : "#fff"), + BlendMode.SrcIn ) - const paintClock = useMemo(() => Skia.Paint(), []) - paintClock.setColorFilter( - Skia.ColorFilter.MakeBlend( - Skia.Color(isLight ? "#424967" : "#fff"), - BlendMode.SrcIn - ) + ) + const paintClock = useMemo(() => Skia.Paint(), []) + paintClock.setColorFilter( + Skia.ColorFilter.MakeBlend( + Skia.Color(isLight ? "#424967" : "#fff"), + BlendMode.SrcIn ) + ) - const startDate = new Date(props.startDate) - const endDate = new Date(startDate.getTime() + 8 * 60 * 60 * 1000) - const startHour = startDate.getHours().toString().padStart(2, "0") - const endhour = endDate.getHours().toString().padStart(2, "0") + const startDate = new Date(props.startDate) + const endDate = new Date(startDate.getTime() + 8 * 60 * 60 * 1000) + const startHour = startDate.getHours().toString().padStart(2, "0") + const endhour = endDate.getHours().toString().padStart(2, "0") - const { hoursLeft, minutesLeft, isPositive } = extractTimeLeft(startDate) + const { hoursLeft, minutesLeft, isPositive } = extractTimeLeft(startDate) - return ( - - + + Libera: + + + + + + + Da + + + - Libera: - + paddingHorizontal: 2, + }} + > + {startHour} : 00 + + + + - - - - - Da - - - - {startHour} : 00 - - - - - - A - - - - {endhour} : 00 - - - - - - - - {clock && clockSvg && ( - - - - - - - - )} - - - Mancano: - - - {isPositive - ? `${hoursLeft} h ${minutesLeft}'` - : "-- h -- '"} - - - - + > + A + + + + {endhour} : 00 + + + - ) + + + {clock && clockSvg && ( + + + + + + + + )} + + + Mancano: + + + {isPositive ? `${hoursLeft} h ${minutesLeft}'` : "-- h -- '"} + + + + + + + ) } diff --git a/src/components/FreeClass/DateTimePicker/DateTimeBox.tsx b/src/components/FreeClass/DateTimePicker/DateTimeBox.tsx index 7b5bc170..6dd38622 100644 --- a/src/components/FreeClass/DateTimePicker/DateTimeBox.tsx +++ b/src/components/FreeClass/DateTimePicker/DateTimeBox.tsx @@ -4,36 +4,36 @@ import { Text } from "components/Text" import { usePalette } from "utils/colors" export interface DateTimeBoxProps { - value?: string + value?: string } /** * custom box which contains a 2 digit number, used * inside {@link DateTimePicker} */ export const DateTimeBox: FC = props => { - const { isLight } = usePalette() - return ( - - - {props.value} - - - ) + const { isLight } = usePalette() + return ( + + + {props.value} + + + ) } diff --git a/src/components/FreeClass/DateTimePicker/DateTimePicker.tsx b/src/components/FreeClass/DateTimePicker/DateTimePicker.tsx index a6ba616c..33d3cdd6 100644 --- a/src/components/FreeClass/DateTimePicker/DateTimePicker.tsx +++ b/src/components/FreeClass/DateTimePicker/DateTimePicker.tsx @@ -8,131 +8,112 @@ import DateTimePickerModal from "react-native-modal-datetime-picker" import { StyleSheet } from "react-native" export interface DateTimePickerProps { - date: Date - setDate: (date: Date) => void + date: Date + setDate: (date: Date) => void } /** * custom DateTime picker */ export const DateTimePicker: FC = props => { - const { year, month, day, hour, minute } = destructureDate(props.date) - const { isLight, primary } = usePalette() + const { year, month, day, hour, minute } = destructureDate(props.date) + const { isLight, primary } = usePalette() - const [isDatePickerVisible, setDatePickerVisibility] = useState(false) + const [isDatePickerVisible, setDatePickerVisibility] = useState(false) - //there would be also "date-time" but we don't use it - const [dateMode, setDateMode] = useState<"date" | "time">("date") + //there would be also "date-time" but we don't use it + const [dateMode, setDateMode] = useState<"date" | "time">("date") - const showDatePicker = () => { - setDateMode("date") - setDatePickerVisibility(true) - } + const showDatePicker = () => { + setDateMode("date") + setDatePickerVisibility(true) + } - const showTimePicker = () => { - setDateMode("time") - setDatePickerVisibility(true) - } + const showTimePicker = () => { + setDateMode("time") + setDatePickerVisibility(true) + } - const hideDateOrTimePicker = () => { - setDatePickerVisibility(false) - } + const hideDateOrTimePicker = () => { + setDatePickerVisibility(false) + } - const handleConfirm = (date: Date) => { - hideDateOrTimePicker() - props.setDate(date) - } - return ( + const handleConfirm = (date: Date) => { + hideDateOrTimePicker() + props.setDate(date) + } + return ( + + - - - - Data - - - - - - . - - - - . - - - - - - Ora - - - - - - : - - - - - + + Data + - ) + + + + . + + + + . + + + + + + Ora + + + + + + : + + + + + + + ) } const styles = StyleSheet.create({ - dot: { - alignSelf: "flex-end", - marginHorizontal: 4, - fontSize: 14, - }, - colon: { alignSelf: "center", marginHorizontal: 4, fontSize: 14 }, + dot: { + alignSelf: "flex-end", + marginHorizontal: 4, + fontSize: 14, + }, + colon: { alignSelf: "center", marginHorizontal: 4, fontSize: 14 }, }) diff --git a/src/components/FreeClass/FreeClassList.tsx b/src/components/FreeClass/FreeClassList.tsx index 9867b0e2..bec7ab6e 100644 --- a/src/components/FreeClass/FreeClassList.tsx +++ b/src/components/FreeClass/FreeClassList.tsx @@ -6,214 +6,207 @@ import { usePalette } from "utils/colors" import timerIcon from "assets/freeClassrooms/timer.svg" import overcrowdingIcon from "assets/freeClassrooms/overcrowding.svg" import fireIcon from "assets/freeClassrooms/fire.svg" -import { ScrollView } from "react-native-gesture-handler" import { useNavigation } from "navigation/NavigationTypes" import { api } from "api" +import { FlatList } from "react-native-gesture-handler" +import { RoomSimplified } from "api/rooms" const { width } = Dimensions.get("window") interface FreeClassListProps { - data: number[] - date: Date + data: RoomSimplified[] | undefined + date: Date } +/** + * It handles a list of freeclassrooms available. + */ export const FreeClassList: FC = props => { - const { palette } = usePalette() - const timerSVG = useSVG(timerIcon) - const overcrowdingSVG = useSVG(overcrowdingIcon) - const fireSVG = useSVG(fireIcon) + const { palette } = usePalette() + const timerSVG = useSVG(timerIcon) + const overcrowdingSVG = useSVG(overcrowdingIcon) + const fireSVG = useSVG(fireIcon) + const { navigate } = useNavigation() - const { navigate } = useNavigation() - - return ( - index.toString()} + renderItem={({ item }) => ( + { + try { + const selectedRoom = await api.rooms.getRoomInfo(item.roomId) + navigate("RoomDetails", { + room: selectedRoom, + startDate: props.date.toISOString(), + roomId: item.roomId, + }) + } catch (err) { + console.log(err) + } + }} > - {props.data.map((room, index) => ( - { - try { - const selectedRoom = await api.rooms.getRoomInfo( - room - ) - navigate("RoomDetails", { - room: selectedRoom, - startDate: props.date.toISOString(), - roomId: room, - }) - } catch (err) { - console.log(err) - } - }} - > - - - {timerSVG && ( - - )} - - - Libera per{"\n"} - - 2 h 23{"'"} - - - - - - Mediamente{"\n"} - - affollato - - - - - {overcrowdingSVG && ( - - )} - - - {fireSVG && ( - - )} - - - - - - {room} - - - - ))} - - ) + + + {timerSVG && ( + + )} + + + Libera per{"\n"} + + 2 h 23{"'"} + + + + + + Mediamente{"\n"} + + affollato + + + + + {overcrowdingSVG && ( + + )} + + + {fireSVG && ( + + )} + + + + + + {item.name} + + + + )} + /> + ) } diff --git a/src/components/FreeClass/Map.tsx b/src/components/FreeClass/Map.tsx index 5d75117e..5328f004 100644 --- a/src/components/FreeClass/Map.tsx +++ b/src/components/FreeClass/Map.tsx @@ -1,15 +1,23 @@ -import React, { FC } from "react" -import { View, ActivityIndicator } from "react-native" -import MapView, { Marker } from "react-native-maps" +import React, { FC, useEffect, useState } from "react" +import { + View, + ActivityIndicator, + Platform, + Text, + Pressable, +} from "react-native" +import MapView, { Callout, Marker, Region } from "react-native-maps" import { PermissionStatus } from "expo-location" import { BodyText } from "components/Text" +import { BuildingItem } from "pages/FreeClass/BuildingChoice" +import AsyncStorage from "@react-native-async-storage/async-storage" interface MapProps { - latitude: number - longitude: number - locationStatus: PermissionStatus - currentCampus?: number[] - onPressMarker: () => void + userLatitude: number + userLongitude: number + locationStatus: PermissionStatus + buildingList: BuildingItem[] | undefined + onPressMarker: (campusName: string[], buildingName: string) => void } /** @@ -17,54 +25,134 @@ interface MapProps { * So far, the initial region is represented by the user coordinates. */ export const Map: FC = props => { - return ( - (false) + + const [region, setRegion] = useState() + + useEffect(() => { + if (props.userLatitude === undefined && props.userLongitude === undefined) { + setTimeout(() => setTimer(true), 15000) //15 sec + } + }, []) + + useEffect(() => { + AsyncStorage.getItem("lastRegionVisited") + .then(regionJSON => { + if (regionJSON) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const tempRegion: Region = JSON.parse(regionJSON) + setRegion(tempRegion) + } + }) + .catch(err => console.log(err)) + }, []) + + return ( + + {props.userLatitude === undefined || props.userLongitude === undefined ? ( + props.locationStatus !== PermissionStatus.GRANTED || timer === true ? ( + + Mappa non Disponibile + + ) : ( + + ) + ) : ( + { + setRegion(region) + AsyncStorage.setItem( + "lastRegionVisited", + JSON.stringify(region) + ).catch(err => console.log(err)) + }} + showsUserLocation={true} + mapPadding={{ + top: 0, + right: 0, + bottom: Platform.OS === "ios" ? 170 : 165, + left: 0, + }} > - {props.latitude === undefined || props.longitude === undefined ? ( - props.locationStatus !== PermissionStatus.GRANTED ? ( - - Mappa non Disponibile - - ) : ( - - ) - ) : ( - - {props.currentCampus !== undefined ? ( - props.onPressMarker()} - > - ) : undefined} - - )} - - ) + {Platform.OS === "ios" ? ( + { + const region: Region = { + latitude: props.userLatitude, + longitude: props.userLongitude, + latitudeDelta: 0.002, + longitudeDelta: 0.002, + } + setRegion(region) + AsyncStorage.setItem( + "lastRegionVisited", + JSON.stringify(region) + ).catch(err => console.log(err)) + }} + /> + ) : undefined} + {props.buildingList?.map((building, index) => ( + + + props.onPressMarker(building.campus.name, building.name) + } + > + {building.name} + + + ))} + + )} + + ) } diff --git a/src/components/FreeClass/ModalWithGestures.tsx b/src/components/FreeClass/ModalWithGestures.tsx index 75ad1f41..e160b8d6 100644 --- a/src/components/FreeClass/ModalWithGestures.tsx +++ b/src/components/FreeClass/ModalWithGestures.tsx @@ -5,19 +5,19 @@ import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" import { deleteSvg as icon } from "assets/modal" import { gestureHandlerRootHOC } from "react-native-gesture-handler" export interface ModalWithGesturesProps { - /** - * content of the modal - */ - children: React.ReactNode + /** + * content of the modal + */ + children: React.ReactNode - /** - * whether ot not to show the modal - */ - isShowing: boolean - /** - * this function hides the modal by changing the state in the parent component - */ - onClose: () => void + /** + * whether ot not to show the modal + */ + isShowing: boolean + /** + * this function hides the modal by changing the state in the parent component + */ + onClose: () => void } /** @@ -27,102 +27,100 @@ export interface ModalWithGesturesProps { * */ export const ModalWithGestures: FC = props => { - const { backgroundSecondary, modalBarrier } = usePalette() + const { backgroundSecondary, modalBarrier } = usePalette() - const deleteSvg = useSVG(icon.svg) + const deleteSvg = useSVG(icon.svg) - // eslint-disable-next-line @typescript-eslint/naming-convention - const ContentRootHOC = gestureHandlerRootHOC(() => ( + // eslint-disable-next-line @typescript-eslint/naming-convention + const ContentRootHOC = gestureHandlerRootHOC(() => ( + + props.onClose()} > - + - props.onClose()} - > - - - {deleteSvg && ( - - )} - - - - - - {props.children} - - - + {deleteSvg && ( + + )} + + - )) - - return ( - //TODO: animationType fade or slide? - - - - ) + {props.children} + + + + )) + + return ( + //TODO: animationType fade or slide? + + + + ) } const styles = StyleSheet.create({ - pageWrapper: { - flex: 1, - justifyContent: "center", - alignItems: "center", - }, - contentWrapper: { - borderRadius: 12, + pageWrapper: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + contentWrapper: { + borderRadius: 12, - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 3, - }, - shadowOpacity: 0.27, - shadowRadius: 4.65, - elevation: 6, - }, - circle: { - width: 30, - height: 30, - backgroundColor: "#ffffff", - borderRadius: 15, - marginTop: 96, - marginBottom: 8, - justifyContent: "center", - alignItems: "center", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 3, }, + shadowOpacity: 0.27, + shadowRadius: 4.65, + elevation: 6, + }, + circle: { + width: 30, + height: 30, + backgroundColor: "#ffffff", + borderRadius: 15, + marginTop: 96, + marginBottom: 8, + justifyContent: "center", + alignItems: "center", + }, }) diff --git a/src/components/FreeClass/PositionModality.tsx b/src/components/FreeClass/PositionModality.tsx new file mode 100644 index 00000000..00dc1a93 --- /dev/null +++ b/src/components/FreeClass/PositionModality.tsx @@ -0,0 +1,137 @@ +import React, { FC, useState } from "react" +import { View, Pressable } from "react-native" +import { usePalette } from "utils/colors" +import { BodyText } from "components/Text" +import { Map } from "./Map" +import { FreeClassList } from "./FreeClassList" +import { PermissionStatus } from "expo-location" +import { BuildingItem } from "pages/FreeClass/BuildingChoice" +import { RoomSimplified } from "api/rooms" +import { PositionSearchBar } from "./PositionSearchBar" + +interface PositionModalityProps { + currentCoords: number[] + locationStatus: PermissionStatus + buildingList: BuildingItem[] | undefined +} + +enum ButtonType { + MAP, + LIST, +} + +/** + * It handles the button's state and the two modality: Map or List. + */ +export const PositionModality: FC = props => { + const [search, setSearch] = useState("") + + const { primary, isDark, palette } = usePalette() + + const [status, setStatus] = useState(ButtonType.MAP) + + const [roomList, setRoomList] = useState([]) + + const extractRooms = (campusName: string[], buildingName: string) => { + const tempRooms: RoomSimplified[] = [] + props.buildingList?.map(building => { + if ( + building.campus.name === campusName && + building.name === buildingName + ) { + tempRooms.push(...building.freeRoomList) + } + }) + setRoomList(tempRooms) + } + + return ( + <> + + setSearch(searchKey)} /> + + + setStatus(ButtonType.MAP)} + > + + Mappa + + + setStatus(ButtonType.LIST)} + > + + Lista + + + + {status === ButtonType.LIST ? ( + + + + ) : ( + { + setStatus(ButtonType.LIST) + void extractRooms(campusName, buildingName) + }} + /> + )} + + ) +} diff --git a/src/components/FreeClass/PositionSearchBar.tsx b/src/components/FreeClass/PositionSearchBar.tsx new file mode 100644 index 00000000..a65a40e5 --- /dev/null +++ b/src/components/FreeClass/PositionSearchBar.tsx @@ -0,0 +1,132 @@ +import React, { FC, useEffect, useState, useRef } from "react" +import { + TextInput, + Animated, + Pressable, + StyleProp, + ViewStyle, + Keyboard, +} from "react-native" +import { usePalette } from "utils/colors" +import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" +import searchLight from "assets/menu/searchLight.svg" +import searchDark from "assets/menu/searchDark.svg" + +/** + * the search bar, which requests a search everytime the input text changes + */ +export const PositionSearchBar: FC<{ + onChange: (searchKey: string) => void + style?: StyleProp +}> = ({ onChange, style }) => { + const { fieldBackground, fieldText, bodyText, isLight } = usePalette() + + const svg = useSVG(isLight ? searchLight : searchDark) + + const [isFocused, setIsFocused] = useState(false) + const shadowAnim = useRef(new Animated.Value(0)).current + const inputText = useRef(null) + + useEffect(() => { + const keyboardDidHideListener = Keyboard.addListener( + "keyboardDidHide", + () => { + inputText.current?.blur() + } + ) + + return () => { + keyboardDidHideListener.remove() + } + }, []) + + useEffect(() => { + const duration = 100 + if (isFocused) + Animated.timing(shadowAnim, { + duration, + toValue: 1, + useNativeDriver: true, + }).start() + else + Animated.timing(shadowAnim, { + duration, + toValue: 0, + useNativeDriver: true, + }).start() + }, [isFocused, shadowAnim]) + + return ( + + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + /> + { + inputText.current?.focus() + }} + > + + {svg && ( + + )} + + + + ) +} diff --git a/src/components/FreeClass/buildingCoords.json b/src/components/FreeClass/buildingCoords.json new file mode 100644 index 00000000..61263d9e --- /dev/null +++ b/src/components/FreeClass/buildingCoords.json @@ -0,0 +1,1165 @@ +[ + { + "acronym": "COE", + "campus": [ + { + "name": ["Anzani"], + "buildings": [ + { + "name": "Edificio 4", + "address": "Via Anzani 42, Como, 22100", + "coords": { + "latitude": 45.80120256319488, + "longitude": 9.092668671639375 + } + } + ] + }, + { + "name": ["Castelnuovo"], + "buildings": [ + { + "name": "Edificio 1", + "address": "Via Castelnuovo 7, Como, 22100", + "coords": { + "latitude": 45.80276883790487, + "longitude": 9.09505146871641 + } + } + ] + }, + { + "name": ["Zezio"], + "buildings": [ + { + "name": "Residenza La Presentazione", + "address": "Via Zezio 58, Como, 22100", + "coords": { + "latitude": 45.80645610020381, + "longitude": 9.09718826871654 + } + } + ] + } + ] + }, + { + "acronym": "CRG", + "campus": [ + { + "name": ["Sesto"], + "buildings": [ + { + "name": "Edificio C", + "address": "Via Sesto 39, Cremona, 26100", + "coords": { + "latitude": 45.147121757649806, + "longitude": 10.00162101166769 + } + }, + { + "name": "Palazzina A", + "address": "Via Sesto 39, Cremona, 26100", + "coords": { + "latitude": 45.14624583991279, + "longitude": 10.002210009658127 + } + }, + { + "name": "Palazzina B", + "address": "Via Sesto 39, Cremona, 26100", + "coords": { + "latitude": 45.146619394711394, + "longitude": 10.001899530097274 + } + } + ] + } + ] + }, + { + "acronym": "LCF", + "campus": [ + { + "name": ["Ghislanzoni"], + "buildings": [ + { + "name": "Residenza Loos", + "address": "Via Ghislanzoni 24, Lecco, 23900", + "coords": { + "latitude": 45.84944207561925, + "longitude": 9.397439941157165 + } + }, + { + "name": "BEEpilot", + "address": "Via Previati 1C, Lecco, 23900", + "coords": { + "latitude": 45.84889332151791, + "longitude": 9.39735373582034 + } + }, + { + "name": "Edificio 10", + "address": "Via Previati 1C, Lecco, 23900", + "coords": { + "latitude": 45.84945959386757, + "longitude": 9.396282096446393 + } + }, + { + "name": "Edificio 11", + "address": "Via Previati 1C, Lecco, 23900", + "coords": { + "latitude": 45.84976868042763, + "longitude": 9.397024120135281 + } + }, + { + "name": "Edificio 12", + "address": "Via Previati 1C, Lecco, 23900", + "coords": { + "latitude": 45.850239482856416, + "longitude": 9.396348203485335 + } + }, + { + "name": "Edificio 5", + "address": "Via Previati 1C, Lecco, 23900", + "coords": { + "latitude": 45.84992561501641, + "longitude": 9.396852458762153 + } + }, + { + "name": "Edificio 8", + "address": "Via Previati 1C, Lecco, 23900", + "coords": { + "latitude": 45.84920819614676, + "longitude": 9.39701875573217 + } + }, + { + "name": "Edificio 9", + "address": "Via Previati 1C, Lecco, 23900", + "coords": { + "latitude": 45.84890553228907, + "longitude": 9.396487678342055 + } + }, + { + "name": "Edificio CNR", + "address": "Via Previati 1C, Lecco, 23900", + "coords": { + "latitude": 45.84854681743879, + "longitude": 9.397254790092957 + } + } + ] + } + ] + }, + { + "acronym": "MNI", + "campus": [ + { + "name": ["Scarsellini"], + "buildings": [ + { + "name": "Edificio 1", + "address": "Via Scarsellini 15, Mantova, 46100", + "coords": { + "latitude": 45.160352957787765, + "longitude": 10.788892210362015 + } + }, + { + "name": "Edificio B", + "address": "Via Scarsellini 2, Mantova, 46100", + "coords": { + "latitude": 45.16071402717656, + "longitude": 10.78813888256507 + } + } + ] + } + ] + }, + { + "acronym": "MIB", + "campus": [ + { + "name": ["Bovisa", "Durando"], + "buildings": [ + { + "name": "Edificio B1", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.50526063108648, + "longitude": 9.166960640981896 + } + }, + { + "name": "Edificio B2", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.505856940521184, + "longitude": 9.16684246746718 + } + }, + { + "name": "Edificio B3", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.506291745504754, + "longitude": 9.16657066831335 + } + }, + { + "name": "Edificio B4", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.50582827267731, + "longitude": 9.1657801030213 + } + }, + { + "name": "Edificio B5", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.504051263792235, + "longitude": 9.165419660659406 + } + }, + { + "name": "Edificio B5A", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.50424070168094, + "longitude": 9.165416008016528 + } + }, + { + "name": "Edificio B6", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.50481669134817, + "longitude": 9.16627437531528 + } + }, + { + "name": "Edificio B7", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.504682097750845, + "longitude": 9.164140249884532 + } + }, + { + "name": "Edificio B8", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.50468698803176, + "longitude": 9.164754281608419 + } + }, + { + "name": "Edificio B9", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.50521269076449, + "longitude": 9.164468198661584 + } + }, + { + "name": "Edificio B9A", + "address": "Via Candiani 72, Milano, 20158", + "coords": { + "latitude": 45.50541074493551, + "longitude": 9.164988032336918 + } + }, + { + "name": "Infopoint", + "address": "Via Verità 25, Milano, 20158", + "coords": { + "latitude": 45.50448148112203, + "longitude": 9.165766681881529 + } + } + ] + }, + { + "name": ["Bovisa", "La Masa"], + "buildings": [ + { + "name": "Edificio B11", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.500846286602666, + "longitude": 9.155348601436222 + } + }, + { + "name": "Edificio B12", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50148227512907, + "longitude": 9.154387194659964 + } + }, + { + "name": "Edificio B13", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50157313004546, + "longitude": 9.155059639273848 + } + }, + { + "name": "Edificio B14", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.501563666, + "longitude": 9.156285702980878 + } + }, + { + "name": "Edificio B14A", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50161287903062, + "longitude": 9.156733999417478 + } + }, + { + "name": "Edificio B15", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50188922830498, + "longitude": 9.156825819163954 + } + }, + { + "name": "Edificio B16", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.501845693924324, + "longitude": 9.15774941782049 + } + }, + { + "name": "Edificio B16A", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50196304740825, + "longitude": 9.158189612507442 + } + }, + { + "name": "Edificio B18A", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50203686641096, + "longitude": 9.156566563416906 + } + }, + { + "name": "Edificio B18B", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50223182381833, + "longitude": 9.15721470280378 + } + }, + { + "name": "Edificio B18C", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50228860740229, + "longitude": 9.157603586435906 + } + }, + { + "name": "Edificio B19", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.502271572336156, + "longitude": 9.155699676957584 + } + }, + { + "name": "Edificio B20", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50239838883989, + "longitude": 9.15493811315239 + } + }, + { + "name": "Edificio B21", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50104305808609, + "longitude": 9.15633786717546 + } + }, + { + "name": "Edificio B22", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50135848414706, + "longitude": 9.157643792042283 + } + }, + { + "name": "Edificio B23", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50095617559604, + "longitude": 9.15800589026771 + } + }, + { + "name": "Edificio B24", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50170591921368, + "longitude": 9.153366080649027 + } + }, + { + "name": "Edificio B29", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.50128945237324, + "longitude": 9.15353385381083 + } + }, + { + "name": "Edificio B30", + "address": "Via La Masa 34, Milano, 20156", + "coords": { + "latitude": 45.501549132062195, + "longitude": 9.155347901172796 + } + }, + { + "name": "Edificio B37", + "address": "Via Lambruschini 4, Milano, 20156", + "coords": { + "latitude": 45.5029063064303, + "longitude": 9.156305606322812 + } + }, + { + "name": "Edificio BL25", + "address": "Via Lambruschini 4, Milano, 20156", + "coords": { + "latitude": 45.50309248664372, + "longitude": 9.155917630899358 + } + }, + { + "name": "Edificio BL25A", + "address": "Via Lambruschini 4, Milano, 20156", + "coords": { + "latitude": 45.50284016331667, + "longitude": 9.15555412238212 + } + }, + { + "name": "Edificio BL26", + "address": "Via Lambruschini 4, Milano, 20156", + "coords": { + "latitude": 45.50339625303833, + "longitude": 9.156018993853149 + } + }, + { + "name": "Edificio BL27", + "address": "Via Lambruschini 4, Milano, 20156", + "coords": { + "latitude": 45.50366327214294, + "longitude": 9.157438075212218 + } + }, + { + "name": "Edificio BL28", + "address": "Via Lambruschini 4, Milano, 20156", + "coords": { + "latitude": 45.503143931072984, + "longitude": 9.157036118688078 + } + } + ] + } + ] + }, + { + "acronym": "MIA", + "campus": [ + { + "name": ["Leonardo"], + "buildings": [ + { + "name": "Edificio 29", + "address": "Piazza Leonardo da Vinci 26, Milano, 20133", + "coords": { + "latitude": 45.47668740966992, + "longitude": 9.226326859295256 + } + }, + { + "name": "Edificio 1", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.47811790554435, + "longitude": 9.227725945733395 + } + }, + { + "name": "Edificio 10", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.47912809822935, + "longitude": 9.229883268245544 + } + }, + { + "name": "Edificio 2", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.4788249919485, + "longitude": 9.227210008150676 + } + }, + { + "name": "Edificio 2A", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.47876288697592, + "longitude": 9.227837695286086 + } + }, + { + "name": "Edificio 3", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.477435603580254, + "longitude": 9.227835389634977 + } + }, + { + "name": "Edificio 3A", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.47729656843257, + "longitude": 9.22805442460651 + } + }, + { + "name": "Edificio 4", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.478602838628646, + "longitude": 9.22845329881462 + } + }, + { + "name": "Edificio 4A", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.478974667422236, + "longitude": 9.228990510865588 + } + }, + { + "name": "Edificio 5", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.47769427270723, + "longitude": 9.228464826976882 + } + }, + { + "name": "Edificio 6", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.47825202398337, + "longitude": 9.22875994776181 + } + }, + { + "name": "Edificio 7", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.47814209083751, + "longitude": 9.229737535377756 + } + }, + { + "name": "Edificio 8", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.478809769736515, + "longitude": 9.229442414594473 + } + }, + { + "name": "Edificio 9", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.47759727192502, + "longitude": 9.229230296528302 + } + }, + { + "name": "Edificio 9A", + "address": "Piazza Leonardo da Vinci 32, Milano, 20133", + "coords": { + "latitude": 45.4771381326113, + "longitude": 9.229815926853346 + } + } + ] + }, + { + "name": ["Bassini"], + "buildings": [ + { + "name": "Edificio 19", + "address": "Via Ponzio 34/3, Milano, 20133", + "coords": { + "latitude": 45.47879081655148, + "longitude": 9.231155363015363 + } + }, + { + "name": "Edificio 20", + "address": "Via Ponzio 34/5, Milano, 20133", + "coords": { + "latitude": 45.478695312571354, + "longitude": 9.232531759044313 + } + }, + { + "name": "Edificio 21", + "address": "Via Golgi 39, Milano, 20133", + "coords": { + "latitude": 45.479048844048855, + "longitude": 9.233791065831895 + } + }, + { + "name": "Edificio 36", + "address": "Via Golgi 39, Milano, 20133", + "coords": { + "latitude": 45.47839707102326, + "longitude": 9.232902143392922 + } + }, + { + "name": "Edificio 36A", + "address": "Via Golgi 39, Milano, 20133", + "coords": { + "latitude": 45.47840377309843, + "longitude": 9.233573614377585 + } + }, + { + "name": "Edificio 37", + "address": "Via Golgi 39, Milano, 20133", + "coords": { + "latitude": 45.47840377309843, + "longitude": 9.233934440407387 + } + }, + { + "name": "Edificio 42", + "address": "Via Bassini 34/3, Milano, 20133", + "coords": { + "latitude": 45.47751909234621, + "longitude": 9.231437333034433 + } + }, + { + "name": "Edificio 43", + "address": "Via Bassini 34/3, Milano, 20133", + "coords": { + "latitude": 45.477644758070845, + "longitude": 9.231425385160406 + } + }, + { + "name": "Isola Ecologica", + "address": "Via Bassini 2, Milano, 20133", + "coords": { + "latitude": 45.478764590939626, + "longitude": 9.234032129716745 + } + } + ] + }, + { + "name": ["Bonardi"], + "buildings": [ + { + "name": "Edificio 11", + "address": "Via Ampère 2, Milano, 20133", + "coords": { + "latitude": 45.479700368402455, + "longitude": 9.227144319107985 + } + }, + { + "name": "Edificio 11B", + "address": "Via Ampère 4, Milano, 20133", + "coords": { + "latitude": 45.48039066193507, + "longitude": 9.226718974511959 + } + }, + { + "name": "Edificio 12", + "address": "Via Bonardi 3, Milano, 20133", + "coords": { + "latitude": 45.479559627904706, + "longitude": 9.227662857193996 + } + }, + { + "name": "Edificio 13", + "address": "Via Bonardi 9, Milano, 20133", + "coords": { + "latitude": 45.47986624067945, + "longitude": 9.228233965961486 + } + }, + { + "name": "Edificio 14", + "address": "Via Bonardi 9, Milano, 20133", + "coords": { + "latitude": 45.47964507754049, + "longitude": 9.22878834771603 + } + }, + { + "name": "Edificio 14A", + "address": "Via Bonardi 9, Milano, 20133", + "coords": { + "latitude": 45.48014771978483, + "longitude": 9.229170679933038 + } + }, + { + "name": "Edificio 14B", + "address": "Via Bonardi 9, Milano, 20133", + "coords": { + "latitude": 45.48014771978483, + "longitude": 9.228752504070686 + } + }, + { + "name": "Edificio 15", + "address": "Via Ponzio 31, Milano, 20133", + "coords": { + "latitude": 45.47966685878651, + "longitude": 9.229574518342371 + } + }, + { + "name": "Edificio 16A", + "address": "Via Bonardi 9, Milano, 20133", + "coords": { + "latitude": 45.479556276937224, + "longitude": 9.228453806990538 + } + }, + { + "name": "Edificio 16B", + "address": "Via Bonardi 9, Milano, 20133", + "coords": { + "latitude": 45.48007902560742, + "longitude": 9.227603117798322 + } + }, + { + "name": "Edificio 16C", + "address": "Via Bonardi 9, Milano, 20133", + "coords": { + "latitude": 45.479629998198945, + "longitude": 9.229118109245283 + } + }, + { + "name": "Edificio 18", + "address": "Via Ponzio 33, Milano, 20133", + "coords": { + "latitude": 45.48018625550555, + "longitude": 9.229739399120684 + } + } + ] + }, + { + "name": ["Colombo"], + "buildings": [ + { + "name": "Edificio 32.1", + "address": "Via Colombo 40, Milano, 20133", + "coords": { + "latitude": 45.47213230193092, + "longitude": 9.22686863256574 + } + }, + { + "name": "Edificio 32.2", + "address": "Via Colombo 40, Milano, 20133", + "coords": { + "latitude": 45.47207569302287, + "longitude": 9.227194415489148 + } + }, + { + "name": "Edificio 32.3", + "address": "Via Colombo 40, Milano, 20133", + "coords": { + "latitude": 45.47184925682209, + "longitude": 9.227171351211384 + } + }, + { + "name": "Edificio 32.4", + "address": "Via Colombo 40, Milano, 20133", + "coords": { + "latitude": 45.47190990946507, + "longitude": 9.226883047739344 + } + }, + { + "name": "Edificio 32.5", + "address": "Via Colombo 40, Milano, 20133", + "coords": { + "latitude": 45.47175625597605, + "longitude": 9.226926293260147 + } + }, + { + "name": "Edificio 30", + "address": "Via Colombo 81, Milano, 20133", + "coords": { + "latitude": 45.47586392362531, + "longitude": 9.226261178708338 + } + } + ] + }, + { + "name": ["Golgi"], + "buildings": [ + { + "name": "Edificio 26", + "address": "Via Golgi 20, Milano, 20133", + "coords": { + "latitude": 45.47569395835836, + "longitude": 9.235024035751083 + } + }, + { + "name": "Edificio 27", + "address": "Via Valvassori Peroni, Milano, 20133", + "coords": { + "latitude": 45.475868563542505, + "longitude": 9.236353204609438 + } + }, + { + "name": "Edificio 22", + "address": "Via Golgi 42, Milano, 20133", + "coords": { + "latitude": 45.47796163250129, + "longitude": 9.234955773844867 + } + }, + { + "name": "Edificio 23", + "address": "Via Golgi 40, Milano, 20133", + "coords": { + "latitude": 45.47770332464026, + "longitude": 9.234945870954828 + } + }, + { + "name": "Edificio 24", + "address": "Via Golgi 40, Milano, 20133", + "coords": { + "latitude": 45.47747417957433, + "longitude": 9.23470424043846 + } + }, + { + "name": "Edificio 25", + "address": "Via U.B. Secondo 3, Milano, 20133", + "coords": { + "latitude": 45.47750473230304, + "longitude": 9.23513402586612 + } + } + ] + }, + { + "name": ["Mancinelli"], + "buildings": [ + { + "name": "Edificio 28", + "address": "Via Mancinelli 7, Milano, 20133", + "coords": { + "latitude": 45.49016309824922, + "longitude": 9.226888094653553 + } + } + ] + }, + { + "name": ["Pascoli"], + "buildings": [ + { + "name": "Edificio 38", + "address": "Via Pascoli 70/3, Milano, 20133", + "coords": { + "latitude": 45.47547780481562, + "longitude": 9.22570000206852 + } + }, + { + "name": "Edificio 39", + "address": "Via Pascoli 70/3, Milano, 20133", + "coords": { + "latitude": 45.475677913901855, + "longitude": 9.225758755850668 + } + }, + { + "name": "Edificio 40", + "address": "Via Pascoli 70/3, Milano, 20133", + "coords": { + "latitude": 45.47591627820953, + "longitude": 9.225771345946843 + } + } + ] + }, + { + "name": ["Romagna"], + "buildings": [ + { + "name": "Casa dello Studente", + "address": "Viale Romagna 62, Milano, 20133", + "coords": { + "latitude": 45.47678010082273, + "longitude": 9.224452739758812 + } + } + ] + } + ] + }, + { + "acronym": "MIC", + "campus": [ + { + "name": ["Off", "Campus"], + "buildings": [ + { + "name": "NOLO", + "address": "Viale Monza 54, Milano, 20128", + "coords": { + "latitude": 45.49326461001558, + "longitude": 9.21927698715338 + } + }, + { + "name": "San Siro", + "address": "Viale Gigante, Milano, 20148", + "coords": { + "latitude": 45.471604941331265, + "longitude": 9.138379698192313 + } + } + ] + }, + { + "name": ["Residenza", "Einstein"], + "buildings": [ + { + "name": "Einstein", + "address": "Via Einstein 6, Milano, 20137", + "coords": { + "latitude": 45.45033404949838, + "longitude": 9.216969652664448 + } + } + ] + }, + { + "name": ["Residenza", "Newton"], + "buildings": [ + { + "name": "Newton", + "address": "Via Mario Borsa 25, Milano, 20151", + "coords": { + "latitude": 45.50201311059991, + "longitude": 9.100693099933826 + } + } + ] + }, + { + "name": ["Residenza", "Pareto"], + "buildings": [ + { + "name": "Pareto", + "address": "Via Maggianico 6, Milano, 20156", + "coords": { + "latitude": 45.50331624885291, + "longitude": 9.145443677552473 + } + } + ] + } + ] + }, + { + "acronym": "MID", + "campus": [ + { + "name": ["Calabria"], + "buildings": [ + { + "name": "Edificio 1", + "address": "Via Calabria 4, Milano, 20098", + "coords": { + "latitude": 45.39350641657452, + "longitude": 9.257432613353823 + } + } + ] + } + ] + }, + { + "acronym": "PCL", + "campus": [ + { + "name": ["Scalabrini"], + "buildings": [ + { + "name": "Padiglione 11", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.04671253164241, + "longitude": 9.703538101434237 + } + }, + { + "name": "Padiglione 12", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.04746700688415, + "longitude": 9.703336354556205 + } + }, + { + "name": "Padiglione 13", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.04736378936126, + "longitude": 9.703562450197184 + } + }, + { + "name": "Padiglione 14", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.04705133747066, + "longitude": 9.702656152043621 + } + }, + { + "name": "Padiglione 15", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.04645536532436, + "longitude": 9.703416037344196 + } + }, + { + "name": "Padiglione 2", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.04688035667178, + "longitude": 9.703307226084346 + } + }, + { + "name": "Padiglione 3", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.04654833371806, + "longitude": 9.70320343683441 + } + }, + { + "name": "Padiglione 4", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.04667055098037, + "longitude": 9.702900718188767 + } + }, + { + "name": "Padiglione 5", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.046444448839786, + "longitude": 9.702814227147154 + } + }, + { + "name": "Padiglione 6", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.04676832460221, + "longitude": 9.702612414716725 + } + }, + { + "name": "Padiglione 8", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.0465911097912, + "longitude": 9.702324111225455 + } + }, + { + "name": "Padiglione 9", + "address": "Via Scalabrini 113, Piacenza, 29100", + "coords": { + "latitude": 45.046383339999025, + "longitude": 9.702499976344225 + } + }, + { + "name": "Edificio 1", + "address": "Via Scalabrini 76, Piacenza, 29100", + "coords": { + "latitude": 45.04830239234551, + "longitude": 9.701883338558217 + } + } + ] + } + ] + } +] diff --git a/src/components/Groups/AnimatedLine.tsx b/src/components/Groups/AnimatedLine.tsx index 98045b11..8dc31d5c 100644 --- a/src/components/Groups/AnimatedLine.tsx +++ b/src/components/Groups/AnimatedLine.tsx @@ -3,52 +3,52 @@ import { Animated, Easing, ViewStyle } from "react-native" import { usePalette } from "utils/colors" export interface AnimatedLineProps { - /** - * animate when this value changes - */ - mounted: boolean - color?: string - height?: number - width?: number - style?: ViewStyle + /** + * animate when this value changes + */ + mounted: boolean + color?: string + height?: number + width?: number + style?: ViewStyle } export const AnimatedLine: FC = props => { - const { isLight } = usePalette() + const { isLight } = usePalette() - const { current: widthAnim } = React.useRef( - new Animated.Value(1) - ) + const { current: widthAnim } = React.useRef( + new Animated.Value(1) + ) - React.useEffect(() => { - if (props.mounted) { - Animated.timing(widthAnim, { - toValue: 250, - duration: 300, - easing: Easing.ease, - useNativeDriver: false, - }).start() - } else { - Animated.timing(widthAnim, { - toValue: 0, - duration: 300, - easing: Easing.ease, - useNativeDriver: false, - }).start() - } - }, [props.mounted]) + React.useEffect(() => { + if (props.mounted) { + Animated.timing(widthAnim, { + toValue: 250, + duration: 300, + easing: Easing.ease, + useNativeDriver: false, + }).start() + } else { + Animated.timing(widthAnim, { + toValue: 0, + duration: 300, + easing: Easing.ease, + useNativeDriver: false, + }).start() + } + }, [props.mounted]) - return ( - - ) + return ( + + ) } diff --git a/src/components/Groups/AnimatedPoliSearchBar.tsx b/src/components/Groups/AnimatedPoliSearchBar.tsx index a990a25e..ddac60d6 100644 --- a/src/components/Groups/AnimatedPoliSearchBar.tsx +++ b/src/components/Groups/AnimatedPoliSearchBar.tsx @@ -4,26 +4,26 @@ import { View, ViewStyle } from "react-native" import { AnimatedLine } from "./AnimatedLine" export interface AnimatedPoliSearchBarProps { - onSearch: (val: string) => void - style?: ViewStyle + onSearch: (val: string) => void + style?: ViewStyle } export const AnimatedPoliSearchBar: FC = props => { - const [isSearching, setIsSearching] = useState(false) - return ( - - { - props.onSearch(val) - if (val !== "") { - setIsSearching(true) - } else if (isSearching === true) { - setIsSearching(false) - } - }} - style={{ marginTop: 0, marginBottom: 0 }} - /> - - - ) + const [isSearching, setIsSearching] = useState(false) + return ( + + { + props.onSearch(val) + if (val !== "") { + setIsSearching(true) + } else if (isSearching === true) { + setIsSearching(false) + } + }} + style={{ marginTop: 0, marginBottom: 0 }} + /> + + + ) } diff --git a/src/components/Groups/Filters.tsx b/src/components/Groups/Filters.tsx index a2fec793..4ff5deb0 100644 --- a/src/components/Groups/Filters.tsx +++ b/src/components/Groups/Filters.tsx @@ -2,195 +2,193 @@ import React, { FC, useState } from "react" import { View } from "react-native" import { OutlinedButton } from "./OutlinedButton" import { StyleSheet } from "react-native" -import { ModalSelection, SelectTile } from "components/Settings" +import { ModalWithButtons } from "components/ModalWithButtons" +import { SelectTile } from "components/Settings" import { getNameFromMode, ValidModalType } from "utils/groups" export interface FiltersProps { - filters: Filters - onFilterChange: (filters: Filters) => void + filters: Filters + onFilterChange: (filters: Filters) => void } export interface Filters { - year?: string - course?: string - platform?: string - type?: string + year?: string + course?: string + platform?: string + type?: string } interface ModalItemList { - itemsToShow: string[] - itemsToSave: string[] + itemsToShow: string[] + itemsToSave: string[] } const yearsList: ModalItemList = { - itemsToShow: [ - "2022/2023", - "2021/2022", - "2020/2021", - "2019/2020", - "2018/2019", - ], - itemsToSave: [ - "2022/2023", - "2021/2022", - "2020/2021", - "2019/2020", - "2018/2019", - ], + itemsToShow: [ + "2022/2023", + "2021/2022", + "2020/2021", + "2019/2020", + "2018/2019", + ], + itemsToSave: [ + "2022/2023", + "2021/2022", + "2020/2021", + "2019/2020", + "2018/2019", + ], } const coursesList: ModalItemList = { - itemsToShow: ["Triennale", "Magistrale", "Ciclo unico"], - itemsToSave: ["LT", "LM", "LU"], + itemsToShow: ["Triennale", "Magistrale", "Ciclo unico"], + itemsToSave: ["LT", "LM", "LU"], } const typesList: ModalItemList = { - itemsToShow: ["Scuola", "Corso", "Extra"], - itemsToSave: ["S", "C", "E"], + itemsToShow: ["Scuola", "Corso", "Extra"], + itemsToSave: ["S", "C", "E"], } const platformsList: ModalItemList = { - itemsToShow: ["Whatsapp", "Facebook", "Telegram"], - itemsToSave: ["WA", "FB", "TG"], + itemsToShow: ["Whatsapp", "Facebook", "Telegram"], + itemsToSave: ["WA", "FB", "TG"], } - export const Filters: FC = props => { - //show or hide modal - const [isModalShowing, setIsModalShowing] = useState(false) - //type of modal: year - type - course - platform - const [modalMode, setModalMode] = useState("year") - //items to show inside modal - const [modalItems, setModalItems] = useState(yearsList) - //currently selected item inside modal - const [selectedItem, setSelectedItem] = useState( - undefined - ) + //show or hide modal + const [isModalShowing, setIsModalShowing] = useState(false) + //type of modal: year - type - course - platform + const [modalMode, setModalMode] = useState("year") + //items to show inside modal + const [modalItems, setModalItems] = useState(yearsList) + //currently selected item inside modal + const [selectedItem, setSelectedItem] = useState( + undefined + ) - //reset state on "reset" - const reset = () => { - props.onFilterChange({}) - } + //reset state on "reset" + const reset = () => { + props.onFilterChange({}) + } - return ( - - - { - setModalMode("year") - setModalItems(yearsList) - setSelectedItem(props.filters.year) - setIsModalShowing(true) - }} - /> - { - setModalMode("course") - setModalItems(coursesList) - setSelectedItem(props.filters.course) - setIsModalShowing(true) - }} - /> - { - setModalMode("type") - setModalItems(typesList) - setSelectedItem(props.filters.type) - setIsModalShowing(true) - }} - /> - { - setModalMode("platform") - setModalItems(platformsList) - setSelectedItem(props.filters.platform) - setIsModalShowing(true) - }} - /> - - - { - setIsModalShowing(false) - }} - onOK={() => { - if (modalMode === "course") { - props.onFilterChange({ - ...props.filters, - course: selectedItem, - }) - } else if (modalMode === "platform") { - props.onFilterChange({ - ...props.filters, - platform: selectedItem, - }) - } else if (modalMode === "year") { - props.onFilterChange({ - ...props.filters, - year: selectedItem, - }) - } else if (modalMode === "type") { - props.onFilterChange({ - ...props.filters, - type: selectedItem, - }) - } - setIsModalShowing(false) - }} - > - { - setSelectedItem(undefined) - }} - /> - {modalItems?.itemsToShow.map((itemName, index) => { - return ( - { - setSelectedItem(modalItems.itemsToSave[index]) - }} - /> - ) - })} - - - ) + return ( + + + { + setModalMode("year") + setModalItems(yearsList) + setSelectedItem(props.filters.year) + setIsModalShowing(true) + }} + /> + { + setModalMode("course") + setModalItems(coursesList) + setSelectedItem(props.filters.course) + setIsModalShowing(true) + }} + /> + { + setModalMode("type") + setModalItems(typesList) + setSelectedItem(props.filters.type) + setIsModalShowing(true) + }} + /> + { + setModalMode("platform") + setModalItems(platformsList) + setSelectedItem(props.filters.platform) + setIsModalShowing(true) + }} + /> + + + { + setIsModalShowing(false) + }} + onOK={() => { + if (modalMode === "course") { + props.onFilterChange({ + ...props.filters, + course: selectedItem, + }) + } else if (modalMode === "platform") { + props.onFilterChange({ + ...props.filters, + platform: selectedItem, + }) + } else if (modalMode === "year") { + props.onFilterChange({ + ...props.filters, + year: selectedItem, + }) + } else if (modalMode === "type") { + props.onFilterChange({ + ...props.filters, + type: selectedItem, + }) + } + setIsModalShowing(false) + }} + > + { + setSelectedItem(undefined) + }} + /> + {modalItems?.itemsToShow.map((itemName, index) => { + return ( + { + setSelectedItem(modalItems.itemsToSave[index]) + }} + /> + ) + })} + + + ) } const styles = StyleSheet.create({ - buttonCustomMargin: { - marginRight: 4, - marginLeft: 4, - marginBottom: 8, - }, + buttonCustomMargin: { + marginRight: 4, + marginLeft: 4, + marginBottom: 8, + }, }) diff --git a/src/components/Groups/GroupTile.tsx b/src/components/Groups/GroupTile.tsx index bfd5aac3..f2477822 100644 --- a/src/components/Groups/GroupTile.tsx +++ b/src/components/Groups/GroupTile.tsx @@ -1,8 +1,8 @@ import { - Canvas, - DataSourceParam, - ImageSVG, - useSVG, + Canvas, + DataSourceParam, + ImageSVG, + useSVG, } from "@shopify/react-native-skia" import { Divider } from "components/Divider" import { BodyText } from "components/Text" @@ -11,95 +11,95 @@ import { Pressable, View } from "react-native" import { usePalette } from "utils/colors" export interface GroupTileProps { - text?: string - icon?: { svg: DataSourceParam; width: number; heigth: number } - members?: string - onClick?: () => void + text?: string + icon?: { svg: DataSourceParam; width: number; heigth: number } + members?: string + onClick?: () => void } export const GroupTile: FC = props => { - const { isLight } = usePalette() + const { isLight } = usePalette() - const iconSvg = useSVG(props.icon?.svg) + const iconSvg = useSVG(props.icon?.svg) - return ( - - + + + + {props.icon && iconSvg && ( + + + {iconSvg && ( + + )} + + + )} + + + - - - {props.icon && iconSvg && ( - - - {iconSvg && ( - - )} - - - )} - - - - {props.text} - + {props.text} + - {props.members && ( - - {props.members} members - - )} - - - - + {props.members && ( + + {props.members} members + + )} + - ) + + + + ) } diff --git a/src/components/Groups/ModalGroup.tsx b/src/components/Groups/ModalGroup.tsx index d52e2680..55b44197 100644 --- a/src/components/Groups/ModalGroup.tsx +++ b/src/components/Groups/ModalGroup.tsx @@ -1,36 +1,36 @@ import React, { FC } from "react" import { View, Modal, StyleSheet, Pressable } from "react-native" import { usePalette } from "utils/colors" -import { ButtonCustom } from "components/Settings" +import { ButtonCustom } from "components/Button" import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" import { deleteSvg as icon } from "assets/modal" import { Group } from "api/groups" export interface ModalGroupProps { - /** - * content of the modal - */ - children: React.ReactNode + /** + * content of the modal + */ + children: React.ReactNode - /** - * whether ot not to show the modal - */ - isShowing: boolean - /** - * this function hides the modal by changing the state in the parent component - */ - onClose: () => void + /** + * whether ot not to show the modal + */ + isShowing: boolean + /** + * this function hides the modal by changing the state in the parent component + */ + onClose: () => void - /** - * function called when button "JOIN GROUP" is pressed - */ - onJoin: (group?: Group) => void + /** + * function called when button "JOIN GROUP" is pressed + */ + onJoin: (group?: Group) => void - group?: Group - /** - * modal wrapper height, specify if height is fixed - */ - height?: number + group?: Group + /** + * modal wrapper height, specify if height is fixed + */ + height?: number } /** @@ -38,115 +38,113 @@ export interface ModalGroupProps { * */ export const ModalGroup: FC = props => { - const { backgroundSecondary, modalBarrier, isLight } = usePalette() + const { backgroundSecondary, modalBarrier, isLight } = usePalette() - const deleteSvg = useSVG(icon.svg) - return ( - //TODO: animationType fade or slide? - - + + + props.onClose()} + > + - - props.onClose()} - > - - - {deleteSvg && ( - - )} - - - - - {props.children} - - props.onJoin(props.group)} - /> - - - - - - ) + + {deleteSvg && ( + + )} + + + + + {props.children} + + props.onJoin(props.group)} + /> + + + + + + ) } const styles = StyleSheet.create({ - pageWrapper: { - flex: 1, - justifyContent: "center", - alignItems: "center", - }, - contentWrapper: { - flexDirection: "column", - justifyContent: "space-between", - width: 320, - borderRadius: 12, - marginHorizontal: 15, + pageWrapper: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + contentWrapper: { + flexDirection: "column", + justifyContent: "space-between", + width: 320, + borderRadius: 12, + marginHorizontal: 15, - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 3, - }, - shadowOpacity: 0.27, - shadowRadius: 4.65, - elevation: 6, - }, - title: { - fontSize: 32, - fontWeight: "900", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 3, }, + shadowOpacity: 0.27, + shadowRadius: 4.65, + elevation: 6, + }, + title: { + fontSize: 32, + fontWeight: "900", + }, }) diff --git a/src/components/Groups/ModalGroupItem.tsx b/src/components/Groups/ModalGroupItem.tsx index c6eac75b..58968f5c 100644 --- a/src/components/Groups/ModalGroupItem.tsx +++ b/src/components/Groups/ModalGroupItem.tsx @@ -7,92 +7,92 @@ import { choosePlatformIcon } from "utils/groups" import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" export interface ModalGroupItemProps { - /** - * ResetButton - */ - group?: Group + /** + * ResetButton + */ + group?: Group } export const ModalGroupItem: FC = props => { - const { isLight } = usePalette() - const icon = choosePlatformIcon(props.group?.platform) - const iconSvg = useSVG(icon?.svg) + const { isLight } = usePalette() + const icon = choosePlatformIcon(props.group?.platform) + const iconSvg = useSVG(icon?.svg) - const scaleFactor = 2.5 + const scaleFactor = 2.5 - return ( - + + {icon && iconSvg && ( + + {iconSvg && ( + + )} + + )} + + + {props.group?.class} + + + {props.group?.members && ( + + {props.group.members} members + + )} + - - {icon && iconSvg && ( - - {iconSvg && ( - - )} - - )} - - - {props.group?.class} - - - {props.group?.members && ( - - {props.group.members} members - - )} - - {props.group?.year} - - - - ) + {props.group?.year} + + + + ) } diff --git a/src/components/Groups/OutlinedButton.tsx b/src/components/Groups/OutlinedButton.tsx index df89c456..0774e752 100644 --- a/src/components/Groups/OutlinedButton.tsx +++ b/src/components/Groups/OutlinedButton.tsx @@ -4,66 +4,62 @@ import { Pressable, StyleProp, ViewStyle } from "react-native" import { usePalette } from "utils/colors" import { StyleSheet } from "react-native" export interface OutlinedButtonProps { - text?: string - /** - * ResetButton - */ - isSpecial?: boolean - isSelected?: boolean - onPress?: () => void - buttonStyle?: StyleProp + text?: string + /** + * ResetButton + */ + isSpecial?: boolean + isSelected?: boolean + onPress?: () => void + buttonStyle?: StyleProp } export const OutlinedButton: FC = props => { - const { isLight } = usePalette() - return ( - - - {props.text} - - - ) + const { isLight } = usePalette() + return ( + + + {props.text} + + + ) } const styles = StyleSheet.create({ - button: { - borderWidth: 2, - height: 32, - minWidth: 100, - borderRadius: 60, - flexDirection: "row", - justifyContent: "center", - alignItems: "center", - }, + button: { + borderWidth: 2, + height: 32, + minWidth: 100, + borderRadius: 60, + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + }, }) diff --git a/src/components/Groups/PageWrapper.tsx b/src/components/Groups/PageWrapper.tsx index 8c46e51e..e5480fda 100644 --- a/src/components/Groups/PageWrapper.tsx +++ b/src/components/Groups/PageWrapper.tsx @@ -7,50 +7,50 @@ import { usePalette } from "utils/colors" * Groups page Wrapper */ export const PageWrapper: FC<{ - children: React.ReactNode + children: React.ReactNode - title?: string - /** - * Remove the navbar from the bottom of the page. - */ - hideNavbar?: boolean - /** - * Props for the navbar, see {@link NavBar} - */ - navbarOptions?: NavbarProps - marginTop?: number - style?: ViewStyle + title?: string + /** + * Remove the navbar from the bottom of the page. + */ + hideNavbar?: boolean + /** + * Props for the navbar, see {@link NavBar} + */ + navbarOptions?: NavbarProps + marginTop?: number + style?: ViewStyle }> = props => { - const { background, isLight, primary } = usePalette() + const { background, isLight, primary } = usePalette() - const navbar = !props.hideNavbar + const navbar = !props.hideNavbar - return ( - - - {props.children} - - {navbar ? : null} - - ) + return ( + + + {props.children} + + {navbar ? : null} + + ) } diff --git a/src/components/Home/Highlights/CustomFlatlist.tsx b/src/components/Home/Highlights/CustomFlatlist.tsx new file mode 100644 index 00000000..2c54371f --- /dev/null +++ b/src/components/Home/Highlights/CustomFlatlist.tsx @@ -0,0 +1,158 @@ +import { BodyText } from "components/Text" +import React, { FC, useState, useRef } from "react" +import { View, Dimensions, Pressable, ImageBackground } from "react-native" +import Animated from "react-native-reanimated" +import { PaginationCarousel } from "./PaginationCarousel" +import lecturesImage from "assets/carousel-lectures.png" +import iseeImage from "assets/carousel-isee.png" +import { useNavigation } from "navigation/NavigationTypes" +import { WidgetType, CarouselItem, formatTitle } from "utils/carousel" +import { Divider } from "components/Divider" + +const { width } = Dimensions.get("window") + +export const CustomFlatlist: FC<{ dataToShow: CarouselItem[] }> = ({ + dataToShow, +}) => { + const [currentIndex, setCurrentIndex] = useState(0) + const scrollX = useRef(new Animated.Value(0)).current + + //questa parte funziona ma non va bene scritta cosi + const handleViewableItemsChanged = useRef( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ({ changed }: { changed: any }) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (changed[0].isViewable) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + setCurrentIndex(changed[0].index) + } + } + ).current + + const viewAbilityConfig = { viewAreaCoveragePercentThreshold: 50 } + + const { navigate } = useNavigation() + + return ( + <> + index.toString()} + bounces={true} + pagingEnabled={true} + horizontal + showsHorizontalScrollIndicator={false} + onScroll={Animated.event( + [ + { + nativeEvent: { + contentOffset: { x: scrollX }, + }, + }, + ], + { useNativeDriver: false } + )} + renderItem={({ item }) => { + return ( + navigate("Error404")} + > + + + + + {formatTitle(item.title)} + + + + + + + + {item.date} + {" "} + {item.time} + + + + + {item.room} + + + + + + ) + }} + viewabilityConfig={viewAbilityConfig} + onViewableItemsChanged={handleViewableItemsChanged} + /> + + + ) +} diff --git a/src/components/Home/Highlights/HighlightsManager.tsx b/src/components/Home/Highlights/HighlightsManager.tsx index a10f0472..a88249ad 100644 --- a/src/components/Home/Highlights/HighlightsManager.tsx +++ b/src/components/Home/Highlights/HighlightsManager.tsx @@ -1,37 +1,15 @@ -import React, { FC, useEffect, useState } from "react" +import React, { FC, useContext, useEffect, useState } from "react" import { View } from "react-native" -import { PoliCarousel, WidgetType } from "./PoliCarousel" -import lectures from "./lectures.json" -import { Lecture } from "api/timetable" - -//ideally this is the object we want to pass to the carousel -//this might be expansed in the future -export interface CarouselItem { - /** - * number to identify the object and the relative widget - */ - id: number - /** - * enum field to identify the type of the widget - */ - type: WidgetType - /** - * string to identify the date of the event(or deadline) contained in the widget - */ - date: string - /** - * string to identify the time of the event(or deadline) contained in the widget - */ - time: string - /** - * string to identify the title of the event contained in the widget - */ - title: string - /** - * string to identify eventually the room when the event takes place - */ - room?: string -} +import { PoliCarousel } from "./PoliCarousel" +import { LoginContext } from "contexts/login" +import { Event } from "api/event" +import { + CarouselItem, + checkEventType, + createWidget, + WidgetType, +} from "utils/carousel" +import { api } from "api" /** * Component that decides the content of the carousel. @@ -39,104 +17,62 @@ export interface CarouselItem { * the correct data to the PolimiCarousel */ export const HighlightsManager: FC = () => { - const [isLessonFound, setIsLessonFound] = useState(false) - const [data, setData] = useState([ - { - id: 0, - type: WidgetType.ISEE, - date: "Mer 22/10/2022", - time: "8:00", - title: "Scadenza ISEE 2023", - }, - { - id: 1, - type: WidgetType.ISEE, - date: "Mer 22/10/2022", - time: "8:00", - title: "Scadenza ISEE 2023", - }, - { - id: 2, - type: WidgetType.ISEE, - date: "Mer 22/10/2022", - time: "8:00", - title: "Scadenza ISEE 2023", - }, - ]) - - //function used to add a "0" to the day is this one is lower than 10 (ex. n=8 -> res=08) - const pad = (n: number) => { - return n < 10 ? "0" + n : n - } - - const days = ["Lun", "Mar", "Mer", "Gio", "Ven", "Sab", "Dom"] + const { loggedIn, userInfo } = useContext(LoginContext) - /** - * function to determine which is the next lesson according to the current date and time - * @param timetable is the API response - */ - const findNextLecture = (timetable: Lecture[]) => { - /** - * sorting dates in ascending order, priority is the 'date_start' field.If there are two lecture with the same - * 'date_start' we check the 'date_end' field! - * @example - * lecture 1{date_start: "2022-12-04T08:15:00",date_end: "2022-12-04T13:15:00"} - * lecture 2{date_start: "2022-12-04T08:15:00",date_end: "2022-12-04T14:00:00"} - * So in this case lecture 1 has more priority than lecture 2 - */ - const sorted = timetable.sort((a, b) => { - let dateA = new Date(a.date_start).getTime() - let dateB = new Date(b.date_start).getTime() - if (dateA !== dateB) { - return dateA - dateB - } else { - dateA = new Date(a.date_end).getTime() - dateB = new Date(b.date_end).getTime() - return dateA - dateB - } - }) + const [widgets, setWidgets] = useState([]) - const now = new Date() + /** + * This function gets as parameters the list of events extracted and then it filters it. + * @param events + */ + const extractNextEvents = (events: Event[]) => { + let firstLecture = true + return events + .filter(x => checkEventType(x.event_type.typeId)) + .filter(x => { + if (x.event_type.typeId === WidgetType.LECTURES) { + if (firstLecture) { + firstLecture = false + return true + } + return false + } else return true + }) + .map(e => createWidget(e)) + } - for (let i = 0; i < sorted.length; i++) { - if ( - new Date(sorted[i].date_start) > new Date(now) || - (new Date(now) > new Date(sorted[i].date_start) && - new Date(now) < new Date(sorted[i].date_end)) - ) { - const currentObject = sorted[i] - const dateObj = new Date(currentObject.date_start) - const resultDate = - days[(dateObj.getDay() - 1 + 7) % 7] + - " " + - pad(dateObj.getDate()) + - "/" + - (dateObj.getMonth() + 1) + - "/" + - dateObj.getFullYear() - const nextLecture: CarouselItem = { - id: currentObject.event_id, - type: WidgetType.LECTURES, - date: resultDate, - time: currentObject.date_start.toString().slice(11, 16), - title: currentObject.title.it, - room: currentObject.room.acronym_dn, - } - setData([nextLecture, ...data]) - setIsLessonFound(true) - break - } - } + /** + * This function calls the events API. + */ + const setNextEvents = async () => { + if (!loggedIn) return + try { + const { matricola } = userInfo.careers[0] + const startDate = new Date().toISOString().substring(0, 10) + const response = await api.events.getEvents( + matricola, + startDate, + 10 //nEvents 10 + ) + if (response.length !== 0) { + //we extract the events if there is something + const newWidgets = extractNextEvents(response) + setWidgets(newWidgets) + } + } catch (error) { + console.log(error) } + } - useEffect( - () => (!isLessonFound ? findNextLecture(lectures) : undefined), - [isLessonFound] - ) + useEffect(() => { + if (loggedIn) { + void setNextEvents() + } + }, [loggedIn]) - return ( - - - - ) + return ( + + + + ) } diff --git a/src/components/Home/Highlights/PaginationCarousel.tsx b/src/components/Home/Highlights/PaginationCarousel.tsx index 1cf1f861..fd106220 100644 --- a/src/components/Home/Highlights/PaginationCarousel.tsx +++ b/src/components/Home/Highlights/PaginationCarousel.tsx @@ -1,10 +1,10 @@ import React, { FC } from "react" import Animated, { - Extrapolate, - interpolateColors, + Extrapolate, + interpolateColors, } from "react-native-reanimated" import { View, Dimensions } from "react-native" -import { CarouselItem } from "./HighlightsManager" +import { CarouselItem } from "utils/carousel" import { usePalette } from "utils/colors" const { width } = Dimensions.get("window") @@ -13,76 +13,75 @@ const { width } = Dimensions.get("window") * Custom Pagination component to handle the animation of the dots */ export const PaginationCarousel: FC<{ - dataToShow: CarouselItem[] - scrollX: Animated.Value - currentIndex: number + dataToShow: CarouselItem[] + scrollX: Animated.Value + currentIndex: number }> = ({ dataToShow, scrollX, currentIndex }) => { - const { palette } = usePalette() - return ( - - idx.toString()} - horizontal - bounces={false} - style={{ - flexDirection: "row", - alignSelf: "center", - }} - renderItem={({ index }) => { - const inputRange = [ - (index - 1) * width, - index * width, - (index + 1) * width, - ] + const { palette } = usePalette() + return ( + + idx.toString()} + horizontal + bounces={false} + style={{ + flexDirection: "row", + alignSelf: "center", + }} + renderItem={({ index }) => { + const inputRange = [ + (index - 1) * width, + index * width, + (index + 1) * width, + ] - const scaleOutputRange = [0.5, 1, 0.5] - const colorOutputRange = [ - palette.lighter, - palette.accent, - palette.lighter, - ] + const scaleOutputRange = [0.5, 1, 0.5] + const colorOutputRange = [ + palette.lighter, + palette.accent, + palette.lighter, + ] - const dotScale = scrollX.interpolate({ - inputRange, - outputRange: scaleOutputRange, - extrapolate: Extrapolate.CLAMP, - }) + const dotScale = scrollX.interpolate({ + inputRange, + outputRange: scaleOutputRange, + extrapolate: Extrapolate.CLAMP, + }) - return ( - - - - ) + return ( + + - - ) + /> + + ) + }} + /> + + ) } diff --git a/src/components/Home/Highlights/PoliCarousel.tsx b/src/components/Home/Highlights/PoliCarousel.tsx index 27dbbefb..e2102bed 100644 --- a/src/components/Home/Highlights/PoliCarousel.tsx +++ b/src/components/Home/Highlights/PoliCarousel.tsx @@ -1,171 +1,66 @@ import { BodyText } from "components/Text" -import React, { FC, useState, useRef } from "react" -import { View, Dimensions, Pressable, ImageBackground } from "react-native" -import Animated from "react-native-reanimated" -import { PaginationCarousel } from "./PaginationCarousel" +import React, { FC } from "react" +import { View, Dimensions, ImageBackground } from "react-native" import lecturesImage from "assets/carousel-lectures.png" -import iseeImage from "assets/carousel-isee.png" -import { CarouselItem } from "./HighlightsManager" +import { CarouselItem } from "utils/carousel" +import { CustomFlatlist } from "./CustomFlatlist" const { width } = Dimensions.get("window") -/** - * enum to differentiate the different types of widget we could have - * different widget types have different background images - */ -export enum WidgetType { - LECTURES, - ISEE, -} - /** * custom Carousel component, it receives the data to show from the HighlightsManager */ export const PoliCarousel: FC<{ dataToShow: CarouselItem[] }> = ({ - dataToShow, + dataToShow, }) => { - const [currentIndex, setCurrentIndex] = useState(0) - const scrollX = useRef(new Animated.Value(0)).current - - //questa parte funziona ma non va bene scritta cosi - const handleViewableItemsChanged = useRef( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ({ changed }: { changed: any }) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (changed[0].isViewable) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access - setCurrentIndex(changed[0].index) - } - } - ).current - - const viewAbilityConfig = { viewAreaCoveragePercentThreshold: 50 } - - return ( - - index.toString()} - bounces={true} - pagingEnabled={true} - horizontal - showsHorizontalScrollIndicator={false} - onScroll={Animated.event( - [ - { - nativeEvent: { - contentOffset: { x: scrollX }, - }, - }, - ], - { useNativeDriver: false } - )} - renderItem={({ item }) => { - return ( - - - console.log("Highlights premuto!") - } - > - - - - {item.title} - - - - - - - - {item.date} - {" "} - {item.time} - - - - - {item.room} - - - - - - ) + return ( + + {dataToShow === undefined || dataToShow.length === 0 ? ( + //if the widget's list is empty, I display a single default widget! + + + + - + > + Non ci sono novità + + + - ) + ) : ( + + )} + + ) } diff --git a/src/components/Home/Highlights/lectures.json b/src/components/Home/Highlights/lectures.json deleted file mode 100644 index 233768e7..00000000 --- a/src/components/Home/Highlights/lectures.json +++ /dev/null @@ -1,674 +0,0 @@ -[ - { - "event_id":52647, - "date_start":"2022-10-20T08:15:00", - "date_end":"2022-10-20T10:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1 Squadra 1", - "en":"MATHEMATICAL ANALYSIS 1 Team 1" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":2074, - "acronym_dn":"T.2.1", - "classroom_id":-2147483648, - "room_dn":"006" - } - }, - { - "event_id":138464, - "date_start":"2022-10-20T08:15:00", - "date_end":"2022-10-20T10:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1 Squadra 2", - "en":"MATHEMATICAL ANALYSIS 1 Team 2" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":9020, - "acronym_dn":"IV", - "classroom_id":-2147483648, - "room_dn":"005" - } - }, - { - "event_id":52648, - "date_start":"2022-10-20T10:15:00", - "date_end":"2022-10-20T13:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"FONDAMENTI DI INFORMATICA", - "en":"FUNDAMENTALS OF COMPUTER SCIENCE" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":2074, - "acronym_dn":"T.2.1", - "classroom_id":-2147483648, - "room_dn":"006" - } - }, - { - "event_id":141561, - "date_start":"2022-10-21T14:15:00", - "date_end":"2022-10-21T18:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"FONDAMENTI DI INFORMATICA", - "en":"FUNDAMENTALS OF COMPUTER SCIENCE" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":14820, - "acronym_dn":"T.1.1", - "classroom_id":-2147483648, - "room_dn":"006" - } - }, - { - "event_id":54051, - "date_start":"2022-10-24T10:15:00", - "date_end":"2022-10-24T12:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"FONDAMENTI DI INFORMATICA", - "en":"FUNDAMENTALS OF COMPUTER SCIENCE" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":11207, - "acronym_dn":"3.0.3", - "classroom_id":-2147483648, - "room_dn":"061a" - } - }, - { - "event_id":54053, - "date_start":"2022-10-24T12:15:00", - "date_end":"2022-10-24T14:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"GEOMETRIA E ALGEBRA LINEARE", - "en":"GEOMETRY AND LINEAR ALGEBRA" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":11207, - "acronym_dn":"3.0.3", - "classroom_id":-2147483648, - "room_dn":"061a" - } - }, - { - "event_id":39848, - "date_start":"2022-10-24T15:15:00", - "date_end":"2022-10-24T18:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"GEOMETRIA E ALGEBRA LINEARE Squadra 2", - "en":"GEOMETRY AND LINEAR ALGEBRA Team 2" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":13975, - "acronym_dn":"6.0.1 ", - "classroom_id":-2147483648, - "room_dn":"057" - } - }, - { - "event_id":44934, - "date_start":"2022-10-24T15:15:00", - "date_end":"2022-10-24T18:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"GEOMETRIA E ALGEBRA LINEARE Squadra 1", - "en":"GEOMETRY AND LINEAR ALGEBRA Team 1" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":7289, - "acronym_dn":"2.0.2", - "classroom_id":-2147483648, - "room_dn":"030" - } - }, - { - "event_id":37937, - "date_start":"2022-10-25T10:15:00", - "date_end":"2022-10-25T13:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"FONDAMENTI DI INFORMATICA", - "en":"FUNDAMENTALS OF COMPUTER SCIENCE" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":9955, - "acronym_dn":"9.0.1", - "classroom_id":-2147483648, - "room_dn":"040" - } - }, - { - "event_id":76675, - "date_start":"2022-10-25T14:15:00", - "date_end":"2022-10-25T15:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1 Squadra 1", - "en":"MATHEMATICAL ANALYSIS 1 Team 1" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":12924, - "acronym_dn":"B.4.2", - "classroom_id":-2147483648, - "room_dn":"004" - } - }, - { - "event_id":125427, - "date_start":"2022-10-25T14:15:00", - "date_end":"2022-10-25T15:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1 Squadra 2", - "en":"MATHEMATICAL ANALYSIS 1 Team 2" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":7377, - "acronym_dn":"3.1.4", - "classroom_id":-2147483648, - "room_dn":"023" - } - }, - { - "event_id":134108, - "date_start":"2022-12-25T15:15:00", - "date_end":"2022-12-25T17:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1", - "en":"MATHEMATICAL ANALYSIS 1" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":12924, - "acronym_dn":"B.4.2", - "classroom_id":-2147483648, - "room_dn":"004" - } - }, - { - "event_id":43473, - "date_start":"2022-10-26T08:15:00", - "date_end":"2022-10-26T11:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1", - "en":"MATHEMATICAL ANALYSIS 1" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":3524, - "acronym_dn":"8.0.1", - "classroom_id":-2147483648, - "room_dn":"011" - } - }, - { - "event_id":43475, - "date_start":"2022-10-26T11:15:00", - "date_end":"2022-10-26T13:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"GEOMETRIA E ALGEBRA LINEARE", - "en":"GEOMETRY AND LINEAR ALGEBRA" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":3524, - "acronym_dn":"8.0.1", - "classroom_id":-2147483648, - "room_dn":"011" - } - }, - { - "event_id":52679, - "date_start":"2022-10-27T08:15:00", - "date_end":"2022-10-27T10:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1 Squadra 1", - "en":"MATHEMATICAL ANALYSIS 1 Team 1" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":2074, - "acronym_dn":"T.2.1", - "classroom_id":-2147483648, - "room_dn":"006" - } - }, - { - "event_id":138465, - "date_start":"2022-10-27T08:15:00", - "date_end":"2022-10-27T10:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1 Squadra 2", - "en":"MATHEMATICAL ANALYSIS 1 Team 2" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":9020, - "acronym_dn":"IV", - "classroom_id":-2147483648, - "room_dn":"005" - } - }, - { - "event_id":52680, - "date_start":"2022-10-27T10:15:00", - "date_end":"2022-10-27T13:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"FONDAMENTI DI INFORMATICA", - "en":"FUNDAMENTALS OF COMPUTER SCIENCE" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":2074, - "acronym_dn":"T.2.1", - "classroom_id":-2147483648, - "room_dn":"006" - } - }, - { - "event_id":141563, - "date_start":"2022-10-28T14:15:00", - "date_end":"2022-10-28T18:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"FONDAMENTI DI INFORMATICA", - "en":"FUNDAMENTALS OF COMPUTER SCIENCE" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":14820, - "acronym_dn":"T.1.1", - "classroom_id":-2147483648, - "room_dn":"006" - } - }, - { - "event_id":43497, - "date_start":"2022-11-02T08:15:00", - "date_end":"2022-11-02T11:15:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1", - "en":"MATHEMATICAL ANALYSIS 1" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":3524, - "acronym_dn":"8.0.1", - "classroom_id":-2147483648, - "room_dn":"011" - } - }, - { - "event_id":43499, - "date_start":"2022-12-09T16:15:00", - "date_end":"2022-12-09T16:30:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"GEOMETRIA E ALGEBRA LINEARE", - "en":"GEOMETRY AND LINEAR ALGEBRA" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":3524, - "acronym_dn":"8.0.1", - "classroom_id":-2147483648, - "room_dn":"011" - } - }, - { - "event_id":52697, - "date_start":"2022-12-09T17:15:00", - "date_end":"2022-12-09T18:00:00", - "favourite":false, - "show_agenda":true, - "matricola":"211834", - "title":{ - "it":"ANALISI MATEMATICA 1 Squadra 1", - "en":"MATHEMATICAL ANALYSIS 1 Team 1" - }, - "event_type":{ - "typeId":1, - "type_dn":{ - "it":"Lezione", - "en":"Lecture" - } - }, - "calendar":{ - "calendar_id":0, - "calendar_dn":{ - "it":"Accademico", - "en":"Academic" - } - }, - "room":{ - "room_id":2074, - "acronym_dn":"T.2.1", - "classroom_id":-2147483648, - "room_dn":"006" - } - } - ] \ No newline at end of file diff --git a/src/components/Home/MainMenu.tsx b/src/components/Home/MainMenu.tsx index 2f38fd85..dfa12ee7 100644 --- a/src/components/Home/MainMenu.tsx +++ b/src/components/Home/MainMenu.tsx @@ -16,25 +16,23 @@ import grading_book from "assets/menu/grading_book.svg" import tests from "assets/menu/tests.svg" import add from "assets/menu/add.svg" import { ModalCustom } from "components/Modal" -import { Text } from "components/Text" import AsyncStorage from "@react-native-async-storage/async-storage" import { useOutsideClick } from "utils/outsideClick" -import { usePalette } from "utils/colors" /** * the buttons and their features */ export const defaultIcons: ButtonInterface[] = [ - { id: 0, title: "Calendario", icon: calendar }, - { id: 1, title: "Orario Lezioni", icon: clock }, - { id: 2, title: "PoliAssociazioni", icon: association }, - { id: 3, title: "Aule Libere", icon: free_classrooms }, - { id: 4, title: "Materiali", icon: materials }, - { id: 5, title: "Gruppi", icon: groups }, - { id: 6, title: "Valutazioni", icon: marks }, - { id: 7, title: "Libretto", icon: grading_book }, - { id: 8, title: "Test e Prove", icon: tests }, - { id: 9, title: "Aggiungi", icon: add }, + { id: 0, title: "Calendario", icon: calendar }, + { id: 1, title: "Orario Lezioni", icon: clock }, + { id: 2, title: "PoliAssociazioni", icon: association }, + { id: 3, title: "Aule Libere", icon: free_classrooms }, + { id: 4, title: "Materiali", icon: materials }, + { id: 5, title: "Gruppi", icon: groups }, + { id: 6, title: "Valutazioni", icon: marks }, + { id: 7, title: "Libretto", icon: grading_book }, + { id: 8, title: "Test e Prove", icon: tests }, + { id: 9, title: "Aggiungi", icon: add }, ] type ButtonState = ButtonInterface & { shown: boolean } @@ -43,180 +41,147 @@ type ButtonState = ButtonInterface & { shown: boolean } * the main menu of the app, an horizontal scroll view with the buttons to navigate to the different pages */ export const MainMenu: FC<{ filter?: string }> = ({ filter }) => { - const { navigate } = useNavigation() - - const [icons, setIcons] = useState( - defaultIcons.map(icon => ({ ...icon, shown: true })) - ) - - const [isModalVisible, setModalVisible] = useState(false) - const [isDeleting, setIsDeleting] = useState(false) - - const scrollView = useOutsideClick(() => { - setIsDeleting(false) - }, isDeleting) - - useEffect(() => { - scrollView.current?.scrollTo({ x: 0, y: 0, animated: true }) - }, [filter, scrollView]) - - const { homeBackground, isLight } = usePalette() - - useEffect(() => { - AsyncStorage.getItem("menu:icons") - .then(iconJSON => { - if (iconJSON) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const showns: number[] = JSON.parse(iconJSON) + const { navigate } = useNavigation() + + const [icons, setIcons] = useState( + defaultIcons.map(icon => ({ ...icon, shown: true })) + ) + + const [isModalVisible, setModalVisible] = useState(false) + const [isDeleting, setIsDeleting] = useState(false) + + const scrollView = useOutsideClick(() => { + setIsDeleting(false) + }, isDeleting) + + useEffect(() => { + scrollView.current?.scrollTo({ x: 0, y: 0, animated: true }) + }, [filter, scrollView]) + + useEffect(() => { + AsyncStorage.getItem("menu:icons") + .then(iconJSON => { + if (iconJSON) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const showns: number[] = JSON.parse(iconJSON) + setIcons( + icons.map(icon => ({ + ...icon, + shown: showns.includes(icon.id), + })) + ) + } + }) + .catch(err => console.log(err)) + }, []) + + useEffect(() => { + AsyncStorage.setItem( + "menu:icons", + JSON.stringify(icons.filter(i => i.shown).map(i => i.id)) + ).catch(err => console.log(err)) + }, [icons]) + + // divide iconsToAdd in triplets + const triplets = icons + .filter(i => !i.shown) + .reduce((acc, cur, i) => { + if (i % 3 === 0) { + acc.push([cur]) + } else { + acc[acc.length - 1].push(cur) + } + return acc + }, [] as ButtonInterface[][]) + + return ( + + setModalVisible(false)} + > + + {triplets.map((triplet, i) => ( + + {triplet.map(buttonIcon => ( + { setIcons( - icons.map(icon => ({ - ...icon, - shown: showns.includes(icon.id), - })) + icons.map(i => + i.id === buttonIcon.id ? { ...i, shown: true } : i + ) ) + }} + buttonIcon={buttonIcon} + isDeleting={false} + key={"menu_add_icon" + buttonIcon.id} + inMenu + /> + ))} + + ))} + + + {icons + .filter(i => i.shown) + .filter( + i => + i.id === 9 || + (filter + ? i.title.toLowerCase().includes(filter.toLowerCase()) + : true) + ) + .map(buttonIcon => ( + { + if (isDeleting) setIsDeleting(false) + if (buttonIcon.id === 9) setModalVisible(true) + // TODO: actual navigation + + if (!isDeleting && buttonIcon.id !== 9) { + if (buttonIcon.id == 3) { + navigate("FreeClassrooms") + } else if (buttonIcon.id === 5) { + navigate("Groups") + } else { + navigate("Error404") } - }) - .catch(err => console.log(err)) - }, []) - - useEffect(() => { - AsyncStorage.setItem( - "menu:icons", - JSON.stringify(icons.filter(i => i.shown).map(i => i.id)) - ).catch(err => console.log(err)) - }, [icons]) - - // divide iconsToAdd in triplets - const triplets = icons - .filter(i => !i.shown) - .reduce((acc, cur, i) => { - if (i % 3 === 0) { - acc.push([cur]) - } else { - acc[acc.length - 1].push(cur) - } - return acc - }, [] as ButtonInterface[][]) - - return ( - - setModalVisible(false)} - > - - Aggiungi features - - - Personalizza la tua bacheca - - - {triplets.map((triplet, i) => ( - - {triplet.map(buttonIcon => ( - { - setIcons( - icons.map(i => - i.id === buttonIcon.id - ? { ...i, shown: true } - : i - ) - ) - }} - buttonIcon={buttonIcon} - isDeleting={false} - key={"menu_add_icon" + buttonIcon.id} - inMenu - /> - ))} - - ))} - - - {icons - .filter(i => i.shown) - .filter( - i => - i.id === 9 || - (filter - ? i.title - .toLowerCase() - .includes(filter.toLowerCase()) - : true) - ) - .map(buttonIcon => ( - { - if (isDeleting) setIsDeleting(false) - if (buttonIcon.id === 9) setModalVisible(true) - // TODO: actual navigation - - if (!isDeleting && buttonIcon.id !== 9) { - if (buttonIcon.id == 3) { - navigate("FreeClassrooms") - } else if (buttonIcon.id === 5) { - navigate("Groups") - } else { - navigate("Error404") - } - } - }} - onLongPress={() => { - if (buttonIcon.id !== 9) setIsDeleting(!isDeleting) - }} - buttonIcon={buttonIcon} - isDeleting={isDeleting} - onDelete={() => { - const { id } = buttonIcon - setIcons( - icons.map(i => - i.id === id ? { ...i, shown: false } : i - ) - ) - }} - key={"menu_" + buttonIcon.id} - /> - ))} - - ) + } + }} + onLongPress={() => { + if (buttonIcon.id !== 9) setIsDeleting(!isDeleting) + }} + buttonIcon={buttonIcon} + isDeleting={isDeleting} + onDelete={() => { + const { id } = buttonIcon + setIcons( + icons.map(i => (i.id === id ? { ...i, shown: false } : i)) + ) + }} + key={"menu_" + buttonIcon.id} + /> + ))} + + ) } diff --git a/src/components/Home/MainTitle.tsx b/src/components/Home/MainTitle.tsx index 3be465c3..9c051730 100644 --- a/src/components/Home/MainTitle.tsx +++ b/src/components/Home/MainTitle.tsx @@ -5,45 +5,45 @@ import MaskedView from "@react-native-masked-view/masked-view" import { Text } from "components/Text" export const MainTitle: FC = () => { - return ( - - - POLI - - - FEMO - - - } - > - - - ) + return ( + + + POLI + + + FEMO + + + } + > + + + ) } diff --git a/src/components/Home/MenuButton.tsx b/src/components/Home/MenuButton.tsx index 93bd04bf..5919b483 100644 --- a/src/components/Home/MenuButton.tsx +++ b/src/components/Home/MenuButton.tsx @@ -8,135 +8,121 @@ import { usePalette } from "utils/colors" import deleteIcon from "assets/menu/delete.svg" export interface ButtonInterface { - id: number - title: string - icon: DataSourceParam + id: number + title: string + icon: DataSourceParam } /** * single buttons for the main menu, with custom icons and titles */ export const MenuButton: FC<{ - onPress: () => void - onLongPress?: () => void - buttonIcon: ButtonInterface - isDeleting: boolean - onDelete?: () => void - inMenu?: boolean + onPress: () => void + onLongPress?: () => void + buttonIcon: ButtonInterface + isDeleting: boolean + onDelete?: () => void + inMenu?: boolean }> = ({ onPress, onLongPress, buttonIcon, isDeleting, onDelete, inMenu }) => { - const { palette, isDark } = usePalette() - const color = isDark && inMenu ? palette.lighter : palette.primary - const svg = useSVG(buttonIcon.icon) - const delIcon = useSVG(deleteIcon) + const { palette, isDark } = usePalette() + const color = isDark && inMenu ? palette.lighter : palette.primary + const svg = useSVG(buttonIcon.icon) + const delIcon = useSVG(deleteIcon) - const animatedValue = new Animated.Value(0) + const animatedValue = new Animated.Value(0) - //function to call to perform the shake of the button - const handleAnimation = () => { - Animated.loop( - Animated.sequence([ - Animated.timing(animatedValue, { - toValue: 1.0, - duration: 100 + (Math.random() * 40 - 20), - useNativeDriver: true, - }), - Animated.timing(animatedValue, { - toValue: -1.0, - duration: 100 + (Math.random() * 40 - 20), - useNativeDriver: true, - }), - ]) - ).start() - } + //function to call to perform the shake of the button + const handleAnimation = () => { + Animated.loop( + Animated.sequence([ + Animated.timing(animatedValue, { + toValue: 1.0, + duration: 100 + (Math.random() * 40 - 20), + useNativeDriver: true, + }), + Animated.timing(animatedValue, { + toValue: -1.0, + duration: 100 + (Math.random() * 40 - 20), + useNativeDriver: true, + }), + ]) + ).start() + } - useEffect(() => { - if (isDeleting) handleAnimation() - else { - Animated.timing(animatedValue, { - toValue: 0, - duration: 100, - useNativeDriver: true, - }).start() - } - }, [isDeleting, animatedValue]) + useEffect(() => { + if (isDeleting) handleAnimation() + else { + Animated.timing(animatedValue, { + toValue: 0, + duration: 100, + useNativeDriver: true, + }).start() + } + }, [isDeleting, animatedValue]) - return ( - - + + + + + {svg && } + + - - - - {svg && ( - - )} - - - {buttonIcon.title} - - - - {isDeleting && buttonIcon.id !== 9 && ( - { - onDelete && onDelete() - }} - hitSlop={10} - > - - {delIcon && ( - - )} - - - )} - - - ) + {buttonIcon.title} + + + + {isDeleting && buttonIcon.id !== 9 && ( + { + onDelete && onDelete() + }} + hitSlop={10} + > + + {delIcon && ( + + )} + + + )} + + + ) } diff --git a/src/components/Home/News/NewsBottomSheet.tsx b/src/components/Home/News/NewsBottomSheet.tsx new file mode 100644 index 00000000..abdbc844 --- /dev/null +++ b/src/components/Home/News/NewsBottomSheet.tsx @@ -0,0 +1,216 @@ +import React, { FC, useEffect, useState, useRef, useContext } from "react" +import { StyleSheet, View, DeviceEventEmitter } from "react-native" +import BottomSheet, { + BottomSheetScrollView, + BottomSheetScrollViewMethods, +} from "@gorhom/bottom-sheet" + +import { Article } from "api/articles" +import { + NewsPreferencesContext, + Preference, + TagWithData, +} from "contexts/newsPreferences" +import { NewsTagsGrid } from "./NewsTagsGrid" +import { Title } from "components/Text" +import { CardWithGradient } from "components/CardWithGradient" +import { NavBar, CLOSE_BOTTOM_SHEET_EVENT_NAME } from "components/NavBar" +import { usePalette } from "utils/colors" +import { useNavigation } from "navigation/NavigationTypes" +import { getUsableScreenHeight } from "utils/height" + +interface NewsBottomSheetProps { + /** + * Tags (news categories) in the favourite section + */ + tags: TagWithData[] + /** + * Article at the top of the news section + */ + highlightedArticle?: Article +} + +/** + * Bottom sheet in the home page to access the news. + * + * This component receives data from the NewsManager and handles the graphic part. + * Its positioning is absolute. + */ +export const NewsBottomSheet: FC = props => { + const navigation = useNavigation() + const { isLight, background } = usePalette() + + const { preferences } = useContext(NewsPreferencesContext) + + // modal state + const [isNewsClosed, setIsNewsClosed] = useState(true) + // the ref for the News bottom sheet, used to open and close it programmatically + const bottomSheetRef = useRef(null) + // The reference to the News scrollview, used to scroll it programmatically + const scrollViewRef = useRef(null) + + // distance of the bottom sheet from the top of the screen, when opened or closed + const distanceFromTop = { + closed: 666, + opened: 106, + } + + const showHighlighted = props.highlightedArticle !== undefined + const showButtonToOtherTags = Object.values(preferences).some( + p => p === Preference.UNFAVOURITE + ) + + const favTags = props.tags.filter( + tag => preferences[tag.name] !== Preference.UNFAVOURITE + ) + + useEffect(() => { + // scrolls to the top of the news scrollview + if (isNewsClosed && scrollViewRef.current) { + // "scrollTo" is deprecated but works fine. + // "scrollToEnd" doesn't work when the news scrollview is fully expanded downwards + scrollViewRef.current.scrollTo({ y: 0, animated: false }) + } + }, [isNewsClosed]) + + useEffect(() => { + // Set up the event listener to close the NewsBottomSheet + // when the home button in the NavBar is clicked + DeviceEventEmitter.addListener(CLOSE_BOTTOM_SHEET_EVENT_NAME, () => { + setIsNewsClosed(true) + }) + return () => { + DeviceEventEmitter.removeAllListeners(CLOSE_BOTTOM_SHEET_EVENT_NAME) + } + }, []) + + return ( + ( + + + + News + + + )} + style={[styles.bottomSheet, { backgroundColor: background }]} + backgroundStyle={{ + backgroundColor: background, + // Not 30 borderRadius because on IOS on dark mode there where white borders + borderTopLeftRadius: 33, + borderTopRightRadius: 33, + }} + onAnimate={fromIndex => { + // fires when the bottom sheet changes position index, keeps track of when the sheet is open/close. + // More responsive than onChange + setIsNewsClosed(fromIndex === 1) + }} + onChange={index => { + // fires when the bottom sheet changes position index, keeps track of when the sheet is open/close. + // In certain cases, onAnimate fails + setIsNewsClosed(index === 0) + }} + index={isNewsClosed ? 0 : 1} // close = 0 and open = 1 + snapPoints={[ + // 0 is at the bottom of the screen + Math.max(getUsableScreenHeight() - distanceFromTop.closed, 42), + getUsableScreenHeight() - distanceFromTop.opened, + ]} + animateOnMount={false} // app should begin stationary + > + + {showHighlighted && ( + 0 + ? props.highlightedArticle.image + : "" + // : "http://rl.airlab.deib.polimi.it/wp-content/uploads/2022/06/1-PolimiCampus_2.jpg" + } + onClick={() => + navigation.navigate("Article", { + article: props.highlightedArticle as Article, + }) + } + style={{ height: 220, marginBottom: 34 }} + /> + )} + + + + {showButtonToOtherTags && ( + + navigation.navigate("OtherCategories", { + tags: props.tags, + }) + } + style={{ height: 80, marginTop: 17 }} + /> + )} + + + + setIsNewsClosed(true)} + overrideHomeBehavior={() => setIsNewsClosed(true)} + /> + + ) +} + +const styles = StyleSheet.create({ + bottomSheet: { + flex: 1, + borderTopLeftRadius: 30, + borderTopRightRadius: 30, + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 7, + }, + shadowOpacity: 0.43, + shadowRadius: 9.51, + elevation: 20, + }, + topBar: { + flex: 1, + justifyContent: "center", + paddingHorizontal: 26, + borderTopLeftRadius: 30, + borderTopRightRadius: 30, + paddingBottom: 8, + }, + dragBar: { + alignSelf: "center", + width: 120, + height: 5, + marginVertical: 16, + borderRadius: 4, + }, +}) diff --git a/src/components/Home/News/NewsManager.tsx b/src/components/Home/News/NewsManager.tsx new file mode 100644 index 00000000..7369a6cf --- /dev/null +++ b/src/components/Home/News/NewsManager.tsx @@ -0,0 +1,136 @@ +import React, { useContext, useEffect, useState } from "react" + +import { api } from "api" +import { Article, Tag } from "api/articles" +import { + TagWithData, + NewsPreferencesContext, + Preference, +} from "contexts/newsPreferences" +import { NewsBottomSheet } from "./NewsBottomSheet" +import { newsTagsPatterns, CardsPattern } from "utils/cardsPatterns" + +/** + * Bottom sheet in the home page to access the news. + * + * This is the component that retrieves and precesses the news content. + * Afterwards, it passes the data to the NewsBottomSheet. + */ +export const NewsManager = () => { + const [tags, setTags] = useState([]) + + const { preferences } = useContext(NewsPreferencesContext) + + // Object to store the last article of each tag + const [lastArticles, setLastArticles] = useState< + Record + >({}) + + // Download the last published article of each tag (news category) in parallel + // and wait until each one has finished + const getLastArticles = async (tags: Tag[]) => { + const tempArticles = {} as typeof lastArticles + const promises = [] + for (const tag of tags) { + promises.push( + api.articles + .getLastArticleByTag(tag.name) + .then(article => { + tempArticles[tag.name] = article + }) + .catch(err => { + console.log(err) + }) + ) + } + await Promise.allSettled(promises) + return tempArticles + } + + useEffect(() => { + // Load tags (news categories) and their last article (one for each tag) + const fetchData = async () => { + const responseTags = await api.tags.getTags() + const responseArticles = await getLastArticles(responseTags) + setTags(responseTags) + setLastArticles(responseArticles) + } + fetchData().catch(err => console.log(err)) + }, []) + + // Function that calculates heights and columns of tag cards using hardcoded patterns. + // Then it returns a new list of tags with that and other usefull data. + const getTagsWithData = () => { + const tempTagsWithData: TagWithData[] = [] + + // store the pattern data of the current batch of cards + let pattern: CardsPattern + // index of the current news tag + let index = 0 + // number of tags remaining + let remaining = tags.length + + while (index < tags.length) { + // choose the dimension of the next batch of tags and get the corresponding pattern + if (index === 0) { + // When first bacth + if (remaining <= 6) { + pattern = newsTagsPatterns.first[remaining] + } else { + pattern = newsTagsPatterns.first[4] + } + } else { + // When other batches. Here it is never possible that remaining === 1 or remaining === 2 + if (remaining % 5 === 1 || remaining % 5 === 3) { + pattern = newsTagsPatterns.other[3] + } else if (remaining % 5 === 2 || remaining % 5 === 4) { + pattern = newsTagsPatterns.other[4] + } else { + pattern = newsTagsPatterns.other[5] + } + } + + for (const [height, column] of pattern) { + tempTagsWithData.push({ + ...tags[index], + column: column, + height: height, + favourite: preferences[tags[index].name] !== Preference.UNFAVOURITE, + }) + index++ + remaining-- + } + } + return tempTagsWithData + } + + const getHighlightedArticle = () => { + const favouriteTags = tags.filter( + tag => preferences[tag.name] !== Preference.UNFAVOURITE + ) + // If there are no favourite tags, choose the highlighted article from all the other tags + const tagsToAnalyze = favouriteTags.length > 0 ? favouriteTags : tags + let tempHighlighted: Article | undefined = undefined + + for (const tag of tagsToAnalyze) { + if (!tempHighlighted) { + tempHighlighted = lastArticles[tag.name] + } else { + const article = lastArticles[tag.name] + const articleDate = new Date(article?.publish_time) + const highlightedDate = new Date(tempHighlighted.publish_time) + if (articleDate > highlightedDate) { + tempHighlighted = article + } + } + } + return tempHighlighted + } + + return ( + + ) +} diff --git a/src/components/Home/News/NewsTagsGrid.tsx b/src/components/Home/News/NewsTagsGrid.tsx new file mode 100644 index 00000000..632036f0 --- /dev/null +++ b/src/components/Home/News/NewsTagsGrid.tsx @@ -0,0 +1,63 @@ +import React, { FC } from "react" +import { View } from "react-native" + +import { TagWithData } from "contexts/newsPreferences" +import { CardWithGradient } from "components/CardWithGradient" +import { useNavigation } from "navigation/NavigationTypes" +import { capitalize } from "utils/strings" + +interface NewsTagsGridProps { + /** + * Tags to be displayed + */ + tags: TagWithData[] +} + +/** + * Component used to display a grid containing the tags (news categories) + * inside of the news bottom sheet in the home page. + */ +export const NewsTagsGrid: FC = props => { + const navigation = useNavigation() + + // Function used when displaying a tag card + const getTagCard = (tag: TagWithData) => { + return ( + + navigation.navigate("ArticlesList", { + tagName: tag.name, + }) + } + closerToCorner={true} + style={{ height: tag.height }} + /> + ) + } + + return ( + <> + {props.tags.length === 1 ? ( + // if there is only 1 news tag, display its card at full width + getTagCard(props.tags[0]) + ) : ( + + + {props.tags + .filter(tag => tag.column === "left") + .map(tag => getTagCard(tag))} + + + + {props.tags + .filter(tag => tag.column === "right") + .map(tag => getTagCard(tag))} + + + )} + + ) +} diff --git a/src/components/Home/News/index.tsx b/src/components/Home/News/index.tsx new file mode 100644 index 00000000..0da08422 --- /dev/null +++ b/src/components/Home/News/index.tsx @@ -0,0 +1 @@ +export * from "./NewsManager" diff --git a/src/components/Home/NewsBottomSheet.tsx b/src/components/Home/NewsBottomSheet.tsx deleted file mode 100644 index ed102d4d..00000000 --- a/src/components/Home/NewsBottomSheet.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import React, { FC, useEffect, useState, useRef } from "react" -import { StyleSheet, View } from "react-native" -import BottomSheet, { - BottomSheetScrollView, - BottomSheetScrollViewMethods, -} from "@gorhom/bottom-sheet" - -import { NewsCategoriesGrid } from "./NewsCategoriesGrid" -import { Title } from "components/Text" -import { NavBar } from "components/NavBar" -import { usePalette } from "utils/colors" -import { getUsableScreenHeight } from "utils/height" - -/** - * Bottom sheet in the home page to access news highlights and news categories. - * - * Its positioning is absolute. - */ -export const NewsBottomSheet: FC = () => { - const { isLight, background } = usePalette() - - // modal state - const [isNewsClosed, setIsNewsClosed] = useState(true) - // the ref for the News bottom sheet, used to open and close it programmatically - const bottomSheetRef = useRef(null) - // The reference to the News scrollview, used to scroll it programmatically - const scrollViewRef = useRef(null) - - // distance of the bottom sheet from the top of the screen, when opened or closed - const distanceFromTop = { - closed: 666, - opened: 106, - } - - useEffect(() => { - // scrolls to the top of the news scrollview when the news bottom sheet is Closed - if (isNewsClosed && scrollViewRef.current) { - // "scrollTo" is deprecated but works fine. - // "scrollToEnd" doesn't work when the news scrollview is fully expanded downwards - scrollViewRef.current.scrollTo({ y: 0, animated: true }) - } - }, [isNewsClosed]) - - return ( - ( - // "News" title top bar component - - - - News - - - )} - style={[styles.bottomSheet, { backgroundColor: background }]} - backgroundStyle={{ - backgroundColor: background, - // Not 30 borderRadius because on IOS on dark mode there where white borders - borderTopLeftRadius: 33, - borderTopRightRadius: 33, - }} - onAnimate={fromIndex => { - // fires when the bottom sheet changes position index, keeps track of when the sheet is open/close. - // More responsive than onChange - setIsNewsClosed(fromIndex === 1) - }} - onChange={index => { - // fires when the bottom sheet changes position index, keeps track of when the sheet is open/close. - // In certain cases, onAnimate fails - setIsNewsClosed(index === 0) - }} - index={isNewsClosed ? 0 : 1} - snapPoints={[ - // 0 is at the bottom of the screen - Math.max(getUsableScreenHeight() - distanceFromTop.closed, 42), - getUsableScreenHeight() - distanceFromTop.opened, - ]} - animateOnMount={false} // app should begin stationary - > - - - - setIsNewsClosed(true)} - overrideHomeBehavior={() => setIsNewsClosed(true)} - /> - - ) -} - -const styles = StyleSheet.create({ - bottomSheet: { - flex: 1, - borderTopLeftRadius: 30, - borderTopRightRadius: 30, - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 7, - }, - shadowOpacity: 0.43, - shadowRadius: 9.51, - elevation: 20, - }, - topBar: { - flex: 1, - justifyContent: "center", - paddingHorizontal: 26, - height: 112, - borderTopLeftRadius: 30, - borderTopRightRadius: 30, - }, - dragBar: { - alignSelf: "center", - width: 120, - height: 5, - borderRadius: 4, - }, -}) diff --git a/src/components/Home/NewsCategoriesGrid.tsx b/src/components/Home/NewsCategoriesGrid.tsx deleted file mode 100644 index f1837582..00000000 --- a/src/components/Home/NewsCategoriesGrid.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import React, { useState, useEffect } from "react" -import { View } from "react-native" - -import { CardWithGradient } from "components/CardWithGradient" -import { api } from "api" -import { useNavigation } from "navigation/NavigationTypes" -import { newsCategoryPatterns, CardsPattern } from "utils/cardsPatterns" -import { Tag } from "api/tags" - -/** Tuple contianing the category object with the height of its card */ -export type CategoryAndHeight = [Tag, number] - -export interface CategoriesColumns { - /** left column of news categories*/ - left: CategoryAndHeight[] - /** right column of news categories*/ - right: CategoryAndHeight[] -} - -/** - * Component used to display the grid of news categories inside of - * the news bottom sheet in the home page. - */ -export const NewsCategoriesGrid = () => { - const navigation = useNavigation() - - // Store the list of news categories with their heights, divided into left and right columns - const [categories, setCategories] = useState({ - left: [], - right: [], - }) - - // Get the news categories from the backend and use hardcoded - // patterns to get heights and positions (left / right) of the cards - const updateNewsCategories = async () => { - try { - const response = await api.tags.getTags() - const tempCategories: CategoriesColumns = { left: [], right: [] } - - // store the pattern data of the current batch of cards - let pattern: CardsPattern - // index of the current category - let index = 0 - // number of categories remaining - let remaining = response.length - - while (index < response.length) { - // choose the dimension of the next batch of categories and get the corresponding pattern - if (index === 0) { - // When first bacth - if (remaining <= 6) { - pattern = newsCategoryPatterns.first[remaining] - } else { - pattern = newsCategoryPatterns.first[4] - } - } else { - // When other batches. Here it is never possible that remaining === 1 or remaining === 2 - if (remaining % 5 === 1 || remaining % 5 === 3) { - pattern = newsCategoryPatterns.other[3] - } else if (remaining % 5 === 2 || remaining % 5 === 4) { - pattern = newsCategoryPatterns.other[4] - } else { - pattern = newsCategoryPatterns.other[5] - } - } - - for (const [height, column] of pattern) { - tempCategories[column].push([response[index], height]) - index++ - remaining-- - } - } - setCategories(tempCategories) - } catch (error) { - console.log(error) - } - } - - // TODO: should add a way to refresh news categories ? - useEffect(() => { - void updateNewsCategories() - }, []) - - // Function used when displaying the cards - const getCategoriesCards = (list: CategoryAndHeight[]) => { - return list.map(([category, height], index) => { - return ( - - navigation.navigate("NewsList", { - categoryName: category.name, - }) - } - closerToCorner={true} - style={{ height: height }} - /> - ) - }) - } - - return ( - - <> - - navigation.navigate("NewsList", { - categoryName: "Evidenza", - }) - } - style={{ height: 220 }} - /> - - {categories.left.length === 1 && - categories.right.length === 0 ? ( - // if there is only 1 news category, display the card at full width - categories.left[0] - ) : ( - - - {getCategoriesCards(categories.left)} - - - - {getCategoriesCards(categories.right)} - - - )} - - - ) -} diff --git a/src/components/Home/PoliSearchBar.tsx b/src/components/Home/PoliSearchBar.tsx index 7e35fd6f..ac828133 100644 --- a/src/components/Home/PoliSearchBar.tsx +++ b/src/components/Home/PoliSearchBar.tsx @@ -1,11 +1,11 @@ import React, { FC, useEffect, useState, useRef } from "react" import { - TextInput, - Animated, - Pressable, - StyleProp, - ViewStyle, - Keyboard, + TextInput, + Animated, + Pressable, + StyleProp, + ViewStyle, + Keyboard, } from "react-native" import { usePalette } from "utils/colors" import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" @@ -16,117 +16,117 @@ import searchDark from "assets/menu/searchDark.svg" * the search bar, which requests a search everytime the input text changes */ export const PoliSearchBar: FC<{ - onChange: (searchKey: string) => void - style?: StyleProp + onChange: (searchKey: string) => void + style?: StyleProp }> = ({ onChange, style }) => { - const { fieldBackground, fieldText, bodyText, isLight } = usePalette() + const { fieldBackground, fieldText, bodyText, isLight } = usePalette() - const svg = useSVG(isLight ? searchLight : searchDark) + const svg = useSVG(isLight ? searchLight : searchDark) - const [isFocused, setIsFocused] = useState(false) - const shadowAnim = useRef(new Animated.Value(0)).current - const inputText = useRef(null) + const [isFocused, setIsFocused] = useState(false) + const shadowAnim = useRef(new Animated.Value(0)).current + const inputText = useRef(null) - useEffect(() => { - const keyboardDidHideListener = Keyboard.addListener( - "keyboardDidHide", - () => { - inputText.current?.blur() - } - ) + useEffect(() => { + const keyboardDidHideListener = Keyboard.addListener( + "keyboardDidHide", + () => { + inputText.current?.blur() + } + ) - return () => { - keyboardDidHideListener.remove() - } - }, []) + return () => { + keyboardDidHideListener.remove() + } + }, []) - useEffect(() => { - const duration = 100 - if (isFocused) - Animated.timing(shadowAnim, { - duration, - toValue: 1, - useNativeDriver: true, - }).start() - else - Animated.timing(shadowAnim, { - duration, - toValue: 0, - useNativeDriver: true, - }).start() - }, [isFocused, shadowAnim]) + useEffect(() => { + const duration = 100 + if (isFocused) + Animated.timing(shadowAnim, { + duration, + toValue: 1, + useNativeDriver: true, + }).start() + else + Animated.timing(shadowAnim, { + duration, + toValue: 0, + useNativeDriver: true, + }).start() + }, [isFocused, shadowAnim]) - return ( - + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + /> + { + inputText.current?.focus() + }} + > + - setIsFocused(true)} - onBlur={() => setIsFocused(false)} + {svg && ( + - { - inputText.current?.focus() - }} - > - - {svg && ( - - )} - - - - ) + )} + + + + ) } diff --git a/src/components/Home/StickyHeader.tsx b/src/components/Home/StickyHeader.tsx index d132edb5..4a4a2bd6 100644 --- a/src/components/Home/StickyHeader.tsx +++ b/src/components/Home/StickyHeader.tsx @@ -1,9 +1,9 @@ import React, { FC } from "react" import { - Animated, - LayoutChangeEvent, - ViewProps, - StyleSheet, + Animated, + LayoutChangeEvent, + ViewProps, + StyleSheet, } from "react-native" /** @@ -13,47 +13,45 @@ import { * one basically scrolls past the upper one) */ export const StickyHeader: FC<{ - scrollAnimatedValue: Animated.AnimatedValue - nextHeaderLayoutY?: number - onLayout: (e: LayoutChangeEvent) => void - children: React.ReactNode - // eslint-disable-next-line react/display-name + scrollAnimatedValue: Animated.AnimatedValue + nextHeaderLayoutY?: number + onLayout: (e: LayoutChangeEvent) => void + children: React.ReactNode + // eslint-disable-next-line react/display-name }> = React.forwardRef((props, ref) => { - const [layoutY, setLayoutY] = React.useState(0) - const { children, scrollAnimatedValue, onLayout } = props + const [layoutY, setLayoutY] = React.useState(0) + const { children, scrollAnimatedValue, onLayout } = props - // needed to get the top offset, otherwise it wont work properly - const childStyle = StyleSheet.flatten( - (children as React.ReactElement)?.props?.style ?? {} - ) + // needed to get the top offset, otherwise it wont work properly + const childStyle = StyleSheet.flatten( + (children as React.ReactElement)?.props?.style ?? {} + ) - return ( - { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const top = childStyle.top - setLayoutY( - e.nativeEvent.layout.y + (typeof top === "number" ? top : 0) - ) - // setLayoutHeight(e.nativeEvent.layout.height) - onLayout(e) - }} - style={{ - zIndex: childStyle.zIndex, - transform: [ - { - // the trick is translating the component of the amount scrolled only once the - // layoutY is reached (the starting y of the header in relation to the scrollview) - translateY: scrollAnimatedValue.interpolate({ - inputRange: [-1, layoutY, layoutY + 1], - outputRange: [0, 0, 1], - }), - }, - ], - }} - > - {children} - - ) + return ( + { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const top = childStyle.top + setLayoutY(e.nativeEvent.layout.y + (typeof top === "number" ? top : 0)) + // setLayoutHeight(e.nativeEvent.layout.height) + onLayout(e) + }} + style={{ + zIndex: childStyle.zIndex, + transform: [ + { + // the trick is translating the component of the amount scrolled only once the + // layoutY is reached (the starting y of the header in relation to the scrollview) + translateY: scrollAnimatedValue.interpolate({ + inputRange: [-1, layoutY, layoutY + 1], + outputRange: [0, 0, 1], + }), + }, + ], + }} + > + {children} + + ) }) diff --git a/src/components/Home/index.tsx b/src/components/Home/index.tsx index 02afcbdc..d8f369f8 100644 --- a/src/components/Home/index.tsx +++ b/src/components/Home/index.tsx @@ -3,4 +3,3 @@ export * from "./MainTitle" export * from "./MenuButton" export * from "./StickyHeader" export * from "./PoliSearchBar" -export * from "./NewsBottomSheet" diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 6273b11e..d1d6a6e3 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,21 +1,30 @@ import React, { FC } from "react" import { View, Modal, StyleSheet, Pressable } from "react-native" +import { Text } from "components/Text" import { usePalette } from "utils/colors" import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" import { deleteSvg as icon } from "assets/modal" export interface ModalCustomProps { - /** - * content of the modal - */ - children: React.ReactNode - /** - * whether ot not to show the modal - */ - isShowing: boolean - /** - * this function hides the modal by changing the state in the parent component - */ - onClose: () => void + /** + * content of the modal + */ + children: React.ReactNode + title: string + subTitle?: string + /** + * whether ot not to show the modal + */ + isShowing: boolean + /** + * this function hides the modal by changing the state in the parent component + */ + onClose: () => void + + /** + * whether ot not to center title and subtitle and apply different margins + * @default false + */ + centerText?: boolean } /** @@ -23,96 +32,121 @@ export interface ModalCustomProps { * */ export const ModalCustom: FC = props => { - const { backgroundSecondary, modalBarrier } = usePalette() - - const deleteSvg = useSVG(icon.svg) - return ( - //TODO: animationType fade or slide? - - + + + props.onClose()} + > + + + {deleteSvg && ( + + )} + + + + + + {props.title} + + - - props.onClose()} - > - - - {deleteSvg && ( - - )} - - - - - - {props.children} - - - - - - ) + {props.subTitle} + + {props.children} + + + + + ) } const styles = StyleSheet.create({ - pageWrapper: { - flex: 1, - justifyContent: "center", - alignItems: "center", - }, - contentWrapper: { - borderRadius: 12, + pageWrapper: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + contentWrapper: { + width: 320, + height: 420, + borderRadius: 12, + marginHorizontal: 15, - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 3, - }, - shadowOpacity: 0.27, - shadowRadius: 4.65, - elevation: 6, - }, - circle: { - width: 30, - height: 30, - backgroundColor: "#ffffff", - borderRadius: 15, - marginTop: 96, - marginBottom: 8, - justifyContent: "center", - alignItems: "center", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 3, }, + shadowOpacity: 0.27, + shadowRadius: 4.65, + elevation: 6, + }, + circle: { + width: 30, + height: 30, + backgroundColor: "#ffffff", + borderRadius: 15, + marginBottom: 8, + justifyContent: "center", + alignItems: "center", + }, + title: { + fontSize: 32, + marginHorizontal: 27, + fontWeight: "900", + }, + subTitle: { + fontSize: 13, + marginHorizontal: 27, + fontWeight: "600", + }, }) diff --git a/src/components/ModalWithButtons.tsx b/src/components/ModalWithButtons.tsx new file mode 100644 index 00000000..4f2b66a0 --- /dev/null +++ b/src/components/ModalWithButtons.tsx @@ -0,0 +1,144 @@ +import React, { FC } from "react" +import { View, Modal, StyleSheet, Pressable } from "react-native" +import { Text } from "components/Text" +import { ButtonCustom } from "components/Button" +import { usePalette } from "utils/colors" + +export interface ModalWithButtonsProps { + /** + * content of the modal + */ + children: React.ReactNode + + title?: string + + leftButtonTitle?: string + rightButtonTitle?: string + + /** + * whether ot not to show the modal + */ + isShowing: boolean + /** + * this function hides the modal by changing the state in the parent component + */ + onClose: () => void + + /** + * function called when the right button is pressed + */ + onOK: () => void + + /** + * modal wrapper height, specify if height is fixed + */ + height?: number +} + +/** + * Custom Modal Component with two buttons at the bottom. + * + */ +export const ModalWithButtons: FC = props => { + const { backgroundSecondary, homeBackground, modalBarrier, isLight } = + usePalette() + + const leftButtonTitle = props.leftButtonTitle ?? "Annulla" + + const rightButtonTitle = props.rightButtonTitle ?? "OK" + + return ( + //TODO: animationType fade or slide? + + + + + {props.title && ( + + {props.title} + + )} + + {props.children} + + + + + + + + + ) +} + +const styles = StyleSheet.create({ + pageWrapper: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + contentWrapper: { + flexDirection: "column", + justifyContent: "space-between", + width: 320, + borderRadius: 12, + marginHorizontal: 15, + + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 3, + }, + shadowOpacity: 0.27, + shadowRadius: 4.65, + elevation: 6, + }, + title: { + fontSize: 32, + fontWeight: "900", + }, +}) diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index 6514f49d..b66dfdf6 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -1,5 +1,5 @@ import React, { FC } from "react" -import { Pressable, View, StyleSheet } from "react-native" +import { Pressable, View, StyleSheet, DeviceEventEmitter } from "react-native" import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" import { useSafeAreaInsets } from "react-native-safe-area-context" @@ -8,44 +8,46 @@ import { useNavigation } from "navigation/NavigationTypes" import { usePalette } from "utils/colors" import { NavbarIcon, navbarIcons } from "assets/navbar" +export const CLOSE_BOTTOM_SHEET_EVENT_NAME = "close-bottom-sheet" + export interface NavbarProps { + /** + * render the Back Button + * @default true + */ + back?: boolean + /** + * render the Home Button + * @default true + */ + home?: boolean + /** + * An array with custom buttons that will be rendered to the right of the navbar + */ + customButtons?: { /** - * render the Back Button - * @default true - */ - back?: boolean - /** - * render the Home Button - * @default true - */ - home?: boolean - /** - * An array with custom buttons that will be rendered to the right of the navbar + * the name of the icon from the {@link navbarIconList} */ - customButtons?: { - /** - * the name of the icon from the {@link navbarIconList} - */ - icon: NavbarIcon - /** - * callbeck when the button is pressed - */ - onPress: () => void - }[] + icon: NavbarIcon /** - * whether or not to render the navbar as elevated with a shadow and rounded corners - * @default true + * callbeck when the button is pressed */ - elevated?: boolean + onPress: () => void + }[] + /** + * whether or not to render the navbar as elevated with a shadow and rounded corners + * @default true + */ + elevated?: boolean - /** - * overrides the default navigation "goBack" behavior - */ - overrideBackBehavior?: () => void - /** - * overrides the default navigation "navigate" to home behavior - */ - overrideHomeBehavior?: () => void + /** + * overrides the default navigation "goBack" behavior + */ + overrideBackBehavior?: () => void + /** + * overrides the default navigation "navigate" to home behavior + */ + overrideHomeBehavior?: () => void } /** @@ -53,183 +55,182 @@ export interface NavbarProps { * download button */ export const NavBar: FC = props => { - const insets = useSafeAreaInsets() - const navigation = useNavigation() - const { buttonFill, background } = usePalette() + const insets = useSafeAreaInsets() + const navigation = useNavigation() + const { buttonFill, background } = usePalette() - const back = props.back ?? true - const home = props.home ?? true + const back = props.back ?? true + const home = props.home ?? true - const homeSVG = useSVG(navbarIcons.home.svg) - const backSVG = useSVG(navbarIcons.back.svg) + const homeSVG = useSVG(navbarIcons.home.svg) + const backSVG = useSVG(navbarIcons.back.svg) - const elevated = props.elevated ?? true + const elevated = props.elevated ?? true - return ( - + {back && ( + navigation.goBack())} + style={[ + styles.button, + { + backgroundColor: buttonFill, + width: 103, + marginRight: 19, + }, + ]} > - {back && ( - navigation.goBack()) - } - style={[ - styles.button, - { - backgroundColor: buttonFill, - width: 103, - marginRight: 19, - }, - ]} - > - - {backSVG && ( - - )} - - - Back - - + + {backSVG && ( + )} + + + Back + + + )} - {home && ( - navigation.navigate("Home")) - } - style={[styles.button, { backgroundColor: buttonFill }]} - > - - {homeSVG && ( - - )} - - + {home && ( + { + navigation.navigate("Home") + // Emit the event to close the news bottom sheet + DeviceEventEmitter.emit(CLOSE_BOTTOM_SHEET_EVENT_NAME) + }) + } + style={[styles.button, { backgroundColor: buttonFill }]} + > + + {homeSVG && ( + )} + + + )} - - {!!props.customButtons && - props.customButtons.map(({ icon, onPress }, i) => { - const iconSVG = navbarIcons[icon] - const svg = useSVG(iconSVG.svg) - return ( - - - {svg && ( - - )} - - - ) - })} - - - ) + + {!!props.customButtons && + props.customButtons.map(({ icon, onPress }, i) => { + const iconSVG = navbarIcons[icon] + const svg = useSVG(iconSVG.svg) + return ( + + + {svg && ( + + )} + + + ) + })} + + + ) } const styles = StyleSheet.create({ - wrapper: { - position: "absolute", - width: "100%", - bottom: 0, - zIndex: 3, - paddingHorizontal: 25, - paddingVertical: 30, + wrapper: { + position: "absolute", + width: "100%", + bottom: 0, + zIndex: 3, + paddingHorizontal: 25, + paddingVertical: 30, - flexDirection: "row", - justifyContent: "flex-start", - alignItems: "center", - }, - wrapperElevated: { - borderTopLeftRadius: 30, - borderTopRightRadius: 30, + flexDirection: "row", + justifyContent: "flex-start", + alignItems: "center", + }, + wrapperElevated: { + borderTopLeftRadius: 30, + borderTopRightRadius: 30, - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 0, - }, - shadowOpacity: 0.2, - shadowRadius: 8.3, - elevation: 13, - }, - button: { - height: 32, - minWidth: 33, - borderRadius: 16, - flexDirection: "row", - justifyContent: "center", - alignItems: "center", + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 0, }, + shadowOpacity: 0.2, + shadowRadius: 8.3, + elevation: 13, + }, + button: { + height: 32, + minWidth: 33, + borderRadius: 16, + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + }, }) diff --git a/src/components/Page.tsx b/src/components/Page.tsx deleted file mode 100644 index 2a37b78d..00000000 --- a/src/components/Page.tsx +++ /dev/null @@ -1,237 +0,0 @@ -import React, { FC, useEffect, useRef } from "react" -import { - RefreshControl, - ScrollView, - View, - Animated, - Switch, -} from "react-native" - -import { Title, Subtitle } from "components/Text" -import { NavBar, NavbarProps } from "components/NavBar" -import { usePalette } from "utils/colors" - -/** - * General component useful for pages with a scrollable content. - * It provides a navbar and a scrollview with margin and rounded corners. - */ -export const Page: FC<{ - /** - * Title in the sticky header. - */ - title?: string - /** - * Subtitle in the sticky header. - */ - subtitle?: string - /** - * Remove the navbar from the bottom of the page. - */ - hideNavbar?: boolean - /** - * Props for the navbar, see {@link NavBar} - */ - navbarOptions?: NavbarProps - /** - * Element to be put behind the page, the main content will scroll on top of it. - */ - backdropElement?: React.ReactNode - /** - * Margin above the title to leave space for the backdrop element. - */ - scrollOffset?: number - /** - * Whether or not to show a toggle switch on the right of the title. - * - * If true, provide also these 2 properties: - * - switchValue: boolean - * - onSwitchToggle: (value: booelan) => void - * - * @default false - */ - showSwitch?: boolean - /** - * Used to draw the switch in the correct state (on / off). - */ - switchValue?: boolean - /** - * Function to change the state of the switch (on / off). - */ - onSwitchToggle?: (value: boolean) => void - /** - * props for the refresh control - */ - refreshControl?: { - onRefresh: () => void - refreshing: boolean - } - children: React.ReactNode -}> = props => { - const { background, homeBackground, palette } = usePalette() - const [isPastTitle, setIsPastTitle] = React.useState(false) - const shadowAnim = useRef(new Animated.Value(0)).current - - const navbar = !props.hideNavbar - - const showHeader = props.title !== undefined - - const showSwitch = props.showSwitch ?? false - - useEffect(() => { - // hook called when the shadown needs to be animated - const duration = 100 - if (isPastTitle) - Animated.timing(shadowAnim, { - toValue: 1, - duration, - useNativeDriver: true, - }).start() - else - Animated.timing(shadowAnim, { - toValue: 0, - duration, - useNativeDriver: true, - }).start() - }, [isPastTitle, shadowAnim]) - - return ( - - {props.backdropElement && ( - // element at the top of the screen, above the page - - {props.backdropElement} - - )} - - - ) : undefined - } - stickyHeaderIndices={props.title ? [0] : undefined} // first child is the sticky header - scrollEventThrottle={100} - onScroll={ - showHeader - ? e => { - const scollThreshold = - 20 + (props.scrollOffset || 0) - if ( - e.nativeEvent.contentOffset.y >= - scollThreshold && - !isPastTitle - ) - setIsPastTitle(true) - else if ( - e.nativeEvent.contentOffset.y < - scollThreshold && - isPastTitle - ) - setIsPastTitle(false) - } - : undefined - } - > - {showHeader && ( - // Sticky page header with the title and subtitle - - - - {props.title} - {props.subtitle && ( - {props.subtitle} - )} - - - {showSwitch && ( - // Toggle switch on the right of the title - - props.onSwitchToggle && - props.onSwitchToggle(value) - } - trackColor={{ - false: homeBackground, // TODO: ask the design team which is the correct color - true: palette.accent, - }} - thumbColor={background} - style={{ - position: "absolute", - alignSelf: "center", - right: 8, - transform: [ - { scaleX: 1.5 }, - { scaleY: 1.5 }, - ], - }} - /> - )} - - - )} - - {props.children} - - - - {navbar ? : null} - - ) -} diff --git a/src/components/ScrollPage.tsx b/src/components/ScrollPage.tsx index fa9790be..1791aa0c 100644 --- a/src/components/ScrollPage.tsx +++ b/src/components/ScrollPage.tsx @@ -11,144 +11,144 @@ import { LinearGradient } from "expo-linear-gradient" * It provides a navbar and a scrollview with margin and rounded corners. */ export const ScrollPage: FC<{ - /** - * Title in the sticky header. - */ - title?: string - /** - * Subtitle in the sticky header. - */ - subtitle?: string - /** - * Remove the navbar from the bottom of the page. - */ - hideNavbar?: boolean - /** - * Props for the navbar, see {@link NavBar} - */ - navbarOptions?: NavbarProps - /** - * Element to be put behind the page, the main content will scroll on top of it. - */ - backdropElement?: React.ReactNode - /** - * props for the refresh control - */ - refreshControl?: { - onRefresh: () => void - refreshing: boolean - } - children: React.ReactNode + /** + * Title in the sticky header. + */ + title?: string + /** + * Subtitle in the sticky header. + */ + subtitle?: string + /** + * Remove the navbar from the bottom of the page. + */ + hideNavbar?: boolean + /** + * Props for the navbar, see {@link NavBar} + */ + navbarOptions?: NavbarProps + /** + * Element to be put behind the page, the main content will scroll on top of it. + */ + backdropElement?: React.ReactNode + /** + * props for the refresh control + */ + refreshControl?: { + onRefresh: () => void + refreshing: boolean + } + children: React.ReactNode }> = props => { - const { background, isLight, primary } = usePalette() + const { background, isLight, primary } = usePalette() - const navbar = !props.hideNavbar + const navbar = !props.hideNavbar - const { height, width } = Dimensions.get("window") + const { height, width } = Dimensions.get("window") - const { articleTitle, articleSubtitle } = usePalette() - return ( + const { articleTitle, articleSubtitle } = usePalette() + return ( + + {isLight && ( + + )} + {!isLight && ( + )} + {props.backdropElement && ( + - {isLight && ( - - )} - {!isLight && ( - - )} - {props.backdropElement && ( - - {props.backdropElement} - - )} + {props.backdropElement} + + )} - + + ) : undefined + } + > + + + {props.title} + + {props.subtitle && ( + - - ) : undefined - } - > - - - {props.title} - - {props.subtitle && ( - - {props.subtitle} - - )} - + > + {props.subtitle} + + )} + - {props.children} - - + {props.children} + + - {navbar ? : null} - - ) + {navbar ? : null} + + ) } diff --git a/src/components/ScrollPageInfinite.tsx b/src/components/ScrollPageInfinite.tsx new file mode 100644 index 00000000..48e8f22d --- /dev/null +++ b/src/components/ScrollPageInfinite.tsx @@ -0,0 +1,258 @@ +import React from "react" +import { + RefreshControl, + FlatList, + View, + Animated, + ActivityIndicator, + StyleSheet, +} from "react-native" +import { Switch } from "react-native-switch" + +import { Title, Subtitle } from "components/Text" +import { NavBar, NavbarProps } from "components/NavBar" +import { usePalette } from "utils/colors" + +interface PageProps { + /** + * Title in the sticky header. + */ + title?: string + /** + * Subtitle in the sticky header. + */ + subtitle?: string + /** + * Remove the navbar from the bottom of the page. + */ + hideNavbar?: boolean + /** + * Props for the navbar, see {@link NavBar} + */ + navbarOptions?: NavbarProps + /** + * Element to be put behind the page, the main content will scroll on top of it. + */ + backdropElement?: React.ReactNode + /** + * Margin above the title to leave space for the backdrop element. + */ + scrollOffset?: number + /** + * Whether or not to show a toggle switch on the right of the title. + * + * If true, provide also the `switchControl` prop + * + * @default false + */ + showSwitch?: boolean + /** + * Props for the toggle switch + */ + switchControl?: { + /** State of the switch */ + toggled: boolean + /** Function fired when the state of the switch changes */ + onToggle: (value: boolean) => void + } + /** + * props for the refresh control + */ + refreshControl?: { + onRefresh: () => void + refreshing: boolean + } + /** + * props to control the fetch of new data + */ + fetchControl?: { + onFetch: () => void + /** @default false */ + fetching?: boolean + } + /** + * List of items displayed in the flat list + */ + items: T[] + /** + * Function used to render an item on the screen + * + * @param item + * @return ReactElement created with the data of the item + */ + render: (item: T, idx: number) => React.ReactElement + /** + * Number from 0 to 1 to control the `onEndReachedThreshold` prop of the flat list + * + * @default 0.05 + */ + endThreshold?: number + + children?: React.ReactNode +} + +/** + * General component useful for pages with an infinte scrollable content. + * It provides a navbar and a flatlist with margin and rounded corners. + */ +export const ScrollPageInfinite = (props: PageProps): JSX.Element => { + const { + background, + backgroundSecondary, + homeBackground, + primary, + palette, + isLight, + } = usePalette() + + const navbar = !props.hideNavbar + + const showHeader = props.title !== undefined + + const showSwitch = props.showSwitch ?? false + + const fetching = props.fetchControl?.fetching ?? false + + const endThreshold = props.endThreshold ?? 0.05 + + return ( + + {props.backdropElement && ( + // element at the top of the screen, above the page + + {props.backdropElement} + + )} + + props.render(item, index)} + onEndReached={props.fetchControl?.onFetch} + onEndReachedThreshold={endThreshold} + refreshControl={ + props.refreshControl ? ( + + ) : undefined + } + stickyHeaderIndices={props.title ? [0] : undefined} // first child is the sticky header + ListHeaderComponent={ + showHeader ? ( + // Sticky page header with the title, subtitle and toggle switch + + + + {props.title} + {props.subtitle && {props.subtitle}} + + {showSwitch && ( + // Toggle switch + + { + props.switchControl?.onToggle(value) + }} + changeValueImmediately={true} + renderActiveText={false} + renderInActiveText={false} + barHeight={27} + switchWidthMultiplier={3} + circleSize={18} + circleActiveColor={backgroundSecondary} + circleInActiveColor={palette.accent} + circleBorderWidth={0} + innerCircleStyle={{ + borderWidth: 1, + borderColor: !props.switchControl?.toggled + ? palette.accent + : isLight + ? "#EBEBEB" + : "#3A4257", + }} + backgroundActive={palette.accent} + backgroundInactive={"#FFF"} + containerStyle={{ + borderWidth: 1, + borderColor: palette.accent, + }} + switchLeftPx={1.5} + switchRightPx={1.3} + /> + + )} + + + ) : undefined + } + ListFooterComponent={ + + } + contentContainerStyle={{ + backgroundColor: background, + paddingTop: !showHeader ? 30 : 0, + paddingBottom: 120, + }} + /> + + {navbar ? : null} + + ) +} + +const styles = StyleSheet.create({ + header: { + zIndex: 1000, + paddingHorizontal: 28, + paddingVertical: 22, + marginBottom: 16, + borderTopLeftRadius: 30, + borderTopRightRadius: 30, + shadowOffset: { + width: 0, + height: 3, + }, + shadowRadius: 4.65, + }, + backdrop: { + zIndex: -1, + height: 200, + width: "100%", + position: "absolute", + justifyContent: "center", + alignItems: "center", + }, + switch: { + position: "absolute", + alignSelf: "center", + right: 5, + }, +}) diff --git a/src/components/Settings/ButtonCustom.tsx b/src/components/Settings/ButtonCustom.tsx deleted file mode 100644 index 51ff3606..00000000 --- a/src/components/Settings/ButtonCustom.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React, { FC } from "react" -import { Pressable, StyleSheet, ViewStyle } from "react-native" -import { Text } from "components/Text" -import { usePalette } from "utils/colors" - -export interface ButtonCustomProps { - text?: string - /** - * when true the button is "light type" in Light Mode and "dark type" - * in Dark Mode - */ - light?: boolean - onPress?: () => void - style?: ViewStyle -} -/** - * Custom button component. Specify param `light` to select button type - * - */ -export const ButtonCustom: FC = props => { - const { palette, isLight } = usePalette() - return ( - - {props.text} - - ) -} - -const styles = StyleSheet.create({ - button: { - height: 32, - minWidth: 33, - borderRadius: 16, - flexDirection: "row", - justifyContent: "center", - alignItems: "center", - }, -}) diff --git a/src/components/Settings/CareerColumn.tsx b/src/components/Settings/CareerColumn.tsx index 09615b62..7389f801 100644 --- a/src/components/Settings/CareerColumn.tsx +++ b/src/components/Settings/CareerColumn.tsx @@ -5,36 +5,36 @@ import { usePalette } from "utils/colors" import { Career } from "api/user" export interface CareerColumnProps { - career: Career + career: Career } /** * Column with two texts with proper formatting for showing a career * */ export const CareerColumn: FC = props => { - const { isLight } = usePalette() - return ( - - - Matricola {props.career.matricola} - - - {props.career.type} - - - ) + const { isLight } = usePalette() + return ( + + + Matricola {props.career.matricola} + + + {props.career.type} + + + ) } diff --git a/src/components/Settings/CareerTile.tsx b/src/components/Settings/CareerTile.tsx index 161c9ee6..0a0bf2be 100644 --- a/src/components/Settings/CareerTile.tsx +++ b/src/components/Settings/CareerTile.tsx @@ -1,13 +1,13 @@ import React, { FC } from "react" import { View } from "react-native" import { TouchableRipple } from "components/TouchableRipple" -import { ButtonCustom } from "./ButtonCustom" +import { ButtonCustom } from "components/Button" import { CareerColumn } from "./CareerColumn" import { Career } from "api/user" export interface CareerTileProps { - career: Career - onPress: () => void + career: Career + onPress: () => void } /** @@ -15,26 +15,26 @@ export interface CareerTileProps { * and a button to open a modal. */ export const CareerTile: FC = props => { - return ( - - - - - - - ) + return ( + + + + + + + ) } diff --git a/src/components/Settings/Description.tsx b/src/components/Settings/Description.tsx new file mode 100644 index 00000000..4dbfb23c --- /dev/null +++ b/src/components/Settings/Description.tsx @@ -0,0 +1,26 @@ +import React from "react" +import { BodyText } from "components/Text" +import { usePalette } from "utils/colors" + +interface DescriptionProps { + last?: boolean + children: React.ReactNode +} + +export const Description: React.FC = props => { + const { articleSubtitle, palette, isLight } = usePalette() + return ( + + {props.children} + + ) +} diff --git a/src/components/Settings/ModalPicker.tsx b/src/components/Settings/ModalPicker.tsx new file mode 100644 index 00000000..4781d8cd --- /dev/null +++ b/src/components/Settings/ModalPicker.tsx @@ -0,0 +1,110 @@ +import React, { useEffect } from "react" +import { ModalCustom } from "components/Modal" +import { Picker } from "@react-native-picker/picker" +import { ButtonCustom } from "components/Button" +import { View } from "react-native" +import { usePalette } from "utils/colors" + +interface ModalPickerProps { + /** + * whether ot not to show the modal + * @default false + * */ + isShowing: boolean + /** + * this function hides the modal by changing the state in the parent component + * */ + onClose: () => void + /** + * whether ot not to center title and subtitle and apply different margins + * @default false + * */ + centerText?: boolean + /** + * title of the modal + * */ + title: string + /** + * subtitle of the modal + * */ + subTitle?: string + /** + * Picker elements + * */ + elements: { value: T; label: string }[] + /** + * default value of the picker + * */ + selectedValue?: T + /** + * function called when an element is selected + * */ + onSelect: (value: T) => void +} + +/** + * Modal with a Picker component + * */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export function ModalPicker(props: ModalPickerProps) { + const { bodyText, palette } = usePalette() + const [selectedValue, setSelectedValue] = React.useState( + props.selectedValue ?? props.elements[0].value + ) + + useEffect(() => { + setSelectedValue(props.selectedValue ?? props.elements[0].value) + }, [props.selectedValue, props.elements]) + + return ( + + setSelectedValue(itemValue)} + > + {props.elements.map((element, idx) => ( + + ))} + + + props.onClose()} + /> + props.onSelect(selectedValue)} + /> + + + ) +} diff --git a/src/components/Settings/ModalSelection.tsx b/src/components/Settings/ModalSelection.tsx deleted file mode 100644 index e7b609cb..00000000 --- a/src/components/Settings/ModalSelection.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import React, { FC } from "react" -import { View, Modal, StyleSheet, Pressable } from "react-native" -import { Text } from "components/Text" -import { usePalette } from "utils/colors" -import { ButtonCustom } from "./ButtonCustom" - -export interface ModalSelectionProps { - /** - * content of the modal - */ - children: React.ReactNode - - title: string - - /** - * whether ot not to show the modal - */ - isShowing: boolean - /** - * this function hides the modal by changing the state in the parent component - */ - onClose: () => void - - /** - * function called when button "OK" is pressed - */ - onOK: () => void - - /** - * modal wrapper height, specify if height is fixed - */ - height?: number -} - -// ? maybe should move this out of Settings folder ? - -/** - * Custom Modal Component with two buttons at the bottom. - * - */ -export const ModalSelection: FC = props => { - const { backgroundSecondary, homeBackground, modalBarrier, isLight } = - usePalette() - - return ( - //TODO: animationType fade or slide? - - - - - - {props.title} - - - {props.children} - - - - - - - - - ) -} - -const styles = StyleSheet.create({ - pageWrapper: { - flex: 1, - justifyContent: "center", - alignItems: "center", - }, - contentWrapper: { - flexDirection: "column", - justifyContent: "space-between", - width: 320, - borderRadius: 12, - marginHorizontal: 15, - - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 3, - }, - shadowOpacity: 0.27, - shadowRadius: 4.65, - elevation: 6, - }, - title: { - fontSize: 32, - fontWeight: "900", - }, -}) diff --git a/src/components/Settings/RadioButtonCustom.tsx b/src/components/Settings/RadioButtonCustom.tsx index b8f809ef..7a2acd9d 100644 --- a/src/components/Settings/RadioButtonCustom.tsx +++ b/src/components/Settings/RadioButtonCustom.tsx @@ -3,26 +3,26 @@ import { View, Animated, StyleSheet } from "react-native" import { usePalette } from "utils/colors" export interface RadioButtonCustomProps { - /** - * if is checked - */ - status: boolean - /** - * override outer circle diameter - */ - outerDiameter?: number - /** - * override inner circle diameter height - */ - innerDiameter?: number - /** - * ovveride default color on LightMode - */ - lightColor?: string - /** - * ovveride default color on DarkMode - */ - darkColor?: string + /** + * if is checked + */ + status: boolean + /** + * override outer circle diameter + */ + outerDiameter?: number + /** + * override inner circle diameter height + */ + innerDiameter?: number + /** + * ovveride default color on LightMode + */ + lightColor?: string + /** + * ovveride default color on DarkMode + */ + darkColor?: string } /** * Custom RadioButton @@ -30,87 +30,87 @@ export interface RadioButtonCustomProps { * made following this: https://github.com/callstack/react-native-paper/blob/main/src/components/RadioButton/RadioButtonAndroid.tsx */ export const RadioButtonCustom: FC = props => { - const width = props.outerDiameter ?? 24 - const height = props.outerDiameter ?? 24 - const fillHeight = props.innerDiameter ?? 12 - const fillWidth = props.innerDiameter ?? 12 - const status = props.status + const width = props.outerDiameter ?? 24 + const height = props.outerDiameter ?? 24 + const fillHeight = props.innerDiameter ?? 12 + const fillWidth = props.innerDiameter ?? 12 + const status = props.status - const { isLight } = usePalette() + const { isLight } = usePalette() - const { current: borderAnim } = React.useRef( - new Animated.Value(2) - ) + const { current: borderAnim } = React.useRef( + new Animated.Value(2) + ) - const { current: radioAnim } = React.useRef( - new Animated.Value(1) - ) + const { current: radioAnim } = React.useRef( + new Animated.Value(1) + ) - const isFirstRendering = React.useRef(true) + const isFirstRendering = React.useRef(true) - const scale = 1.5 + const scale = 1.5 - React.useEffect(() => { - // Do not run animation on very first rendering - if (isFirstRendering.current) { - isFirstRendering.current = false - return - } + React.useEffect(() => { + // Do not run animation on very first rendering + if (isFirstRendering.current) { + isFirstRendering.current = false + return + } - if (status === true) { - radioAnim.setValue(1.2) + if (status === true) { + radioAnim.setValue(1.2) - Animated.timing(radioAnim, { - toValue: 1, - duration: 150 * scale, - useNativeDriver: true, - }).start() - } else { - borderAnim.setValue(10) + Animated.timing(radioAnim, { + toValue: 1, + duration: 150 * scale, + useNativeDriver: true, + }).start() + } else { + borderAnim.setValue(10) - Animated.timing(borderAnim, { - toValue: 2, - duration: 150 * scale, - useNativeDriver: false, - }).start() - } - }, [status, borderAnim, radioAnim, scale]) + Animated.timing(borderAnim, { + toValue: 2, + duration: 150 * scale, + useNativeDriver: false, + }).start() + } + }, [status, borderAnim, radioAnim, scale]) - return ( - + {status == true ? ( + + - {status == true ? ( - - - - ) : null} - - ) + /> + + ) : null} + + ) } const styles = StyleSheet.create({ - radioContainer: { - alignItems: "center", - justifyContent: "center", - }, + radioContainer: { + alignItems: "center", + justifyContent: "center", + }, }) diff --git a/src/components/Settings/SelectTile.tsx b/src/components/Settings/SelectTile.tsx index 4326cd74..a56a9d4d 100644 --- a/src/components/Settings/SelectTile.tsx +++ b/src/components/Settings/SelectTile.tsx @@ -5,62 +5,59 @@ import { usePalette } from "utils/colors" import { RadioButtonCustom } from "./RadioButtonCustom" export interface SelectTileProps { - /** - * if selected - */ - selected: boolean - /** - * function called when a choice is selected - */ - onPress: () => void - /** - * label shown on screen besides radio button if children is undefined - */ - value?: string - /** - *flexstyle of row wrapper - */ - flexStyle?: FlexStyle["justifyContent"] + /** + * if selected + */ + selected: boolean + /** + * function called when a choice is selected + */ + onPress: () => void + /** + * label shown on screen besides radio button if children is undefined + */ + value?: string + /** + *flexstyle of row wrapper + */ + flexStyle?: FlexStyle["justifyContent"] - children?: React.ReactNode + children?: React.ReactNode } /** * A tile designed for a radio button group. */ export const SelectTile: FC = props => { - const { isLight, palette } = usePalette() - return ( - + + {props.children ?? ( + + - - {props.children ?? ( - - - {props.value} - - - )} - - ) + > + {props.value} + + + )} + + ) } diff --git a/src/components/Settings/SettingTile.tsx b/src/components/Settings/SettingTile.tsx index 535631d1..a0c26dc7 100644 --- a/src/components/Settings/SettingTile.tsx +++ b/src/components/Settings/SettingTile.tsx @@ -1,109 +1,123 @@ import React, { FC, useMemo } from "react" -import { View } from "react-native" +import { ActivityIndicator, View } from "react-native" import { TouchableRipple } from "../TouchableRipple" import { - BlendMode, - Canvas, - Group, - ImageSVG, - Skia, - useSVG, + BlendMode, + Canvas, + Group, + ImageSVG, + Skia, + useSVG, } from "@shopify/react-native-skia" -import { Text } from "components/Text" +import { BodyText, Text } from "components/Text" import { Divider } from "components/Divider" import { usePalette } from "utils/colors" -import { SettingOptions } from "utils/settings" +import { IconProps } from "assets/settings" + +/** + * interface representing a setting's UI fields + */ +export interface SettingOptions { + title: string + subtitle?: string + icon?: IconProps + callback?: () => void + loading?: boolean +} export interface SettingTileProps { - setting: SettingOptions + setting: SettingOptions } export const SettingTile: FC = props => { - const icon = props.setting.icon ?? null - const iconSvg = useSVG(icon?.svg) - const { isLight, palette } = usePalette() + const icon = props.setting.icon ?? null + const iconSvg = useSVG(icon?.svg) + const { articleSubtitle } = usePalette() - //changing icon color - //from: https://github.com/Shopify/react-native-skia/issues/462 - const paint = useMemo(() => Skia.Paint(), []) - paint.setColorFilter( - Skia.ColorFilter.MakeBlend( - Skia.Color(isLight ? palette.primary : palette.lighter), - BlendMode.SrcIn - ) - ) + //changing icon color + //from: https://github.com/Shopify/react-native-skia/issues/462 + const paint = useMemo(() => Skia.Paint(), []) + paint.setColorFilter( + Skia.ColorFilter.MakeBlend(Skia.Color(articleSubtitle), BlendMode.SrcIn) + ) - return ( - - {props.setting.title === "Disconnetti" && } - + {props.setting.title === "Disconnetti" && } + {props.setting.loading ? ( + + + + ) : null} + { + if (!props.setting.loading) props.setting.callback?.() + }} + isRoundedTopCorners={false} + > + + {iconSvg && icon && ( + - - {iconSvg && icon && ( - - - - - - - - )} + + + + + + + )} - - - {props.setting.title} - - {props.setting.subtitle && ( - - {props.setting.subtitle} - - )} - - - + + {props.setting.title} + {props.setting.subtitle && ( + + {props.setting.subtitle} + + )} + - ) + + + ) } diff --git a/src/components/Settings/UserAnonymousTile.tsx b/src/components/Settings/UserAnonymousTile.tsx index 170a07d1..cad31abf 100644 --- a/src/components/Settings/UserAnonymousTile.tsx +++ b/src/components/Settings/UserAnonymousTile.tsx @@ -1,75 +1,69 @@ import React, { FC } from "react" import { View, Image } from "react-native" import { TouchableRipple } from "components/TouchableRipple" +import { ButtonCustom } from "components/Button" import { Text } from "components/Text" import { usePalette } from "utils/colors" -import { ButtonCustom } from "./ButtonCustom" export interface UserAnonymousTileProps { - onLogin?: () => void - showRipple?: boolean + onLogin?: () => void + showRipple?: boolean } export const UserAnonymousTile: FC = props => { - const { isLight } = usePalette() - return ( - - + + + + + + + - - - - - - - Utente Anonimo - - - - - - + Utente Anonimo + + + - - ) + + + + + ) } diff --git a/src/components/Settings/UserDetailsTile.tsx b/src/components/Settings/UserDetailsTile.tsx index ff37cbe6..6c0b1b07 100644 --- a/src/components/Settings/UserDetailsTile.tsx +++ b/src/components/Settings/UserDetailsTile.tsx @@ -6,66 +6,66 @@ import { usePalette } from "utils/colors" import { User } from "api/user" export interface UserDetailsTileProps { - user: User - onPress?: () => void + user: User + onPress?: () => void } export const UserDetailsTile: FC = ({ - user, - onPress, + user, + onPress, }) => { - const { isLight } = usePalette() - return ( - - - - - + + + + - - - - {user.firstname} {user.lastname} - - - Codice persona {user.codPersona} - - - - - - ) + borderRadius: 40, + }} + /> + + + + {user.firstname} {user.lastname} + + + Codice persona {user.codPersona} + + + + + + ) } diff --git a/src/components/Settings/index.ts b/src/components/Settings/index.ts index b1393746..5630fb53 100644 --- a/src/components/Settings/index.ts +++ b/src/components/Settings/index.ts @@ -1,10 +1,8 @@ export * from "./RadioButtonCustom" -export * from "./ButtonCustom" export * from "./CareerTile" export * from "./SettingTile" export * from "./SelectTile" export * from "../ContentWrapperScroll" export * from "./UserDetailsTile" -export * from "./ModalSelection" export * from "./UserAnonymousTile" export * from "./CareerColumn" diff --git a/src/components/Text/BodyText.tsx b/src/components/Text/BodyText.tsx index 402248c6..5fe518a7 100644 --- a/src/components/Text/BodyText.tsx +++ b/src/components/Text/BodyText.tsx @@ -8,34 +8,34 @@ import { usePalette } from "utils/colors" * Used in large bodies of text */ export const BodyText: FC = props => { - const { bodyText } = usePalette() - let { style } = props - style = StyleSheet.flatten(style) // so that we can override the fontWeight + const { bodyText } = usePalette() + let { style } = props + style = StyleSheet.flatten(style) // so that we can override the fontWeight - const fontWeight = (style && style.fontWeight) || "normal" + const fontWeight = (style && style.fontWeight) || "normal" - return ( - <_Text - {...props} - style={[ - { - fontFamily: - fontWeight === "900" - ? "Roboto_900Black" - : fontWeight === "bold" || - fontWeight === "700" || - fontWeight === "600" - ? "Roboto_700Bold" - : fontWeight === "300" - ? "Roboto_300Light" - : "Roboto_400Regular", - fontSize: 16, - color: bodyText, - }, - style, - ]} - > - {props.children} - - ) + return ( + <_Text + {...props} + style={[ + { + fontFamily: + fontWeight === "900" + ? "Roboto_900Black" + : fontWeight === "bold" || + fontWeight === "700" || + fontWeight === "600" + ? "Roboto_700Bold" + : fontWeight === "300" + ? "Roboto_300Light" + : "Roboto_400Regular", + fontSize: 16, + color: bodyText, + }, + style, + ]} + > + {props.children} + + ) } diff --git a/src/components/Text/CardTitle.tsx b/src/components/Text/CardTitle.tsx index a9bc2590..ad653acc 100644 --- a/src/components/Text/CardTitle.tsx +++ b/src/components/Text/CardTitle.tsx @@ -8,21 +8,21 @@ import { usePalette } from "utils/colors" * custom font, default size and automatic color. */ export const CardTitle: FC = props => { - const { cardTitle } = usePalette() - const { style, children } = props - return ( - <_Text - {...props} - style={[ - { - fontFamily: "Roboto_900Black", - fontSize: 16, - color: cardTitle, - }, - style, - ]} - > - {children} - - ) + const { cardTitle } = usePalette() + const { style, children } = props + return ( + <_Text + {...props} + style={[ + { + fontFamily: "Roboto_900Black", + fontSize: 16, + color: cardTitle, + }, + style, + ]} + > + {children} + + ) } diff --git a/src/components/Text/HyperLink.tsx b/src/components/Text/HyperLink.tsx new file mode 100644 index 00000000..3ee5fc34 --- /dev/null +++ b/src/components/Text/HyperLink.tsx @@ -0,0 +1,28 @@ +import React, { FC } from "react" +import { Text, Linking, TextProps } from "react-native" +import { usePalette } from "utils/colors" + +type HyperLinkProps = { + onPress?: () => void + href?: string + children: React.ReactNode +} & TextProps + +export const HyperLink: FC = props => { + const { palette } = usePalette() + return ( + { + const canOpen = await Linking.canOpenURL(props.href ?? "") + if (canOpen) await Linking.openURL(props.href ?? "") + }) + } + > + {props.children} + + ) +} diff --git a/src/components/Text/Subtitle.tsx b/src/components/Text/Subtitle.tsx index e5da5267..1cdee10f 100644 --- a/src/components/Text/Subtitle.tsx +++ b/src/components/Text/Subtitle.tsx @@ -7,22 +7,22 @@ import { usePalette } from "utils/colors" * Subtitle used for the Page component, custom font, default size and automatic color. */ export const Subtitle: FC = props => { - const { primary } = usePalette() - const { style, children } = props - return ( - <_Text - {...props} - style={[ - { - fontFamily: "Roboto_500Medium_Italic", - fontSize: 24, - color: primary, - marginTop: -8, - }, - style, - ]} - > - {children} - - ) + const { primary } = usePalette() + const { style, children } = props + return ( + <_Text + {...props} + style={[ + { + fontFamily: "Roboto_500Medium_Italic", + fontSize: 24, + color: primary, + marginTop: -8, + }, + style, + ]} + > + {children} + + ) } diff --git a/src/components/Text/Text.tsx b/src/components/Text/Text.tsx index 45e2d346..44a79897 100644 --- a/src/components/Text/Text.tsx +++ b/src/components/Text/Text.tsx @@ -10,23 +10,23 @@ import { BodyText } from "./BodyText" * Used in text fields all over the app. */ export const Text: FC = props => { - const { buttonText } = usePalette() - let { style } = props - style = StyleSheet.flatten(style) // so that we can override the fontWeight + const { buttonText } = usePalette() + let { style } = props + style = StyleSheet.flatten(style) // so that we can override the fontWeight - return ( - - {props.children} - - ) + return ( + + {props.children} + + ) } diff --git a/src/components/Text/Title.tsx b/src/components/Text/Title.tsx index 7e5155c3..e31d1421 100644 --- a/src/components/Text/Title.tsx +++ b/src/components/Text/Title.tsx @@ -7,21 +7,21 @@ import { usePalette } from "utils/colors" * Title used for the Page component, custom font, default size and automatic color. */ export const Title: FC = props => { - const { primary } = usePalette() - const { style, children } = props - return ( - <_Text - {...props} - style={[ - { - fontFamily: "Roboto_900Black", - fontSize: 42, - color: primary, - }, - style, - ]} - > - {children} - - ) + const { primary } = usePalette() + const { style, children } = props + return ( + <_Text + {...props} + style={[ + { + fontFamily: "Roboto_900Black", + fontSize: 42, + color: primary, + }, + style, + ]} + > + {children} + + ) } diff --git a/src/components/Text/index.tsx b/src/components/Text/index.tsx index 729d8933..99bfbd28 100644 --- a/src/components/Text/index.tsx +++ b/src/components/Text/index.tsx @@ -3,3 +3,4 @@ export * from "./Subtitle" export * from "./Title" export * from "./CardTitle" export * from "./Text" +export * from "./HyperLink" diff --git a/src/components/TouchableRipple.tsx b/src/components/TouchableRipple.tsx index fbd2d4c9..e0c9e9b0 100644 --- a/src/components/TouchableRipple.tsx +++ b/src/components/TouchableRipple.tsx @@ -3,38 +3,38 @@ import { View, Pressable } from "react-native" import { usePalette } from "utils/colors" export interface TouchableRippleProps { - onClick?: () => void - children: React.ReactNode - isRoundedTopCorners?: boolean - showRipple?: boolean + onClick?: () => void + children: React.ReactNode + isRoundedTopCorners?: boolean + showRipple?: boolean } export const TouchableRipple: FC = props => { - const { isLight } = usePalette() - return ( - - - {props.children} - - - ) + const { isLight } = usePalette() + return ( + + + {props.children} + + + ) } diff --git a/src/components/Tray/TrayButton.tsx b/src/components/Tray/TrayButton.tsx index bd59103d..7b7d8ded 100644 --- a/src/components/Tray/TrayButton.tsx +++ b/src/components/Tray/TrayButton.tsx @@ -6,39 +6,39 @@ import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia" import { TrayIcon, trayIcons } from "assets/tray" export const TrayButton: FC<{ - onClick: () => void - label: TrayIcon + onClick: () => void + label: TrayIcon }> = props => { - // const { palette } = usePalette() - // const color = palette.lighter + // const { palette } = usePalette() + // const color = palette.lighter - const icon = trayIcons[props.label] - const iconSVG = useSVG(icon.svg) + const icon = trayIcons[props.label] + const iconSVG = useSVG(icon.svg) - return ( - - - {iconSVG && ( - - )} - - - ) + return ( + + + {iconSVG && ( + + )} + + + ) } diff --git a/src/components/Tray/index.tsx b/src/components/Tray/index.tsx index 68c317e4..d2cbf6a9 100644 --- a/src/components/Tray/index.tsx +++ b/src/components/Tray/index.tsx @@ -7,27 +7,27 @@ import { TrayButton } from "./TrayButton" * download, notifications and settings buttons. */ export const Tray: FC<{ - onDownloads: () => void - onNotifications: () => void - onSettings: () => void + onDownloads: () => void + onNotifications: () => void + onSettings: () => void }> = props => { - return ( - - props.onDownloads()} /> - props.onNotifications()} - /> - props.onSettings()} /> - - ) + return ( + + props.onDownloads()} /> + props.onNotifications()} + /> + props.onSettings()} /> + + ) } diff --git a/src/contexts/login.ts b/src/contexts/login.ts new file mode 100644 index 00000000..466c7cfb --- /dev/null +++ b/src/contexts/login.ts @@ -0,0 +1,44 @@ +import { User } from "api/user" +import React from "react" + +/* eslint-disable @typescript-eslint/naming-convention */ + +export interface PoliNetworkToken { + access_token: string + expires_in: number + ext_expires_in: number + id_token: string + refresh_token: string + scope: string + token_type: string +} + +export interface PolimiToken { + accessToken: string + expiresIn: number + refreshToken: string +} + +export interface Tokens { + polimiToken: PolimiToken + poliNetworkToken: PoliNetworkToken +} + +export type LoginState = + | { + loggedIn: false + userInfo?: undefined + } + | { + loggedIn: true + userInfo: User + } + +export type ILoginContext = LoginState & { + setLoginState(newState: LoginState): void +} + +export const LoginContext = React.createContext({ + loggedIn: false, + setLoginState: () => null, +}) diff --git a/src/contexts/newsPreferences.ts b/src/contexts/newsPreferences.ts new file mode 100644 index 00000000..d802a589 --- /dev/null +++ b/src/contexts/newsPreferences.ts @@ -0,0 +1,32 @@ +import React from "react" +import { Tag } from "api/articles" + +export type TagWithData = Tag & { + /** The column in which the card has to be inserted */ + column: "left" | "right" + /** The height of the card */ + height: number + /** Whether the tag is favourite or not */ + favourite: boolean +} + +export enum Preference { + NONE, + FAVOURITE, + UNFAVOURITE, +} + +export interface NewsPreferences { + preferences: Record +} + +export type NewsPreferencesContextProps = NewsPreferences & { + setArticlesPreferences: (newPreferences: NewsPreferences) => void +} + +// eslint-disable-next-line @typescript-eslint/naming-convention +export const NewsPreferencesContext = + React.createContext({ + preferences: {}, + setArticlesPreferences: () => null, + }) diff --git a/src/utils/settings.ts b/src/contexts/settings.ts similarity index 50% rename from src/utils/settings.ts rename to src/contexts/settings.ts index 81d5f536..9ff40815 100644 --- a/src/utils/settings.ts +++ b/src/contexts/settings.ts @@ -1,31 +1,21 @@ import React from "react" import { ColorSchemeName } from "react-native" -import { IconProps } from "assets/settings" -/** - * interface representing a setting's UI fields - */ -export interface SettingOptions { - title: string - subtitle?: string - icon?: IconProps - callback?: () => void -} /** * `"predefined"` or `"light"` or `"dark"` */ export type ValidColorSchemeName = NonNullable | "predefined" export interface Settings { - theme: ValidColorSchemeName + theme: ValidColorSchemeName } export interface SettingsContextProps { - settings: Settings - setSettings: React.Dispatch> + settings: Settings + setSettings: React.Dispatch> } // eslint-disable-next-line @typescript-eslint/naming-convention export const SettingsContext = React.createContext({ - settings: { theme: "predefined" }, - setSettings: () => null, + settings: { theme: "predefined" }, + setSettings: () => null, }) diff --git a/src/navigation/MainStackNavigator.tsx b/src/navigation/MainStackNavigator.tsx index 91b854b7..90a0fa67 100644 --- a/src/navigation/MainStackNavigator.tsx +++ b/src/navigation/MainStackNavigator.tsx @@ -7,8 +7,8 @@ import React, { FC } from "react" import { createStackNavigator } from "@react-navigation/stack" import { MainStackNavigatorParams } from "navigation/NavigationTypes" import { Home } from "pages/Home" -import { Article } from "pages/ArticleDetails" -import { NewsList } from "pages/NewsList" +import { Article } from "pages/news/ArticleDetails" +import { ArticlesList } from "pages/news/ArticlesList" import { Error404 } from "pages/Error404" import { FreeClassrooms } from "pages/FreeClass/FreeClassrooms" import { CampusChoice } from "pages/FreeClass/CampusChoice" @@ -16,43 +16,40 @@ import { PositionChoice } from "pages/FreeClass/PositionChoice" import { BuildingChoice } from "pages/FreeClass/BuildingChoice" import { ClassChoice } from "pages/FreeClass/ClassChoice" import { RoomDetails } from "pages/FreeClass/RoomDetails" +import { OtherCategories } from "pages/news/OtherCategories" import { Groups } from "pages/Groups" // eslint-disable-next-line @typescript-eslint/naming-convention const MainStackNavigator = createStackNavigator() export const MainStack: FC = () => { - return ( - - - - - - - - - - - - - - ) + return ( + + + + + + + + + + + + + + + + ) } diff --git a/src/navigation/NavigationTypes.ts b/src/navigation/NavigationTypes.ts index e4165f7c..69f59fae 100644 --- a/src/navigation/NavigationTypes.ts +++ b/src/navigation/NavigationTypes.ts @@ -17,15 +17,16 @@ import { StackScreenProps } from "@react-navigation/stack" import { FC } from "react" import { - CompositeScreenProps, - NavigationProp, - useNavigation as nativeUseNav, + CompositeScreenProps, + NavigationProp, + useNavigation as nativeUseNav, } from "@react-navigation/native" import { Article } from "api/articles" import { CampusItem } from "pages/FreeClass/CampusChoice" import { NavigatorScreenParams } from "@react-navigation/native" import { BuildingItem } from "pages/FreeClass/BuildingChoice" -import { RoomDetails } from "api/Room" +import { RoomDetails } from "api/rooms" +import { TagWithData } from "contexts/newsPreferences" /** * interface containing the info about the params for each page of the stack navigator @@ -36,34 +37,36 @@ import { RoomDetails } from "api/Room" * More info: https://reactnavigation.org/docs/typescript/ */ export type RootStackNavigatorParams = { - /* eslint-disable @typescript-eslint/naming-convention */ - MainNav: NavigatorScreenParams - SettingsNav: NavigatorScreenParams - Login: undefined + /* eslint-disable @typescript-eslint/naming-convention */ + MainNav: NavigatorScreenParams + SettingsNav: NavigatorScreenParams + Login: undefined } export type MainStackNavigatorParams = { - Home: undefined - Article: { article: Article } - NewsList: { categoryName: string } - Error404: undefined - FreeClassrooms: undefined - CampusChoice: { currentDate: string } - PositionChoice: undefined - RoomDetails: { room: RoomDetails; startDate: string; roomId: number } - BuildingChoice: { campus: CampusItem; currentDate: string } - ClassChoice: { building: BuildingItem; currentDate: string } - Groups: undefined + Home: undefined + Article: { article: Article } + ArticlesList: { tagName: string } + OtherCategories: { tags: TagWithData[] } + Error404: undefined + FreeClassrooms: undefined + CampusChoice: { currentDate: string } + PositionChoice: undefined + BuildingChoice: { campus: CampusItem; currentDate: string } + RoomDetails: { room: RoomDetails; startDate: string; roomId: number } + ClassChoice: { building: BuildingItem; currentDate: string } + Groups: undefined } export type SettingsStackNavigatorParams = { - Settings: undefined - Help: undefined + Settings: undefined + Help: undefined + Privacy: undefined } export type GlobalStackNavigatorParams = RootStackNavigatorParams & - MainStackNavigatorParams & - SettingsStackNavigatorParams + MainStackNavigatorParams & + SettingsStackNavigatorParams // Here are a couple of magic types, straight from the Underground Realm of Weird Types: @@ -85,19 +88,19 @@ export type GlobalStackNavigatorParams = RootStackNavigatorParams & * ``` */ export type RootStackProps = - StackScreenProps + StackScreenProps export type MainStackProps = - CompositeScreenProps< - StackScreenProps, - StackScreenProps - > + CompositeScreenProps< + StackScreenProps, + StackScreenProps + > export type SettingsStackProps = - CompositeScreenProps< - StackScreenProps, - StackScreenProps - > + CompositeScreenProps< + StackScreenProps, + StackScreenProps + > /** * Type for a component of the root stack navigator, use in place of `FC`. @@ -119,18 +122,18 @@ export type SettingsStackProps = * ``` */ export type RootStackScreen< - T extends keyof RootStackNavigatorParams, - P = Record + T extends keyof RootStackNavigatorParams, + P = Record > = FC & P> export type MainStackScreen< - T extends keyof MainStackNavigatorParams, - P = Record + T extends keyof MainStackNavigatorParams, + P = Record > = FC & P> export type SettingsStackScreen< - T extends keyof SettingsStackNavigatorParams, - P = Record + T extends keyof SettingsStackNavigatorParams, + P = Record > = FC & P> /** * Hook to access the navigation prop of the parent screen anywhere. @@ -163,4 +166,4 @@ export type SettingsStackScreen< * */ export const useNavigation = () => - nativeUseNav>() + nativeUseNav>() diff --git a/src/navigation/RootStackNavigator.tsx b/src/navigation/RootStackNavigator.tsx index 6c03ce04..2b90fba2 100644 --- a/src/navigation/RootStackNavigator.tsx +++ b/src/navigation/RootStackNavigator.tsx @@ -15,17 +15,14 @@ import { Login } from "pages/Login" const RootStackNavigator = createStackNavigator() export const RootStack: FC = () => { - return ( - - - - - - ) + return ( + + + + + + ) } diff --git a/src/navigation/SettingsNavigator.tsx b/src/navigation/SettingsNavigator.tsx index d5fe71a0..f5082f33 100644 --- a/src/navigation/SettingsNavigator.tsx +++ b/src/navigation/SettingsNavigator.tsx @@ -8,20 +8,18 @@ import { createStackNavigator } from "@react-navigation/stack" import { SettingsStackNavigatorParams } from "navigation/NavigationTypes" import { SettingsPage } from "pages/settings/Settings" import { Help } from "pages/settings/Help" +import { Privacy } from "pages/settings/Privacy" + // eslint-disable-next-line @typescript-eslint/naming-convention const SettingsStackNavigator = - createStackNavigator() + createStackNavigator() export const SettingsStack: FC = () => { - return ( - - - - - ) + return ( + + + + + + ) } diff --git a/src/pages/ArticleDetails.tsx b/src/pages/ArticleDetails.tsx deleted file mode 100644 index ea9555ab..00000000 --- a/src/pages/ArticleDetails.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* eslint-disable prettier/prettier */ -import React, { useState } from "react" -import { MainStackScreen } from "navigation/NavigationTypes" -import { Image, Linking } from "react-native" -import { WebView } from "react-native-webview" -import { ScrollPage } from "components/ScrollPage" -import { usePalette } from "utils/colors" -import { Asset } from "expo-asset" -import { Roboto_400Regular } from "@expo-google-fonts/roboto" - -export const Article: MainStackScreen<"Article"> = props => { - const { isLight } = usePalette() - const article = props.route.params.article - const [webHeight, setWebHeight] = useState(400) - let html: string[] = [] - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - html = JSON.parse(article.content) - } catch (error) { - console.log(error) - } - - return ( - - ) : undefined - } - > - { - setWebHeight(parseInt(event.nativeEvent.data)) - }} - /* Questo dovrebbe reindirizzare i link nel browser, - stoppando la webview. Apparentemente non funziona col mio - android, nel senso che non viene chiamata (comunque - il problema dei reindirizzamenti dentro la webview non - si presenta sul mio telefono). Da testare su iOS e altri - Android. */ - onShouldStartLoadWithRequest={event => { - if (event.url.slice(0, 4) === "http") { - void Linking.openURL(event.url) - return false - } - - return true - }} - javaScriptEnabled={true} - injectedJavaScript={webViewScript} - containerStyle={{ height: webHeight, marginBottom: 120 }} - showsVerticalScrollIndicator={false} - setBuiltInZoomControls={false} - nestedScrollEnabled={false} - scrollEnabled={false} - androidHardwareAccelerationDisabled={true} - originWhitelist={["*"]} - source={{ - html: ` -
${html - .map(el => `

${el}

`) - .join("")} -

`, - baseUrl: "", - }} - /> - - ) -} - -//used to set automatic heigth in webview -//solution found here: https://stackoverflow.com/questions/35446209/react-native-webview-height -//the alternative was to install a whole package -const webViewScript = ` - setTimeout(function() { - window.ReactNativeWebView.postMessage(document.documentElement.scrollHeight); - }, 500); - true; // note: this is required, or you'll sometimes get silent failures -` diff --git a/src/pages/Error404.tsx b/src/pages/Error404.tsx index f9094134..81814c49 100644 --- a/src/pages/Error404.tsx +++ b/src/pages/Error404.tsx @@ -9,87 +9,87 @@ import { useNavigation } from "navigation/NavigationTypes" import { NavBar } from "components/NavBar" export const Error404: MainStackScreen<"Error404"> = () => { - const { background, homeBackground, isLight } = usePalette() - const deleteSvg = useSVG(icon.svg) - const navigation = useNavigation() - return ( - + + { + navigation.goBack() + }} + > + - + - { - navigation.goBack() - }} - > - - - {deleteSvg && ( - - )} - - - - - Error 404 - - - - Page not implemented yet - - - - - - ) + {deleteSvg && ( + + )} + + + + + Error 404 + + + + Page not implemented yet + + + + + + ) } diff --git a/src/pages/FreeClass/BuildingChoice.tsx b/src/pages/FreeClass/BuildingChoice.tsx index f774bb9d..bd202fe4 100644 --- a/src/pages/FreeClass/BuildingChoice.tsx +++ b/src/pages/FreeClass/BuildingChoice.tsx @@ -3,232 +3,194 @@ import React, { useState, useEffect } from "react" import { View, FlatList, Pressable } from "react-native" import { usePalette } from "utils/colors" import { Title, BodyText } from "components/Text" -import { NavBar } from "components/NavBar" import { DateTimePicker } from "components/FreeClass/DateTimePicker/DateTimePicker" import { api, RetryType } from "api" import { CampusItem } from "./CampusChoice" +import { PageWrapper } from "components/Groups/PageWrapper" +import { RoomSimplified } from "api/rooms" export interface BuildingItem { - campus: CampusItem - name: string - freeRoomList: number[] + campus: CampusItem + name: string + latitude?: number + longitude?: number + freeRoomList: RoomSimplified[] } /** * In this page the user can select the building. */ export const BuildingChoice: MainStackScreen<"BuildingChoice"> = props => { - const { palette, background, homeBackground } = usePalette() - const { navigate } = useNavigation() + const { palette } = usePalette() + const { navigate } = useNavigation() - const { campus, currentDate } = props.route.params + const { campus, currentDate } = props.route.params - const [buildingList, setBuildingList] = useState() + const [buildingList, setBuildingList] = useState() - //non-ISO format for simplicity (local timezone) and - // compatibility with `handleConfirm` function - const [date, setDate] = useState( - new Date(currentDate) !== new Date() - ? new Date(currentDate) - : new Date() - ) + //non-ISO format for simplicity (local timezone) and + // compatibility with `handleConfirm` function + const [date, setDate] = useState( + new Date(currentDate) !== new Date() ? new Date(currentDate) : new Date() + ) - function addHours(dateStart: Date, hours: number) { - const tempDate = new Date(dateStart.getTime()) - tempDate.setHours(tempDate.getHours() + hours) - return tempDate - } + function addHours(dateStart: Date, hours: number) { + const tempDate = new Date(dateStart.getTime()) + tempDate.setHours(tempDate.getHours() + hours) + return tempDate + } - //the dateEnd is the startDate + 8 hours, the number of hours has not been chosen yet - const dateEnd = addHours(date, 8).toISOString() //8 hours is an example + //the dateEnd is the startDate + 3 hours, the number of hours has not been chosen yet + const dateEnd = addHours(date, 3).toISOString() //3 hours is an example - //main function that handles the call to the API in order to obtain the list of freeclassRooms - const findRoomsAvailable = async () => { - try { - const response = await api.rooms.getFreeRoomsTimeRange( - campus.acronym, - date.toISOString(), - dateEnd, + //main function that handles the call to the API in order to obtain the list of freeclassRooms + const findRoomsAvailable = async () => { + try { + const response = await api.rooms.getFreeRoomsTimeRange( + campus.acronym, + date.toISOString(), + dateEnd, + { maxRetries: 1, retryType: RetryType.RETRY_N_TIMES } + ) + if (response.length > 0) { + const tempBuildingStrings: string[] = [] + const tempBuildings: BuildingItem[] = [] + response.map(room => { + const currentBuildingString = room.building.replace( + "Edificio ", + "Ed. " + ) + if (!tempBuildingStrings.includes(currentBuildingString)) { + const currentBuilding: BuildingItem = { + campus: campus, + name: room.building.replace("Edificio ", "Ed. "), + freeRoomList: [ { - maxRetries: 2, - retryType: RetryType.RETRY_N_TIMES, - } - ) - if (response.length > 0) { - const tempBuildingStrings: string[] = [] - const tempBuildings: BuildingItem[] = [] - response.map(room => { - const currentBuildingString = room.building.replace( - "Edificio ", - "Ed. " - ) - if (!tempBuildingStrings.includes(currentBuildingString)) { - const currentBuilding: BuildingItem = { - campus: campus, - name: room.building.replace("Edificio ", "Ed. "), - freeRoomList: [room.room_id], - } - tempBuildingStrings.push(currentBuildingString) - tempBuildings.push(currentBuilding) - } else { - //element already present in the list - const indexElement = tempBuildingStrings.indexOf( - currentBuildingString - ) - tempBuildings[indexElement].freeRoomList.push( - room.room_id - ) - } - }) - setBuildingList(tempBuildings) + roomId: room.room_id, + name: room.name, + }, + ], } - } catch (error) { - console.log(error) - } + tempBuildingStrings.push(currentBuildingString) + tempBuildings.push(currentBuilding) + } else { + //element already present in the list + const indexElement = tempBuildingStrings.indexOf( + currentBuildingString + ) + tempBuildings[indexElement].freeRoomList.push({ + roomId: room.room_id, + name: room.name, + }) + } + }) + setBuildingList(tempBuildings) + } + } catch (error) { + console.log(error) } + } - useEffect(() => { - void findRoomsAvailable() - }, [date]) + useEffect(() => { + void findRoomsAvailable() + }, [date]) - useEffect(() => { - setDate(new Date(currentDate)) - }, [props.route.params.currentDate]) + useEffect(() => { + setDate(new Date(currentDate)) + }, [props.route.params.currentDate]) - //custom goBack function, in order to maintain the currentDate. - const goBack = () => { - props.navigation.navigate("CampusChoice", { - currentDate: date.toString(), - }) - } + //custom goBack function, in order to maintain the currentDate. + const goBack = () => { + props.navigation.navigate("CampusChoice", { + currentDate: date.toString(), + }) + } - return ( - goBack() }}> + + {campus.name.length > 1 ? ( + + > + {campus.name[0]} + <Title>{" " + campus.name[1]} + + ) : ( + {campus.name} + )} + setDate(date)} /> + + index.toString()} + renderItem={({ item }) => ( + + navigate("ClassChoice", { + building: item, + currentDate: date.toString(), + }) + } + > + - + {item.name.split(" ")[0]} + - - {campus.name.length > 1 ? ( - - {campus.name[0]} - <Title - style={{ fontSize: 40, fontWeight: "900" }} - > - {" " + campus.name[1]} - - - ) : ( - - {campus.name} - - )} - - setDate(date)} - /> - - index.toString()} - renderItem={({ item }) => ( - - navigate("ClassChoice", { - building: item, - currentDate: date.toString(), - }) - } - > - - - {item.name} - - - - )} - /> - - + {" " + item.name.split(" ")[1]} + + - - - ) + + )} + /> + + ) } diff --git a/src/pages/FreeClass/CampusChoice.tsx b/src/pages/FreeClass/CampusChoice.tsx index 30bd358f..d88cc057 100644 --- a/src/pages/FreeClass/CampusChoice.tsx +++ b/src/pages/FreeClass/CampusChoice.tsx @@ -2,194 +2,144 @@ import React, { useEffect, useState } from "react" import { MainStackScreen, useNavigation } from "navigation/NavigationTypes" import { View, Pressable, FlatList } from "react-native" import { usePalette } from "utils/colors" -import { NavBar } from "components/NavBar" import { Title, BodyText } from "components/Text" import { DateTimePicker } from "components/FreeClass/DateTimePicker/DateTimePicker" +import { PageWrapper } from "components/Groups/PageWrapper" export interface CampusItem { - name: string[] - acronym: string - latitude: number - longitude: number + name: string[] + acronym: string + latitude: number + longitude: number } export const campusList: CampusItem[] = [ - { - name: ["Bovisa", "Durando"], - acronym: "MIB", - latitude: 45.504422059752116, - longitude: 9.164129368703703, - }, - { - name: ["Bovisa", "La Masa"], - acronym: "MIB", - latitude: 45.50286551603009, - longitude: 9.156452626872522, - }, - { - name: ["Leonardo"], - acronym: "MIA", - latitude: 45.47791263153159, - longitude: 9.227122387828846, - }, - { - name: ["Colombo"], - acronym: "MIA", - latitude: 45.47190973697382, - longitude: 9.227048868730659, - }, - { - name: ["Mancinelli"], - acronym: "MIA", - latitude: 45.49016811534536, - longitude: 9.227177297538793, - }, + { + name: ["Bovisa", "Durando"], + acronym: "MIB", + latitude: 45.504422059752116, + longitude: 9.164129368703703, + }, + { + name: ["Bovisa", "La Masa"], + acronym: "MIB", + latitude: 45.50286551603009, + longitude: 9.156452626872522, + }, + { + name: ["Leonardo"], + acronym: "MIA", + latitude: 45.47791263153159, + longitude: 9.227122387828846, + }, + { + name: ["Colombo"], + acronym: "MIA", + latitude: 45.47190973697382, + longitude: 9.227048868730659, + }, + { + name: ["Mancinelli"], + acronym: "MIA", + latitude: 45.49016811534536, + longitude: 9.227177297538793, + }, ] /** * In this page the user can select the campus. */ export const CampusChoice: MainStackScreen<"CampusChoice"> = props => { - const { navigate } = useNavigation() - const { homeBackground, background, palette } = usePalette() + const { navigate } = useNavigation() + const { palette } = usePalette() - const { currentDate } = props.route.params + const { currentDate } = props.route.params - //non-ISO format for simplicity (local timezone) and - // compatibility with `handleConfirm` function - const [date, setDate] = useState( - new Date(currentDate) !== new Date() - ? new Date(currentDate) - : new Date() - ) + //non-ISO format for simplicity (local timezone) and + // compatibility with `handleConfirm` function + const [date, setDate] = useState( + new Date(currentDate) !== new Date() ? new Date(currentDate) : new Date() + ) - useEffect(() => { - setDate(new Date(currentDate)) - }, [props.route.params.currentDate]) + useEffect(() => { + setDate(new Date(currentDate)) + }, [props.route.params.currentDate]) - return ( - + + Campus + setDate(date)} /> + + index.toString()} + renderItem={({ item }) => ( + + onPress={() => + navigate("BuildingChoice", { + campus: item, + currentDate: date.toISOString(), + }) + } + > + 1 ? "300" : "900", + color: "white", + fontSize: 20, + textAlign: "center", }} - > - + {item.name[0]} + + {item.name.length > 1 ? ( + - - Campus - - setDate(date)} - /> - - index.toString()} - renderItem={({ item }) => ( - - navigate("BuildingChoice", { - campus: item, - currentDate: date.toISOString(), - }) - } - > - - 1 - ? "300" - : "900", - color: "white", - fontSize: 20, - textAlign: "center", - }} - > - {item.name[0]} - - {item.name.length > 1 ? ( - - {item.name[1]} - - ) : undefined} - - - )} - /> - - + {item.name[1]} + + ) : undefined} - - - ) + + )} + /> + + ) } diff --git a/src/pages/FreeClass/ClassChoice.tsx b/src/pages/FreeClass/ClassChoice.tsx index 7a461c6e..57169c7b 100644 --- a/src/pages/FreeClass/ClassChoice.tsx +++ b/src/pages/FreeClass/ClassChoice.tsx @@ -1,93 +1,55 @@ import { MainStackScreen } from "navigation/NavigationTypes" import React, { useState, useEffect } from "react" import { View } from "react-native" -import { usePalette } from "utils/colors" import { Title } from "components/Text" -import { NavBar } from "components/NavBar" import { FreeClassList } from "components/FreeClass/FreeClassList" import { DateTimePicker } from "components/FreeClass/DateTimePicker/DateTimePicker" +import { PageWrapper } from "components/Groups/PageWrapper" /** * In this page the user can select finally the free class he wants. */ export const ClassChoice: MainStackScreen<"ClassChoice"> = props => { - const { background, homeBackground } = usePalette() - - const { building, currentDate } = props.route.params - - //non-ISO format for simplicity (local timezone) and - // compatibility with `handleConfirm` function - const [date, setDate] = useState( - new Date(currentDate) !== new Date() - ? new Date(currentDate) - : new Date() - ) - - useEffect(() => { - setDate(new Date(currentDate)) - }, [props.route.params.currentDate]) - - //custom goBack function in order to maintain the currenyDate - const goBack = () => { - props.navigation.navigate("BuildingChoice", { - campus: building.campus, - currentDate: date.toString(), - }) - } - - return ( - ( + new Date(currentDate) !== new Date() ? new Date(currentDate) : new Date() + ) + + useEffect(() => { + setDate(new Date(currentDate)) + }, [props.route.params.currentDate]) + + //custom goBack function in order to maintain the currenyDate + const goBack = () => { + props.navigation.navigate("BuildingChoice", { + campus: building.campus, + currentDate: date.toString(), + }) + } + + const buildingName: string[] = building.name.split(" ") // ex. buildingName = ["Ed.","B2"] + + return ( + goBack() }}> + + - <View - style={{ - flex: 1, - marginTop: 106, - }} - > - <View - style={{ - paddingBottom: 400, - backgroundColor: background, - borderTopLeftRadius: 30, - borderTopRightRadius: 30, - - shadowColor: "#000", - shadowOffset: { - width: 0, - height: 7, - }, - shadowOpacity: 0.43, - shadowRadius: 9.51, - - elevation: 15, - }} - > - <View - //view containing the title - style={{ - paddingHorizontal: 28, - marginTop: 28, - }} - > - <Title style={{ fontSize: 40, fontWeight: "900" }}> - {building.name.replace("Ed. ","Edificio ")} - - - setDate(date)} - /> - - - - - - - - ) + {buildingName[0].replace("Ed.", "Edificio")} + {" " + buildingName[1]} + + setDate(date)} /> + + + + + + ) } diff --git a/src/pages/FreeClass/FreeClassrooms.tsx b/src/pages/FreeClass/FreeClassrooms.tsx index 979aef54..a3c6e83b 100644 --- a/src/pages/FreeClass/FreeClassrooms.tsx +++ b/src/pages/FreeClass/FreeClassrooms.tsx @@ -1,9 +1,8 @@ import React, { useState } from "react" import { MainStackScreen, useNavigation } from "navigation/NavigationTypes" -import { View, Dimensions, Pressable, Alert, Platform } from "react-native" +import { View, Dimensions, Pressable, Alert } from "react-native" import { PoliSearchBar } from "components/Home" import { usePalette } from "utils/colors" -import { NavBar } from "components/NavBar" import { Title, BodyText } from "components/Text" import { FlatList } from "react-native-gesture-handler" import { Canvas, useSVG, ImageSVG } from "@shopify/react-native-skia" @@ -11,220 +10,158 @@ import campusIcon from "assets/freeClassrooms/campus.svg" import position1Icon from "assets/freeClassrooms/position1.svg" import position2Icon from "assets/freeClassrooms/position2.svg" import * as Location from "expo-location" +import { PageWrapper } from "components/Groups/PageWrapper" const { width } = Dimensions.get("window") enum SearchClassType { - GPS_POSITION, - CAMPUS, + GPS_POSITION, + CAMPUS, } interface FreeClassInterface { - id: number - type: SearchClassType - text: string[] + id: number + type: SearchClassType + text: string[] } const freeClassButtons: FreeClassInterface[] = [ - { - id: 0, - type: SearchClassType.GPS_POSITION, - text: ["In base alla tua", "posizione"], - }, - { id: 1, type: SearchClassType.CAMPUS, text: ["Scegli il tuo", "campus"] }, + { + id: 0, + type: SearchClassType.GPS_POSITION, + text: ["In base alla tua", "posizione"], + }, + { id: 1, type: SearchClassType.CAMPUS, text: ["Scegli il tuo", "campus"] }, ] /** * This is the first page where the user select the modality to find a free room. - * There are two options: + * There are two options: * - Selection by campus * - Selection by current user position->this feature is available ONLY IF the GPS is enabled. */ export const FreeClassrooms: MainStackScreen<"FreeClassrooms"> = () => { - const [search, setSearch] = useState("") - const { navigate } = useNavigation() - const { homeBackground, background, palette } = usePalette() + const [search, setSearch] = useState("") + const { navigate } = useNavigation() + const { palette } = usePalette() - const campusSVG = useSVG(campusIcon) - const position1SVG = useSVG(position1Icon) - const position2SVG = useSVG(position2Icon) + const campusSVG = useSVG(campusIcon) + const position1SVG = useSVG(position1Icon) + const position2SVG = useSVG(position2Icon) - const [geolocation, setGeoloaction] = useState(false) + const [geolocation, setGeoloaction] = useState(false) - const handlePositionPressed = async () => { - if (geolocation) { //if the geolocation is active, the user can proceed - navigate("PositionChoice") - } else { - const { status } = - await Location.requestForegroundPermissionsAsync() - if (status !== "granted") { - Alert.alert( - "Location Service not enabled", - "Please enable your location services to unlock this feature", - [{ text: "OK" }], - { cancelable: false } - ) - } else { - setGeoloaction(true) - navigate("PositionChoice") - } - } + const handlePositionPressed = async () => { + if (geolocation) { + //if the geolocation is active, the user can proceed + navigate("PositionChoice") + } else { + const { status } = await Location.requestForegroundPermissionsAsync() + if (status !== "granted") { + Alert.alert( + "Location Service not enabled", + "Please enable your location services to unlock this feature", + [{ text: "OK" }], + { cancelable: false } + ) + } else { + setGeoloaction(true) + navigate("PositionChoice") + } } + } - return ( - + + Aule Libere + setSearch(searchKey)} /> + + index.toString()} + renderItem={({ item }) => ( + - + navigate("CampusChoice", { + currentDate: new Date().toString(), + }) + : () => handlePositionPressed() + } + > + + {item.type === SearchClassType.GPS_POSITION && position1SVG && ( + + )} + {item.type === SearchClassType.GPS_POSITION && position2SVG && ( + + )} + {item.type === SearchClassType.CAMPUS && campusSVG && ( + + )} + + + {item.text[0]}{" "} + - - - Aule Libere - - setSearch(searchKey)} - /> - index.toString()} - renderItem={({ item }) => ( - - navigate("CampusChoice", { - currentDate: new Date().toString(), - }) - : () => handlePositionPressed() - } - > - - {item.type === - SearchClassType.GPS_POSITION && - position1SVG && ( - - )} - {item.type === - SearchClassType.GPS_POSITION && - position2SVG && ( - - )} - {item.type === SearchClassType.CAMPUS && - campusSVG && ( - - )} - - - {item.text[0]}{" "} - - {item.text[1]} - - - - )} - /> - - - - - ) + > + {item.text[1]} + + + + )} + /> + + ) } diff --git a/src/pages/FreeClass/PositionChoice.tsx b/src/pages/FreeClass/PositionChoice.tsx index 2a268a12..e528b869 100644 --- a/src/pages/FreeClass/PositionChoice.tsx +++ b/src/pages/FreeClass/PositionChoice.tsx @@ -1,12 +1,7 @@ import React, { useState, useEffect } from "react" import { MainStackScreen } from "navigation/NavigationTypes" -import { Pressable, View } from "react-native" -import { usePalette } from "utils/colors" -import { NavBar } from "components/NavBar" -import { BodyText, Title } from "components/Text" -import { PoliSearchBar } from "components/Home" -import { FreeClassList } from "components/FreeClass/FreeClassList" -import { Map } from "components/FreeClass/Map" +import { Platform, View } from "react-native" +import { Title } from "components/Text" import * as Location from "expo-location" import { LocationGeocodedAddress, PermissionStatus } from "expo-location" import { AddressText } from "components/FreeClass/AddressText" @@ -14,276 +9,200 @@ import { CampusItem, campusList } from "./CampusChoice" import { getDistance } from "geolib" import { api } from "api" import { BuildingItem } from "./BuildingChoice" - -enum ButtonType { - MAP, - LIST, -} - +import { PageWrapper } from "components/Groups/PageWrapper" +import { PositionModality } from "components/FreeClass/PositionModality" +import { addHours } from "api/rooms" +import BuildingListJSON from "components/FreeClass/buildingCoords.json" + +/** + * In this page the user can find a room according to his current position. + */ export const PositionChoice: MainStackScreen<"PositionChoice"> = () => { - const [search, setSearch] = useState("") - const { homeBackground, background, primary, isDark, palette } = - usePalette() - - const [status, setStatus] = useState(ButtonType.MAP) + const [locationStatus, setLocationStatus] = useState( + PermissionStatus.GRANTED + ) - const [locationStatus, setLocationStatus] = useState( - PermissionStatus.GRANTED - ) + const [currentLocation, setCurrentLocation] = + useState() - const [currentLocation, setCurrentLocation] = - useState() + const [currentCoords, setCurrentCoords] = useState([]) - const [currentCoords, setCurrentCoords] = useState([]) + const [buildingList, setBuildingList] = useState() - const [currentCampus, setCurrentCampus] = useState([]) + //the dateEnd is the startDate + 3 hours, the number of hours has not been chosen yet + const dateEnd = addHours(new Date(), 3).toISOString() //3 hours is an example - const [roomList, setRoomList] = useState([]) - - function addHours(dateStart: Date, hours: number) { - const tempDate = new Date(dateStart.getTime()) - tempDate.setHours(tempDate.getHours() + hours) - return tempDate + const compareCampusNames = (c1: string[], c2: string[]) => { + if (c1.length === c2.length) { + if (c1.length > 1) { + if (c1[0] === c2[0] && c1[1] === c2[1]) { + return true + } else { + return false + } + } else { + if (c1[0] === c2[0]) { + return true + } else { + return false + } + } + } else { + return false } - - //the dateEnd is the startDate + 8 hours, the number of hours has not been chosen yet - const dateEnd = addHours(new Date(), 8).toISOString() //8 hours is an example - - //main function that handles the call to the API in order to obtain the list of freeclassRooms - const findRoomsAvailable = async (campus: CampusItem) => { - try { - const response = await api.rooms.getFreeRoomsTimeRange( - campus.acronym, - new Date().toISOString(), - dateEnd, - ) - if (response.length > 0) { - const tempBuildingStrings: string[] = [] - const tempRoomList: number[] = [] - const tempBuildings: BuildingItem[] = [] - response.map(room => { - const currentBuildingString = room.building.replace( - "Edificio ", - "Ed. " - ) - if (!tempBuildingStrings.includes(currentBuildingString)) { - const currentBuilding: BuildingItem = { - campus: campus, - name: room.building.replace("Edificio ", "Ed. "), - freeRoomList: [room.room_id], - } - tempBuildingStrings.push(currentBuildingString) - tempBuildings.push(currentBuilding) - } else { - //element already present in the list - const indexElement = tempBuildingStrings.indexOf( - currentBuildingString - ) - tempBuildings[indexElement].freeRoomList.push( - room.room_id - ) - } - tempRoomList.push(room.room_id) - }) - setRoomList(tempRoomList) + } + + const getBuildingCoords = (campus: CampusItem, buildingName: string) => { + for (const element of BuildingListJSON) { + if (element.acronym === campus.acronym) { + for (const c of element.campus) { + if (compareCampusNames(c.name, campus.name)) { + for (const b of c.buildings) { + if (b.name === buildingName) { + return b.coords + } } - } catch (error) { - console.log(error) + } } + } } - - async function getPosition() { - const { status } = await Location.requestForegroundPermissionsAsync() - if (status !== "granted") { - setLocationStatus(PermissionStatus.UNDETERMINED) - setCurrentLocation(undefined) - } else { - const { coords } = await Location.getCurrentPositionAsync({}) - const { latitude, longitude } = coords - const response = await Location.reverseGeocodeAsync({ - latitude, - longitude, - }) - //temporary solution, too inefficient - campusList.map(campus => { - if ( - getDistance( - { latitude: latitude, longitude: longitude }, - { - latitude: campus.latitude, - longitude: campus.longitude, - } - ) <= 50000 //if the distance between the user and the campus is less than 500m I'll call the API - ) { - //call the API - void findRoomsAvailable(campus) - setCurrentCampus([campus.latitude, campus.longitude]) + } + + //main function that handles the call to the API in order to obtain the list of freeclassRooms + const findRoomsAvailable = async ( + campusList: CampusItem[], + currentLat: number, + currentLong: number + ) => { + const tempBuildingList: BuildingItem[] = [] + for (const campus of campusList) { + if ( + getDistance( + { latitude: currentLat, longitude: currentLong }, + { + latitude: campus.latitude, + longitude: campus.longitude, + } + ) <= 50000 //if the distance between the user and the campus is less than 500m I'll call the API,to test I put 50km + ) { + //call the API + try { + const response = await api.rooms.getFreeRoomsTimeRange( + campus.acronym, + new Date().toISOString(), + dateEnd + ) + if (response.length > 0) { + const tempBuildingStrings: string[] = [] + const tempBuildings: BuildingItem[] = [] + response.map(room => { + const coords = getBuildingCoords(campus, room.building) + const currentBuildingString = room.building.replace( + "Edificio ", + "Ed. " + ) + if (!tempBuildingStrings.includes(currentBuildingString)) { + const currentBuilding: BuildingItem = { + campus: campus, + name: room.building, + latitude: coords?.latitude, + longitude: coords?.longitude, + freeRoomList: [ + { + roomId: room.room_id, + name: room.name, + }, + ], } + tempBuildingStrings.push(currentBuildingString) + tempBuildings.push(currentBuilding) + } else { + //element already in the list + const indexElement = tempBuildingStrings.indexOf( + currentBuildingString + ) + tempBuildings[indexElement].freeRoomList.push({ + roomId: room.room_id, + name: room.name, + }) + } }) - setLocationStatus(PermissionStatus.GRANTED) - setCurrentCoords([latitude, longitude]) - setCurrentLocation(response[0]) + tempBuildingList.push(...tempBuildings) + } + } catch (error) { + console.log(error) } + } } - - async function checkPermission() { - const { status } = await Location.requestForegroundPermissionsAsync() - if (status !== "granted") { - setLocationStatus(PermissionStatus.UNDETERMINED) - setCurrentLocation(undefined) - } + setBuildingList(tempBuildingList) + } + + async function getPosition() { + const { status } = await Location.requestForegroundPermissionsAsync() + if (status !== "granted") { + setLocationStatus(PermissionStatus.UNDETERMINED) + setCurrentLocation(undefined) + } else { + const { coords } = await Location.getCurrentPositionAsync({}) + const { latitude, longitude } = coords + const response = await Location.reverseGeocodeAsync({ + latitude, + longitude, + }) + //temporary solution, too inefficient? + void findRoomsAvailable(campusList, latitude, longitude) + + setLocationStatus(PermissionStatus.GRANTED) + setCurrentCoords([latitude, longitude]) + setCurrentLocation(response[0]) } - - useEffect(() => { - const intervalId = setInterval(() => void checkPermission(), 1000 * 2) // in milliseconds,call every 2 sec(this could be modified) - return () => clearInterval(intervalId) - }, []) - - useEffect(() => { - void getPosition() - }, []) - - return ( - - - - - - Posizione Attuale - - - - - setSearch(searchKey)} - /> - - - setStatus(ButtonType.MAP)} - > - - Mappa - - - setStatus(ButtonType.LIST)} - > - - Lista - - - - - {status === ButtonType.LIST ? ( - - - - ) : ( - setStatus(ButtonType.LIST)} - /> - )} - - - + } + + async function checkPermission() { + if (Platform.OS === "ios") { + // idk but hasServicesEnabledAsync does not work on IOS + const { status } = await Location.requestForegroundPermissionsAsync() + if (status !== "granted") { + setLocationStatus(PermissionStatus.UNDETERMINED) + setCurrentLocation(undefined) + } + } else { + const res = await Location.hasServicesEnabledAsync() + if (!res) { + setLocationStatus(PermissionStatus.UNDETERMINED) + setCurrentLocation(undefined) + } else { + setLocationStatus(PermissionStatus.GRANTED) + } + } + return + } + + useEffect(() => { + const intervalId = setInterval(() => void checkPermission(), 2000) // in milliseconds,call every 2 sec(this could be modified) + return () => clearInterval(intervalId) + }, []) + + useEffect(() => { + void getPosition() + }, []) + + return ( + + + + Posizione attuale + - ) + + + + ) } diff --git a/src/pages/FreeClass/RoomDetails.tsx b/src/pages/FreeClass/RoomDetails.tsx index 1b815575..6861d25b 100644 --- a/src/pages/FreeClass/RoomDetails.tsx +++ b/src/pages/FreeClass/RoomDetails.tsx @@ -7,19 +7,19 @@ import { RoomUtilsSection } from "components/FreeClass/ClassDetails/RoomUtilsSec import { CrowdingSection } from "components/FreeClass/ClassDetails/CrowdingSection" export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { - const { room, startDate, roomId } = props.route.params + const { room, startDate, roomId } = props.route.params - return ( - - - - - - - ) + return ( + + + + + + + ) } diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx index 38fbdc27..08733c4e 100644 --- a/src/pages/Groups.tsx +++ b/src/pages/Groups.tsx @@ -7,9 +7,9 @@ import { api, RetryType } from "api" import { Group } from "api/groups" import { useMounted } from "utils/useMounted" import { - choosePlatformIcon, - createGroupLink, - orderByMostRecentYear, + choosePlatformIcon, + createGroupLink, + orderByMostRecentYear, } from "utils/groups" import { AnimatedPoliSearchBar } from "components/Groups/AnimatedPoliSearchBar" @@ -22,122 +22,122 @@ const deltaTime = 100 //ms let searchTimeout: NodeJS.Timeout export const Groups: MainStackScreen<"Groups"> = () => { - const [search, setSearch] = useState("") - - const [filters, setFilters] = useState({}) - - const [groups, setGroups] = useState([]) - - const [isModalShowing, setIsModalShowing] = useState(false) - - const [modalGroup, setModalGroup] = useState(undefined) - - //tracking first render - const isMounted = useMounted() - - /** - * Api search request. - */ - const searchGroups = async () => { - if (isMounted) { - if (search.length < 3) { - setGroups([]) - return - } - try { - //update last time search - const response = await api.groups.get( - { - name: search.trimEnd(), - year: filters.year, - platform: filters.platform, - type: filters.type, - degree: filters.course, - }, - { maxRetries: 1, retryType: RetryType.RETRY_N_TIMES } - ) - setGroups(response) - - //reset need searching for next render - } catch (error) { - console.log(error) - } - } + const [search, setSearch] = useState("") + + const [filters, setFilters] = useState({}) + + const [groups, setGroups] = useState([]) + + const [isModalShowing, setIsModalShowing] = useState(false) + + const [modalGroup, setModalGroup] = useState(undefined) + + //tracking first render + const isMounted = useMounted() + + /** + * Api search request. + */ + const searchGroups = async () => { + if (isMounted) { + if (search.length < 3) { + setGroups([]) + return + } + try { + //update last time search + const response = await api.groups.get( + { + name: search.trimEnd(), + year: filters.year, + platform: filters.platform, + type: filters.type, + degree: filters.course, + }, + { maxRetries: 1, retryType: RetryType.RETRY_N_TIMES } + ) + setGroups(response) + + //reset need searching for next render + } catch (error) { + console.log(error) + } } - - useEffect(() => { - clearTimeout(searchTimeout) - searchTimeout = setTimeout(() => { - void searchGroups() - }, deltaTime) - }, [search]) - - //if filters are applied after search, search again - useEffect(() => { - if (isMounted && groups) void searchGroups() - }, [filters]) - - const orderedGroups = - filters.year === undefined ? orderByMostRecentYear(groups) : groups - - return ( - - - Gruppi Corsi - setSearch(val)} - style={{ marginTop: 36, marginBottom: 22 }} - /> - setFilters(filters)} - filters={filters} - /> - - ( - { - setModalGroup(item) - setIsModalShowing(true) - }} - icon={choosePlatformIcon(item.platform)} - /> - )} - /> - setIsModalShowing(false)} - onJoin={async (group?: Group) => { - if (!group?.link_id) { - return - } - - const link = createGroupLink(group.link_id, group.platform) - // Checking if the link is supported for links with custom URL scheme. - const supported = await Linking.canOpenURL(link) - - if (supported) { - // Opening the link with some app - await Linking.openURL(link) - } - }} - > - - - - ) + } + + useEffect(() => { + clearTimeout(searchTimeout) + searchTimeout = setTimeout(() => { + void searchGroups() + }, deltaTime) + }, [search]) + + //if filters are applied after search, search again + useEffect(() => { + if (isMounted && groups) void searchGroups() + }, [filters]) + + const orderedGroups = + filters.year === undefined ? orderByMostRecentYear(groups) : groups + + return ( + + + Gruppi Corsi + setSearch(val)} + style={{ marginTop: 36, marginBottom: 22 }} + /> + setFilters(filters)} + filters={filters} + /> + + ( + { + setModalGroup(item) + setIsModalShowing(true) + }} + icon={choosePlatformIcon(item.platform)} + /> + )} + /> + setIsModalShowing(false)} + onJoin={async (group?: Group) => { + if (!group?.link_id) { + return + } + + const link = createGroupLink(group.link_id, group.platform) + // Checking if the link is supported for links with custom URL scheme. + const supported = await Linking.canOpenURL(link) + + if (supported) { + // Opening the link with some app + await Linking.openURL(link) + } + }} + > + + + + ) } diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 1d991842..66d5bfbe 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,152 +1,62 @@ import React, { useState } from "react" import { View } from "react-native" +import { NewsManager } from "components/Home/News" import { MainStackScreen } from "navigation/NavigationTypes" -import { - MainMenu, - MainTitle, - NewsBottomSheet, - PoliSearchBar, -} from "components/Home" -import { usePalette } from "utils/colors" +import { MainMenu, MainTitle, PoliSearchBar } from "components/Home" import { HighlightsManager } from "components/Home/Highlights/HighlightsManager" -import openNavSVG from "assets/menu/open-nav.svg" -import { api, RetryType } from "api" -import AsyncStorage from "@react-native-async-storage/async-storage" -import { Title } from "components/Text" - -const DEFAULT_FETCH_INTERVAL = 1 * 24 * 60 * 60 * 1000 //1 day +import { usePalette } from "utils/colors" /** * Home page containing the POLIFEMO logo, search bar, main horizontal scroll menu and the entry * point for the news section (which is a bottom sheet) */ export const Home: MainStackScreen<"Home"> = () => { - const { homeBackground, background } = usePalette() - - const [search, setSearch] = useState("") - - /** - * Lo scopo di questo useEffect è determinare se è passato `DEFAULT_FETCH_INTERVAL` - * (al momento settato a 1 giorno) dall'ultima volta che è stato eseguito - * un fetch degli articoli. Se la condizione è soddisfatta, - * viene eseguito un fetch degli articoli con data di inizio pari alla data di - * fine dell'ultimo fetch e data di fine pari a oggi, dopodichè salva - * nell'asyncstorage la data di oggi in modo che sia disponibile alla - * prossima apertura dell'app per rivalutare la condizione, etc... - * - * Se si tratta del primo fetch in assoluto, vengono richiesti dall'api - * gli articoli pubblicati da 14 giorni fa in avanti e viene settata - * la data di oggi nell'asyncStorage - * - * ? Questa logica (di prendere e settare date da async storage e - * ? valutare quanto tempo era passato dall'ultima richiesta) - * ? poteva essere implementata anche nell'oggeto api, - * ? tuttavia questo mi sembrava il modo migliore, poichè deve - * ? comunque essere valutata una condizione nella home se eseguire o - * ? meno la richiesta, quindi mi sembrava sensato porre tutto in un posto - * - * - */ - /* useEffect(() => { - AsyncStorage.getItem("date:articles") - .then(dateJSON => { - const endDate = new Date() //today - const end = endDate.toISOString() // this is ISO - if (dateJSON) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const start: string = JSON.parse(dateJSON) //this is ISO - const startDate = new Date(start) //last time fetched - console.log() - //if some time has passed and articles need refetching => refetch - if ( - endDate.getTime() - startDate.getTime() > - DEFAULT_FETCH_INTERVAL - ) { - api.getArticlesFromDateTillDate( - RetryType.RETRY_N_TIMES, - 3, - 3, - start, - end - ) - .then(res => { - const articles = res - console.log(articles[0]) - //now you can cache articles somewhere I guess - AsyncStorage.setItem( - "date:articles", - JSON.stringify(end) - ).catch(err => console.log(err)) - }) - .catch(err => console.log(err)) - } - } else { - api.getArticlesFromDaysAgoTillDate( - RetryType.RETRY_N_TIMES, - 3, - 3, - 14, - end - ) - .then(res => { - const articles = res - console.log(articles[0]) - //now you can cache articles somewhere I guess - AsyncStorage.setItem( - "date:articles", - JSON.stringify(end) - ).catch(err => console.log(err)) - }) - .catch(err => console.log(err)) - } - }) - .catch(err => console.log(err)) - }, []) */ - - return ( + const { homeBackground, background } = usePalette() + + const [search, setSearch] = useState("") + + return ( + + + - - - - setSearch(searchKey)} - /> - - - - - + setSearch(searchKey)} /> + + - ) + + + + ) } diff --git a/src/pages/IntroSlider.tsx b/src/pages/IntroSlider.tsx index 8deccaf3..db966f50 100644 --- a/src/pages/IntroSlider.tsx +++ b/src/pages/IntroSlider.tsx @@ -5,135 +5,121 @@ import IIcon from "react-native-vector-icons/Ionicons" import MIcon from "react-native-vector-icons/MaterialIcons" const styles = StyleSheet.create({ - buttonCircle: { - width: 40, - height: 40, + buttonCircle: { + width: 40, + height: 40, - backgroundColor: "rgba(0, 0, 0, .8)", + backgroundColor: "rgba(0, 0, 0, .8)", - borderRadius: 20, - justifyContent: "center", - alignItems: "center", - }, - slide: { - padding: 5, + borderRadius: 20, + justifyContent: "center", + alignItems: "center", + }, + slide: { + padding: 5, - alignItems: "center", - textAlign: "center", + alignItems: "center", + textAlign: "center", - flex: 1, - }, - title: { - display: "flex", - textAlign: "center", - alignItems: "center", + flex: 1, + }, + title: { + display: "flex", + textAlign: "center", + alignItems: "center", - flex: 1, - }, - text: { - display: "flex", - textAlign: "center", - justifyContent: "center", - alignItems: "center", + flex: 1, + }, + text: { + display: "flex", + textAlign: "center", + justifyContent: "center", + alignItems: "center", - flex: 1, - }, - image: { - width: 150, - height: 150, - resizeMode: "contain", - alignItems: "center", - flex: 0, - }, + flex: 1, + }, + image: { + width: 150, + height: 150, + resizeMode: "contain", + alignItems: "center", + flex: 0, + }, }) export const IntroSlider: FC<{ - onDone: () => void + onDone: () => void }> = props => { - return ( - ( - - {item.title} - - {item.text} - - )} - showSkipButton - showPrevButton - data={slides} - bottomButton={false} - activeDotStyle={{ - backgroundColor: "rgba(100, 200, 150, .9)", - }} - doneLabel={"Done"} - nextLabel={"Next"} - renderDoneButton={() => ( - - - - )} - renderNextButton={() => ( - - - - )} - renderPrevButton={() => ( - - - - )} - renderSkipButton={() => ( - - - - )} - onDone={() => props.onDone()} - /> - ) + return ( + ( + + {item.title} + + {item.text} + + )} + showSkipButton + showPrevButton + data={slides} + bottomButton={false} + activeDotStyle={{ + backgroundColor: "rgba(100, 200, 150, .9)", + }} + doneLabel={"Done"} + nextLabel={"Next"} + renderDoneButton={() => ( + + + + )} + renderNextButton={() => ( + + + + )} + renderPrevButton={() => ( + + + + )} + renderSkipButton={() => ( + + + + )} + onDone={() => props.onDone()} + /> + ) } const slides = [ - { - key: "one", - title: "Title 1", - text: "Description.\nSay something cool", - image: "https://thumbs.dreamstime.com/z/jour-de-terre-d-environnement-dans-les-mains-des-arbres-cultivant-jeunes-plantes-bokeh-verdissent-la-main-femelle-fond-tenant-l-130247647.jpg", - backgroundColor: "#00ff0033", - }, - { - key: "two", - title: "Title 2", - text: "Other cool stuff", - image: "https://thumbs.dreamstime.com/z/jour-de-terre-d-environnement-dans-les-mains-des-arbres-cultivant-jeunes-plantes-bokeh-verdissent-la-main-femelle-fond-tenant-l-130247647.jpg", - backgroundColor: "#0000ff33", - }, - { - key: "three", - title: "Rocket guy", - text: "I'm already out of descriptions\n\nLorem ipsum bla bla bla", - image: "https://thumbs.dreamstime.com/z/jour-de-terre-d-environnement-dans-les-mains-des-arbres-cultivant-jeunes-plantes-bokeh-verdissent-la-main-femelle-fond-tenant-l-130247647.jpg", - backgroundColor: "#ff000033", - }, + { + key: "one", + title: "Title 1", + text: "Description.\nSay something cool", + image: + "https://thumbs.dreamstime.com/z/jour-de-terre-d-environnement-dans-les-mains-des-arbres-cultivant-jeunes-plantes-bokeh-verdissent-la-main-femelle-fond-tenant-l-130247647.jpg", + backgroundColor: "#00ff0033", + }, + { + key: "two", + title: "Title 2", + text: "Other cool stuff", + image: + "https://thumbs.dreamstime.com/z/jour-de-terre-d-environnement-dans-les-mains-des-arbres-cultivant-jeunes-plantes-bokeh-verdissent-la-main-femelle-fond-tenant-l-130247647.jpg", + backgroundColor: "#0000ff33", + }, + { + key: "three", + title: "Rocket guy", + text: "I'm already out of descriptions\n\nLorem ipsum bla bla bla", + image: + "https://thumbs.dreamstime.com/z/jour-de-terre-d-environnement-dans-les-mains-des-arbres-cultivant-jeunes-plantes-bokeh-verdissent-la-main-femelle-fond-tenant-l-130247647.jpg", + backgroundColor: "#ff000033", + }, ] diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 244e67dd..38021ac9 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -6,7 +6,7 @@ import WebView from "react-native-webview" import { usePalette } from "utils/colors" import { api } from "api" import { HttpClient } from "api/HttpClient" -import { PolimiToken, PoliNetworkToken } from "utils/login" +import { PolimiToken, PoliNetworkToken } from "contexts/login" import { NavBar } from "components/NavBar" // TODO: HANDLE ERRORS, this will break as soon as something goes wrong @@ -15,10 +15,10 @@ import { NavBar } from "components/NavBar" // absolute magic url that makes the login go brrr, going to this url prompts the user to log in // for microsoft sso using polimi creditials const magicLoginUrl = - "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=92602f24-dd8e-448e-a378-b1c575310f9d&scope=openid%20offline_access&response_type=code&state=10010&redirect_uri=https://api.polinetwork.org/staging/v1/auth/code&login_hint=nome@mail.polimi.it" + "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=92602f24-dd8e-448e-a378-b1c575310f9d&scope=openid%20offline_access&response_type=code&state=10010&redirect_uri=https://api.polinetwork.org/staging/v1/auth/code&login_hint=nome@mail.polimi.it" // with a polimi session present, this cascade-redirects to the app page with the authcode attached const magicTokenUrl = - "https://oauthidp.polimi.it/oauthidp/oauth2/auth?client_id=1057407812&redirect_uri=https%3A%2F%2Fpolimiapp.polimi.it%2Fpolimi_app%2Fapp&scope=openid%20polimi_app%20aule%20policard%20incarichi%20orario%20account%20webmail%20faqappmobile%20rubrica%20richass%20guasti%20prenotazione%20code%20carriera%20alumni%20webeep%20teamwork%20esami&access_type=offline&response_type=code" + "https://oauthidp.polimi.it/oauthidp/oauth2/auth?client_id=1057407812&redirect_uri=https%3A%2F%2Fpolimiapp.polimi.it%2Fpolimi_app%2Fapp&scope=openid%20polimi_app%20aule%20policard%20incarichi%20orario%20account%20webmail%20faqappmobile%20rubrica%20richass%20guasti%20prenotazione%20code%20carriera%20alumni%20webeep%20teamwork%20esami&access_type=offline&response_type=code" // if this is reached we have reached the polinework access token const polinetworkTargetUrl = "https://api.polinetwork.org/staging/v1/auth/code" @@ -26,29 +26,29 @@ const polinetworkTargetUrl = "https://api.polinetwork.org/staging/v1/auth/code" const polimiTargetUrl = "https://polimiapp.polimi.it/polimi_app/app/?code=" enum LoginStage { - /** - * Waiting for the user to go through the login process - */ - LOGGING_IN, - /** - * PoliNetwork access token retrieved, redirecting to get Polimi AuthCode - */ - GOT_POLINETWORK_TOKEN, - /** - * Polimi AuthCode retrieved, the web part is done, fetching the access token - */ - GOT_POLIMI_CODE, - /** - * Polimi access token retrieved, both tokens are set, login is completed - */ - GOT_POLIMI_TOKEN, + /** + * Waiting for the user to go through the login process + */ + LOGGING_IN, + /** + * PoliNetwork access token retrieved, redirecting to get Polimi AuthCode + */ + GOT_POLINETWORK_TOKEN, + /** + * Polimi AuthCode retrieved, the web part is done, fetching the access token + */ + GOT_POLIMI_CODE, + /** + * Polimi access token retrieved, both tokens are set, login is completed + */ + GOT_POLIMI_TOKEN, } const loginMessage: Record = { - [LoginStage.LOGGING_IN]: "Logging in...", - [LoginStage.GOT_POLINETWORK_TOKEN]: "Retrieving Polimi AuthCode...", - [LoginStage.GOT_POLIMI_CODE]: "Retrieving Polimi access token...", - [LoginStage.GOT_POLIMI_TOKEN]: "All done!", + [LoginStage.LOGGING_IN]: "Logging in...", + [LoginStage.GOT_POLINETWORK_TOKEN]: "Retrieving Polimi AuthCode...", + [LoginStage.GOT_POLIMI_CODE]: "Retrieving Polimi access token...", + [LoginStage.GOT_POLIMI_TOKEN]: "All done!", } const client = HttpClient.getInstance() @@ -61,128 +61,126 @@ const client = HttpClient.getInstance() * forced to retrieve polimi authcode, which is used to get a fresh access token */ export const Login: RootStackScreen<"Login"> = () => { - const navigation = useNavigation() - const { backgroundSecondary, homeBackground, palette } = usePalette() - const webview = useRef(null) - - // keeps track of wich stage of the login we are on - const [loginStage, setLoginStage] = useState(LoginStage.LOGGING_IN) - - // used to force a redirect when polinetwork token is reached - const [currentURL, setCurrentURL] = useState(magicLoginUrl) - - // temporarly save the tokens in the state when they are retrieved - const [poliNetworkToken, setPoliNetworkToken] = useState< - PoliNetworkToken | undefined - >() - const [polimiToken, setPolimiToken] = useState() - - useEffect(() => { - console.log(`Login stage: ${LoginStage[loginStage]}`) - }, [loginStage]) - - useEffect(() => { - // when both tokens are locally set, we are done! - // the tokens should get registered in the api wrapper to be used in calls - if (poliNetworkToken && polimiToken) { - console.log("Login completed! Registering tokens...") - void client - .setTokens({ poliNetworkToken, polimiToken }) - .then(() => { - setTimeout(() => { - navigation.goBack() - }, 1000) - }) - } - }, [poliNetworkToken, polimiToken]) - - return ( + const navigation = useNavigation() + const { backgroundSecondary, homeBackground, palette } = usePalette() + const webview = useRef(null) + + // keeps track of wich stage of the login we are on + const [loginStage, setLoginStage] = useState(LoginStage.LOGGING_IN) + + // used to force a redirect when polinetwork token is reached + const [currentURL, setCurrentURL] = useState(magicLoginUrl) + + // temporarly save the tokens in the state when they are retrieved + const [poliNetworkToken, setPoliNetworkToken] = useState< + PoliNetworkToken | undefined + >() + const [polimiToken, setPolimiToken] = useState() + + useEffect(() => { + console.log(`Login stage: ${LoginStage[loginStage]}`) + }, [loginStage]) + + useEffect(() => { + // when both tokens are locally set, we are done! + // the tokens should get registered in the api wrapper to be used in calls + if (poliNetworkToken && polimiToken) { + console.log("Login completed! Registering tokens...") + void client.setTokens({ poliNetworkToken, polimiToken }).then(() => { + setTimeout(() => { + navigation.goBack() + }, 1000) + }) + } + }, [poliNetworkToken, polimiToken]) + + return ( + + + Login + + { + // the JSON body of the PoliNetwork token is sent via a message + const { url, data } = e.nativeEvent + if (!url || !data) return + + if (url.startsWith(polinetworkTargetUrl)) { + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + setPoliNetworkToken(JSON.parse(data)) + setCurrentURL(magicTokenUrl) + } catch (e) { + console.log("error while parsing!!") + console.log(url) + console.log(data) + } + } + }} + onNavigationStateChange={async ({ url, loading }) => { + // intercept the state change in the webview + if (loading) return + if (!url) return + + if (url.startsWith(polinetworkTargetUrl)) { + // when the polinetwork page is reached, hide the webview and extract the token + setLoginStage(LoginStage.GOT_POLINETWORK_TOKEN) + webview.current?.injectJavaScript( + "window.ReactNativeWebView.postMessage(document.body.innerText)" + ) + } else if (url.startsWith(polimiTargetUrl)) { + // when the polimi page is reached, extract the other token from the url + const authcode = url.replace(polimiTargetUrl, "") + setLoginStage(LoginStage.GOT_POLIMI_CODE) + + // retrieve the access token from the authcode + const token = await api.auth.getPolimiToken(authcode) + setPolimiToken(token) + setLoginStage(LoginStage.GOT_POLIMI_TOKEN) + } + }} + /> + {loginStage !== LoginStage.LOGGING_IN ? ( - ) + ) : undefined} + + + ) } diff --git a/src/pages/NewsList.tsx b/src/pages/NewsList.tsx deleted file mode 100644 index 37518dfa..00000000 --- a/src/pages/NewsList.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useState, useEffect } from "react" -import { View } from "react-native" -import { api, RetryType } from "api" -import { MainStackScreen } from "navigation/NavigationTypes" -import { Page } from "components/Page" -import { CardWithGradient } from "components/CardWithGradient" -import { Article } from "api/articles" - -/** - * News page containing the articles of a specific category. - */ -export const NewsList: MainStackScreen<"NewsList"> = props => { - // Name of the news category, which is the title of the page - const { categoryName } = props.route.params - - // State of the toggle switch in the header (on / off) - const [toggled, setToggled] = useState(false) - - const [articles, setArticles] = useState([]) - - const [refreshing, setRefreshing] = useState(false) - - // TODO: get only the articles of the given category - const updateArticles = async (retryType: RetryType) => { - try { - const response = await api.articles.getFromDaysAgoTillDate( - 3, - new Date().toISOString(), - { retryType: retryType } - ) - setArticles(response) - } catch (error) { - console.log(error) - } - setRefreshing(false) - } - - useEffect(() => { - void updateArticles(RetryType.RETRY_INDEFINETELY) - }, []) - - return ( - { - setRefreshing(true) - void updateArticles(RetryType.NO_RETRY) - }, - }} - showSwitch={true} - switchValue={toggled} - onSwitchToggle={value => setToggled(value)} - > - - {articles.map((article, index) => { - return ( - { - // TODO: here navigate to the article page - console.log(article.title) - }} - style={{ height: 220, marginBottom: 13 }} - /> - ) - })} - - - ) -} diff --git a/src/pages/news/ArticleDetails.tsx b/src/pages/news/ArticleDetails.tsx new file mode 100644 index 00000000..0f31db0f --- /dev/null +++ b/src/pages/news/ArticleDetails.tsx @@ -0,0 +1,125 @@ +/* eslint-disable prettier/prettier */ +import React, { useState } from "react" +import { MainStackScreen } from "navigation/NavigationTypes" +import { Image, Linking } from "react-native" +import { WebView } from "react-native-webview" +import { ScrollPage } from "components/ScrollPage" +import { usePalette } from "utils/colors" +import { Asset } from "expo-asset" +import { Roboto_400Regular } from "@expo-google-fonts/roboto" + +export const Article: MainStackScreen<"Article"> = props => { + const { isLight } = usePalette() + const article = props.route.params.article + const [webHeight, setWebHeight] = useState(400) + let html: string[] = [] + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + html = JSON.parse(article.content) + } catch (error) { + console.log(error) + } + + return ( + + ) : undefined + } + > + { + setWebHeight(parseInt(event.nativeEvent.data)) + }} + /* Questo dovrebbe reindirizzare i link nel browser, + stoppando la webview. Apparentemente non funziona col mio + android, nel senso che non viene chiamata (comunque + il problema dei reindirizzamenti dentro la webview non + si presenta sul mio telefono). Da testare su iOS e altri + Android. */ + + style={{ + backgroundColor: "transparent", + }} + onShouldStartLoadWithRequest={event => { + if (event.url.slice(0, 4) === "http") { + void Linking.openURL(event.url) + return false + } + + return true + }} + javaScriptEnabled={true} + injectedJavaScript={webViewScript} + containerStyle={{ height: webHeight, marginBottom: 120 }} + showsVerticalScrollIndicator={false} + setBuiltInZoomControls={false} + nestedScrollEnabled={false} + scrollEnabled={false} + androidHardwareAccelerationDisabled={true} + originWhitelist={["*"]} + source={{ + html: ` +
${html.map(el => `

${el}

`).join("")} +

`, + baseUrl: "", + }} + /> + + ) +} + +//used to set automatic heigth in webview +//solution found here: https://stackoverflow.com/questions/35446209/react-native-webview-height +//the alternative was to install a whole package +const webViewScript = ` + setTimeout(function() { + window.ReactNativeWebView.postMessage(document.documentElement.scrollHeight); + }, 500); + true; // note: this is required, or you'll sometimes get silent failures +` diff --git a/src/pages/news/ArticlesList.tsx b/src/pages/news/ArticlesList.tsx new file mode 100644 index 00000000..c2d0b7c0 --- /dev/null +++ b/src/pages/news/ArticlesList.tsx @@ -0,0 +1,119 @@ +import React, { useState, useEffect, useRef, useContext } from "react" +import { View } from "react-native" + +import { api, RetryType } from "api" +import { Article } from "api/articles" +import { MainStackScreen, useNavigation } from "navigation/NavigationTypes" +import { ScrollPageInfinite } from "components/ScrollPageInfinite" +import { CardWithGradient } from "components/CardWithGradient" +import { capitalize } from "utils/strings" +import { NewsPreferencesContext, Preference } from "contexts/newsPreferences" + +const MAX_ARTICLES_PER_REQUEST = 8 + +/** + * News page containing the articles of a specific tag. + */ +export const ArticlesList: MainStackScreen<"ArticlesList"> = props => { + const navigation = useNavigation() + const { preferences, setArticlesPreferences } = useContext( + NewsPreferencesContext + ) + const { tagName } = props.route.params + + const [articles, setArticles] = useState([]) + const [toggled, setToggled] = useState( + preferences[tagName] !== Preference.UNFAVOURITE + ) + + const [refresh, setRefresh] = useState(false) + const [isFetching, setIsFetching] = useState(true) + + const offset = useRef(0) + + const fetchArticles = async (keepArticles: boolean) => { + try { + const response = await api.articles.getFromOffsetByTag( + tagName, + MAX_ARTICLES_PER_REQUEST, + offset.current, + { retryType: RetryType.NO_RETRY } + ) + if (keepArticles) { + // Keep previously downloaded articles + setArticles([...articles, ...response]) + } else { + // Overwrite previously downloaded articles + setArticles(response) + } + } catch (error) { + console.log(error) + } + } + + useEffect(() => { + fetchArticles(false).finally(() => { + // Increase the offset so that at the following fetch you get the next articles + offset.current += 1 + setIsFetching(false) + }) + }, []) + + return ( + ( + + + navigation.navigate("Article", { + article: article, + }) + } + style={{ height: 220, marginBottom: 13 }} + /> + + )} + fetchControl={{ + fetching: isFetching, + onFetch: () => { + if (!refresh && !isFetching) { + setIsFetching(true) + fetchArticles(true).finally(() => { + offset.current += 1 + setIsFetching(false) + }) + } + }, + }} + refreshControl={{ + refreshing: refresh, + onRefresh: () => { + if (!refresh && !isFetching) { + setRefresh(true) + offset.current = 0 + fetchArticles(false).finally(() => { + offset.current += 1 + setRefresh(false) + }) + } + }, + }} + showSwitch={true} + switchControl={{ + toggled: toggled, + onToggle: value => { + setToggled(value) + const newFavorites = { ...preferences } + if (value) newFavorites[tagName] = Preference.FAVOURITE + else newFavorites[tagName] = Preference.UNFAVOURITE + setArticlesPreferences({ preferences: newFavorites }) + }, + }} + /> + ) +} diff --git a/src/pages/news/OtherCategories.tsx b/src/pages/news/OtherCategories.tsx new file mode 100644 index 00000000..dd187f56 --- /dev/null +++ b/src/pages/news/OtherCategories.tsx @@ -0,0 +1,61 @@ +import React, { useContext } from "react" +import { View } from "react-native" + +import { MainStackScreen, useNavigation } from "navigation/NavigationTypes" +import { CardWithGradient } from "components/CardWithGradient" +import { capitalize } from "utils/strings" +import { + NewsPreferencesContext, + Preference, + TagWithData, +} from "contexts/newsPreferences" +import { ScrollPageInfinite } from "components/ScrollPageInfinite" + +/** + * News page containing the articles of a specific tag. + */ +export const OtherCategories: MainStackScreen<"OtherCategories"> = props => { + const navigation = useNavigation() + const { preferences } = useContext(NewsPreferencesContext) + + const { tags } = props.route.params + + // Function used when displaying a tag card + const getTagCard = (tag: TagWithData) => { + return ( + + navigation.navigate("ArticlesList", { + tagName: tag.name, + }) + } + closerToCorner={true} + style={{ height: tag.height }} + /> + ) + } + + const nonFavTags = tags.filter( + tag => preferences[tag.name] === Preference.UNFAVOURITE + ) + if ( + nonFavTags.length === 0 && + navigation.isFocused() && + navigation.canGoBack() + ) { + navigation.goBack() + } + + return ( + ( + {getTagCard(tag)} + )} + /> + ) +} diff --git a/src/pages/settings/Help.tsx b/src/pages/settings/Help.tsx index 46df9bc9..c8115281 100644 --- a/src/pages/settings/Help.tsx +++ b/src/pages/settings/Help.tsx @@ -2,38 +2,33 @@ import React from "react" import { View } from "react-native" import { SettingsStackScreen } from "navigation/NavigationTypes" import { ContentWrapperScroll } from "components/ContentWrapperScroll" -import { SettingTile } from "components/Settings/SettingTile" -import { SettingOptions } from "utils/settings" +import { SettingTile, SettingOptions } from "components/Settings/SettingTile" export const settingsList: SettingOptions[] = [ - { - title: "Centro Assistenza", - subtitle: "descrizione placeholder", - }, - { - title: "Contattaci", - subtitle: "descrizione placeholder", - }, - { - title: "Termini e privacy", - subtitle: "descrizione plaiceholder", - }, - { - title: "Informazioni App", - subtitle: "descrizione placeholder", - }, + { + title: "Centro Assistenza", + subtitle: "descrizione placeholder", + }, + { + title: "Contattaci", + subtitle: "descrizione placeholder", + }, + { + title: "Informazioni App", + subtitle: "descrizione placeholder", + }, ] /** * Notifications Settings Page */ export const Help: SettingsStackScreen<"Help"> = () => { - return ( - - - {settingsList.map((setting, index) => { - return - })} - - - ) + return ( + + + {settingsList.map((setting, index) => { + return + })} + + + ) } diff --git a/src/pages/settings/Privacy.tsx b/src/pages/settings/Privacy.tsx new file mode 100644 index 00000000..0eecab6d --- /dev/null +++ b/src/pages/settings/Privacy.tsx @@ -0,0 +1,235 @@ +import React, { useContext, useEffect, useState } from "react" +import { Alert, Linking } from "react-native" +import { SettingsStackScreen, useNavigation } from "navigation/NavigationTypes" +import { ContentWrapperScroll } from "components/ContentWrapperScroll" +import { Divider } from "components/Divider" +import { SettingTile } from "components/Settings/SettingTile" +import { BodyText, HyperLink } from "components/Text" +import { api } from "api" +import * as Sharing from "expo-sharing" +import * as FileSystem from "expo-file-system" +import { HttpClient } from "api/HttpClient" +import { LoginContext } from "contexts/login" +import { ModalPicker } from "components/Settings/ModalPicker" +import { Description } from "components/Settings/Description" + +const client = HttpClient.getInstance() + +const autodeleteTimes: { value: number; label: string }[] = [ + { value: 30, label: "30 giorni" }, + { value: 60, label: "60 giorni" }, + { value: 90, label: "90 giorni" }, + { value: 180, label: "6 mesi" }, + { value: 365, label: "1 anno" }, + { value: 730, label: "2 anni" }, + { value: 1095, label: "3 anni" }, + { value: 1460, label: "4 anni" }, + { value: 1825, label: "5 anni" }, +] + +const exportDesc = + "Scarica una copia in formato JSON di tutti i dati che PoliNetwork ha in suo possesso in modo semplice e sicuro." + +const autodeleteDesc = + "Dopo un periodo di inattività, i tuoi dati verranno cancellati automaticamente. Il periodo predefinito è 2 anni senza accesso, puoi modificarlo da un minimo di 30 giorni a un massimo di 5 anni." + +const deleteAlertMessage = `Questa azione è irreversibile + +Tutti i tuoi dati verranno eliminati e non potrai più accedere a PoliNetwork con questo account. + +Sei sicuro di voler procedere?` + +/** + * Privacy Page + */ +export const Privacy: SettingsStackScreen<"Privacy"> = () => { + const { loggedIn } = useContext(LoginContext) + const { navigate } = useNavigation() + + const [loadingExport, setLoadingExport] = useState(false) + + const [showingAutodeleteModal, setShowingAutodeleteModal] = useState(false) + const [autodeleteTime, setAutodeleteTime] = useState(730) + + useEffect(() => { + if (loggedIn) { + api.user + .getPoliNetworkSettings() + .then(settings => { + setAutodeleteTime(settings.expire_in_days) + }) + .catch(() => { + console.log("Error while getting settings in privacy page") + }) + } + }, [loggedIn]) + + return ( + + + PoliNetwork riconosce la privacy come un valore fondamentale e sostiene + il diritto degli utenti di avere il pieno controllo dei propri dati. + Nonostante l'app sia progettata per archiviare la minima quantità + possibile di dati, e che non ci è possibile risalire all'identità + di una persona fisica partendo dai dati in nostro possesso, PoliNetwork + fornisce comunque gli strumenti per una buona gestione della privacy, + come la cancellazione e l'esportazione dei dati.{"\n"} + + + Per eventuali domande o dubbi sulla privacy, si prega di leggere la + Privacy Policy o di contattare{" "} + + privacy@polinetwork.org + + . + + {loggedIn && ( + <> + + { + setLoadingExport(true) + try { + const data = await api.user.exportPoliNetworkMe() + const uri = + FileSystem.cacheDirectory + "polinetwork_data.json" + await FileSystem.writeAsStringAsync( + uri, + JSON.stringify(data, null, 2) + ) + void Sharing.shareAsync(uri) + } catch (e) { + Alert.alert("Errore durante l'esportazione dei dati", e + "") + console.error(e) + } finally { + setLoadingExport(false) + } + }, + }} + /> + setShowingAutodeleteModal(true), + }} + /> + { + Alert.alert( + "Sicuro di voler cancellare l'account?", + deleteAlertMessage, + [ + { + text: "Annulla", + style: "cancel", + }, + { + text: "Elimina Account", + style: "destructive", + onPress: async () => { + try { + await api.user.deletePoliNetworkMe() + Alert.alert( + "Account cancellato", + "Il tuo account è stato cancellato con successo" + ) + void client.destroyTokens() + navigate("Home") + } catch (e) { + Alert.alert( + "Errore durante la cancellazione dell'account", + e + "" + ) + console.error(e) + } + }, + }, + ] + ) + }, + }} + /> + + La possibilità di cancellare i tuoi dati in modo semplice è un + aspetto cruciale per garantire la tua privacy e la tua sicurezza + nella nostra app. Questa opzione ti permette di rimuovere in modo + permanente tutte le informazioni che hai fornito a PoliNetwork. + {"\n"}È sempre possibile verificare i dati collegati al tuo account + salvati da noi tramite la sezione "Esporta" delle + impostazioni privacy.{"\n"}Una volta che i dati sono stati + cancellati, non saranno più disponibili o recuperabili. Questa + opzione fornisce agli utenti il pieno controllo sui propri dati. La + cancellazione dei dati può essere eseguita in qualsiasi momento. + + + )} + + + Linking.openURL("https://polinetwork.org/learnmore/privacy/"), + }} + /> + + In PoliNetwork, la privacy è un valore fondamentale, per questo abbiamo + progettato la nostra app tenendo sempre presente questo aspetto. La + Privacy Policy è molto importante per PoliNetwork e fornisce ai nostri + utenti informazioni complete sulle nostre politiche sulla privacy e + sulla protezione dei dati personali. Per garantire la tua privacy, molte + funzionalità dell'app possono essere utilizzate anche senza + effettuare il login. Per le funzionalità che richiedono il login, + abbiamo scelto di utilizzare hashing, per proteggere i tuoi dati nel + modo migliore. Non conosciamo chi sei, ma possiamo solo autenticarti. + Puoi sempre verificare cosa sappiamo su di te usando il tasto + "Esporta" nelle impostazioni privacy.{"\n\n"}Se hai domande o + dubbi sulla privacy, ti invitiamo a leggere la Privacy Policy per avere + maggiori informazioni, o scriverci a{" "} + + privacy@polinetwork.org + + . + + setShowingAutodeleteModal(false)} + elements={autodeleteTimes} + selectedValue={autodeleteTime} + onSelect={async value => { + try { + await api.user.updatePoliNetworkSettings({ + // eslint-disable-next-line @typescript-eslint/naming-convention + expire_in_days: value, + }) + setAutodeleteTime(value) + setShowingAutodeleteModal(false) + Alert.alert( + "Impostazioni aggiornate", + `Nuovo periodo di inattività per la cancellazione automatica dei dati impostato a ${value} giorni` + ) + } catch (e) { + Alert.alert("Errore durante l'aggiornamento", e + "") + console.error(e) + } + }} + /> + + ) +} diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx index 4391640c..ce19719b 100644 --- a/src/pages/settings/Settings.tsx +++ b/src/pages/settings/Settings.tsx @@ -3,20 +3,16 @@ import { View } from "react-native" import { SettingsStackScreen, useNavigation } from "navigation/NavigationTypes" import { ContentWrapperScroll } from "components/Settings" import { Divider } from "components/Divider" -import { SettingTile } from "components/Settings" +import { SettingTile, SettingOptions } from "components/Settings" import { settingsIcons } from "assets/settings" import { UserDetailsTile } from "components/Settings" -import { ModalSelection } from "components/Settings" import { CareerTile } from "components/Settings" import { SelectTile } from "components/Settings" import { UserAnonymousTile } from "components/Settings" -import { - SettingOptions, - SettingsContext, - ValidColorSchemeName, -} from "utils/settings" +import { ModalWithButtons } from "components/ModalWithButtons" +import { SettingsContext, ValidColorSchemeName } from "contexts/settings" import { CareerColumn } from "components/Settings" -import { LoginContext } from "utils/login" +import { LoginContext } from "contexts/login" import { Career } from "api/user" import { HttpClient } from "api/HttpClient" @@ -28,146 +24,150 @@ const client = HttpClient.getInstance() * Settings Page */ export const SettingsPage: SettingsStackScreen<"Settings"> = () => { - //for testing logged in/out view - const { loggedIn, userInfo } = useContext(LoginContext) - const { settings, setSettings } = useContext(SettingsContext) - const theme = settings.theme + //for testing logged in/out view + const { loggedIn, userInfo } = useContext(LoginContext) + const { settings, setSettings } = useContext(SettingsContext) + const theme = settings.theme - //RadioButtonGroup theme state and setter - const [selectedTheme, setSelectedTheme] = - useState(theme) + //RadioButtonGroup theme state and setter + const [selectedTheme, setSelectedTheme] = + useState(theme) - //actual career and setter. It will be moved in app state eventually. - const [career, setCareer] = useState( - userInfo?.careers[0] - ) + //actual career and setter. It will be moved in app state eventually. + const [career, setCareer] = useState(userInfo?.careers[0]) - //currently selected career and setter. - const [selectedCareer, setSelectedCareer] = useState( - career ?? { - matricola: "N/A", - type: "Nessuna Carriera", - } - ) + //currently selected career and setter. + const [selectedCareer, setSelectedCareer] = useState( + career ?? { + matricola: "N/A", + type: "Nessuna Carriera", + } + ) - //control theme selector modal's visibility - const [isModalThemeVisible, setModalThemeVisible] = useState(false) + //control theme selector modal's visibility + const [isModalThemeVisible, setModalThemeVisible] = useState(false) - //control career selector modal's visibility - const [isModalCareerVisible, setModalCareerVisible] = useState(false) + //control career selector modal's visibility + const [isModalCareerVisible, setModalCareerVisible] = useState(false) - const { navigate } = useNavigation() + const { navigate } = useNavigation() - const settingsList: SettingOptions[] = [ - { - title: "Aspetto", - subtitle: "Dark, light mode", - icon: settingsIcons.modify, - callback: () => { - setModalThemeVisible(true) - }, - }, - { - title: "Aiuto", - subtitle: "Centro assistenza, contattaci, informativa privacy", - icon: settingsIcons.help, - callback: () => { - navigate("Help") - }, - }, - { - title: "Disconnetti", - icon: settingsIcons.disconnect, - callback: async () => { - await client.destroyTokens() - }, - }, - ] + const settingsList: SettingOptions[] = [ + { + title: "Aspetto", + subtitle: "Dark, light mode", + icon: settingsIcons.modify, + callback: () => { + setModalThemeVisible(true) + }, + }, + { + title: "Aiuto", + subtitle: "Centro assistenza, contattaci", + icon: settingsIcons.help, + callback: () => { + navigate("Help") + }, + }, + { + title: "Privacy", + subtitle: + "Informativa sulla privacy\nImpostazioni del tuo account relative alla privacy", + icon: settingsIcons.privacy, + callback: () => { + navigate("Privacy") + }, + }, + { + title: "Disconnetti", + icon: settingsIcons.disconnect, + callback: async () => { + await client.destroyTokens() + }, + }, + ] - return ( - - - {loggedIn ? ( - - ) : ( - navigate("Login")} - /> - )} - {loggedIn && ( - - setModalCareerVisible(true)} - /> - - )} - + return ( + + + {loggedIn ? ( + + ) : ( + navigate("Login")} + /> + )} + {loggedIn && ( + + setModalCareerVisible(true)} + /> + + )} + - {settingsList.map((setting, index) => { - return - })} - + {settingsList.map((setting, index) => { + return + })} + - { - //restore real theme value - setSelectedTheme(theme) - setModalThemeVisible(false) - }} - onOK={() => { - setSettings({ ...settings, theme: selectedTheme }) - setModalThemeVisible(false) - }} + { + //restore real theme value + setSelectedTheme(theme) + setModalThemeVisible(false) + }} + onOK={() => { + setSettings({ ...settings, theme: selectedTheme }) + setModalThemeVisible(false) + }} + > + {themes?.map((themeName, index) => { + return ( + { + setSelectedTheme(themesToSave[index]) + }} + /> + ) + })} + + { + //restore selectedCareer to career + if (career) setSelectedCareer(career) + setModalCareerVisible(false) + }} + onOK={() => { + //change career to selectedCareer + setCareer(selectedCareer) + setModalCareerVisible(false) + }} + > + {userInfo?.careers?.map((careerOfIndex, index) => { + return ( + { + setSelectedCareer(careerOfIndex) + }} + flexStyle={"space-between"} > - {themes?.map((themeName, index) => { - return ( - { - setSelectedTheme(themesToSave[index]) - }} - /> - ) - })} - - { - //restore selectedCareer to career - if (career) setSelectedCareer(career) - setModalCareerVisible(false) - }} - onOK={() => { - //change career to selectedCareer - setCareer(selectedCareer) - setModalCareerVisible(false) - }} - > - {userInfo?.careers?.map((careerOfIndex, index) => { - return ( - { - setSelectedCareer(careerOfIndex) - }} - flexStyle={"space-between"} - > - - - ) - })} - - - ) + + + ) + })} + + + ) } diff --git a/src/utils/cardsPatterns.ts b/src/utils/cardsPatterns.ts index 6acc1683..0d92780c 100644 --- a/src/utils/cardsPatterns.ts +++ b/src/utils/cardsPatterns.ts @@ -14,66 +14,66 @@ export type CardsPattern = CardData[] * with a given number of cards to a visual pattern */ export interface CardsPatterns { - /** patterns used by the first batch of cards */ - first: { [key: number]: CardsPattern } - /** patterns used by all the following batches of cards */ - other: { [key: number]: CardsPattern } + /** patterns used by the first batch of cards */ + first: { [key: number]: CardsPattern } + /** patterns used by all the following batches of cards */ + other: { [key: number]: CardsPattern } } -export const newsCategoryPatterns: CardsPatterns = { - // first batch of news category cards - first: { - [1]: [[190, "left"]], - [2]: [ - [274, "left"], - [274, "right"], - ], - [3]: [ - [277, "right"], - [130, "left"], - [130, "left"], - ], - [4]: [ - [274, "left"], - [193, "right"], - [130, "left"], - [211, "right"], - ], - [5]: [ - [304, "left"], - [193, "right"], - [207, "left"], - [171, "right"], - [130, "right"], - ], - [6]: [ - [274, "left"], - [178, "right"], - [147, "left"], - [178, "right"], - [147, "left"], - [212, "right"], - ], - }, - // other batches of news category cards - other: { - [3]: [ - [277, "right"], - [130, "left"], - [130, "left"], - ], - [4]: [ - [244, "left"], - [183, "right"], - [160, "left"], - [221, "right"], - ], - [5]: [ - [133, "left"], - [193, "right"], - [132, "left"], - [221, "right"], - [132, "left"], - ], - }, +export const newsTagsPatterns: CardsPatterns = { + // first batch of news category cards + first: { + [1]: [[190, "left"]], + [2]: [ + [274, "left"], + [274, "right"], + ], + [3]: [ + [130, "left"], + [130, "left"], + [277, "right"], + ], + [4]: [ + [274, "left"], + [193, "right"], + [130, "left"], + [211, "right"], + ], + [5]: [ + [304, "left"], + [193, "right"], + [207, "left"], + [171, "right"], + [130, "right"], + ], + [6]: [ + [274, "left"], + [178, "right"], + [147, "left"], + [178, "right"], + [147, "left"], + [212, "right"], + ], + }, + // other batches of news category cards + other: { + [3]: [ + [277, "right"], + [130, "left"], + [130, "left"], + ], + [4]: [ + [244, "left"], + [183, "right"], + [160, "left"], + [221, "right"], + ], + [5]: [ + [133, "left"], + [193, "right"], + [132, "left"], + [221, "right"], + [132, "left"], + ], + }, } diff --git a/src/utils/carousel.ts b/src/utils/carousel.ts new file mode 100644 index 00000000..e6c24e28 --- /dev/null +++ b/src/utils/carousel.ts @@ -0,0 +1,113 @@ +import { Event } from "api/event" +/** + * enum to differentiate the different types of widget we could have + * different widget types have different background images + */ +export enum WidgetType { + LECTURES = 1, + EXAMS = 2, + NEWS = 3, + DEADLINE = 4, + CUSTOM = 5, +} + +export interface CarouselItem { + /** + * number to identify the object and the relative widget + */ + id: number + /** + * enum field to identify the type of the widget + */ + type: WidgetType + /** + * string to identify the date of the event(or deadline) contained in the widget + */ + date: string + /** + * string to identify the time of the event(or deadline) contained in the widget + */ + time: string + /** + * string to identify the title of the event contained in the widget + */ + title: string + /** + * string to identify eventually the room when the event takes place + */ + room?: string +} + +/** + * Function that helps to format the title of an Highlight. I decided to transform the entire title to lowercase + * except for the first letter of each word with more than 3 characters. + * @param title + * @returns the transformed title + */ +export function formatTitle(title: string) { + return title + .split(" ") + .map(item => + item.length > 3 + ? item.charAt(0).toUpperCase() + item.slice(1).toLowerCase() + : item.toLowerCase() + ) + .join(" ") +} + +/** + * Function that allows to check the event_type of an event. So far, we filter the events that are lectures, exams or deadlines. + * @param typeId of the event + * @returns true/false + */ +export function checkEventType(typeId: number) { + return ( + typeId === WidgetType.LECTURES || + typeId === WidgetType.EXAMS || + typeId === WidgetType.DEADLINE + ) +} + +export function createWidget(event: Event) { + const days = [ + "Domenica", + "Lunedì", + "Martedì", + "Mercoledì", + "Giovedì", + "Venerdì", + "Sabato", + ] + const months = [ + "Gennaio", + "Febbraio", + "Marzo", + "Aprile", + "Maggio", + "Giugno", + "Luglio", + "Agosto", + "Settembre", + "Ottobre", + "Novembre", + "Dicembre", + ] + const dateObj = new Date(event.date_start) + const resultDate = + days[dateObj.getDay()] + + " " + + dateObj.getDate().toString().padStart(2, "0") + + " " + + months[dateObj.getMonth()] + + " " + + dateObj.getFullYear() + const nextEvent: CarouselItem = { + id: event.event_id, + type: event.event_type.typeId, + date: resultDate, + time: event.date_start.toString().slice(11, 16), + title: event.title.it, + room: event.room?.acronym_dn, + } + return nextEvent +} diff --git a/src/utils/colors.ts b/src/utils/colors.ts index 5ca97bdb..279e0da1 100644 --- a/src/utils/colors.ts +++ b/src/utils/colors.ts @@ -1,24 +1,18 @@ import { useContext } from "react" import { useColorScheme, ColorSchemeName } from "react-native" -import { SettingsContext } from "./settings" - -export interface Palette { - primary: string - lighter: string - darker: string - variant1: string - variant2: string - accent: string -} +import { SettingsContext } from "contexts/settings" -const palette: Palette = { - primary: "#424967", - lighter: "#8791BD", - darker: "#232A3E", - variant1: "#414867", - variant2: "#010B40", - accent: "#FFB544", -} +const palette = { + primary: "#424967", + lighter: "#8791BD", + darker: "#232A3E", + lessDark: "#2B344A", + variant1: "#414867", + variant2: "#010B40", + accent: "#FFB544", +} as const + +export type Palette = typeof palette /** * Interface containing strings with the hex values for most used colors in the app, additional info @@ -29,91 +23,91 @@ const palette: Palette = { * *"Stop Remote Debugging"* */ export interface ColorTheme { - /** - * Default background color for most UI views. - * White in light mode, deep blue in dark mode. - */ - background: string - - /** - * Accent background color for stacked UI elements, a slightly lighter version of the background - * color. - */ - backgroundSecondary: string - - /** - * Background for the home screen. - * Lilac in light theme, deep blue in dark theme. - */ - homeBackground: string - - /** - * Primary color, used mostly in titles. - * Deep blue in dark mode, orange in light mode. - * Im not a designer i dont know colors - */ - primary: string - - /** - * Color for bulk text, e.g the body for an article. - * Straight up black in dark mode, almost white in light mode. - */ - bodyText: string - - /** - * Fill color for most buttons that do not require sudden attention. - * Gray in light mode, lilac again in dark mode. - */ - buttonFill: string - - /** - * Text color for most buttons. - * Yeah that's purple i know this one. - */ - buttonText: string - - /** - * Fill color for the search button - */ - fieldText: string - - /** - * Darker in white mode, Lighter in dark mode background color, used in the text input fields. - */ - fieldBackground: string - - /** - * Fill color for modal barriers. - * different shades of blue I guess - */ - modalBarrier: string - - /** - * Color for article title - */ - articleTitle: string - - /** - * Color for article subtitle - */ - articleSubtitle: string - - /** - * Dark blue used for the title in cards with a background image and a yellowish gradient. - */ - cardTitle: string + /** + * Default background color for most UI views. + * White in light mode, deep blue in dark mode. + */ + background: string + + /** + * Accent background color for stacked UI elements, a slightly lighter version of the background + * color. + */ + backgroundSecondary: string + + /** + * Background for the home screen. + * Lilac in light theme, deep blue in dark theme. + */ + homeBackground: string + + /** + * Primary color, used mostly in titles. + * Deep blue in dark mode, orange in light mode. + * Im not a designer i dont know colors + */ + primary: string + + /** + * Color for bulk text, e.g the body for an article. + * Straight up black in dark mode, almost white in light mode. + */ + bodyText: string + + /** + * Fill color for most buttons that do not require sudden attention. + * Gray in light mode, lilac again in dark mode. + */ + buttonFill: string + + /** + * Text color for most buttons. + * Yeah that's purple i know this one. + */ + buttonText: string + + /** + * Fill color for the search button + */ + fieldText: string + + /** + * Darker in white mode, Lighter in dark mode background color, used in the text input fields. + */ + fieldBackground: string + + /** + * Fill color for modal barriers. + * different shades of blue I guess + */ + modalBarrier: string + + /** + * Color for article title + */ + articleTitle: string + + /** + * Color for article subtitle + */ + articleSubtitle: string + + /** + * Dark blue used for the title in cards with a background image and a yellowish gradient. + */ + cardTitle: string } /** * contains info about the color theme, taken from react-native's [useColorScheme hook](https://reactnative.dev/docs/usecolorscheme) */ export interface ColorSchemeInfo { - /** true when in light mode */ - isLight: boolean - /** true when in dark mode */ - isDark: boolean - /** see {@link https://reactnative.dev/docs/usecolorscheme} */ - colorScheme: NonNullable + /** true when in light mode */ + isLight: boolean + /** true when in dark mode */ + isDark: boolean + /** see {@link https://reactnative.dev/docs/usecolorscheme} */ + colorScheme: NonNullable } /** @@ -154,66 +148,66 @@ export interface ColorSchemeInfo { * ``` */ export const usePalette: () => ColorTheme & - ColorSchemeInfo & { - /** object containing the colors of the palette */ - palette: Palette - /** the colors for the light theme, see {@link ColorTheme} */ - lightTheme: ColorTheme - /** the colors for the dark theme, see {@link ColorTheme} */ - darkTheme: ColorTheme - } = () => { - const context = useContext(SettingsContext) - const chosenTheme = context.settings.theme - - let colorScheme = useColorScheme() ?? "light" - if (chosenTheme !== "predefined") { - colorScheme = chosenTheme - } - - const isDark = colorScheme === "dark" - const isLight = !isDark - - const lightTheme: ColorTheme = { - background: "#FFFFFF", - backgroundSecondary: "#FFFFFF", - homeBackground: palette.primary, // "#424967", - primary: palette.variant1, // "#414867", - bodyText: "#000000", - buttonFill: palette.primary, // "#424967", - buttonText: "#FFFFFF", - fieldText: palette.primary, //"#424967", - fieldBackground: "#F6F7FC", - modalBarrier: "rgba(1, 27, 41, 0.45)", - articleTitle: palette.darker, - articleSubtitle: palette.primary, - cardTitle: palette.variant2, // "#010B40" - } - - const darkTheme: ColorTheme = { - background: palette.darker, // "#232A3E", - backgroundSecondary: palette.primary, // "#424967", - homeBackground: palette.darker, // "#232A3E", - primary: palette.accent, // "#FFB544", - bodyText: "#FFFFFF", - buttonFill: palette.lighter, // "#8791BD", - buttonText: "#FFFFFF", - fieldText: "#D4D4D4", - fieldBackground: "#343E5A", - modalBarrier: "rgba(1, 27, 41, 0.6)", - articleTitle: "#FFFFFF", - articleSubtitle: palette.lighter, - cardTitle: palette.variant2, // "#010B40" - } - - const colors = isLight ? lightTheme : darkTheme - - return { - ...colors, - isLight, - isDark, - colorScheme, - lightTheme, - darkTheme, - palette, - } + ColorSchemeInfo & { + /** object containing the colors of the palette */ + palette: Palette + /** the colors for the light theme, see {@link ColorTheme} */ + lightTheme: ColorTheme + /** the colors for the dark theme, see {@link ColorTheme} */ + darkTheme: ColorTheme + } = () => { + const context = useContext(SettingsContext) + const chosenTheme = context.settings.theme + + let colorScheme = useColorScheme() ?? "light" + if (chosenTheme !== "predefined") { + colorScheme = chosenTheme + } + + const isDark = colorScheme === "dark" + const isLight = !isDark + + const lightTheme: ColorTheme = { + background: "#FFFFFF", + backgroundSecondary: "#FFFFFF", + homeBackground: palette.primary, // "#424967", + primary: palette.variant1, // "#414867", + bodyText: "#000000", + buttonFill: palette.primary, // "#424967", + buttonText: "#FFFFFF", + fieldText: palette.primary, //"#424967", + fieldBackground: "#F6F7FC", + modalBarrier: "rgba(1, 27, 41, 0.45)", + articleTitle: palette.darker, + articleSubtitle: palette.primary, + cardTitle: palette.variant2, // "#010B40" + } + + const darkTheme: ColorTheme = { + background: palette.darker, // "#232A3E", + backgroundSecondary: palette.primary, // "#424967", + homeBackground: palette.darker, // "#232A3E", + primary: palette.accent, // "#FFB544", + bodyText: "#FFFFFF", + buttonFill: palette.lighter, // "#8791BD", + buttonText: "#FFFFFF", + fieldText: "#D4D4D4", + fieldBackground: "#343E5A", + modalBarrier: "rgba(1, 27, 41, 0.6)", + articleTitle: "#FFFFFF", + articleSubtitle: palette.lighter, + cardTitle: palette.variant2, // "#010B40" + } + + const colors = isLight ? lightTheme : darkTheme + + return { + ...colors, + isLight, + isDark, + colorScheme, + lightTheme, + darkTheme, + palette, + } } diff --git a/src/utils/functions.ts b/src/utils/functions.ts index e3f352b2..e7f527e5 100644 --- a/src/utils/functions.ts +++ b/src/utils/functions.ts @@ -1,20 +1,22 @@ /** - * takes as input a number, let's say "n". - * Returns the ISO date string of "n" days ago + * @param date reference date + * @param n number + * + * @returns the date of "n" days before the reference date */ -export function getIsoStringFromDaysPassed(n: number): string { - //today's date in milliseconds from the beginning of time - const time = Date.now() +export function getDateFromDaysBefore(date: Date, n: number): Date { + //today's date in milliseconds from the beginning of time + const time = date.getTime() - //milliseconds in n days - const timeToSubtract = n * 24 * 60 * 60 * 1000 + //milliseconds in n days + const timeToSubtract = n * 24 * 60 * 60 * 1000 - //n-days-ago's date in milliseconds from the beginning of time - const newTime = time - timeToSubtract - //n-days-ago's date - const newDate = new Date(newTime) + //n-days-ago's date in milliseconds from the beginning of time + const newTime = time - timeToSubtract + //n-days-ago's date + const newDate = new Date(newTime) - return newDate.toISOString() + return newDate } /** @@ -22,12 +24,12 @@ export function getIsoStringFromDaysPassed(n: number): string { * ready for use of {@link DateTimePicker} */ export function destructureDate(date: Date) { - const year = date.getFullYear().toString().substring(2, 4) //only last two digits - const month = (date.getMonth() + 1).toString().padStart(2, "0") - const day = date.getDate().toString().padStart(2, "0") - const hour = date.getHours().toString().padStart(2, "0") - const minute = date.getMinutes().toString().padStart(2, "0") - return { year, month, day, hour, minute } + const year = date.getFullYear().toString().substring(2, 4) //only last two digits + const month = (date.getMonth() + 1).toString().padStart(2, "0") + const day = date.getDate().toString().padStart(2, "0") + const hour = date.getHours().toString().padStart(2, "0") + const minute = date.getMinutes().toString().padStart(2, "0") + return { year, month, day, hour, minute } } /** @@ -35,5 +37,5 @@ export function destructureDate(date: Date) { * @param ms milliseconds to wait */ export function wait(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)) + return new Promise(resolve => setTimeout(resolve, ms)) } diff --git a/src/utils/groups.ts b/src/utils/groups.ts index 0d17fff8..07636e6f 100644 --- a/src/utils/groups.ts +++ b/src/utils/groups.ts @@ -5,90 +5,87 @@ import { platformIcons } from "assets/groups" * see {@link Groups} Page */ export function orderByMostRecentYear(groups: Group[]) { - let hasChanged: boolean - try { - do { - hasChanged = false - for (let n = 0; n < groups.length - 1; n++) { - if ( - compareBiYear(groups[n].year, groups[n + 1].year) === true - ) { - //swap - const temp = groups[n] - groups[n] = groups[n + 1] - groups[n + 1] = temp - hasChanged = true - } - } - } while (hasChanged === true) - } catch (error) { - console.log(error) - } - return groups + let hasChanged: boolean + try { + do { + hasChanged = false + for (let n = 0; n < groups.length - 1; n++) { + if (compareBiYear(groups[n].year, groups[n + 1].year) === true) { + //swap + const temp = groups[n] + groups[n] = groups[n + 1] + groups[n + 1] = temp + hasChanged = true + } + } + } while (hasChanged === true) + } catch (error) { + console.log(error) + } + return groups } function compareBiYear(first: string | null, second: string | null) { - //apparently null != undefined :( code breaks if I use undefined instead of null - if ((first === "?/?" || first === null) && second !== null) { - return true - } else if (second === null || first === null) { - return false + //apparently null != undefined :( code breaks if I use undefined instead of null + if ((first === "?/?" || first === null) && second !== null) { + return true + } else if (second === null || first === null) { + return false + } + //standard year format "2021/2022" + const regexStandard = /^\d{4}\/\d{4}$/ + //for inconsistencies in the db ex "2021/22" + const regexNonStandard = /^\d{4}\/\d{2}$/ + if ( + (regexStandard.test(first) && regexStandard.test(second)) || + (regexNonStandard.test(first) && regexNonStandard.test(second)) + ) { + if (parseInt(first.substring(5)) < parseInt(second.substring(5))) { + return true } - //standard year format "2021/2022" - const regexStandard = /^\d{4}\/\d{4}$/ - //for inconsistencies in the db ex "2021/22" - const regexNonStandard = /^\d{4}\/\d{2}$/ - if ( - (regexStandard.test(first) && regexStandard.test(second)) || - (regexNonStandard.test(first) && regexNonStandard.test(second)) - ) { - if (parseInt(first.substring(5)) < parseInt(second.substring(5))) { - return true - } - } else if (regexNonStandard.test(first) && regexStandard.test(second)) { - if (parseInt(first.substring(5)) < parseInt(second.substring(7))) { - return true - } - } else if (regexStandard.test(first) && regexNonStandard.test(second)) { - if (parseInt(first.substring(7)) < parseInt(second.substring(5))) { - return true - } + } else if (regexNonStandard.test(first) && regexStandard.test(second)) { + if (parseInt(first.substring(5)) < parseInt(second.substring(7))) { + return true } - return false + } else if (regexStandard.test(first) && regexNonStandard.test(second)) { + if (parseInt(first.substring(7)) < parseInt(second.substring(5))) { + return true + } + } + return false } export type ValidModalType = "year" | "course" | "type" | "platform" export const getNameFromMode = (mode: ValidModalType) => { - if (mode === "year") { - return "Anno" - } else if (mode === "course") { - return "Corso" - } else if (mode === "platform") { - return "Piattaforma" - } else { - return "Tipo" - } + if (mode === "year") { + return "Anno" + } else if (mode === "course") { + return "Corso" + } else if (mode === "platform") { + return "Piattaforma" + } else { + return "Tipo" + } } export function createGroupLink(idLink: string, platform: string) { - if (platform === "TG") { - return `https://t.me/joinchat/${idLink}` - } else if (platform === "WA") { - return `https://chat.whatsapp.com/${idLink}` - } else { - return `https://www.facebook.com/groups/${idLink}` - } + if (platform === "TG") { + return `https://t.me/joinchat/${idLink}` + } else if (platform === "WA") { + return `https://chat.whatsapp.com/${idLink}` + } else { + return `https://www.facebook.com/groups/${idLink}` + } } - -export function choosePlatformIcon(platform? : string){ - if(platform === "TG"){ - return platformIcons.telegram - }else if(platform ==="FB"){ - return platformIcons.facebook - }else if(platform === "WA"){ - return platformIcons.whatsapp - } - return undefined -} \ No newline at end of file +export function choosePlatformIcon(platform?: string) { + if (platform === "TG") { + return platformIcons.telegram + } else if (platform === "FB") { + return platformIcons.facebook + } else if (platform === "WA") { + return platformIcons.whatsapp + } + return undefined +} diff --git a/src/utils/height.ts b/src/utils/height.ts index 0e3c83bc..7e7fb3a1 100644 --- a/src/utils/height.ts +++ b/src/utils/height.ts @@ -12,23 +12,23 @@ import { getStatusBarHeight } from "react-native-status-bar-height" * using the onLayout prop, and then put that value in a context so that it can be used everywhere */ export const getUsableScreenHeight: () => number = () => { - const windowHeight = Dimensions.get("window").height - const screenHeight = Dimensions.get("screen").height - const statusBarHeight = getStatusBarHeight() - const minBottomBarHeight = 40 // usually the height of the android bottom bar is 42px or 48px + const windowHeight = Dimensions.get("window").height + const screenHeight = Dimensions.get("screen").height + const statusBarHeight = getStatusBarHeight() + const minBottomBarHeight = 40 // usually the height of the android bottom bar is 42px or 48px - // On IOS the WindowHeight always include the StatusbarHeight - if (Platform.OS === "ios") { - return windowHeight - } + // On IOS the WindowHeight always include the StatusbarHeight + if (Platform.OS === "ios") { + return windowHeight + } - // On ANDROID if you take the whole ScreenHeight, you subtract the WindowHeight - // and the StatusbarHeight, and there is not enough space left for the bottom navigation bar, - // it means that the WindowHeight already includes the StatusbarHeight - if (screenHeight - windowHeight - statusBarHeight < minBottomBarHeight) { - return windowHeight - } + // On ANDROID if you take the whole ScreenHeight, you subtract the WindowHeight + // and the StatusbarHeight, and there is not enough space left for the bottom navigation bar, + // it means that the WindowHeight already includes the StatusbarHeight + if (screenHeight - windowHeight - statusBarHeight < minBottomBarHeight) { + return windowHeight + } - // ANDROID if the WindowHeight doesn't already include the StatusBarHeight - return windowHeight + statusBarHeight + // ANDROID if the WindowHeight doesn't already include the StatusBarHeight + return windowHeight + statusBarHeight } diff --git a/src/utils/loadTokens.ts b/src/utils/loadTokens.ts index cdff9b85..e28ce443 100644 --- a/src/utils/loadTokens.ts +++ b/src/utils/loadTokens.ts @@ -9,9 +9,9 @@ const client = HttpClient.getInstance() * @returns true if the promise has returned, false otherwise */ export const useLoadTokens = () => { - const [loaded, setLoaded] = React.useState(false) - React.useEffect(() => { - void client.loadTokens().then(() => setLoaded(true)) - }, [client]) // this has to be here for hot reload to work - return loaded + const [loaded, setLoaded] = React.useState(false) + React.useEffect(() => { + void client.loadTokens().then(() => setLoaded(true)) + }, [client]) // this has to be here for hot reload to work + return loaded } diff --git a/src/utils/login.ts b/src/utils/login.ts deleted file mode 100644 index 61ebccba..00000000 --- a/src/utils/login.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { User } from "api/user" -import React from "react" - - -/* eslint-disable @typescript-eslint/naming-convention */ - -export interface PoliNetworkToken { - access_token: string - expires_in: number - ext_expires_in: number - id_token: string - refresh_token: string - scope: string - token_type: string -} - -export interface PolimiToken { - accessToken: string - expiresIn: number - refreshToken: string -} - -export interface Tokens { - polimiToken: PolimiToken - poliNetworkToken: PoliNetworkToken -} - -export type LoginState = - | { - loggedIn: false - userInfo?: undefined - } - | { - loggedIn: true - userInfo: User - } - -export type ILoginContext = LoginState & { - setLoginState(newState: LoginState): void -} - -export const LoginContext = React.createContext({ - loggedIn: false, - setLoginState: () => null, -}) diff --git a/src/utils/outsideClick.tsx b/src/utils/outsideClick.tsx index efbd260d..3756017d 100644 --- a/src/utils/outsideClick.tsx +++ b/src/utils/outsideClick.tsx @@ -1,27 +1,25 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import React, { - FC, - useCallback, - useContext, - useRef, - useEffect, - useState, - createContext, + FC, + useCallback, + useContext, + useRef, + useEffect, + useState, + createContext, } from "react" import { GestureResponderEvent, View, ViewProps } from "react-native" export const outsideClickContext = createContext<{ - addListener: (listener: (event: GestureResponderEvent) => boolean) => void - removeListener: ( - listener: (event: GestureResponderEvent) => boolean - ) => void + addListener: (listener: (event: GestureResponderEvent) => boolean) => void + removeListener: (listener: (event: GestureResponderEvent) => boolean) => void }>({ - addListener: () => { - // noop - }, - removeListener: () => { - // noop - }, + addListener: () => { + // noop + }, + removeListener: () => { + // noop + }, }) /** @@ -31,23 +29,23 @@ export const outsideClickContext = createContext<{ * @returns false if the click is not within the component, true otherwise */ function isTapInsideComponent( - target: GestureResponderEvent["target"], - component: React.Component + target: GestureResponderEvent["target"], + component: React.Component ) { - if (target === component) return true + if (target === component) return true - // everything below here is fucked beyond repair - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any - const curr = component as any + // everything below here is fucked beyond repair + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any + const curr = component as any - if (curr._children && curr._children.length) { - // check all of the children until one is the target - for (const child of curr._children) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - if (child && isTapInsideComponent(target, child)) return true - } + if (curr._children && curr._children.length) { + // check all of the children until one is the target + for (const child of curr._children) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + if (child && isTapInsideComponent(target, child)) return true } - return false + } + return false } /** @@ -74,36 +72,36 @@ function isTapInsideComponent( * @returns a ref that must be used to link the component the click should be outside of */ export function useOutsideClick( - callback: () => void, - listening: boolean + callback: () => void, + listening: boolean ) { - const ref = useRef(null) + const ref = useRef(null) - const listener = useCallback( - (event: GestureResponderEvent) => { - if ( - listening && - ref.current && - !isTapInsideComponent(event.target, ref.current) - ) { - // outside click! - callback() - return true // the outside click handler should set responder - } - return false - }, - [listening] - ) + const listener = useCallback( + (event: GestureResponderEvent) => { + if ( + listening && + ref.current && + !isTapInsideComponent(event.target, ref.current) + ) { + // outside click! + callback() + return true // the outside click handler should set responder + } + return false + }, + [listening] + ) - const { addListener, removeListener } = useContext(outsideClickContext) + const { addListener, removeListener } = useContext(outsideClickContext) - useEffect(() => { - if (!listening) return // only add the listener while, well, listening + useEffect(() => { + if (!listening) return // only add the listener while, well, listening - addListener(listener) - return () => removeListener(listener) // remove on cleanup - }, [addListener, removeListener, listener, listening]) - return ref + addListener(listener) + return () => removeListener(listener) // remove on cleanup + }, [addListener, removeListener, listener, listening]) + return ref } const { Provider } = outsideClickContext @@ -112,58 +110,57 @@ const { Provider } = outsideClickContext * OutsideClick context provider, also wraps children in a view used to listen to global click events */ export const OutsideClickProvider: FC< - { children: React.ReactNode } & ViewProps + { children: React.ReactNode } & ViewProps > = ({ children, ...viewProps }) => { - // a set of listeners actively listening for a gloabl click event - const [listeners, setListeners] = useState< - Set<(evt: GestureResponderEvent) => boolean> - >(new Set()) + // a set of listeners actively listening for a gloabl click event + const [listeners, setListeners] = useState< + Set<(evt: GestureResponderEvent) => boolean> + >(new Set()) - const addListener = useCallback( - (listener: (evt: GestureResponderEvent) => boolean) => { - setListeners(listeners => { - const newListeners = new Set(listeners) - newListeners.add(listener) - return newListeners - }) - }, - [] - ) + const addListener = useCallback( + (listener: (evt: GestureResponderEvent) => boolean) => { + setListeners(listeners => { + const newListeners = new Set(listeners) + newListeners.add(listener) + return newListeners + }) + }, + [] + ) - const removeListener = useCallback( - (listener: (evt: GestureResponderEvent) => boolean) => { - setListeners(listeners => { - const newListeners = new Set(listeners) - newListeners.delete(listener) - return newListeners - }) - }, - [] - ) + const removeListener = useCallback( + (listener: (evt: GestureResponderEvent) => boolean) => { + setListeners(listeners => { + const newListeners = new Set(listeners) + newListeners.delete(listener) + return newListeners + }) + }, + [] + ) - const globalClickEvent: (evt: GestureResponderEvent) => boolean = - useCallback( - evt => { - let result = false - listeners.forEach(listener => { - result = listener(evt) || result - }) - return result - }, - [listeners] - ) + const globalClickEvent: (evt: GestureResponderEvent) => boolean = useCallback( + evt => { + let result = false + listeners.forEach(listener => { + result = listener(evt) || result + }) + return result + }, + [listeners] + ) - return ( - - { - return globalClickEvent(evt) - }} - > - {children} - - - ) + return ( + + { + return globalClickEvent(evt) + }} + > + {children} + + + ) } diff --git a/src/utils/rooms.ts b/src/utils/rooms.ts index fd8c124c..e8f081eb 100644 --- a/src/utils/rooms.ts +++ b/src/utils/rooms.ts @@ -1,20 +1,20 @@ import { ValidCrowdStatus } from "components/FreeClass/ClassDetails/CrowdingSection" export function extractRoom(val: string) { - const arr = val.split(".") - if (arr.length > 2) { - return arr.slice(1).join(".") - } else { - return val - } + const arr = val.split(".") + if (arr.length > 2) { + return arr.slice(1).join(".") + } else { + return val + } } export function extractBuilding(val: string) { - const arr = val.split(" ") - if (arr.length === 2) { - return arr[1] - } - return undefined + const arr = val.split(" ") + if (arr.length === 2) { + return arr[1] + } + return undefined } /* function containsNumber(val: string) { @@ -24,34 +24,33 @@ export function extractBuilding(val: string) { } */ export function extractTimeLeft(startDate: Date) { - const deltaMilliseconds = startDate.getTime() - Date.now() - const hours = Math.floor(deltaMilliseconds / 3.6e6) - const minutes = Math.floor( - (deltaMilliseconds - hours * 60 * 60 * 1000) / 60000 - ) - const hoursLeft = hours.toString() - const minutesLeft = minutes.toString() - const isPositive = hours >= 0 && minutes >= 0 - return { hoursLeft, minutesLeft, isPositive } + const deltaMilliseconds = startDate.getTime() - Date.now() + const hours = Math.floor(deltaMilliseconds / 3.6e6) + const minutes = Math.floor( + (deltaMilliseconds - hours * 60 * 60 * 1000) / 60000 + ) + const hoursLeft = hours.toString() + const minutesLeft = minutes.toString() + const isPositive = hours >= 0 && minutes >= 0 + return { hoursLeft, minutesLeft, isPositive } } export function getCrowdStatus(pos: number, width: number): ValidCrowdStatus { - if (pos < 0) { - pos = 0 - } + if (pos < 0) { + pos = 0 + } - const radius = 14 - const percentage = (pos + radius) / (width + 2 * radius) - if (percentage < 0.2) { - return 1 - } else if (percentage < 0.4) { - return 2 - } else if (percentage < 0.6) { - return 3 - } else if (percentage < 0.8) { - return 4 - } else { - return 5 - } + const radius = 14 + const percentage = (pos + radius) / (width + 2 * radius) + if (percentage < 0.2) { + return 1 + } else if (percentage < 0.4) { + return 2 + } else if (percentage < 0.6) { + return 3 + } else if (percentage < 0.8) { + return 4 + } else { + return 5 + } } - diff --git a/src/utils/strings.ts b/src/utils/strings.ts new file mode 100644 index 00000000..e06e7fbb --- /dev/null +++ b/src/utils/strings.ts @@ -0,0 +1,18 @@ +/** + * @param str a word or a phrase + * @param notCapitalize do not capitalize the words that have a length lower than this value + * @default 0 + * + * @returns the capaitalized string + */ +export function capitalize(str: string, notCapitalize = 0): string { + const arr = str.split(" ") + for (let i = 0; i < arr.length; i++) { + if (arr[i].length <= notCapitalize) { + arr[i] = arr[i].toLowerCase() + } else { + arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].slice(1).toLowerCase() + } + } + return arr.join(" ") +} diff --git a/src/utils/useMounted.ts b/src/utils/useMounted.ts index 3c36dc4b..3c0575d1 100644 --- a/src/utils/useMounted.ts +++ b/src/utils/useMounted.ts @@ -34,10 +34,10 @@ import { useEffect, useState } from "react" * ``` */ export function useMounted() { - const [isMounted, setIsMounted] = useState(false) + const [isMounted, setIsMounted] = useState(false) - useEffect(() => { - setIsMounted(true) - }, []) - return isMounted + useEffect(() => { + setIsMounted(true) + }, []) + return isMounted } diff --git a/tsconfig.json b/tsconfig.json index ad206c0e..27f58faf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,19 @@ { - "extends": "expo/tsconfig.base", - "compilerOptions": { - "jsx": "react", - "strict": true, - "baseUrl": ".", - "paths": { - "assets/*": ["assets/*"], - "components/*": ["src/components/*"], - "navigation/*": ["src/navigation/*"], - "pages/*": ["src/pages/*"], - "utils/*": ["src/utils/*"], - "api": ["src/api"], - "api/*": ["src/api/*"] - }, - "forceConsistentCasingInFileNames": true - } + "extends": "expo/tsconfig.base", + "compilerOptions": { + "jsx": "react", + "strict": true, + "baseUrl": ".", + "paths": { + "assets/*": ["assets/*"], + "components/*": ["src/components/*"], + "navigation/*": ["src/navigation/*"], + "pages/*": ["src/pages/*"], + "utils/*": ["src/utils/*"], + "contexts/*": ["src/contexts/*"], + "api": ["src/api"], + "api/*": ["src/api/*"] + }, + "forceConsistentCasingInFileNames": true + } } diff --git a/yarn.lock b/yarn.lock index ec746771..c8414e89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,12 +29,7 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" - integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== - -"@babel/compat-data@^7.20.5": +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": version "7.20.10" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== @@ -61,67 +56,28 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.13.16", "@babel/core@^7.14.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59" - integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.9" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-module-transforms" "^7.18.9" - "@babel/helpers" "^7.18.9" - "@babel/parser" "^7.18.9" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - -"@babel/core@^7.19.3": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.7.tgz#37072f951bd4d28315445f66e0ec9f6ae0c8c35f" - integrity sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw== +"@babel/core@^7.13.16", "@babel/core@^7.14.0", "@babel/core@^7.19.3": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" + integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" "@babel/generator" "^7.20.7" "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.7" + "@babel/helper-module-transforms" "^7.20.11" "@babel/helpers" "^7.20.7" "@babel/parser" "^7.20.7" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" + "@babel/traverse" "^7.20.12" "@babel/types" "^7.20.7" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.1" + json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.14.0", "@babel/generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" - integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== - dependencies: - "@babel/types" "^7.18.9" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/generator@^7.19.0", "@babel/generator@^7.9.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.0.tgz#785596c06425e59334df2ccee63ab166b738419a" - integrity sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg== - dependencies: - "@babel/types" "^7.19.0" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/generator@^7.20.7": +"@babel/generator@^7.14.0", "@babel/generator@^7.20.7", "@babel/generator@^7.9.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== @@ -145,17 +101,7 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" - integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== - dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.20.2" - semver "^6.3.0" - -"@babel/helper-compilation-targets@^7.20.7": +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== @@ -166,31 +112,32 @@ lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" - integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5", "@babel/helper-create-class-features-plugin@^7.20.7": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" + integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-member-expression-to-functions" "^7.20.7" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/helper-split-export-declaration" "^7.18.6" -"@babel/helper-create-regexp-features-plugin@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" - integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" + integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.1.0" + regexpu-core "^5.2.1" -"@babel/helper-define-polyfill-provider@^0.3.1", "@babel/helper-define-polyfill-provider@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" - integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== +"@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== dependencies: "@babel/helper-compilation-targets" "^7.17.7" "@babel/helper-plugin-utils" "^7.16.7" @@ -199,7 +146,7 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.6", "@babel/helper-environment-visitor@^7.18.9": +"@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== @@ -211,15 +158,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" - integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== - dependencies: - "@babel/template" "^7.18.6" - "@babel/types" "^7.18.9" - -"@babel/helper-function-name@^7.19.0": +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== @@ -234,12 +173,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" - integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== +"@babel/helper-member-expression-to-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" + integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.20.7" "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.18.6": version "7.18.6" @@ -248,21 +187,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" - integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.18.6" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-module-transforms@^7.20.7": +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.9.0": version "7.20.11" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== @@ -276,20 +201,6 @@ "@babel/traverse" "^7.20.10" "@babel/types" "^7.20.7" -"@babel/helper-module-transforms@^7.9.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" - integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.18.6" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.0" - "@babel/types" "^7.19.0" - "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -297,17 +208,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" - integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== - -"@babel/helper-plugin-utils@^7.20.2": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== -"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": +"@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== @@ -317,23 +223,17 @@ "@babel/helper-wrap-function" "^7.18.9" "@babel/types" "^7.18.9" -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" - integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== dependencies: "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-simple-access@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" - integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== - dependencies: - "@babel/types" "^7.18.6" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" "@babel/helper-simple-access@^7.20.2": version "7.20.2" @@ -342,12 +242,12 @@ dependencies: "@babel/types" "^7.20.2" -"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" - integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== dependencies: - "@babel/types" "^7.18.9" + "@babel/types" "^7.20.0" "@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" @@ -356,22 +256,12 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" - integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== - "@babel/helper-string-parser@^7.19.4": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== -"@babel/helper-validator-identifier@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" - integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== - -"@babel/helper-validator-identifier@^7.19.1": +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": version "7.19.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== @@ -382,25 +272,16 @@ integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== "@babel/helper-wrap-function@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz#ae1feddc6ebbaa2fd79346b77821c3bd73a39646" - integrity sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ== - dependencies: - "@babel/helper-function-name" "^7.18.9" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helpers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" - integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== dependencies: - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" -"@babel/helpers@^7.20.7": +"@babel/helpers@^7.20.7", "@babel/helpers@^7.9.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce" integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA== @@ -409,15 +290,6 @@ "@babel/traverse" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/helpers@^7.9.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.0.tgz#f30534657faf246ae96551d88dd31e9d1fa1fc18" - integrity sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg== - dependencies: - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.19.0" - "@babel/types" "^7.19.0" - "@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -427,22 +299,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" - integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== - -"@babel/parser@^7.18.10": - version "7.18.11" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" - integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== - -"@babel/parser@^7.19.1", "@babel/parser@^7.9.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c" - integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A== - -"@babel/parser@^7.20.7": +"@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.20.7", "@babel/parser@^7.9.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== @@ -455,34 +312,24 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" - integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" + integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" - "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" -"@babel/plugin-proposal-async-generator-functions@^7.0.0": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz#85ea478c98b0095c3e4102bff3b67d306ed24952" - integrity sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew== +"@babel/plugin-proposal-async-generator-functions@^7.0.0", "@babel/plugin-proposal-async-generator-functions@^7.20.1": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== dependencies: "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-async-generator-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz#aedac81e6fc12bb643374656dd5f2605bf743d17" - integrity sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w== - dependencies: - "@babel/helper-environment-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-remap-async-to-generator" "^7.18.6" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" @@ -492,24 +339,24 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-class-static-block@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" - integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz#92592e9029b13b15be0f7ce6a7aedc2879ca45a7" + integrity sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-decorators@^7.12.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.9.tgz#d09d41ffc74af8499d2ac706ed0dbd5474711665" - integrity sha512-KD7zDNaD14CRpjQjVbV4EnH9lsKYlcpUrhZH37ei2IY+AlXrfAPy5pTmRUE4X6X1k8EsKXPraykxeaogqQvSGA== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.7.tgz#05d37453c2ce818f3e47bbeda9468c8de947eecc" + integrity sha512-JB45hbUweYpwAGjkiM7uCyXMENH2lG+9r3G2E+ttc2PRXAoEkpfd/KW5jDg4j8RS6tLtTG1jZi9LbHZVSfs1/A== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-create-class-features-plugin" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/plugin-syntax-decorators" "^7.18.6" + "@babel/plugin-syntax-decorators" "^7.19.0" "@babel/plugin-proposal-dynamic-import@^7.18.6": version "7.18.6" @@ -520,9 +367,9 @@ "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-proposal-export-default-from@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.9.tgz#9dfad26452e53cae8f045c6153e82dc50e9bee89" - integrity sha512-1qtsLNCDm5awHLIt+2qAFDi31XC94r4QepMQcOosC7FpY6O+Bgay5f2IyAQt2wvm1TARumpFprnQt5pTIJ9nUg== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.10.tgz#091f4794dbce4027c03cf4ebc64d3fb96b75c206" + integrity sha512-5H2N3R2aQFxkV4PIBUR/i7PUSwgTZjouJKzI8eKswfIjT0PhvzkPn0t0wIS5zn6maQuvtT0t1oHtMUz61LOuow== dependencies: "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-default-from" "^7.18.6" @@ -544,11 +391,11 @@ "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-proposal-logical-assignment-operators@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" - integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": @@ -567,18 +414,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.0.0", "@babel/plugin-proposal-object-rest-spread@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" - integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== - dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.18.8" - -"@babel/plugin-proposal-object-rest-spread@^7.12.13": +"@babel/plugin-proposal-object-rest-spread@^7.0.0", "@babel/plugin-proposal-object-rest-spread@^7.12.13", "@babel/plugin-proposal-object-rest-spread@^7.20.2": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== @@ -597,13 +433,13 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" - integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== +"@babel/plugin-proposal-optional-chaining@^7.0.0", "@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz#49f2b372519ab31728cc14115bb0998b15bfda55" + integrity sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.18.6": @@ -615,13 +451,13 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-private-property-in-object@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" - integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz#309c7668f2263f1c711aa399b5a9a6291eef6135" + integrity sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": @@ -653,12 +489,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-decorators@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz#2e45af22835d0b0f8665da2bfd4463649ce5dbc1" - integrity sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ== +"@babel/plugin-syntax-decorators@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz#5f13d1d8fce96951bea01a10424463c9a5b3a599" + integrity sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-dynamic-import@^7.0.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" @@ -688,12 +524,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-syntax-import-assertions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz#cd6190500a4fa2fe31990a963ffab4b63e4505e4" - integrity sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ== +"@babel/plugin-syntax-import-assertions@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" + integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" @@ -765,28 +601,28 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" - integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== +"@babel/plugin-syntax-typescript@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" + integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-arrow-functions@^7.0.0", "@babel/plugin-transform-arrow-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" - integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" + integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-async-to-generator@^7.0.0", "@babel/plugin-transform-async-to-generator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" - integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== dependencies: "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-remap-async-to-generator" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-transform-block-scoped-functions@^7.0.0", "@babel/plugin-transform-block-scoped-functions@^7.18.6": version "7.18.6" @@ -795,40 +631,42 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-block-scoping@^7.0.0", "@babel/plugin-transform-block-scoping@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" - integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== +"@babel/plugin-transform-block-scoping@^7.0.0", "@babel/plugin-transform-block-scoping@^7.20.2": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz#9f5a3424bd112a3f32fe0cf9364fbb155cff262a" + integrity sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da" - integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g== +"@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.20.2": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" + integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0", "@babel/plugin-transform-computed-properties@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" - integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" + integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" -"@babel/plugin-transform-destructuring@^7.0.0", "@babel/plugin-transform-destructuring@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292" - integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA== +"@babel/plugin-transform-destructuring@^7.0.0", "@babel/plugin-transform-destructuring@^7.20.2": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" + integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.18.6" @@ -854,11 +692,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-flow-strip-types@^7.0.0", "@babel/plugin-transform-flow-strip-types@^7.18.6": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.9.tgz#5b4cc521426263b5ce08893a2db41097ceba35bf" - integrity sha512-+G6rp2zRuOAInY5wcggsx4+QVao1qPM0osC9fTUVlAV3zOrzTCnrMAFVnR6+a3T8wz1wFIH7KhYMcMB3u1n80A== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz#e9e8606633287488216028719638cbbb2f2dde8f" + integrity sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-flow" "^7.18.6" "@babel/plugin-transform-for-of@^7.0.0", "@babel/plugin-transform-for-of@^7.18.8": @@ -891,35 +729,32 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-modules-amd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" - integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== +"@babel/plugin-transform-modules-amd@^7.19.6": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" + integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" - integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== +"@babel/plugin-transform-modules-commonjs@^7.0.0", "@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.19.6": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" + integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-simple-access" "^7.20.2" -"@babel/plugin-transform-modules-systemjs@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz#545df284a7ac6a05125e3e405e536c5853099a06" - integrity sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A== +"@babel/plugin-transform-modules-systemjs@^7.19.6": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" + integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== dependencies: "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-validator-identifier" "^7.18.6" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-identifier" "^7.19.1" "@babel/plugin-transform-modules-umd@^7.18.6": version "7.18.6" @@ -929,13 +764,13 @@ "@babel/helper-module-transforms" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-named-capturing-groups-regex@^7.0.0", "@babel/plugin-transform-named-capturing-groups-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz#c89bfbc7cc6805d692f3a49bc5fc1b630007246d" - integrity sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg== +"@babel/plugin-transform-named-capturing-groups-regex@^7.0.0", "@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-new-target@^7.18.6": version "7.18.6" @@ -959,14 +794,7 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" - integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-parameters@^7.20.7": +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== @@ -995,30 +823,30 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx-source@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz#06e9ae8a14d2bc19ce6e3c447d842032a50598fc" - integrity sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw== + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86" + integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-react-jsx@^7.0.0", "@babel/plugin-transform-react-jsx@^7.12.17": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz#2721e96d31df96e3b7ad48ff446995d26bc028ff" - integrity sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.7.tgz#025d85a1935fd7e19dfdcb1b1d4df34d4da484f7" + integrity sha512-Tfq7qqD+tRj3EoDhY00nn2uP2hsRxgYGi5mLQ5TimKav0a9Lrpd4deE+fcLXU8zFYRjlKPHZhpCvfEA6qnBxqQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.18.6" + "@babel/types" "^7.20.7" "@babel/plugin-transform-regenerator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" - integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" + integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - regenerator-transform "^0.15.0" + "@babel/helper-plugin-utils" "^7.20.2" + regenerator-transform "^0.15.1" "@babel/plugin-transform-reserved-words@^7.18.6": version "7.18.6" @@ -1028,15 +856,15 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-runtime@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz#d9e4b1b25719307bfafbf43065ed7fb3a83adb8f" - integrity sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg== + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194" + integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw== dependencies: "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.9" - babel-plugin-polyfill-corejs2 "^0.3.1" - babel-plugin-polyfill-corejs3 "^0.5.2" - babel-plugin-polyfill-regenerator "^0.3.1" + "@babel/helper-plugin-utils" "^7.19.0" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.0.0", "@babel/plugin-transform-shorthand-properties@^7.18.6": @@ -1046,13 +874,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-spread@^7.0.0", "@babel/plugin-transform-spread@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664" - integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA== +"@babel/plugin-transform-spread@^7.0.0", "@babel/plugin-transform-spread@^7.19.0": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-transform-sticky-regex@^7.0.0", "@babel/plugin-transform-sticky-regex@^7.18.6": version "7.18.6" @@ -1076,20 +904,20 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.18.6", "@babel/plugin-transform-typescript@^7.5.0": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0" - integrity sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.7.tgz#673f49499cd810ae32a1ea5f3f8fab370987e055" + integrity sha512-m3wVKEvf6SoszD8pu4NZz3PvfKRCMgk6D6d0Qi9hNnlM5M6CFS92EgF4EiHVLKbU0r/r7ty1hg7NPZwE7WRbYw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-typescript" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-typescript" "^7.20.0" -"@babel/plugin-transform-unicode-escapes@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz#0d01fb7fb2243ae1c033f65f6e3b4be78db75f27" - integrity sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw== +"@babel/plugin-transform-unicode-escapes@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-unicode-regex@^7.0.0", "@babel/plugin-transform-unicode-regex@^7.18.6": version "7.18.6" @@ -1100,17 +928,17 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-env@^7.12.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.9.tgz#9b3425140d724fbe590322017466580844c7eaff" - integrity sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg== + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" + integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/compat-data" "^7.20.1" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-async-generator-functions" "^7.18.6" + "@babel/plugin-proposal-async-generator-functions" "^7.20.1" "@babel/plugin-proposal-class-properties" "^7.18.6" "@babel/plugin-proposal-class-static-block" "^7.18.6" "@babel/plugin-proposal-dynamic-import" "^7.18.6" @@ -1119,7 +947,7 @@ "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.18.9" + "@babel/plugin-proposal-object-rest-spread" "^7.20.2" "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" "@babel/plugin-proposal-optional-chaining" "^7.18.9" "@babel/plugin-proposal-private-methods" "^7.18.6" @@ -1130,7 +958,7 @@ "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.18.6" + "@babel/plugin-syntax-import-assertions" "^7.20.0" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -1143,10 +971,10 @@ "@babel/plugin-transform-arrow-functions" "^7.18.6" "@babel/plugin-transform-async-to-generator" "^7.18.6" "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.18.9" - "@babel/plugin-transform-classes" "^7.18.9" + "@babel/plugin-transform-block-scoping" "^7.20.2" + "@babel/plugin-transform-classes" "^7.20.2" "@babel/plugin-transform-computed-properties" "^7.18.9" - "@babel/plugin-transform-destructuring" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.20.2" "@babel/plugin-transform-dotall-regex" "^7.18.6" "@babel/plugin-transform-duplicate-keys" "^7.18.9" "@babel/plugin-transform-exponentiation-operator" "^7.18.6" @@ -1154,30 +982,30 @@ "@babel/plugin-transform-function-name" "^7.18.9" "@babel/plugin-transform-literals" "^7.18.9" "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.18.6" - "@babel/plugin-transform-modules-commonjs" "^7.18.6" - "@babel/plugin-transform-modules-systemjs" "^7.18.9" + "@babel/plugin-transform-modules-amd" "^7.19.6" + "@babel/plugin-transform-modules-commonjs" "^7.19.6" + "@babel/plugin-transform-modules-systemjs" "^7.19.6" "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" "@babel/plugin-transform-new-target" "^7.18.6" "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-parameters" "^7.20.1" "@babel/plugin-transform-property-literals" "^7.18.6" "@babel/plugin-transform-regenerator" "^7.18.6" "@babel/plugin-transform-reserved-words" "^7.18.6" "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.18.9" + "@babel/plugin-transform-spread" "^7.19.0" "@babel/plugin-transform-sticky-regex" "^7.18.6" "@babel/plugin-transform-template-literals" "^7.18.9" "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.18.6" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.18.9" - babel-plugin-polyfill-corejs2 "^0.3.1" - babel-plugin-polyfill-corejs3 "^0.5.2" - babel-plugin-polyfill-regenerator "^0.3.1" - core-js-compat "^3.22.1" + "@babel/types" "^7.20.2" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + core-js-compat "^3.25.1" semver "^6.3.0" "@babel/preset-flow@^7.13.13": @@ -1221,31 +1049,13 @@ source-map-support "^0.5.16" "@babel/runtime@^7.0.0", "@babel/runtime@^7.14.0", "@babel/runtime@^7.18.6", "@babel/runtime@^7.8.4": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.0.0", "@babel/template@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" - integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.6" - "@babel/types" "^7.18.6" - -"@babel/template@^7.18.10", "@babel/template@^7.8.6": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" + integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" + regenerator-runtime "^0.13.11" -"@babel/template@^7.20.7": +"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.8.6": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== @@ -1254,26 +1064,10 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.18.9", "@babel/traverse@^7.19.0", "@babel/traverse@^7.4.5", "@babel/traverse@^7.9.0": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.1.tgz#0fafe100a8c2a603b4718b1d9bf2568d1d193347" - integrity sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.19.0" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.19.1" - "@babel/types" "^7.19.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.7": - version "7.20.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.10.tgz#2bf98239597fcec12f842756f186a9dde6d09230" - integrity sha512-oSf1juCgymrSez8NI4A2sr4+uB/mFd9MXplYGPEBnfAuWmmyeVcHa6xLPiaRBcXkcb/28bgxmQLTVwFKE1yfsg== +"@babel/traverse@^7.14.0", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.4.5", "@babel/traverse@^7.9.0": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5" + integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ== dependencies: "@babel/code-frame" "^7.18.6" "@babel/generator" "^7.20.7" @@ -1286,33 +1080,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.4.4": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" - integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - to-fast-properties "^2.0.0" - -"@babel/types@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" - integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== - dependencies: - "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" - to-fast-properties "^2.0.0" - -"@babel/types@^7.19.0", "@babel/types@^7.9.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" - integrity sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA== - dependencies: - "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" - to-fast-properties "^2.0.0" - -"@babel/types@^7.20.2", "@babel/types@^7.20.7": +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.4.4", "@babel/types@^7.9.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== @@ -1350,15 +1118,15 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@eslint/eslintrc@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" - integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== +"@eslint/eslintrc@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" + integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.2" - globals "^13.15.0" + espree "^9.4.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" @@ -1380,16 +1148,16 @@ mv "~2" safe-json-stringify "~1" -"@expo/cli@0.4.10": - version "0.4.10" - resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.4.10.tgz#e965b97888c83cecdaddbb8dca3d5827643a0f36" - integrity sha512-c8NJOVa5b8g9CYj8ahdaN21cVE2wPwUaFrtTE0kLeRR5ASy8reWLFEOcstEtt6eufdcN/uGgBWQ0FLovgLZuzw== +"@expo/cli@0.4.11": + version "0.4.11" + resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.4.11.tgz#b5284b6c8c74eea2b8c410c681f2eddb33b2dda3" + integrity sha512-L9Ci9RBh0aPFEDF1AjDYPk54OgeUJIKzxF3lRgITm+lQpI3IEKjAc9LaYeQeO1mlZMUQmPkHArF8iyz1eOeVoQ== dependencies: "@babel/runtime" "^7.14.0" "@expo/code-signing-certificates" "0.0.5" "@expo/config" "~7.0.2" "@expo/config-plugins" "~5.0.3" - "@expo/dev-server" "0.1.123" + "@expo/dev-server" "0.1.124" "@expo/devcert" "^1.0.0" "@expo/json-file" "^8.2.35" "@expo/metro-config" "~0.5.0" @@ -1512,16 +1280,16 @@ xcode "^3.0.0" xml-js "^1.6.11" -"@expo/dev-server@0.1.123": - version "0.1.123" - resolved "https://registry.yarnpkg.com/@expo/dev-server/-/dev-server-0.1.123.tgz#71304323b47db9ce300b9a774571ef2312b9d581" - integrity sha512-N6UVzzeemfX0AONUSWInvkAAbqon8hRXpyYE/nMPaC6TvAmgGY5ILZAGoXwlpxwY2VKNT0Lx4s/UJ53ytIaHbA== +"@expo/dev-server@0.1.124": + version "0.1.124" + resolved "https://registry.yarnpkg.com/@expo/dev-server/-/dev-server-0.1.124.tgz#81fca9eff42893a7cb9d51315f2c0dcf860c5eec" + integrity sha512-iHczVcf+rgWupCY/3b3ePIizNtzsy1O/w8jdKv3bKvoOfXiVIVOo4KGiVDpAJOahKiMOsRlbKeemB8OLNKzdSA== dependencies: "@expo/bunyan" "4.0.0" "@expo/metro-config" "~0.5.1" "@expo/osascript" "2.0.33" "@expo/spawn-async" "^1.5.0" - body-parser "1.19.0" + body-parser "^1.20.1" chalk "^4.0.0" connect "^3.7.0" fs-extra "9.0.0" @@ -1535,9 +1303,9 @@ temp-dir "^2.0.0" "@expo/devcert@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@expo/devcert/-/devcert-1.0.0.tgz#79df9431e806bc546f6399e35934b9876384f0a9" - integrity sha512-cahGyQCmpZmHpn2U04NR9KwsOIZy7Rhsw8Fg4q+A6563lIJxbkrgPnxq/O3NQAh3ohEvOXOOnoFx0b4yycCkpQ== + version "1.1.0" + resolved "https://registry.yarnpkg.com/@expo/devcert/-/devcert-1.1.0.tgz#d148eb9180db6753c438192e73a123fb13b662ac" + integrity sha512-ghUVhNJQOCTdQckSGTHctNp/0jzvVoMMkVh+6SHn+TZj8sU15U/npXIDt8NtQp0HedlPaCgkVdMu8Sacne0aEA== dependencies: application-config-path "^0.1.0" command-exists "^1.2.4" @@ -1551,7 +1319,7 @@ rimraf "^2.6.2" sudo-prompt "^8.2.0" tmp "^0.0.33" - tslib "^1.10.0" + tslib "^2.4.0" "@expo/image-utils@0.3.22": version "0.3.22" @@ -1587,7 +1355,7 @@ semver "7.3.2" tempy "0.3.0" -"@expo/json-file@8.2.36", "@expo/json-file@^8.2.35": +"@expo/json-file@8.2.36": version "8.2.36" resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-8.2.36.tgz#62a505cb7f30a34d097386476794680a3f7385ff" integrity sha512-tOZfTiIFA5KmMpdW9KF7bc6CFiGjb0xnbieJhTGlHrLL+ps2G0OkqmuZ3pFEXBOMnJYUVpnSy++52LFxvpa5ZQ== @@ -1596,6 +1364,15 @@ json5 "^1.0.1" write-file-atomic "^2.3.0" +"@expo/json-file@^8.2.35": + version "8.2.37" + resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-8.2.37.tgz#9c02d3b42134907c69cc0a027b18671b69344049" + integrity sha512-YaH6rVg11JoTS2P6LsW7ybS2CULjf40AbnAHw2F1eDPuheprNjARZMnyHFPkKv7GuxCy+B9GPcbOKgc4cgA80Q== + dependencies: + "@babel/code-frame" "~7.10.4" + json5 "^2.2.2" + write-file-atomic "^2.3.0" + "@expo/metro-config@~0.5.0", "@expo/metro-config@~0.5.1": version "0.5.2" resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.5.2.tgz#9474454dcf8c2e3d66231f36f8bbbae5e9e0c3dc" @@ -1619,9 +1396,9 @@ exec-async "^2.2.0" "@expo/package-manager@~0.0.53": - version "0.0.56" - resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-0.0.56.tgz#214a8db48752cde968827c20c5b54a88187b5422" - integrity sha512-PGk34uz4XDyhoNIlPh2D+BDsiXYuW2jXavTiax8d32uvHlRO6FN0cAsqlWD6fx3H2hRn8cU/leTuc4M7pYovCQ== + version "0.0.57" + resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-0.0.57.tgz#1cd71da0632c52a9a001b45e5d0d7e1e16de97d3" + integrity sha512-Y4RpSL9EqaPF+Vd2GrK6r7Xx7Dv0Xdq3AGAD9C0KwV21WqP/scj/dpjxFY+ABwmdhNsFzYXb8fmDyh4tiKenPQ== dependencies: "@expo/json-file" "8.2.36" "@expo/spawn-async" "^1.5.0" @@ -1697,9 +1474,9 @@ integrity sha512-TI+l71+5aSKnShYclFa14Kum+hQMZ86b95SH6tQUG3qZEmLTarvWpKwqtTwQKqvlJSJrpFiSFu3eCuZokY6zWA== "@expo/webpack-config@^0.17.2": - version "0.17.3" - resolved "https://registry.yarnpkg.com/@expo/webpack-config/-/webpack-config-0.17.3.tgz#a0935b8d77c75b5d64eb79a350fb7fb905791411" - integrity sha512-EcnHHmMscC7mL7qGQpXoSSpOrXbyfnMErUfqaBVjMYz7I4xVvoPQqiM13v4JXnz9TnZHDxD9t7+VSa9hPJVssA== + version "0.17.4" + resolved "https://registry.yarnpkg.com/@expo/webpack-config/-/webpack-config-0.17.4.tgz#c2cb670a8f431dc76a645d183a38465ed235e3a7" + integrity sha512-vn37RDhYowfLc2oRaXhmbI/9FStjQFXPONG3yRLfwUnA4dRtfXapJUSKHJKRwOy4fBBmTrb2tthtsdb4zeWmsw== dependencies: "@babel/core" "7.9.0" babel-loader "8.1.0" @@ -1741,24 +1518,24 @@ find-up "^5.0.0" js-yaml "^4.1.0" -"@fortawesome/fontawesome-common-types@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.2.tgz#c1095b1bbabf19f37f9ff0719db38d92a410bcfe" - integrity sha512-wBaAPGz1Awxg05e0PBRkDRuTsy4B3dpBm+zreTTyd9TH4uUM27cAL4xWyWR0rLJCrRwzVsQ4hF3FvM6rqydKPA== +"@fortawesome/fontawesome-common-types@6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.1.tgz#411e02a820744d3f7e0d8d9df9d82b471beaa073" + integrity sha512-Sz07mnQrTekFWLz5BMjOzHl/+NooTdW8F8kDQxjWwbpOJcnoSg4vUDng8d/WR1wOxM0O+CY9Zw0nR054riNYtQ== "@fortawesome/fontawesome-svg-core@^6.1.1": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.2.tgz#11e2e8583a7dea75d734e4d0e53d91c63fae7511" - integrity sha512-853G/Htp0BOdXnPoeCPTjFrVwyrJHpe8MhjB/DYE9XjwhnNDfuBCd3aKc2YUYbEfHEcBws4UAA0kA9dymZKGjA== + version "6.2.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.2.1.tgz#e87e905e444b5e7b715af09b64d27b53d4c8f9d9" + integrity sha512-HELwwbCz6C1XEcjzyT1Jugmz2NNklMrSPjZOWMlc+ZsHIVk+XOvOXLGGQtFBwSyqfJDNgRq4xBCwWOaZ/d9DEA== dependencies: - "@fortawesome/fontawesome-common-types" "6.1.2" + "@fortawesome/fontawesome-common-types" "6.2.1" "@fortawesome/free-solid-svg-icons@^6.1.1": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.2.tgz#491d668b8a6603698d0ce1ac620f66fd22b74c84" - integrity sha512-lTgZz+cMpzjkHmCwOG3E1ilUZrnINYdqMmrkv30EC3XbRsGlbIOL8H9LaNp5SV4g0pNJDfQ4EdTWWaMvdwyLiQ== + version "6.2.1" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.2.1.tgz#2290ea5adcf1537cbd0c43de6feb38af02141d27" + integrity sha512-oKuqrP5jbfEPJWTij4sM+/RvgX+RMFwx3QZCZcK9PrBDgxC35zuc7AOFsyMjMd/PIFPeB2JxyqDr5zs/DZFPPw== dependencies: - "@fortawesome/fontawesome-common-types" "6.1.2" + "@fortawesome/fontawesome-common-types" "6.2.1" "@fortawesome/react-native-fontawesome@^0.2.7": version "0.2.7" @@ -1779,23 +1556,21 @@ integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== "@gorhom/bottom-sheet@^4.4.3": - version "4.4.3" - resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-4.4.3.tgz#4c31f214342faa0ba9cd453a98f8c1e0e780c428" - integrity sha512-ZEbCLbvLaEVNa9c+om/o9Le0q7o9GkEM7smhuoFkqj9CIjv3XbpZof7uEKV86yyta+ONAInNYrCdVSpYQ1Rjkg== + version "4.4.5" + resolved "https://registry.yarnpkg.com/@gorhom/bottom-sheet/-/bottom-sheet-4.4.5.tgz#b9041b01ce1af9a936e7c0fc1d78f026d759eebe" + integrity sha512-Z5Z20wshLUB8lIdtMKoJaRnjd64wBR/q8EeVPThrg+skrcBwBPHfUwZJ2srB0rEszA/01ejSJy/ixyd7Ra7vUA== dependencies: - "@gorhom/portal" "1.0.13" + "@gorhom/portal" "1.0.14" invariant "^2.2.4" - nanoid "^3.3.3" - react-native-redash "^16.1.1" -"@gorhom/portal@1.0.13": - version "1.0.13" - resolved "https://registry.yarnpkg.com/@gorhom/portal/-/portal-1.0.13.tgz#da3af4d427e1fa68d264107de4b3072a4adf35ce" - integrity sha512-ViClKPkyGnj8HVMW45OGQSnGbWBVh8i3tgMOkGqpm6Cv0WVcDfUL7SER6zyGQy8Wdoj3GUDpAJFMqVOxpmRpzw== +"@gorhom/portal@1.0.14": + version "1.0.14" + resolved "https://registry.yarnpkg.com/@gorhom/portal/-/portal-1.0.14.tgz#1953edb76aaba80fb24021dc774550194a18e111" + integrity sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A== dependencies: nanoid "^3.3.1" -"@graphql-typed-document-node/core@^3.1.0", "@graphql-typed-document-node/core@^3.1.1": +"@graphql-typed-document-node/core@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052" integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg== @@ -1812,14 +1587,19 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@humanwhocodes/config-array@^0.9.2": - version "0.9.5" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" - integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" @@ -1827,18 +1607,18 @@ integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== "@jest/create-cache-key-function@^29.0.3": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.3.1.tgz#3a0970ea595ab3d9507244edbcef14d6b016cdc9" - integrity sha512-4i+E+E40gK13K78ffD/8cy4lSSqeWwyXeTZoq16tndiCP12hC8uQsPJdIu5C6Kf22fD8UbBk71so7s/6VwpUOQ== + version "29.4.1" + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.4.1.tgz#d0d4402a4b582d6c7e275196929eda816c05f162" + integrity sha512-ioKzAGdBQZ2BK44ZP7Gs1Mxvx3yuo3yFnvjCp4qk9Vn1Zmcu20fweX+GyU1e5CuVoHx1rsKrapyDCVk6yoRwUQ== dependencies: - "@jest/types" "^29.3.1" + "@jest/types" "^29.4.1" -"@jest/schemas@^29.0.0": - version "29.0.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" - integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== +"@jest/schemas@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.0.tgz#0d6ad358f295cc1deca0b643e6b4c86ebd539f17" + integrity sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ== dependencies: - "@sinclair/typebox" "^0.24.1" + "@sinclair/typebox" "^0.25.16" "@jest/types@^26.6.2": version "26.6.2" @@ -1862,12 +1642,12 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.3.1.tgz#7c5a80777cb13e703aeec6788d044150341147e3" - integrity sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA== +"@jest/types@^29.4.1": + version "29.4.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.1.tgz#f9f83d0916f50696661da72766132729dcb82ecb" + integrity sha512-zbrAXDUOnpJ+FMST2rV7QZOgec8rskg2zv8g2ajeqitp4tvZiyqTCYXANrKsM+ryj5o+LI+ZN2EgU9drrkiwSA== dependencies: - "@jest/schemas" "^29.0.0" + "@jest/schemas" "^29.4.0" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -1891,7 +1671,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== @@ -1901,18 +1681,18 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== "@jridgewell/trace-mapping@^0.3.9": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" - integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1927,7 +1707,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -2155,22 +1935,32 @@ resolved "https://registry.yarnpkg.com/@react-native-masked-view/masked-view/-/masked-view-0.2.8.tgz#34405a4361882dae7c81b1b771fe9f5fbd545a97" integrity sha512-+1holBPDF1yi/y0uc1WB6lA5tSNHhM7PpTMapT3ypvSnKQ9+C6sy/zfjxNxRA/llBQ1Ci6f94EaK56UCKs5lTA== +"@react-native-picker/picker@2.4.8": + version "2.4.8" + resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-2.4.8.tgz#a1a21f3d6ecadedbc3f0b691a444ddd7baa081f8" + integrity sha512-5NQ5XPo1B03YNqKFrV6h9L3CQaHlB80wd4ETHUEABRP2iLh7FHLVObX2GfziD+K/VJb8G4KZcZ23NFBFP1f7bg== + "@react-native/assets@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@react-native/assets/-/assets-1.0.0.tgz#c6f9bf63d274bafc8e970628de24986b30a55c8e" integrity sha512-KrwSpS1tKI70wuKl68DwJZYEvXktDHdZMG0k2AXD/rJVSlB23/X2CB2cutVR0HwNMJIal9HOUOBB2rVfa6UGtQ== -"@react-native/normalize-color@2.0.0", "@react-native/normalize-color@^2.0.0": +"@react-native/normalize-color@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.0.0.tgz#da955909432474a9a0fe1cbffc66576a0447f567" integrity sha512-Wip/xsc5lw8vsBlmY2MO/gFLp3MvuZ2baBZjDeTjjndMgM0h5sxz7AZR62RDPGgstp8Np7JzjvVqVT7tpFZqsw== +"@react-native/normalize-color@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91" + integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA== + "@react-native/polyfills@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa" integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ== -"@react-navigation/core@^6.4.0", "@react-navigation/core@~6.2.2": +"@react-navigation/core@^6.4.6", "@react-navigation/core@~6.2.2": version "6.2.2" resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-6.2.2.tgz#29ca539e22581616aad82b7451d47c5efe936f31" integrity sha512-gEJ1gRqt1EIqRrnJIpSQ0wWJRue9maAQNKYrlQ0a/LSKErF3g6w+sD2wW4Bbb1yj88pGhKeuI4wdB9MVK766Pg== @@ -2181,34 +1971,34 @@ query-string "^7.0.0" react-is "^16.13.0" -"@react-navigation/elements@^1.3.6": - version "1.3.6" - resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.6.tgz#fa700318528db93f05144b1be4b691b9c1dd1abe" - integrity sha512-pNJ8R9JMga6SXOw6wGVN0tjmE6vegwPmJBL45SEMX2fqTfAk2ykDnlJHodRpHpAgsv0DaI8qX76z3A+aqKSU0w== +"@react-navigation/elements@^1.3.13": + version "1.3.13" + resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.13.tgz#5105fa26df8d32810cd9f14d6ec5a3d2c2bb26d2" + integrity sha512-LqqK5s2ZfYHn2cQ376jC5V9dQztLH5ixkkJj9WR7JY2g4SghDd39WJhL3Jillw1Mu3F3b9sZwvAK+QkXhnDeAA== "@react-navigation/native@^6.0.13": - version "6.0.13" - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.0.13.tgz#ec504120e193ea6a7f24ffa765a1338be5a3160a" - integrity sha512-CwaJcAGbhv3p3ECablxBkw8QBCGDWXqVRwQ4QbelajNW623m3sNTC9dOF6kjp8au6Rg9B5e0KmeuY0xWbPk79A== + version "6.1.2" + resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.1.2.tgz#6fffbf4787c233687fff8fe9ce7364ffce696d38" + integrity sha512-qLUe0asHofr5EhxKjvUBJ9DrPPmR4535IEwmW3oU4DRb3cLbNysjajJKHL8kcYtqPvn9Bx9QZG2x0PMb2vN23A== dependencies: - "@react-navigation/core" "^6.4.0" + "@react-navigation/core" "^6.4.6" escape-string-regexp "^4.0.0" fast-deep-equal "^3.1.3" nanoid "^3.1.23" "@react-navigation/routers@^6.1.1": - version "6.1.3" - resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-6.1.3.tgz#1df51959e9a67c44367462e8b929b7360a5d2555" - integrity sha512-idJotMEzHc3haWsCh7EvnnZMKxvaS4YF/x2UyFBkNFiEFUaEo/1ioQU6qqmVLspdEv4bI/dLm97hQo7qD8Yl7Q== + version "6.1.6" + resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-6.1.6.tgz#f57f2a73855d329255aa225fdad75ae8e7700c6d" + integrity sha512-Z5DeCW3pUvMafbU9Cjy1qJYC2Bvl8iy3+PfsB0DsAwQ6zZ3WAXW5FTMX4Gb9H+Jg6qHWGbMFFwlYpS3UJ3tlVQ== dependencies: nanoid "^3.1.23" "@react-navigation/stack@^6.3.2": - version "6.3.2" - resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-6.3.2.tgz#ba0a65e10e2b165185f20718046f25d8c9abb076" - integrity sha512-wb8koMp4OTrG5geOqEFPDatTyl8dsSyRBHN4h0wzgNT29V/JjkS3LYwkGLLfUmMfeLXFyIfEPILAjYLFmnk3dA== + version "6.3.11" + resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-6.3.11.tgz#5f5e93a34e4492d7baa3f29bddf822489724ec4e" + integrity sha512-GWOAyJfPEsjVwDWec1ERwWL5LvManJucCRUZetJqCBs4/mV7HXEt2x6l3SMitHxH1+K+9XuYXI+wBTbK1WDYOA== dependencies: - "@react-navigation/elements" "^1.3.6" + "@react-navigation/elements" "^1.3.13" color "^4.2.3" warn-once "^0.1.0" @@ -2237,19 +2027,19 @@ "@hapi/hoek" "^9.0.0" "@sideway/formula@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" - integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== "@sideway/pinpoint@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== -"@sinclair/typebox@^0.24.1": - version "0.24.51" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" - integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== +"@sinclair/typebox@^0.25.16": + version "0.25.21" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" + integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== "@types/geojson@^7946.0.8": version "7946.0.10" @@ -2309,9 +2099,9 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*": - version "18.6.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.3.tgz#4e4a95b6fe44014563ceb514b2598b3e623d1c98" - integrity sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg== + version "18.11.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" + integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== "@types/prop-types@*": version "15.7.5" @@ -2331,17 +2121,17 @@ "@types/react" "*" "@types/react-native-vector-icons@^6.4.10": - version "6.4.11" - resolved "https://registry.yarnpkg.com/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.11.tgz#8783d4248377aec14e06420397037b96ff242684" - integrity sha512-jUy9CsTu1Cl3k6WRE7GnqBRHbuSv55PRVfMEel+fF73HKpF8g5JmTzTVMBDX8NfY3PlfIY5VlxiqWZxdjm38Pg== + version "6.4.12" + resolved "https://registry.yarnpkg.com/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.12.tgz#4b9c63bf85eb42fca7c5b0a55ec317900e4fb427" + integrity sha512-gSXtv3NMOsRwSXJ/gvGebm7CNjHbkeFKCse9h/Pvi+x2yjCLOkJR1FBfec06DhaFJpsK7Y8WRQpwOS0eLqx5Rg== dependencies: "@types/react" "*" "@types/react-native" "*" "@types/react-native@*": - version "0.69.4" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.69.4.tgz#3ab61e4738b3223dda53d13f365ef806f84ad4b4" - integrity sha512-K6ebyJYZiMZ0JsIfnX5/RbPxYuGt9ajH0kQk6An7upp9vrnU1T9Xg1cIWhnfYojAhqNxEo3okIcH0lR8+qzrmg== + version "0.71.3" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.71.3.tgz#537f669ed6b38b5ae47444bd9d253c4cff23bed7" + integrity sha512-0Uqw1YZ0qbVla0MMWFTANFm6W8KYWNvGQmYfucdecbXivLMcQ2v4PovuYFKr7bE6Bc5nDCUEaga962Y8gcDF7A== dependencies: "@types/react" "*" @@ -2352,16 +2142,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@~18.0.20": - version "18.0.21" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.21.tgz#b8209e9626bb00a34c76f55482697edd2b43cc67" - integrity sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@~18.0.24": +"@types/react@*", "@types/react@~18.0.20", "@types/react@~18.0.24": version "18.0.26" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917" integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug== @@ -2375,6 +2156,11 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== +"@types/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -2386,9 +2172,9 @@ integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== "@types/uglify-js@*": - version "3.17.0" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.0.tgz#95271e7abe0bf7094c60284f76ee43232aef43b9" - integrity sha512-3HO6rm0y+/cqvOyA8xcYLweF0TKXlAxmQASjbOi49Co51A1N4nR4bEwBgRoD9kNM+rqFGArjKr654SLp2CoGmQ== + version "3.17.1" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.1.tgz#e0ffcef756476410e5bce2cb01384ed878a195b5" + integrity sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g== dependencies: source-map "^0.6.1" @@ -2402,9 +2188,9 @@ source-map "^0.7.3" "@types/webpack@^4.4.31", "@types/webpack@^4.41.8": - version "4.41.32" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.32.tgz#a7bab03b72904070162b2f169415492209e94212" - integrity sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg== + version "4.41.33" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.33.tgz#16164845a5be6a306bcbe554a8e67f9cac215ffc" + integrity sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g== dependencies: "@types/node" "*" "@types/tapable" "^1" @@ -2419,104 +2205,107 @@ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^15.0.0": - version "15.0.14" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" - integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== + version "15.0.15" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.15.tgz#e609a2b1ef9e05d90489c2f5f45bbfb2be092158" + integrity sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg== dependencies: "@types/yargs-parser" "*" "@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + version "16.0.5" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" + integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== dependencies: "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.17" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.17.tgz#5672e5621f8e0fca13f433a8017aae4b7a2a03e7" - integrity sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g== + version "17.0.19" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.19.tgz#8dbecdc9ab48bee0cb74f6e3327de3fa0d0c98ae" + integrity sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.18.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.31.0.tgz#cae1967b1e569e6171bbc6bec2afa4e0c8efccfe" - integrity sha512-VKW4JPHzG5yhYQrQ1AzXgVgX8ZAJEvCz0QI6mLRX4tf7rnFfh5D8SKm0Pq6w5PyNfAWJk6sv313+nEt3ohWMBQ== + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz#deee67e399f2cb6b4608c935777110e509d8018c" + integrity sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ== dependencies: - "@typescript-eslint/scope-manager" "5.31.0" - "@typescript-eslint/type-utils" "5.31.0" - "@typescript-eslint/utils" "5.31.0" + "@typescript-eslint/scope-manager" "5.48.1" + "@typescript-eslint/type-utils" "5.48.1" + "@typescript-eslint/utils" "5.48.1" debug "^4.3.4" - functional-red-black-tree "^1.0.1" ignore "^5.2.0" + natural-compare-lite "^1.4.0" regexpp "^3.2.0" semver "^7.3.7" tsutils "^3.21.0" "@typescript-eslint/parser@^5.18.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.31.0.tgz#7f42d7dcc68a0a6d80a0f3d9a65063aee7bb8d2c" - integrity sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw== + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.1.tgz#d0125792dab7e232035434ab8ef0658154db2f10" + integrity sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA== dependencies: - "@typescript-eslint/scope-manager" "5.31.0" - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/typescript-estree" "5.31.0" + "@typescript-eslint/scope-manager" "5.48.1" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/typescript-estree" "5.48.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz#f47a794ba84d9b818ab7f8f44fff55a61016c606" - integrity sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg== +"@typescript-eslint/scope-manager@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz#39c71e4de639f5fe08b988005beaaf6d79f9d64d" + integrity sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ== dependencies: - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/visitor-keys" "5.31.0" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/visitor-keys" "5.48.1" -"@typescript-eslint/type-utils@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.31.0.tgz#70a0b7201360b5adbddb0c36080495aa08f6f3d9" - integrity sha512-7ZYqFbvEvYXFn9ax02GsPcEOmuWNg+14HIf4q+oUuLnMbpJ6eHAivCg7tZMVwzrIuzX3QCeAOqKoyMZCv5xe+w== +"@typescript-eslint/type-utils@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.48.1.tgz#5d94ac0c269a81a91ad77c03407cea2caf481412" + integrity sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ== dependencies: - "@typescript-eslint/utils" "5.31.0" + "@typescript-eslint/typescript-estree" "5.48.1" + "@typescript-eslint/utils" "5.48.1" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.31.0.tgz#7aa389122b64b18e473c1672fb3b8310e5f07a9a" - integrity sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g== +"@typescript-eslint/types@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.1.tgz#efd1913a9aaf67caf8a6e6779fd53e14e8587e14" + integrity sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg== -"@typescript-eslint/typescript-estree@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz#eb92970c9d6e3946690d50c346fb9b1d745ee882" - integrity sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw== +"@typescript-eslint/typescript-estree@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz#9efa8ee2aa471c6ab62e649f6e64d8d121bc2056" + integrity sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA== dependencies: - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/visitor-keys" "5.31.0" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/visitor-keys" "5.48.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.31.0.tgz#e146fa00dca948bfe547d665b2138a2dc1b79acd" - integrity sha512-kcVPdQS6VIpVTQ7QnGNKMFtdJdvnStkqS5LeALr4rcwx11G6OWb2HB17NMPnlRHvaZP38hL9iK8DdE9Fne7NYg== +"@typescript-eslint/utils@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.48.1.tgz#20f2f4e88e9e2a0961cbebcb47a1f0f7da7ba7f9" + integrity sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.31.0" - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/typescript-estree" "5.31.0" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.48.1" + "@typescript-eslint/types" "5.48.1" + "@typescript-eslint/typescript-estree" "5.48.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" + semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz#b0eca264df01ce85dceb76aebff3784629258f54" - integrity sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg== +"@typescript-eslint/visitor-keys@5.48.1": + version "5.48.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz#79fd4fb9996023ef86849bf6f904f33eb6c8fccb" + integrity sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA== dependencies: - "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/types" "5.48.1" eslint-visitor-keys "^3.3.0" "@urql/core@2.3.6": @@ -2528,12 +2317,11 @@ wonka "^4.0.14" "@urql/core@>=2.3.1": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@urql/core/-/core-2.6.1.tgz#c10ee972c5e81df6d7bf1e778ef1b5d30e2906e5" - integrity sha512-gYrEHy3tViJhwIhauK6MIf2Qp09QTsgNHZRd0n71rS+hF6gdwjspf1oKljl4m25+272cJF7fPjBUGmjaiEr7Kg== + version "3.1.1" + resolved "https://registry.yarnpkg.com/@urql/core/-/core-3.1.1.tgz#a49cd572360d01f2469a786b294fba2269a65e53" + integrity sha512-Mnxtq4I4QeFJsgs7Iytw+HyhiGxISR6qtyk66c9tipozLZ6QVxrCiUPF2HY4BxNIabaxcp+rivadvm8NAnXj4Q== dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - wonka "^4.0.14" + wonka "^6.1.2" "@urql/exchange-retry@0.3.0": version "0.3.0" @@ -2689,9 +2477,9 @@ "@xtuc/long" "4.2.2" "@xmldom/xmldom@~0.7.0": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.5.tgz#09fa51e356d07d0be200642b0e4f91d8e6dd408d" - integrity sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A== + version "0.7.9" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.9.tgz#7f9278a50e737920e21b297b8a35286e9942c056" + integrity sha512-yceMpm/xd4W2a85iqZyO09gTnHvXF6pyiWjD2jcOJs7hRoZtNNOO1eJlhHj1ixA+xip2hOyGn+LgcvLCMo5zXA== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -2710,11 +2498,6 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -abs-svg-path@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/abs-svg-path/-/abs-svg-path-0.1.1.tgz#df601c8e8d2ba10d4a76d625e236a9a39c2723bf" - integrity sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA== - absolute-path@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/absolute-path/-/absolute-path-0.0.0.tgz#a78762fbdadfb5297be99b15d35a785b2f095bf7" @@ -2739,9 +2522,9 @@ acorn@^6.4.1: integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== acorn@^8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== address@1.1.2: version "1.1.2" @@ -2749,9 +2532,9 @@ address@1.1.2: integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== address@^1.0.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/address/-/address-1.2.1.tgz#25bb61095b7522d65b357baa11bc05492d4c8acd" - integrity sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA== + version "1.2.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" + integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== agent-base@6: version "6.0.2" @@ -2872,9 +2655,9 @@ anymatch@^2.0.0: normalize-path "^2.1.1" anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" @@ -2885,9 +2668,9 @@ appdirsjs@^1.2.4: integrity sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw== application-config-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/application-config-path/-/application-config-path-0.1.0.tgz#193c5f0a86541a4c66fba1e2dc38583362ea5e8f" - integrity sha512-lljTpVvFteShrHuKRvweZfa9o/Nc34Y8r5/1Lqh/yyKaspRT2J3fkEiSSk1YLG8ZSVyU7yHysRy9zcDDS2aH1Q== + version "0.1.1" + resolved "https://registry.yarnpkg.com/application-config-path/-/application-config-path-0.1.1.tgz#8b5ac64ff6afdd9bd70ce69f6f64b6998f5f756e" + integrity sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw== aproba@^1.1.1: version "1.2.0" @@ -2936,15 +2719,15 @@ array-flatten@^2.1.0: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== -array-includes@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" - integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== +array-includes@^3.1.5, array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" - get-intrinsic "^1.1.1" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" is-string "^1.0.7" array-union@^1.0.1: @@ -2969,27 +2752,38 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.flatmap@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" - integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.reduce@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" - integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== +array.prototype.reduce@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" + integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + asap@~2.0.3, asap@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -3031,9 +2825,9 @@ astral-regex@^1.0.0: integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.6.tgz#52f1d9403818c179b7561e11a5d1b77eb2160e77" + integrity sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg== async-limiter@~1.0.0: version "1.0.1" @@ -3067,10 +2861,15 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -axios@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" - integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +axios@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.1.tgz#44cf04a3c9f0c2252ebd85975361c026cb9f864a" + integrity sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" @@ -3092,13 +2891,6 @@ babel-loader@8.1.0: pify "^4.0.1" schema-utils "^2.6.5" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - babel-plugin-module-resolver@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-4.1.0.tgz#22a4f32f7441727ec1fbf4967b863e1e3e9f33e2" @@ -3110,34 +2902,34 @@ babel-plugin-module-resolver@^4.1.0: reselect "^4.0.0" resolve "^1.13.1" -babel-plugin-polyfill-corejs2@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" - integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== +babel-plugin-polyfill-corejs2@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== dependencies: "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.2" + "@babel/helper-define-polyfill-provider" "^0.3.3" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" - integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.2" - core-js-compat "^3.21.0" + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" -babel-plugin-polyfill-regenerator@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== +babel-plugin-polyfill-regenerator@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.3" babel-plugin-react-native-web@~0.18.2: - version "0.18.7" - resolved "https://registry.yarnpkg.com/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.18.7.tgz#d32750aca96e5518122504338196502034393261" - integrity sha512-DF7huAePyphXsqWhGyshjQAU9qektOqOSP2NHevtUBhsgLu57D4gEGZM1xPtbJYvW6/DoxuaXUAqjYqfexT+gQ== + version "0.18.10" + resolved "https://registry.yarnpkg.com/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.18.10.tgz#028c349d1c4c929f86dc757a4e724d3e651d3424" + integrity sha512-2UiwS6G7XKJvpo0X5OFkzGjHGFuNx9J+DgEG8TEmm+X5S0z6EB59W11RDEZghdKzsQzVbs1jB+2VHBuVgjMTiw== "babel-plugin-styled-components@>= 1.12.0": version "2.0.7" @@ -3297,26 +3089,10 @@ bn.js@^5.0.0, bn.js@^5.1.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -body-parser@1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" - integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== +body-parser@1.20.1, body-parser@^1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== dependencies: bytes "3.1.2" content-type "~1.0.4" @@ -3326,7 +3102,7 @@ body-parser@1.20.0: http-errors "2.0.0" iconv-lite "0.4.24" on-finished "2.4.1" - qs "6.10.3" + qs "6.11.0" raw-body "2.5.1" type-is "~1.6.18" unpipe "1.0.0" @@ -3476,7 +3252,7 @@ browserslist@4.14.2: escalade "^3.0.2" node-releases "^1.1.61" -browserslist@^4.0.0: +browserslist@^4.0.0, browserslist@^4.21.3, browserslist@^4.21.4: version "4.21.4" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== @@ -3486,16 +3262,6 @@ browserslist@^4.0.0: node-releases "^2.0.6" update-browserslist-db "^1.0.9" -browserslist@^4.20.2, browserslist@^4.21.3: - version "4.21.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" - integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== - dependencies: - caniuse-lite "^1.0.30001370" - electron-to-chromium "^1.4.202" - node-releases "^2.0.6" - update-browserslist-db "^1.0.5" - bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -3568,11 +3334,6 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -3689,9 +3450,9 @@ camelcase@^6.0.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== camelize@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" - integrity sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg== + version "1.0.1" + resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" + integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== caniuse-api@^3.0.0: version "3.0.0" @@ -3704,14 +3465,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001400: - version "1.0.30001412" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001412.tgz#30f67d55a865da43e0aeec003f073ea8764d5d7c" - integrity sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA== - -caniuse-lite@^1.0.30001370: - version "1.0.30001373" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz#2dc3bc3bfcb5d5a929bec11300883040d7b4b4be" - integrity sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ== + version "1.0.30001443" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001443.tgz#8fc85f912d5471c9821acacf9e715c421ca0dd1f" + integrity sha512-jUo8svymO8+Mkj3qbUbVjR8zv8LUGpGkUM/jKvc9SO2BvjCI980dp9fQbf/dyLs6RascPzgR4nhAKFA4OHeSaA== canvaskit-wasm@0.36.1: version "0.36.1" @@ -3795,9 +3551,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0, ci-info@^3.3.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" - integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== + version "3.7.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" + integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" @@ -4010,9 +3766,9 @@ commander@^7.2.0: integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== commander@^9.4.0: - version "9.4.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd" - integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw== + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== commander@~2.13.0: version "2.13.0" @@ -4107,16 +3863,14 @@ content-disposition@0.5.4: safe-buffer "5.2.1" content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== cookie-signature@1.0.6: version "1.0.6" @@ -4162,13 +3916,12 @@ copy-webpack-plugin@~6.0.3: serialize-javascript "^4.0.0" webpack-sources "^1.4.3" -core-js-compat@^3.21.0, core-js-compat@^3.22.1: - version "3.24.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de" - integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw== +core-js-compat@^3.25.1: + version "3.27.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.27.1.tgz#b5695eb25c602d72b1d30cbfba3cb7e5e4cf0a67" + integrity sha512-Dg91JFeCDA17FKnneN7oCMz4BkQ4TcffkgHP4OWwp9yx3pi7ubqMDXXSacfNak1PQqjc95skyt+YBLHQJnkJwA== dependencies: - browserslist "^4.21.3" - semver "7.0.0" + browserslist "^4.21.4" core-util-is@~1.0.0: version "1.0.3" @@ -4301,13 +4054,12 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-in-js-utils@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" - integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== +css-in-js-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb" + integrity sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A== dependencies: - hyphenate-style-name "^1.0.2" - isobject "^3.0.1" + hyphenate-style-name "^1.0.3" css-loader@~3.6.0: version "3.6.0" @@ -4366,9 +4118,9 @@ css-select@^5.1.0: nth-check "^2.0.1" css-to-react-native@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756" - integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ== + version "3.1.0" + resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.1.0.tgz#e783474149997608986afcff614405714a8fe1ac" + integrity sha512-AryfkFA29b4I3vG7N4kxFboq15DxwSXzhXM37XNEjwJMgjYIc8BcqfiprpAqX0zadI5PMByEIwAMzXxk5Vcc4g== dependencies: camelize "^1.0.0" css-color-keywords "^1.0.0" @@ -4481,9 +4233,9 @@ csso@^4.0.2: css-tree "^1.1.2" csstype@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" - integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== + version "3.1.1" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" + integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== cyclist@^1.0.1: version "1.0.1" @@ -4496,9 +4248,9 @@ dag-map@~1.0.0: integrity sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw== dayjs@^1.8.15: - version "1.11.4" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.4.tgz#3b3c10ca378140d8917e06ebc13a4922af4f433e" - integrity sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g== + version "1.11.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" + integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0: version "2.6.9" @@ -4526,7 +4278,7 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -decode-uri-component@^0.2.0: +decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== @@ -4567,9 +4319,9 @@ default-gateway@^4.2.0: ip-regex "^2.1.0" defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" @@ -4862,14 +4614,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.3.564, electron-to-chromium@^1.4.251: - version "1.4.261" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.261.tgz#31f14ad60c6f95bec404a77a2fd5e1962248e112" - integrity sha512-fVXliNUGJ7XUVJSAasPseBbVgJIeyw5M1xIkgXdTSRjlmCqBbiSTsEdLOCJS31Fc8B7CaloQ/BFAg8By3ODLdg== - -electron-to-chromium@^1.4.202: - version "1.4.206" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.206.tgz#580ff85b54d7ec0c05f20b1e37ea0becdd7b0ee4" - integrity sha512-h+Fadt1gIaQ06JaIiyqPsBjJ08fV5Q7md+V8bUvQW/9OvXfL2LRICTz2EcnnCP7QzrFTS6/27MRV6Bl9Yn97zA== + version "1.4.284" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== elliptic@^6.5.3: version "6.5.4" @@ -4974,70 +4721,59 @@ errorhandler@^1.5.0: accepts "~1.3.7" escape-html "~1.0.3" -es-abstract@^1.17.2, es-abstract@^1.20.1: - version "1.20.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.3.tgz#90b143ff7aedc8b3d189bcfac7f1e3e3f81e9da1" - integrity sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw== +es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.21.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" + integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== dependencies: + available-typed-arrays "^1.0.5" call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" function-bind "^1.1.1" function.prototype.name "^1.1.5" get-intrinsic "^1.1.3" get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" has "^1.0.3" has-property-descriptors "^1.0.0" + has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.6" + internal-slot "^1.0.4" + is-array-buffer "^3.0.1" + is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" + is-typed-array "^1.1.10" is-weakref "^1.0.2" object-inspect "^1.12.2" object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" - unbox-primitive "^1.0.2" - -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: - version "1.20.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" - integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-weakref "^1.0.2" - object-inspect "^1.12.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - regexp.prototype.flags "^1.4.3" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -5080,9 +4816,9 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-prettier@^8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + version "8.6.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz#dec1d29ab728f4fa63061774e1672ac4e363d207" + integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== eslint-plugin-prettier@^4.0.0: version "4.2.1" @@ -5092,24 +4828,25 @@ eslint-plugin-prettier@^4.0.0: prettier-linter-helpers "^1.0.0" eslint-plugin-react@^7.31.8: - version "7.31.8" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz#3a4f80c10be1bcbc8197be9e8b641b2a3ef219bf" - integrity sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw== + version "7.32.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.0.tgz#d80f794a638c5770f952ba2ae793f0a516be7c09" + integrity sha512-vSBi1+SrPiLZCGvxpiZIa28fMEUaMjXtCplrvxcIxGzmFiYdsXQDwInEjuv5/i/2CTTxbkS87tE8lsQ0Qxinbw== dependencies: - array-includes "^3.1.5" - array.prototype.flatmap "^1.3.0" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" doctrine "^2.1.0" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.5" - object.fromentries "^2.0.5" - object.hasown "^1.1.1" - object.values "^1.1.5" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" prop-types "^15.8.1" - resolve "^2.0.0-next.3" + resolve "^2.0.0-next.4" semver "^6.3.0" - string.prototype.matchall "^4.0.7" + string.prototype.matchall "^4.0.8" eslint-scope@^4.0.3: version "4.0.3" @@ -5153,12 +4890,14 @@ eslint-visitor-keys@^3.3.0: integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@^8.13.0: - version "8.20.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.20.0.tgz#048ac56aa18529967da8354a478be4ec0a2bc81b" - integrity sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA== - dependencies: - "@eslint/eslintrc" "^1.3.0" - "@humanwhocodes/config-array" "^0.9.2" + version "8.31.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.31.0.tgz#75028e77cbcff102a9feae1d718135931532d524" + integrity sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA== + dependencies: + "@eslint/eslintrc" "^1.4.1" + "@humanwhocodes/config-array" "^0.11.8" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -5168,18 +4907,21 @@ eslint@^8.13.0: eslint-scope "^7.1.1" eslint-utils "^3.0.0" eslint-visitor-keys "^3.3.0" - espree "^9.3.2" + espree "^9.4.0" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^6.0.1" - globals "^13.15.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" @@ -5191,12 +4933,11 @@ eslint@^8.13.0: strip-ansi "^6.0.1" strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^9.3.2: - version "9.3.3" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" - integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== +espree@^9.4.0: + version "9.4.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" + integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" @@ -5318,6 +5059,11 @@ expo-asset@~8.7.0: path-browserify "^1.0.0" url-parse "^1.5.9" +expo-clipboard@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/expo-clipboard/-/expo-clipboard-4.0.1.tgz#74385c2deacbfc75583ebb22e95067bd8f79a037" + integrity sha512-ikVitSk8ltbbdmufQE/7+BpkDFU1hsLw5lhjEqknG6PHjtn4TP+ypMxKobBftU2VpFiz9O09bpRt86E8CzsJgQ== + expo-constants@~14.0.0, expo-constants@~14.0.2: version "14.0.2" resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-14.0.2.tgz#2cb1dec8f41a64c2fc5b4eecaf77d7661cad01cc" @@ -5377,10 +5123,10 @@ expo-manifests@~0.4.0: dependencies: expo-json-utils "~0.4.0" -expo-modules-autolinking@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-1.0.0.tgz#2daac20035e1ecf8e66d74dca9bd1b0d6c09166c" - integrity sha512-MoRRkOVMoGUH/Lr8XS6UmBIZT/qrwbRt2IzUBALcM6MWZKtDn9Uct9XgMRxue82FJhRCfy9p1xZJVKHBRo4zEA== +expo-modules-autolinking@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-1.0.2.tgz#f072f342ab797e43b16ddcdef251fcd4db851e1a" + integrity sha512-skAUXERKw1gtSw8xsvft9DE0KVhBvw4dujAtgCZoG2l513fN7ds+B5+30ZVgZATMC+EjtlmjKXzhp5QS44DCFA== dependencies: chalk "^4.1.0" commander "^7.2.0" @@ -5388,10 +5134,10 @@ expo-modules-autolinking@1.0.0: find-up "^5.0.0" fs-extra "^9.1.0" -expo-modules-core@1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-1.0.4.tgz#0f3c7feaa3a625680e22941f9000ae3f2bb7b5a1" - integrity sha512-Cf2G8f2h3TGASKNDYXlOwe5CBMmRt32wZzBAmau6qComw0e13Y9sDYORSfgk+NJWvLnHJrInN4qGytxHfal/aA== +expo-modules-core@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-1.1.1.tgz#06502379274bdcb356fcbe225c3c6bc4926e462c" + integrity sha512-+AcaYmaWphIfkBcccu65dyOhWnpOJ3+SQpoI4lI/Plg1nNjOLuBjmrdVvpiJOvkN+CqbNGsJ5Yll8LLk+C107Q== dependencies: compare-versions "^3.4.0" invariant "^2.2.4" @@ -5406,6 +5152,11 @@ expo-pwa@0.0.124: commander "2.20.0" update-check "1.5.3" +expo-sharing@~11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/expo-sharing/-/expo-sharing-11.0.1.tgz#a2c6beb7458e04762a53b584c3b7b79ae422e2da" + integrity sha512-GlI2+fRxfdCNUftFPfqhbCSd6OQaVf/WNAADWZnRanRQRbQdQjM6L0oMmUTwQStc36cmzl+vGgNlouwdmTDseA== + expo-splash-screen@~0.17.5: version "0.17.5" resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.17.5.tgz#a18dc59c1cc28ebbedbf0a7529a419d18ab0b311" @@ -5448,12 +5199,12 @@ expo-updates@~0.15.6: uuid "^3.4.0" expo@^47.0.0: - version "47.0.9" - resolved "https://registry.yarnpkg.com/expo/-/expo-47.0.9.tgz#2b4de45ef9870f8d90dbb4586eec89b2e125b14e" - integrity sha512-NZtwk2Q+8DYlEpY/vBWDOVeN20AdlsVlZlhLOHAqvXpUSlNhG5XYBJD3wW+GQ1BQetq/8Hxe5NaZu0N5YkYsvA== + version "47.0.13" + resolved "https://registry.yarnpkg.com/expo/-/expo-47.0.13.tgz#f53f82e7f9e209f8a8b25f2493f58439958368cb" + integrity sha512-9VjjGdViCJ9NfWbUE7brkwFBDvKuA35V345vMtHFYNKoGJjXib36yitmawreMDQFv0kMTqTnzc7T2191Pod7Ng== dependencies: "@babel/runtime" "^7.14.0" - "@expo/cli" "0.4.10" + "@expo/cli" "0.4.11" "@expo/config" "7.0.3" "@expo/config-plugins" "5.0.4" "@expo/vector-icons" "^13.0.0" @@ -5465,8 +5216,8 @@ expo@^47.0.0: expo-file-system "~15.1.1" expo-font "~11.0.1" expo-keep-awake "~11.0.1" - expo-modules-autolinking "1.0.0" - expo-modules-core "1.0.4" + expo-modules-autolinking "1.0.2" + expo-modules-core "1.1.1" fbemitter "^3.0.0" getenv "^1.0.0" invariant "^2.2.4" @@ -5478,13 +5229,13 @@ expo@^47.0.0: expo-error-recovery "~4.0.1" express@^4.17.1: - version "4.18.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" - integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.0" + body-parser "1.20.1" content-disposition "0.5.4" content-type "~1.0.4" cookie "0.5.0" @@ -5503,7 +5254,7 @@ express@^4.17.1: parseurl "~1.3.3" path-to-regexp "0.1.7" proxy-addr "~2.0.7" - qs "6.10.3" + qs "6.11.0" range-parser "~1.2.1" safe-buffer "5.2.1" send "0.18.0" @@ -5553,7 +5304,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.1.1, fast-glob@^3.2.4: +fast-glob@^3.1.1, fast-glob@^3.2.4, fast-glob@^3.2.5, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -5564,17 +5315,6 @@ fast-glob@^3.1.1, fast-glob@^3.2.4: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.2.5, fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -5585,10 +5325,15 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-loops@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.3.tgz#ce96adb86d07e7bf9b4822ab9c6fac9964981f75" + integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g== + fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" @@ -5607,9 +5352,9 @@ faye-websocket@~0.11.1: websocket-driver ">=0.5.1" fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" @@ -5786,14 +5531,14 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" - integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== flow-parser@0.*: - version "0.183.1" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.183.1.tgz#633387855028cbeb38d65ed0a0d64729e1599a3b" - integrity sha512-xBnvBk8D7aBY7gAilyjjGaNJe+9PGU6I/D2g6lGkkKyl4dW8nzn2eAc7Sc7RNRRr2NNYwpgHOOxBTjJKdKOXcA== + version "0.196.3" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.196.3.tgz#dd923f29a6c194770a4f999f8026ef1da79d428b" + integrity sha512-R8wj12eHW6og+IBWeRS6aihkdac1Prh4zw1bfxtt/aeu8r5OFmQEZjnmINcjO/5Q+OKvI4Eg367ygz2SHvtH+w== flow-parser@^0.121.0: version "0.121.0" @@ -5818,6 +5563,13 @@ fontfaceobserver@^2.1.0: resolved "https://registry.yarnpkg.com/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz#5fb392116e75d5024b7ec8e4f2ce92106d1488c8" integrity sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -5981,11 +5733,6 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== - functions-have-names@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -6006,16 +5753,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-intrinsic@^1.1.3: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== @@ -6069,7 +5807,7 @@ glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: +glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -6132,13 +5870,20 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== +globals@^13.19.0: + version "13.19.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" + integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== dependencies: type-fest "^0.20.2" +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + globby@11.0.1: version "11.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" @@ -6174,11 +5919,23 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + graphql-tag@^2.10.1: version "2.12.6" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" @@ -6226,6 +5983,11 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" @@ -6439,17 +6201,6 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -6513,7 +6264,7 @@ humps@^2.0.1: resolved "https://registry.yarnpkg.com/humps/-/humps-2.0.1.tgz#dd02ea6081bd0568dc5d073184463957ba9ef9aa" integrity sha512-E0eIbrFWUhwfXJmsbdjRQFQPrl5pTEoKlz163j1mTqqUnU9PgR4AgB8AIITzuB3vLBdxZXyZ9TDIrwB2OASz4g== -hyphenate-style-name@^1.0.2: +hyphenate-style-name@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== @@ -6543,9 +6294,9 @@ iferr@^0.1.5: integrity sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA== ignore@^5.1.4, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== image-size@^0.6.0: version "0.6.3" @@ -6637,11 +6388,12 @@ ini@^1.3.5, ini@~1.3.0: integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== inline-style-prefixer@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz#c5c0e43ba8831707afc5f5bbfd97edf45c1fa7ae" - integrity sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ== + version "6.0.4" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.4.tgz#4290ed453ab0e4441583284ad86e41ad88384f44" + integrity sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg== dependencies: - css-in-js-utils "^2.0.0" + css-in-js-utils "^3.1.0" + fast-loops "^1.1.3" internal-ip@4.3.0, internal-ip@^4.3.0: version "4.3.0" @@ -6651,12 +6403,12 @@ internal-ip@4.3.0, internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== +internal-slot@^1.0.3, internal-slot@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" + integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== dependencies: - get-intrinsic "^1.1.0" + get-intrinsic "^1.1.3" has "^1.0.3" side-channel "^1.0.4" @@ -6714,6 +6466,15 @@ is-arguments@^1.0.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-array-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" + integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -6758,12 +6519,7 @@ is-buffer@^1.1.5, is-buffer@~1.1.1, is-buffer@~1.1.6: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - -is-callable@^1.2.6: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== @@ -6781,9 +6537,9 @@ is-color-stop@^1.0.0: rgba-regex "^1.0.0" is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" @@ -6949,7 +6705,7 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" -is-path-inside@^3.0.2: +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -7015,6 +6771,17 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" @@ -7139,9 +6906,9 @@ jimp-compact@0.16.1: integrity sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww== joi@^17.2.1: - version "17.6.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" - integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== + version "17.7.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" + integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" @@ -7154,6 +6921,11 @@ join-component@^1.1.0: resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" integrity sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ== +js-sdsl@^4.1.4: + version "4.2.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.2.0.tgz#278e98b7bea589b8baaf048c20aeb19eb7ad09d0" + integrity sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -7254,16 +7026,16 @@ json5@^0.5.1: integrity sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw== json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== +json5@^2.1.2, json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonfile@^2.1.0: version "2.4.0" @@ -7289,12 +7061,12 @@ jsonfile@^6.0.1: graceful-fs "^4.1.6" "jsx-ast-utils@^2.4.1 || ^3.0.0": - version "3.3.2" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz#afe5efe4332cd3515c065072bd4d6b0aa22152bd" - integrity sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q== + version "3.3.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" + integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== dependencies: array-includes "^3.1.5" - object.assign "^4.1.2" + object.assign "^4.1.3" killable@^1.0.1: version "1.0.1" @@ -7387,9 +7159,9 @@ loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: json5 "^1.0.1" loader-utils@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" - integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" @@ -7477,9 +7249,9 @@ logkitty@^0.7.1: yargs "^15.1.0" loglevel@^1.6.8: - version "1.8.0" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" - integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== + version "1.8.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" + integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" @@ -8011,7 +7783,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -8025,16 +7797,11 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0: +minimist@^1.2.0, minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== -minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -8064,12 +7831,17 @@ minipass@3.1.6: yallist "^4.0.0" minipass@^3.0.0, minipass@^3.1.1: - version "3.3.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" - integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw== + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: yallist "^4.0.0" +minipass@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.1.tgz#2b9408c6e81bb8b338d600fb3685e375a370a057" + integrity sha512-V9esFpNbK0arbN3fm2sxDKqMYgIp7XtVdE4Esj+PE4Qaaxdg1wIw48ITQIOn1sc8xXSmUviVL3cyjMqPlrVkiA== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -8173,11 +7945,11 @@ mz@^2.7.0: thenify-all "^1.0.0" nan@^2.12.1: - version "2.16.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" - integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== + version "2.17.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" + integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== -nanoid@^3.1.23, nanoid@^3.3.1, nanoid@^3.3.3: +nanoid@^3.1.23, nanoid@^3.3.1: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== @@ -8199,6 +7971,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -8249,13 +8026,20 @@ node-dir@^0.1.17: dependencies: minimatch "^3.0.2" -node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" +node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.6.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== + dependencies: + whatwg-url "^5.0.0" + node-forge@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -8313,9 +8097,9 @@ node-releases@^1.1.61: integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== + version "2.0.8" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" + integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== node-stream-zip@^1.9.1: version "1.15.0" @@ -8339,13 +8123,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-svg-path@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz#0e614eca23c39f0cffe821d6be6cd17e569a766c" - integrity sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg== - dependencies: - svg-arc-to-cubic-bezier "^3.0.0" - normalize-url@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" @@ -8406,7 +8183,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.12.0, object-inspect@^1.12.2, object-inspect@^1.9.0: +object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== @@ -8431,17 +8208,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.assign@^4.1.4: +object.assign@^4.1.3, object.assign@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== @@ -8451,41 +8218,41 @@ object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.0, object.entries@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== +object.entries@^1.1.0, object.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" -object.fromentries@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" - integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: - version "2.1.4" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" - integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== + version "2.1.5" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.5.tgz#db5a9002489b64eef903df81d6623c07e5b4b4d3" + integrity sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw== dependencies: - array.prototype.reduce "^1.0.4" + array.prototype.reduce "^1.0.5" call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.20.1" + es-abstract "^1.20.4" -object.hasown@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" - integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== +object.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" + integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== dependencies: define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" object.pick@^1.3.0: version "1.3.0" @@ -8494,14 +8261,14 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0, object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== +object.values@^1.1.0, object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" @@ -8773,11 +8540,6 @@ parse-srcset@^1.0.2: resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" integrity sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q== -parse-svg-path@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/parse-svg-path/-/parse-svg-path-0.1.2.tgz#7a7ec0d1eb06fa5325c7d3e009b859a09b5d49eb" - integrity sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ== - parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -9255,9 +9017,9 @@ postcss-selector-parser@^3.0.0: uniq "^1.0.1" postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: - version "6.0.10" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" - integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + version "6.0.11" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" + integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -9311,9 +9073,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^2.6.2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + version "2.8.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.2.tgz#c4ea1b5b454d7c4b59966db2e06ed7eec5dfd160" + integrity sha512-BtRV9BcncDyI2tsuS19zzhzoxD8Dh8LiCx7j7tHzrkz8GFXAexeWFdi22mjE1d16dftH2qNaytVxqiRTGlMfpw== pretty-bytes@5.6.0: version "5.6.0" @@ -9388,7 +9150,7 @@ prompts@^2.3.2, prompts@^2.4.0: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.6.0, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -9463,9 +9225,9 @@ punycode@^1.2.4: integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.2.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.2.0.tgz#2092cc57cd2582c38e4e7e8bb869dc8d3148bc74" + integrity sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw== q@^1.1.2: version "1.5.1" @@ -9477,24 +9239,19 @@ qrcode-terminal@0.11.0: resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz#ffc6c28a2fc0bfb47052b47e23f4f446a5fbdb9e" integrity sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ== -qs@6.10.3: - version "6.10.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" - integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== dependencies: side-channel "^1.0.4" -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - query-string@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1" - integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w== + version "7.1.3" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" + integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== dependencies: - decode-uri-component "^0.2.0" + decode-uri-component "^0.2.2" filter-obj "^1.1.0" split-on-first "^1.0.0" strict-uri-encode "^2.0.0" @@ -9546,16 +9303,6 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" @@ -9710,15 +9457,6 @@ react-native-reanimated@~2.12.0: setimmediate "^1.0.5" string-hash-64 "^1.0.3" -react-native-redash@^16.1.1: - version "16.3.0" - resolved "https://registry.yarnpkg.com/react-native-redash/-/react-native-redash-16.3.0.tgz#a9112ff1b0e0b506a2e2ae50967597e73b69d343" - integrity sha512-dhmeYbQ/usGzxZSGZmzmRuIFF2LrtJUKqgseKgf9Jdj0JQ7VM20m/LqTg60+wjxeiyAh2D/vKsQ2U7rMkuoplQ== - dependencies: - abs-svg-path "^0.1.1" - normalize-svg-path "^1.0.1" - parse-svg-path "^0.1.2" - react-native-safe-area-context@4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.4.1.tgz#239c60b8a9a80eac70a38a822b04c0f1d15ffc01" @@ -9752,6 +9490,13 @@ react-native-svg@13.4.0: css-select "^5.1.0" css-tree "^1.1.3" +react-native-switch@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/react-native-switch/-/react-native-switch-1.5.1.tgz#da032f66547053aa004c7a159422dbca8fadd944" + integrity sha512-nfuPrrPKzeZL1mQH0u1rlEGv9W1+okU/xOcNNaDNz/LhSdzWOZ04ogJ4iEj7Sth3hkBxv4XwFQZTgCc07we+cg== + dependencies: + prop-types "^15.6.0" + react-native-vector-icons@^9.1.0: version "9.2.0" resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz#3c0c82e95defd274d56363cbe8fead8d53167ebd" @@ -9761,9 +9506,9 @@ react-native-vector-icons@^9.1.0: yargs "^16.1.1" react-native-web@~0.18.7: - version "0.18.10" - resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.18.10.tgz#fb4db047f4be7f9cf35f37ec8d52f7d1c450600f" - integrity sha512-YV2gtZa1n7ulTGp+HcxH+KsAtaDPBI/dKd9oOQS31zyFHURjObLUVkKnGjkmlYAUReWfvmlU64GzyNwoZF9/tA== + version "0.18.12" + resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.18.12.tgz#d4bb3a783ece2514ba0508d7805b09c0a98f5a8e" + integrity sha512-fboP7yqobJ8InSr4fP+bQ3scOtSQtUoPcR+HWasH8b/fk/RO+mWcJs/8n+lewy9WTZc2D68ha7VwRDviUshEWA== dependencies: "@babel/runtime" "^7.18.6" create-react-class "^15.7.0" @@ -9907,10 +9652,10 @@ recursive-readdir@2.2.2: dependencies: minimatch "3.0.4" -regenerate-unicode-properties@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" - integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== dependencies: regenerate "^1.4.2" @@ -9919,15 +9664,15 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== dependencies: "@babel/runtime" "^7.8.4" @@ -9939,7 +9684,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -9953,17 +9698,17 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -regexpu-core@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" - integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== +regexpu-core@^5.2.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.2.tgz#3e4e5d12103b64748711c3aad69934d7718e75fc" + integrity sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw== dependencies: regenerate "^1.4.2" - regenerate-unicode-properties "^10.0.1" - regjsgen "^0.6.0" - regjsparser "^0.8.2" + regenerate-unicode-properties "^10.1.0" + regjsgen "^0.7.1" + regjsparser "^0.9.1" unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" registry-auth-token@3.3.2: version "3.3.2" @@ -9980,15 +9725,15 @@ registry-url@3.1.0: dependencies: rc "^1.0.1" -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== +regjsgen@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" + integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== -regjsparser@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" - integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== dependencies: jsesc "~0.5.0" @@ -10058,9 +9803,9 @@ requires-port@^1.0.0: integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== reselect@^4.0.0: - version "4.1.6" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.6.tgz#19ca2d3d0b35373a74dc1c98692cdaffb6602656" - integrity sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ== + version "4.1.7" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.7.tgz#56480d9ff3d3188970ee2b76527bd94a95567a42" + integrity sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A== resolve-cwd@^2.0.0: version "2.0.0" @@ -10098,7 +9843,7 @@ resolve@^1.13.1, resolve@^1.14.2, resolve@^1.3.2: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.3: +resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== @@ -10309,11 +10054,6 @@ selfsigned@^1.10.7: dependencies: node-forge "^0.10.0" -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - semver@7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" @@ -10330,9 +10070,9 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@^7.3.5, semver@^7.3.7, semver@~7.3.2: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -10422,11 +10162,6 @@ setprototypeof@1.1.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -10482,9 +10217,9 @@ shell-quote@1.7.2: integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== shell-quote@^1.6.1, shell-quote@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" - integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== + version "1.7.4" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" + integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== side-channel@^1.0.4: version "1.0.4" @@ -10726,7 +10461,7 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: +"statuses@>= 1.4.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== @@ -10796,37 +10531,37 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.matchall@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" - integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== +string.prototype.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" + integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" has-symbols "^1.0.3" internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.1" + regexp.prototype.flags "^1.4.3" side-channel "^1.0.4" -string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" -string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" - es-abstract "^1.19.5" + es-abstract "^1.20.4" string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" @@ -10924,14 +10659,14 @@ stylehacks@^4.0.0: postcss-selector-parser "^3.0.0" styleq@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/styleq/-/styleq-0.1.2.tgz#052b46af5ca4f920b1bdae2735ffb1e3970f53cd" - integrity sha512-EBNuMVSxpssuFcJq/c4zmZ4tpCyX9E27hz5xPJhw4URjRHcYXPHh8rDHY/tJsw5gtP0+tIL3IBYeQVIYjdZFhg== + version "0.1.3" + resolved "https://registry.yarnpkg.com/styleq/-/styleq-0.1.3.tgz#8efb2892debd51ce7b31dc09c227ad920decab71" + integrity sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA== sucrase@^3.20.0: - version "3.24.0" - resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.24.0.tgz#66a8f2cc845bc441706ce5f3056de283289067b6" - integrity sha512-SevqflhW356TKEyWjFHg2e5f3eH+5rzmsMJxrVMDvZIEHh/goYrpzDGA6APEj4ME9MdGm8oNgIzi1eF3c3dDQA== + version "3.29.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.29.0.tgz#3207c5bc1b980fdae1e539df3f8a8a518236da7d" + integrity sha512-bZPAuGA5SdFHuzqIhTAqt9fvNEo9rESqXIG3oiKdF8K4UmkQxC4KlNL3lVyAErXp+mPvUqZ5l13qx6TrDIGf3A== dependencies: commander "^4.0.0" glob "7.1.6" @@ -10984,9 +10719,9 @@ supports-color@^8.0.0: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== dependencies: has-flag "^4.0.0" supports-color "^7.0.0" @@ -10996,11 +10731,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svg-arc-to-cubic-bezier@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz#390c450035ae1c4a0104d90650304c3bc814abe6" - integrity sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g== - svgo@^1.0.0: version "1.3.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" @@ -11026,13 +10756,13 @@ tapable@^1.0.0, tapable@^1.1.3: integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== tar@^6.0.2, tar@^6.0.5: - version "6.1.11" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" - integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + version "6.1.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" + integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" - minipass "^3.0.0" + minipass "^4.0.0" minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" @@ -11237,11 +10967,6 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -11253,9 +10978,9 @@ tr46@~0.0.3: integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== traverse@~0.6.6: - version "0.6.6" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" - integrity sha512-kdf4JKs8lbARxWdp7RKdNzoJBhGUcIalSYibuGyHJbmk40pOysQ0+QPvlkCOICOivDWU2IJo2rkrxyTK2AH4fw== + version "0.6.7" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.7.tgz#46961cd2d57dd8706c36664acde06a248f1173fe" + integrity sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg== ts-interface-checker@^0.1.9: version "0.1.13" @@ -11267,15 +10992,15 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -tslib@^1.10.0, tslib@^1.8.1: +tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== tsutils@^3.21.0: version "3.21.0" @@ -11326,7 +11051,7 @@ type-fest@^0.7.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== -type-is@~1.6.17, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -11334,15 +11059,24 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typescript@^4.6.3: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + version "4.9.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" + integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== ua-parser-js@^0.7.30: version "0.7.33" @@ -11380,15 +11114,15 @@ unicode-match-property-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript "^2.0.0" unicode-property-aliases-ecmascript "^2.0.0" -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== union-value@^1.0.0: version "1.0.1" @@ -11476,18 +11210,10 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-browserslist-db@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" - integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - update-browserslist-db@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" - integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -11614,11 +11340,6 @@ uuid@^8.0.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== - valid-url@~1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" @@ -11855,6 +11576,18 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -11874,6 +11607,11 @@ wonka@^4.0.14: resolved "https://registry.yarnpkg.com/wonka/-/wonka-4.0.15.tgz#9aa42046efa424565ab8f8f451fcca955bf80b89" integrity sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg== +wonka@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/wonka/-/wonka-6.1.2.tgz#2c66fa5b26a12f002a03619b988258313d0b5352" + integrity sha512-zNrXPMccg/7OEp9tSfFkMgTvhhowqasiSHdJ3eCZolXxVTV/aT6HUTofoZk9gwRbGoFey/Nss3JaZKUMKMbofg== + word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" From 725810168fb4a13dd9f54873e61894aac40ae593 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Mon, 20 Feb 2023 23:34:53 +0100 Subject: [PATCH 10/15] TimeLeft component fix latitude and longitude props occupancies --- src/api/rooms.ts | 3 +- .../FreeClass/ClassDetails/InfoMapTile.tsx | 12 +++-- .../FreeClass/ClassDetails/TimeLeftTile.tsx | 37 +++++++++---- src/navigation/NavigationTypes.ts | 9 +++- src/pages/FreeClass/RoomDetails.tsx | 7 ++- src/utils/rooms.ts | 53 +++++++++++++++++-- 6 files changed, 99 insertions(+), 22 deletions(-) diff --git a/src/api/rooms.ts b/src/api/rooms.ts index 3e0763fe..7a18030d 100644 --- a/src/api/rooms.ts +++ b/src/api/rooms.ts @@ -11,6 +11,7 @@ export interface Room { building: string power: boolean link: string + occupancies: Record } export interface RoomSimplified { roomId: number @@ -19,7 +20,7 @@ export interface RoomSimplified { export interface RoomDetails { name: string - capacity: string + capacity: number building: string address: string power: boolean diff --git a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx index c3660bd6..b0010956 100644 --- a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx +++ b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx @@ -11,7 +11,9 @@ interface InfoMapTileProps { roomName: string building: string address?: string - capacity?: string + capacity?: number + roomLongitude?: number + roomLatitude?: number } export const InfoMapTile: FC = props => { @@ -132,8 +134,8 @@ export const InfoMapTile: FC = props => { onPress={() => openAddressOnMap( props.building ? building + "." + roomName : roomName, - "45.478053", - "9.228061" + props.roomLatitude?.toString() ?? "45.478053", + props.roomLongitude?.toString() ?? "9.228061" ) } > @@ -192,8 +194,8 @@ export const InfoMapTile: FC = props => { bottom: -25, }} initialRegion={{ - latitude: 45.478053, - longitude: 9.228061, + latitude: props.roomLatitude ?? 45.478053, + longitude: props.roomLongitude ?? 9.228061, latitudeDelta: 0.01, longitudeDelta: 0.01, }} diff --git a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx index 6870b77f..97999649 100644 --- a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx +++ b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx @@ -12,10 +12,11 @@ import { useSVG, } from "@shopify/react-native-skia" import clock from "assets/freeClassrooms/clock.svg" -import { extractTimeLeft } from "utils/rooms" +import { extractTimeLeft, getEndDate } from "utils/rooms" interface TimeLeftTileProps { startDate: string + occupancies?: Record } export const TimeLeftTile: FC = props => { @@ -39,12 +40,23 @@ export const TimeLeftTile: FC = props => { ) const startDate = new Date(props.startDate) - const endDate = new Date(startDate.getTime() + 8 * 60 * 60 * 1000) + + const endDate = getEndDate(props.occupancies) + const startHour = startDate.getHours().toString().padStart(2, "0") - const endhour = endDate.getHours().toString().padStart(2, "0") + const startMinutes = startDate.getMinutes().toString().padStart(2, "0") + + let endhour = endDate?.getHours().toString().padStart(2, "0") ?? undefined + let endMinutes = + endDate?.getMinutes().toString().padStart(2, "0") ?? undefined - const { hoursLeft, minutesLeft, isPositive } = extractTimeLeft(startDate) + const { hoursLeft, minutesLeft } = extractTimeLeft(startDate, endDate) + // ! temporary fix + if (endDate && endDate.getTime() - startDate.getTime() < 0) { + endhour = undefined + endMinutes = undefined + } return ( = props => { flexDirection: "row", alignItems: "center", marginBottom: 8, - width: 96, + width: 100, }} > = props => { borderColor: isLight ? "#454773" : "#fff", borderWidth: 0.5, borderRadius: 5, + minWidth: 70, + alignItems: "center", }} > = props => { paddingHorizontal: 2, }} > - {startHour} : 00 + {startHour} : {startMinutes} @@ -107,7 +121,6 @@ export const TimeLeftTile: FC = props => { style={{ flexDirection: "row", alignItems: "center", - width: 96, }} > = props => { borderColor: isLight ? "#454773" : "#fff", borderWidth: 0.5, borderRadius: 5, + minWidth: 70, + alignItems: "center", }} > = props => { paddingHorizontal: 2, }} > - {endhour} : 00 + {endhour && endMinutes + ? `${endhour} : ${endMinutes}` + : "-- : --"} @@ -200,7 +217,9 @@ export const TimeLeftTile: FC = props => { color: isLight ? "#414867" : "#fff", }} > - {isPositive ? `${hoursLeft} h ${minutesLeft}'` : "-- h -- '"} + {hoursLeft && minutesLeft + ? `${hoursLeft} h ${minutesLeft}'` + : "-- h -- '"} diff --git a/src/navigation/NavigationTypes.ts b/src/navigation/NavigationTypes.ts index 69f59fae..506ea6d3 100644 --- a/src/navigation/NavigationTypes.ts +++ b/src/navigation/NavigationTypes.ts @@ -53,7 +53,14 @@ export type MainStackNavigatorParams = { CampusChoice: { currentDate: string } PositionChoice: undefined BuildingChoice: { campus: CampusItem; currentDate: string } - RoomDetails: { room: RoomDetails; startDate: string; roomId: number } + RoomDetails: { + room: RoomDetails + startDate: string + roomId: number + roomLatitude?: number + roomLongitude?: number + occupancies?: Record + } ClassChoice: { building: BuildingItem; currentDate: string } Groups: undefined } diff --git a/src/pages/FreeClass/RoomDetails.tsx b/src/pages/FreeClass/RoomDetails.tsx index 6861d25b..acaa9e04 100644 --- a/src/pages/FreeClass/RoomDetails.tsx +++ b/src/pages/FreeClass/RoomDetails.tsx @@ -7,7 +7,8 @@ import { RoomUtilsSection } from "components/FreeClass/ClassDetails/RoomUtilsSec import { CrowdingSection } from "components/FreeClass/ClassDetails/CrowdingSection" export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { - const { room, startDate, roomId } = props.route.params + const { room, startDate, roomId, roomLatitude, roomLongitude, occupancies } = + props.route.params return ( @@ -16,8 +17,10 @@ export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { building={room.building} capacity={room.capacity} roomName={room.name} + roomLatitude={roomLatitude} + roomLongitude={roomLongitude} /> - + diff --git a/src/utils/rooms.ts b/src/utils/rooms.ts index e8f081eb..d62ac159 100644 --- a/src/utils/rooms.ts +++ b/src/utils/rooms.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { ValidCrowdStatus } from "components/FreeClass/ClassDetails/CrowdingSection" export function extractRoom(val: string) { @@ -23,16 +24,28 @@ export function extractBuilding(val: string) { return regExp.test(val) } */ -export function extractTimeLeft(startDate: Date) { - const deltaMilliseconds = startDate.getTime() - Date.now() +export function extractTimeLeft(now: Date, targetDate?: Date) { + if (!targetDate) { + return { hoursLeft: undefined, minutesLeft: undefined } + } + const deltaMilliseconds = targetDate.getTime() - now.getTime() + if (deltaMilliseconds <= 0) { + return { hoursLeft: undefined, minutesLeft: undefined } + } const hours = Math.floor(deltaMilliseconds / 3.6e6) const minutes = Math.floor( (deltaMilliseconds - hours * 60 * 60 * 1000) / 60000 ) const hoursLeft = hours.toString() const minutesLeft = minutes.toString() - const isPositive = hours >= 0 && minutes >= 0 - return { hoursLeft, minutesLeft, isPositive } + + console.log(hoursLeft) + + console.log(minutesLeft) + return { + hoursLeft: hoursLeft, + minutesLeft: minutesLeft, + } } export function getCrowdStatus(pos: number, width: number): ValidCrowdStatus { @@ -54,3 +67,35 @@ export function getCrowdStatus(pos: number, width: number): ValidCrowdStatus { return 5 } } + +/** + * Return the correct end date in which the room is free given a record of occcupancies + * + * return undefined in case of errors + * + */ +export function getEndDate(occupancies?: Record) { + let time + + if (occupancies !== undefined) { + time = Object.keys(occupancies).find( + time => occupancies[time] === "OCCUPIED" + ) + } + if (time === undefined) { + const endDate = new Date() + endDate.setHours(20, 0, 0, 0) + return endDate + } else { + try { + const hour = parseInt(time.substring(0, 2)) + const minutes = parseInt(time.substring(3)) + const endDate = new Date() + endDate.setHours(hour, minutes, 0, 0) + return endDate + } catch (err) { + console.log(err) + return undefined + } + } +} From 6f911f0f9b36d2d32d5e70617c3df0820b55e5d8 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Mon, 20 Feb 2023 23:51:00 +0100 Subject: [PATCH 11/15] endDate bug fix --- src/components/FreeClass/ClassDetails/TimeLeftTile.tsx | 2 +- src/utils/rooms.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx index 97999649..7121cbbd 100644 --- a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx +++ b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx @@ -41,7 +41,7 @@ export const TimeLeftTile: FC = props => { const startDate = new Date(props.startDate) - const endDate = getEndDate(props.occupancies) + const endDate = getEndDate(startDate, props.occupancies) const startHour = startDate.getHours().toString().padStart(2, "0") const startMinutes = startDate.getMinutes().toString().padStart(2, "0") diff --git a/src/utils/rooms.ts b/src/utils/rooms.ts index d62ac159..86b6bc1e 100644 --- a/src/utils/rooms.ts +++ b/src/utils/rooms.ts @@ -74,7 +74,10 @@ export function getCrowdStatus(pos: number, width: number): ValidCrowdStatus { * return undefined in case of errors * */ -export function getEndDate(occupancies?: Record) { +export function getEndDate( + startDate: Date, + occupancies?: Record +) { let time if (occupancies !== undefined) { @@ -83,7 +86,7 @@ export function getEndDate(occupancies?: Record) { ) } if (time === undefined) { - const endDate = new Date() + const endDate = new Date(startDate) endDate.setHours(20, 0, 0, 0) return endDate } else { From e6ea2c220db8b7a70ad862ade3c7086227511c1b Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Mon, 20 Feb 2023 23:58:25 +0100 Subject: [PATCH 12/15] fix TimeLeft not correct --- src/components/FreeClass/ClassDetails/TimeLeftTile.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx index 7121cbbd..7b01fb29 100644 --- a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx +++ b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx @@ -41,6 +41,8 @@ export const TimeLeftTile: FC = props => { const startDate = new Date(props.startDate) + const now = new Date() + const endDate = getEndDate(startDate, props.occupancies) const startHour = startDate.getHours().toString().padStart(2, "0") @@ -50,7 +52,7 @@ export const TimeLeftTile: FC = props => { let endMinutes = endDate?.getMinutes().toString().padStart(2, "0") ?? undefined - const { hoursLeft, minutesLeft } = extractTimeLeft(startDate, endDate) + const { hoursLeft, minutesLeft } = extractTimeLeft(now, endDate) // ! temporary fix if (endDate && endDate.getTime() - startDate.getTime() < 0) { From 9f1775c61f9dee6b04c11f789e2cd0851463dff8 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Tue, 21 Feb 2023 20:21:12 +0100 Subject: [PATCH 13/15] fixes and real data link --- src/MainContainer.tsx | 20 +- src/api/rooms.ts | 6 +- .../FreeClass/ClassDetails/InfoMapTile.tsx | 213 +++++++++--------- .../FreeClass/ClassDetails/TimeLeftTile.tsx | 15 +- src/components/FreeClass/FreeClassList.tsx | 5 + src/pages/FreeClass/BuildingChoice.tsx | 2 + src/pages/FreeClass/ClassChoice.tsx | 13 +- src/pages/FreeClass/PositionChoice.tsx | 40 +--- src/pages/FreeClass/RoomDetails.tsx | 4 +- src/utils/rooms.ts | 44 +++- 10 files changed, 199 insertions(+), 163 deletions(-) diff --git a/src/MainContainer.tsx b/src/MainContainer.tsx index da440944..bd6e522d 100644 --- a/src/MainContainer.tsx +++ b/src/MainContainer.tsx @@ -65,7 +65,25 @@ export const MainContainer: FC = () => { console.log("downloads") }} onNotifications={() => { - console.log("notifications") + navigate("RoomDetails", { + room: { + name: "2.0.1", + capacity: 380, + building: "Edificio 2", + address: "Piazza Leonardo da Vinci, 32 - 20133 - Milano (MI)", + power: false, + }, + startDate: "2023-02-22T16:15:00Z", + roomId: 32, + roomLatitude: 45.4788249919485, + roomLongitude: 9.227210008150676, + occupancies: { + // eslint-disable-next-line @typescript-eslint/naming-convention + "19:00": "FREE", + // eslint-disable-next-line @typescript-eslint/naming-convention + "19:35": "OCCUPIED", + }, + }) }} onSettings={() => { navigate("SettingsNav", { diff --git a/src/api/rooms.ts b/src/api/rooms.ts index 7a18030d..1bf40ac5 100644 --- a/src/api/rooms.ts +++ b/src/api/rooms.ts @@ -3,7 +3,7 @@ import { AuthType, HttpClient, RequestOptions } from "./HttpClient" /* eslint-disable @typescript-eslint/naming-convention */ export interface Rooms { - freeRooms: Room[] + free_rooms: Room[] } export interface Room { room_id: number @@ -11,11 +11,13 @@ export interface Room { building: string power: boolean link: string + occupancy_rate: number | null occupancies: Record } export interface RoomSimplified { roomId: number name: string + occupancies: Record } export interface RoomDetails { @@ -57,7 +59,7 @@ export const rooms = { }, } ) - return response.data.freeRooms + return response.data.free_rooms }, async getOccupancyRate(roomId: number, options?: RequestOptions) { diff --git a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx index b0010956..784cf493 100644 --- a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx +++ b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx @@ -12,20 +12,21 @@ interface InfoMapTileProps { building: string address?: string capacity?: number - roomLongitude?: number - roomLatitude?: number + longitude?: number + latitude?: number } export const InfoMapTile: FC = props => { const { isLight, primary } = usePalette() - console.log(props.building) const building = extractBuilding(props.building) - console.log(props.roomName) const roomName = extractRoom(props.roomName) const expandSvg = useSVG(expand) + const latitude = props.latitude + const longitude = props.longitude + /*from https://stackoverflow.com/questions/73653813/how-to-open-google-map-with-latitude-and-longitude*/ const openAddressOnMap = (label: string, lat: string, lng: string) => { const scheme = Platform.select({ @@ -120,119 +121,121 @@ export const InfoMapTile: FC = props => { - - - - openAddressOnMap( - props.building ? building + "." + roomName : roomName, - props.roomLatitude?.toString() ?? "45.478053", - props.roomLongitude?.toString() ?? "9.228061" - ) - } - > - + + + openAddressOnMap( + props.building, + latitude.toString(), + longitude.toString() + ) + } > - {expand && expandSvg && ( - - + {expand && expandSvg && ( + - - - - )} - - - - - consulta la{" "} - - + + + + )} + + + - mappa - - - + + consulta la{" "} + + + mappa + + + + - + )} ) } diff --git a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx index 7b01fb29..9cde63d2 100644 --- a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx +++ b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx @@ -39,26 +39,21 @@ export const TimeLeftTile: FC = props => { ) ) - const startDate = new Date(props.startDate) - const now = new Date() + const startDate = new Date(props.startDate) + const endDate = getEndDate(startDate, props.occupancies) const startHour = startDate.getHours().toString().padStart(2, "0") const startMinutes = startDate.getMinutes().toString().padStart(2, "0") - let endhour = endDate?.getHours().toString().padStart(2, "0") ?? undefined - let endMinutes = + const endhour = endDate?.getHours().toString().padStart(2, "0") ?? undefined + const endMinutes = endDate?.getMinutes().toString().padStart(2, "0") ?? undefined const { hoursLeft, minutesLeft } = extractTimeLeft(now, endDate) - // ! temporary fix - if (endDate && endDate.getTime() - startDate.getTime() < 0) { - endhour = undefined - endMinutes = undefined - } return ( = props => { borderWidth: 0.5, flexDirection: "row", borderRadius: 5, - width: 200, + paddingRight: 12, height: 64, alignItems: "center", }} diff --git a/src/components/FreeClass/FreeClassList.tsx b/src/components/FreeClass/FreeClassList.tsx index bec7ab6e..7f50d4e2 100644 --- a/src/components/FreeClass/FreeClassList.tsx +++ b/src/components/FreeClass/FreeClassList.tsx @@ -16,6 +16,8 @@ const { width } = Dimensions.get("window") interface FreeClassListProps { data: RoomSimplified[] | undefined date: Date + latitude?: number + longitude?: number } /** @@ -57,6 +59,9 @@ export const FreeClassList: FC = props => { room: selectedRoom, startDate: props.date.toISOString(), roomId: item.roomId, + roomLatitude: props.latitude, + roomLongitude: props.longitude, + occupancies: item.occupancies, }) } catch (err) { console.log(err) diff --git a/src/pages/FreeClass/BuildingChoice.tsx b/src/pages/FreeClass/BuildingChoice.tsx index bd202fe4..7b43f548 100644 --- a/src/pages/FreeClass/BuildingChoice.tsx +++ b/src/pages/FreeClass/BuildingChoice.tsx @@ -68,6 +68,7 @@ export const BuildingChoice: MainStackScreen<"BuildingChoice"> = props => { { roomId: room.room_id, name: room.name, + occupancies: room.occupancies, }, ], } @@ -81,6 +82,7 @@ export const BuildingChoice: MainStackScreen<"BuildingChoice"> = props => { tempBuildings[indexElement].freeRoomList.push({ roomId: room.room_id, name: room.name, + occupancies: room.occupancies, }) } }) diff --git a/src/pages/FreeClass/ClassChoice.tsx b/src/pages/FreeClass/ClassChoice.tsx index 57169c7b..a243db8c 100644 --- a/src/pages/FreeClass/ClassChoice.tsx +++ b/src/pages/FreeClass/ClassChoice.tsx @@ -5,6 +5,7 @@ import { Title } from "components/Text" import { FreeClassList } from "components/FreeClass/FreeClassList" import { DateTimePicker } from "components/FreeClass/DateTimePicker/DateTimePicker" import { PageWrapper } from "components/Groups/PageWrapper" +import { getBuildingCoords } from "utils/rooms" /** * In this page the user can select finally the free class he wants. @@ -32,6 +33,11 @@ export const ClassChoice: MainStackScreen<"ClassChoice"> = props => { const buildingName: string[] = building.name.split(" ") // ex. buildingName = ["Ed.","B2"] + const coords = getBuildingCoords( + building.campus, + building.name.replace("Ed. ", "Edificio ") + ) + return ( goBack() }}> @@ -48,7 +54,12 @@ export const ClassChoice: MainStackScreen<"ClassChoice"> = props => { setDate(date)} /> - + ) diff --git a/src/pages/FreeClass/PositionChoice.tsx b/src/pages/FreeClass/PositionChoice.tsx index e528b869..4782fbf6 100644 --- a/src/pages/FreeClass/PositionChoice.tsx +++ b/src/pages/FreeClass/PositionChoice.tsx @@ -12,7 +12,7 @@ import { BuildingItem } from "./BuildingChoice" import { PageWrapper } from "components/Groups/PageWrapper" import { PositionModality } from "components/FreeClass/PositionModality" import { addHours } from "api/rooms" -import BuildingListJSON from "components/FreeClass/buildingCoords.json" +import { getBuildingCoords } from "utils/rooms" /** * In this page the user can find a room according to his current position. @@ -32,42 +32,6 @@ export const PositionChoice: MainStackScreen<"PositionChoice"> = () => { //the dateEnd is the startDate + 3 hours, the number of hours has not been chosen yet const dateEnd = addHours(new Date(), 3).toISOString() //3 hours is an example - const compareCampusNames = (c1: string[], c2: string[]) => { - if (c1.length === c2.length) { - if (c1.length > 1) { - if (c1[0] === c2[0] && c1[1] === c2[1]) { - return true - } else { - return false - } - } else { - if (c1[0] === c2[0]) { - return true - } else { - return false - } - } - } else { - return false - } - } - - const getBuildingCoords = (campus: CampusItem, buildingName: string) => { - for (const element of BuildingListJSON) { - if (element.acronym === campus.acronym) { - for (const c of element.campus) { - if (compareCampusNames(c.name, campus.name)) { - for (const b of c.buildings) { - if (b.name === buildingName) { - return b.coords - } - } - } - } - } - } - } - //main function that handles the call to the API in order to obtain the list of freeclassRooms const findRoomsAvailable = async ( campusList: CampusItem[], @@ -111,6 +75,7 @@ export const PositionChoice: MainStackScreen<"PositionChoice"> = () => { { roomId: room.room_id, name: room.name, + occupancies: room.occupancies, }, ], } @@ -124,6 +89,7 @@ export const PositionChoice: MainStackScreen<"PositionChoice"> = () => { tempBuildings[indexElement].freeRoomList.push({ roomId: room.room_id, name: room.name, + occupancies: room.occupancies, }) } }) diff --git a/src/pages/FreeClass/RoomDetails.tsx b/src/pages/FreeClass/RoomDetails.tsx index acaa9e04..f23f962f 100644 --- a/src/pages/FreeClass/RoomDetails.tsx +++ b/src/pages/FreeClass/RoomDetails.tsx @@ -17,8 +17,8 @@ export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { building={room.building} capacity={room.capacity} roomName={room.name} - roomLatitude={roomLatitude} - roomLongitude={roomLongitude} + latitude={roomLatitude} + longitude={roomLongitude} /> diff --git a/src/utils/rooms.ts b/src/utils/rooms.ts index 86b6bc1e..54dece4e 100644 --- a/src/utils/rooms.ts +++ b/src/utils/rooms.ts @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { ValidCrowdStatus } from "components/FreeClass/ClassDetails/CrowdingSection" +import BuildingListJSON from "components/FreeClass/buildingCoords.json" +import { CampusItem } from "pages/FreeClass/CampusChoice" export function extractRoom(val: string) { const arr = val.split(".") @@ -38,10 +40,6 @@ export function extractTimeLeft(now: Date, targetDate?: Date) { ) const hoursLeft = hours.toString() const minutesLeft = minutes.toString() - - console.log(hoursLeft) - - console.log(minutesLeft) return { hoursLeft: hoursLeft, minutesLeft: minutesLeft, @@ -93,7 +91,7 @@ export function getEndDate( try { const hour = parseInt(time.substring(0, 2)) const minutes = parseInt(time.substring(3)) - const endDate = new Date() + const endDate = new Date(startDate) endDate.setHours(hour, minutes, 0, 0) return endDate } catch (err) { @@ -102,3 +100,39 @@ export function getEndDate( } } } + +export function getBuildingCoords(campus: CampusItem, buildingName: string) { + for (const element of BuildingListJSON) { + if (element.acronym === campus.acronym) { + for (const c of element.campus) { + if (compareCampusNames(c.name, campus.name)) { + for (const b of c.buildings) { + if (b.name === buildingName) { + return b.coords + } + } + } + } + } + } +} + +const compareCampusNames = (c1: string[], c2: string[]) => { + if (c1.length === c2.length) { + if (c1.length > 1) { + if (c1[0] === c2[0] && c1[1] === c2[1]) { + return true + } else { + return false + } + } else { + if (c1[0] === c2[0]) { + return true + } else { + return false + } + } + } else { + return false + } +} From fdf87a2250e7dbefba5d9c76be3288f82deec795 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Wed, 22 Feb 2023 22:42:03 +0100 Subject: [PATCH 14/15] requested changes --- src/api/rooms.ts | 10 ++-- src/components/ContentWrapperScroll.tsx | 46 +++++++++------ .../CrowdSlider/CrowdSliderDynamic.tsx | 9 ++- .../CrowdSlider/CrowdSliderLabels.tsx | 8 +-- .../CrowdSlider/CrowdSliderStatic.tsx | 7 +-- .../ClassDetails/CrowdingSection.tsx | 23 ++++---- .../FreeClass/ClassDetails/InfoMapTile.tsx | 20 +++---- .../FreeClass/ClassDetails/PageWrapper.tsx | 59 ------------------- .../ClassDetails/RoomUtilsSection.tsx | 4 +- .../FreeClass/ClassDetails/RoomUtilsTile.tsx | 12 ++-- .../FreeClass/ClassDetails/TimeLeftTile.tsx | 33 +++++------ src/navigation/NavigationTypes.ts | 4 +- src/pages/FreeClass/RoomDetails.tsx | 9 ++- src/utils/colors.ts | 22 +++++++ src/utils/rooms.ts | 45 +++++++------- 15 files changed, 141 insertions(+), 170 deletions(-) delete mode 100644 src/components/FreeClass/ClassDetails/PageWrapper.tsx diff --git a/src/api/rooms.ts b/src/api/rooms.ts index f82916ec..8ff79e64 100644 --- a/src/api/rooms.ts +++ b/src/api/rooms.ts @@ -1,4 +1,3 @@ -import { ValidCrowdStatus } from "components/FreeClass/ClassDetails/CrowdingSection" import { AuthType, HttpClient, RequestOptions } from "./HttpClient" /* eslint-disable @typescript-eslint/naming-convention */ @@ -12,12 +11,15 @@ export interface Room { power: boolean link: string occupancy_rate: number | null - occupancies: Record + occupancies: Occupancies } + +export type Occupancies = Record<`${number}:${number}`, "FREE" | "OCCUPIED"> + export interface RoomSimplified { roomId: number name: string - occupancies: Record + occupancies: Occupancies occupancyRate: number | undefined } @@ -31,7 +33,7 @@ export interface RoomDetails { export interface OccupancyInfo { room_id: number - occupancy_rate: null | ValidCrowdStatus + occupancy_rate: null | number } const client = HttpClient.getInstance() diff --git a/src/components/ContentWrapperScroll.tsx b/src/components/ContentWrapperScroll.tsx index a30aab87..2290a84b 100644 --- a/src/components/ContentWrapperScroll.tsx +++ b/src/components/ContentWrapperScroll.tsx @@ -1,5 +1,5 @@ import React, { FC } from "react" -import { ScrollView, View } from "react-native" +import { ScrollView, View, ViewStyle } from "react-native" import { Text } from "components/Text" import { NavBar, NavbarProps } from "components/NavBar" import { usePalette } from "utils/colors" @@ -21,7 +21,10 @@ export const ContentWrapperScroll: FC<{ * Props for the navbar, see {@link NavBar} */ navbarOptions?: NavbarProps - marginTop?: number + + style?: ViewStyle + + scrollViewStyle?: ViewStyle }> = props => { const { background, isLight, primary } = usePalette() @@ -56,23 +59,26 @@ export const ContentWrapperScroll: FC<{ )} - + {props.children} diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx index c8a6bc5e..56d0ec3a 100644 --- a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderDynamic.tsx @@ -11,17 +11,16 @@ import { GestureDetector, GestureHandlerRootView, } from "react-native-gesture-handler" -import { ValidCrowdStatus } from "../CrowdingSection" import { getCrowdStatus } from "utils/rooms" interface CrowdSliderDynamicProps { usableWidth?: number - startingPos: ValidCrowdStatus - onSlideEnd: (pos: ValidCrowdStatus) => void + startingPos: number + onSlideEnd: (pos: number) => void } export const CrowdSliderDynamic: FC = props => { - const { isLight } = usePalette() + const { sliderBorderColor } = usePalette() const wrapper = (pos: number, width: number) => { const crowdStatus = getCrowdStatus(pos, width) @@ -64,7 +63,7 @@ export const CrowdSliderDynamic: FC = props => { position: "absolute", borderBottomWidth: 0.5, width: "100%", - borderColor: isLight ? "#454773" : "#fff", + borderColor: sliderBorderColor, marginTop: 15, marginBottom: 18, }} diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx index a614bb55..7c5ea091 100644 --- a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderLabels.tsx @@ -4,7 +4,7 @@ import { BodyText } from "components/Text" import { View } from "react-native" export const CrowdSliderLabels: FC = () => { - const { isLight } = usePalette() + const { labelsHighContrast } = usePalette() return ( { style={{ fontSize: 13, fontWeight: "600", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, alignSelf: "flex-start", }} > @@ -26,7 +26,7 @@ export const CrowdSliderLabels: FC = () => { style={{ fontSize: 13, fontWeight: "600", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, position: "absolute", alignSelf: "center", }} @@ -37,7 +37,7 @@ export const CrowdSliderLabels: FC = () => { style={{ fontSize: 13, fontWeight: "600", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, position: "absolute", alignSelf: "flex-end", }} diff --git a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx index 9d0fe35b..931b908f 100644 --- a/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdSlider/CrowdSliderStatic.tsx @@ -1,14 +1,13 @@ import React, { FC } from "react" import { Dimensions, View } from "react-native" import { usePalette } from "utils/colors" -import { ValidCrowdStatus } from "../CrowdingSection" interface CrowdSliderStaticProps { - position: ValidCrowdStatus + position: number } export const CrowdSliderStatic: FC = props => { - const { isLight } = usePalette() + const { sliderBorderColor } = usePalette() //56 is padding, 28 is circle diameter const usableWidth = Dimensions.get("screen").width - 56 - 28 @@ -21,7 +20,7 @@ export const CrowdSliderStatic: FC = props => { style={{ borderBottomWidth: 0.5, width: "100%", - borderColor: isLight ? "#454773" : "#fff", + borderColor: sliderBorderColor, marginTop: 15, marginBottom: 18, }} diff --git a/src/components/FreeClass/ClassDetails/CrowdingSection.tsx b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx index 030df5b5..4e8097a0 100644 --- a/src/components/FreeClass/ClassDetails/CrowdingSection.tsx +++ b/src/components/FreeClass/ClassDetails/CrowdingSection.tsx @@ -15,16 +15,14 @@ interface CrowdingSectionProps { onSlided?: () => void } -export type ValidCrowdStatus = 1 | 2 | 3 | 4 | 5 - export const CrowdingSection: FC = props => { - const { isLight } = usePalette() + const { iconHighContrast, labelsHighContrast } = usePalette() const [isModalVisible, setIsModalVisible] = useState(false) - const [occupancyRate, setOccupancyRate] = useState(1) + const [occupancyRate, setOccupancyRate] = useState(1) - let occupancyRateUser: ValidCrowdStatus = 3 + let occupancyRateUser = 3 const getOccupancyRate = async () => { try { @@ -52,13 +50,12 @@ export const CrowdingSection: FC = props => { } } return ( - + Affollamento: @@ -71,7 +68,7 @@ export const CrowdingSection: FC = props => { style={{ fontSize: 13, fontWeight: "400", - color: isLight ? "#454773" : "#fff", + color: iconHighContrast, }} > Se il dato sull'affollamento non è corretto @@ -81,7 +78,7 @@ export const CrowdingSection: FC = props => { style={{ fontSize: 13, fontWeight: "900", - color: isLight ? "#454773" : "#fff", + color: iconHighContrast, textDecorationLine: "underline", }} > @@ -108,7 +105,7 @@ export const CrowdingSection: FC = props => { style={{ fontSize: 32, fontWeight: "900", - color: isLight ? "#454773" : "#fff", + color: iconHighContrast, textAlign: "center", }} > @@ -118,7 +115,7 @@ export const CrowdingSection: FC = props => { style={{ fontSize: 13, fontWeight: "900", - color: isLight ? "#454773" : "#fff", + color: iconHighContrast, textAlign: "center", }} > @@ -127,7 +124,7 @@ export const CrowdingSection: FC = props => { (occupancyRateUser = pos)} + onSlideEnd={(pos: number) => (occupancyRateUser = pos)} /> diff --git a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx index 784cf493..91c18ac9 100644 --- a/src/components/FreeClass/ClassDetails/InfoMapTile.tsx +++ b/src/components/FreeClass/ClassDetails/InfoMapTile.tsx @@ -17,7 +17,7 @@ interface InfoMapTileProps { } export const InfoMapTile: FC = props => { - const { isLight, primary } = usePalette() + const { labelsHighContrast, isLight, palette, primary } = usePalette() const building = extractBuilding(props.building) const roomName = extractRoom(props.roomName) @@ -64,7 +64,7 @@ export const InfoMapTile: FC = props => { style={{ fontSize: 40, fontWeight: "300", - color: isLight ? "#414867" : primary, + color: isLight ? palette.variant1 : primary, }} > {building && roomName ? `${building}.` : undefined} @@ -73,7 +73,7 @@ export const InfoMapTile: FC = props => { style={{ fontSize: 40, fontWeight: "900", - color: isLight ? "#414867" : primary, + color: isLight ? palette.variant1 : primary, }} > {building && roomName ? roomName : props.roomName} @@ -84,7 +84,7 @@ export const InfoMapTile: FC = props => { style={{ fontSize: 16, fontWeight: "900", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, }} > Indirizzo : @@ -93,7 +93,7 @@ export const InfoMapTile: FC = props => { style={{ fontSize: 13, fontWeight: "400", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, }} > {props.address} @@ -104,7 +104,7 @@ export const InfoMapTile: FC = props => { style={{ fontSize: 16, fontWeight: "900", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, flex: 1, }} > @@ -114,7 +114,7 @@ export const InfoMapTile: FC = props => { style={{ fontSize: 13, fontWeight: "400", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, }} > {props.capacity} @@ -145,7 +145,7 @@ export const InfoMapTile: FC = props => { style={{ width: 100, height: 100, - backgroundColor: isLight ? "#414867" : "#fff", + backgroundColor: labelsHighContrast, borderRadius: 10, overflow: "hidden", shadowColor: "#000", @@ -217,7 +217,7 @@ export const InfoMapTile: FC = props => { style={{ fontSize: 13, fontWeight: "400", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, }} > consulta la{" "} @@ -226,7 +226,7 @@ export const InfoMapTile: FC = props => { style={{ fontSize: 16, fontWeight: "900", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, }} > mappa diff --git a/src/components/FreeClass/ClassDetails/PageWrapper.tsx b/src/components/FreeClass/ClassDetails/PageWrapper.tsx deleted file mode 100644 index 031d392c..00000000 --- a/src/components/FreeClass/ClassDetails/PageWrapper.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { FC } from "react" -import { ScrollView, View, ViewStyle } from "react-native" -import { NavBar, NavbarProps } from "components/NavBar" -import { usePalette } from "utils/colors" - -/** - * Page Wrapper for Class Details page, maybe unify this with Settings Wrapper or any other wrapper - * somewhere in the app. - */ -export const PageWrapper: FC<{ - hideNavbar?: boolean - /** - * Props for the navbar, see {@link NavBar} - */ - navbarOptions?: NavbarProps - - style?: ViewStyle - - children: React.ReactNode -}> = props => { - const { background, isLight, primary } = usePalette() - - const navbar = !props.hideNavbar - - return ( - - - - {props.children} - - - - {navbar ? : null} - - ) -} diff --git a/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx b/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx index 16cefade..0cef6ab4 100644 --- a/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx +++ b/src/components/FreeClass/ClassDetails/RoomUtilsSection.tsx @@ -11,7 +11,7 @@ interface RoomUtilsSectionProps { } export const RoomUtilsSection: FC = props => { - const { isLight } = usePalette() + const { labelsHighContrast } = usePalette() return ( @@ -19,7 +19,7 @@ export const RoomUtilsSection: FC = props => { style={{ fontSize: 20, fontWeight: "900", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, }} > Info Utili: diff --git a/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx b/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx index 81acea93..2619988e 100644 --- a/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx +++ b/src/components/FreeClass/ClassDetails/RoomUtilsTile.tsx @@ -19,7 +19,7 @@ interface RoomUtilsTileProps { } export const RoomUtilsTile: FC = props => { - const { isLight } = usePalette() + const { palette, isLight, sliderBorderColor } = usePalette() const tickSvg = useSVG(tick) @@ -29,11 +29,11 @@ export const RoomUtilsTile: FC = props => { Skia.Color( props.status ? isLight - ? "#424967" - : "#8791BD" + ? palette.primary + : palette.lighter : isLight - ? "#8791BD" - : "#424967" + ? palette.lighter + : palette.primary ), BlendMode.SrcIn ) @@ -72,7 +72,7 @@ export const RoomUtilsTile: FC = props => { style={{ fontSize: 13, fontWeight: "400", - color: isLight ? "#414867" : "#fff", + color: sliderBorderColor, }} > {props.name} diff --git a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx index 9cde63d2..9623804f 100644 --- a/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx +++ b/src/components/FreeClass/ClassDetails/TimeLeftTile.tsx @@ -13,28 +13,27 @@ import { } from "@shopify/react-native-skia" import clock from "assets/freeClassrooms/clock.svg" import { extractTimeLeft, getEndDate } from "utils/rooms" +import { Occupancies } from "api/rooms" interface TimeLeftTileProps { startDate: string - occupancies?: Record + occupancies?: Occupancies } export const TimeLeftTile: FC = props => { - const { isLight } = usePalette() + const { isLight, labelsHighContrast, iconHighContrast, primary } = + usePalette() const clockSvg = useSVG(clock) const paint = useMemo(() => Skia.Paint(), []) paint.setColorFilter( - Skia.ColorFilter.MakeBlend( - Skia.Color(isLight ? "#414867" : "#fff"), - BlendMode.SrcIn - ) + Skia.ColorFilter.MakeBlend(Skia.Color(labelsHighContrast), BlendMode.SrcIn) ) const paintClock = useMemo(() => Skia.Paint(), []) paintClock.setColorFilter( Skia.ColorFilter.MakeBlend( - Skia.Color(isLight ? "#424967" : "#fff"), + Skia.Color(isLight ? primary : "#fff"), BlendMode.SrcIn ) ) @@ -60,7 +59,7 @@ export const TimeLeftTile: FC = props => { style={{ fontSize: 20, fontWeight: "900", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, }} > Libera: @@ -86,7 +85,7 @@ export const TimeLeftTile: FC = props => { style={{ fontSize: 14, fontWeight: "400", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, flex: 1, paddingRight: 12, }} @@ -95,7 +94,7 @@ export const TimeLeftTile: FC = props => { = props => { style={{ fontSize: 20, fontWeight: "400", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, paddingHorizontal: 2, }} > @@ -124,7 +123,7 @@ export const TimeLeftTile: FC = props => { style={{ fontSize: 14, fontWeight: "400", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, paddingRight: 12, flex: 1, textAlign: "right", @@ -134,7 +133,7 @@ export const TimeLeftTile: FC = props => { = props => { style={{ fontSize: 20, fontWeight: "400", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, paddingHorizontal: 2, }} > @@ -160,7 +159,7 @@ export const TimeLeftTile: FC = props => { = props => { style={{ fontSize: 14, fontWeight: "300", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, }} > Mancano: @@ -211,7 +210,7 @@ export const TimeLeftTile: FC = props => { style={{ fontSize: 36, fontWeight: "700", - color: isLight ? "#414867" : "#fff", + color: labelsHighContrast, }} > {hoursLeft && minutesLeft diff --git a/src/navigation/NavigationTypes.ts b/src/navigation/NavigationTypes.ts index c6c9bb1f..2da8db13 100644 --- a/src/navigation/NavigationTypes.ts +++ b/src/navigation/NavigationTypes.ts @@ -25,7 +25,7 @@ import { Article } from "api/articles" import { CampusItem } from "pages/FreeClass/CampusChoice" import { NavigatorScreenParams } from "@react-navigation/native" import { BuildingItem } from "pages/FreeClass/BuildingChoice" -import { RoomDetails } from "api/rooms" +import { Occupancies, RoomDetails } from "api/rooms" import { TagWithData } from "contexts/newsPreferences" /** @@ -59,7 +59,7 @@ export type MainStackNavigatorParams = { roomId: number roomLatitude?: number roomLongitude?: number - occupancies?: Record + occupancies?: Occupancies occupancyRate?: number | null } ClassChoice: { building: BuildingItem; currentDate: string } diff --git a/src/pages/FreeClass/RoomDetails.tsx b/src/pages/FreeClass/RoomDetails.tsx index f23f962f..d680636a 100644 --- a/src/pages/FreeClass/RoomDetails.tsx +++ b/src/pages/FreeClass/RoomDetails.tsx @@ -1,17 +1,20 @@ import { MainStackScreen } from "navigation/NavigationTypes" import React from "react" -import { PageWrapper } from "components/FreeClass/ClassDetails/PageWrapper" import { InfoMapTile } from "components/FreeClass/ClassDetails/InfoMapTile" import { TimeLeftTile } from "components/FreeClass/ClassDetails/TimeLeftTile" import { RoomUtilsSection } from "components/FreeClass/ClassDetails/RoomUtilsSection" import { CrowdingSection } from "components/FreeClass/ClassDetails/CrowdingSection" +import { ContentWrapperScroll } from "components/ContentWrapperScroll" export const RoomDetails: MainStackScreen<"RoomDetails"> = props => { const { room, startDate, roomId, roomLatitude, roomLongitude, occupancies } = props.route.params return ( - + = props => { - + ) } diff --git a/src/utils/colors.ts b/src/utils/colors.ts index 279e0da1..14ce3745 100644 --- a/src/utils/colors.ts +++ b/src/utils/colors.ts @@ -9,6 +9,7 @@ const palette = { lessDark: "#2B344A", variant1: "#414867", variant2: "#010B40", + variant3: "#454773", accent: "#FFB544", } as const @@ -96,6 +97,21 @@ export interface ColorTheme { * Dark blue used for the title in cards with a background image and a yellowish gradient. */ cardTitle: string + + /** + * for RoomDetails page's slider + */ + sliderBorderColor: string + + /** + * dark blue in light mode, white in dark mode, used in slider labels + */ + labelsHighContrast: string + + /** + * purple/blueish in light mode, white in dark mode, used in TimeLeftTile + */ + iconHighContrast: string } /** @@ -181,6 +197,9 @@ export const usePalette: () => ColorTheme & articleTitle: palette.darker, articleSubtitle: palette.primary, cardTitle: palette.variant2, // "#010B40" + sliderBorderColor: "#454773", + labelsHighContrast: palette.variant1, // "#414867", + iconHighContrast: palette.variant3, // "#454773", } const darkTheme: ColorTheme = { @@ -197,6 +216,9 @@ export const usePalette: () => ColorTheme & articleTitle: "#FFFFFF", articleSubtitle: palette.lighter, cardTitle: palette.variant2, // "#010B40" + sliderBorderColor: "#FFFFFF", + labelsHighContrast: "#FFFFFF", + iconHighContrast: "#FFFFFF", } const colors = isLight ? lightTheme : darkTheme diff --git a/src/utils/rooms.ts b/src/utils/rooms.ts index 54dece4e..50b5d238 100644 --- a/src/utils/rooms.ts +++ b/src/utils/rooms.ts @@ -1,7 +1,6 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { ValidCrowdStatus } from "components/FreeClass/ClassDetails/CrowdingSection" import BuildingListJSON from "components/FreeClass/buildingCoords.json" import { CampusItem } from "pages/FreeClass/CampusChoice" +import { Occupancies } from "api/rooms" export function extractRoom(val: string) { const arr = val.split(".") @@ -46,24 +45,18 @@ export function extractTimeLeft(now: Date, targetDate?: Date) { } } -export function getCrowdStatus(pos: number, width: number): ValidCrowdStatus { +/** + * @param pos slider current pos + * @param width slider max width + * @returns a float number from 1 to 5 + */ +export function getCrowdStatus(pos: number, width: number): number { if (pos < 0) { - pos = 0 - } - - const radius = 14 - const percentage = (pos + radius) / (width + 2 * radius) - if (percentage < 0.2) { return 1 - } else if (percentage < 0.4) { - return 2 - } else if (percentage < 0.6) { - return 3 - } else if (percentage < 0.8) { - return 4 - } else { - return 5 } + const percentage = pos / width + + return 1 + percentage * 4 } /** @@ -72,16 +65,20 @@ export function getCrowdStatus(pos: number, width: number): ValidCrowdStatus { * return undefined in case of errors * */ -export function getEndDate( - startDate: Date, - occupancies?: Record -) { +export function getEndDate(startDate: Date, occupancies?: Occupancies) { let time if (occupancies !== undefined) { - time = Object.keys(occupancies).find( - time => occupancies[time] === "OCCUPIED" - ) + try { + time = Object.keys(occupancies).find(time => { + const hour = parseInt(time.substring(0, 2)) + const minutes = parseInt(time.substring(3)) + occupancies[`${hour}:${minutes}`] === "OCCUPIED" + }) + } catch (err) { + console.log(err) + return undefined + } } if (time === undefined) { const endDate = new Date(startDate) From 5fc574986c1aac89b338514e41bcbefbdd6cb240 Mon Sep 17 00:00:00 2001 From: DiegoZaff Date: Wed, 22 Feb 2023 22:48:44 +0100 Subject: [PATCH 15/15] forgot return --- src/utils/rooms.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/rooms.ts b/src/utils/rooms.ts index 50b5d238..42ef3d82 100644 --- a/src/utils/rooms.ts +++ b/src/utils/rooms.ts @@ -73,7 +73,7 @@ export function getEndDate(startDate: Date, occupancies?: Occupancies) { time = Object.keys(occupancies).find(time => { const hour = parseInt(time.substring(0, 2)) const minutes = parseInt(time.substring(3)) - occupancies[`${hour}:${minutes}`] === "OCCUPIED" + return occupancies[`${hour}:${minutes}`] === "OCCUPIED" }) } catch (err) { console.log(err)