diff --git a/client/package.json b/client/package.json index 6bee9315..f6a2e850 100644 --- a/client/package.json +++ b/client/package.json @@ -26,11 +26,11 @@ "@capacitor/core": "^4.0.1", "@capacitor/ios": "^4.0.1", "@capacitor/status-bar": "^4.0.1", - "@fortawesome/fontawesome-svg-core": "^1.2.36", - "@fortawesome/free-brands-svg-icons": "^5.15.4", - "@fortawesome/free-regular-svg-icons": "^5.15.4", - "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@fortawesome/react-fontawesome": "^0.1.16", + "@fortawesome/fontawesome-svg-core": "^6.1.2", + "@fortawesome/free-brands-svg-icons": "^6.1.2", + "@fortawesome/free-regular-svg-icons": "^6.1.2", + "@fortawesome/free-solid-svg-icons": "^6.1.2", + "@fortawesome/react-fontawesome": "^0.2.0", "@react-spring/three": "^9.5.2", "@react-spring/web": "^9.5.2", "@react-three/drei": "^9.21.0", @@ -50,6 +50,7 @@ "ramda": "^0.27.1", "react": "^18.2.0", "react-aria": "^3.18.0", + "react-cool-dimensions": "^2.0.7", "react-div-100vh": "^0.7.0", "react-dom": "^18.2.0", "react-helmet": "^6.1.0", diff --git a/client/src/app/MainGameStructure.tsx b/client/src/app/MainGameStructure.tsx index be8b09db..9355251d 100644 --- a/client/src/app/MainGameStructure.tsx +++ b/client/src/app/MainGameStructure.tsx @@ -13,7 +13,7 @@ import { toggleIsOpen } from "../features/gamelist/gamelistSlice"; import { raf } from "@react-spring/shared"; // default tailwind breakpoints -const BREAKPOINTS = { +export const BREAKPOINTS = { xs: 0, sm: 640, md: 768, @@ -84,17 +84,17 @@ const MainGameStructure: React.FC = () => { }); return ( - +
- {renderSidebar && } + diff --git a/client/src/app/SidebarRightSide.tsx b/client/src/app/SidebarRightSide.tsx index e90b68b0..e896d443 100644 --- a/client/src/app/SidebarRightSide.tsx +++ b/client/src/app/SidebarRightSide.tsx @@ -32,7 +32,7 @@ const SidebarRightSide: React.FC<{ return (
) diff --git a/client/src/assets/Bee.tsx b/client/src/assets/Bee.tsx index c0a0bba6..082aa740 100644 --- a/client/src/assets/Bee.tsx +++ b/client/src/assets/Bee.tsx @@ -42,10 +42,10 @@ const Bee = (props: GroupProps) => { const bind = useGesture({ onDrag: ({ down, delta: [mx, my] }) => { - invalidate(); rotateSpringApi.set({ x: mx / aspect + rotateSpring.x.get(), }); + invalidate(); isDragging.current = down; }, }); diff --git a/client/src/features/canvas/Canvas.tsx b/client/src/features/canvas/Canvas.tsx index 47e4e36d..1b747f50 100644 --- a/client/src/features/canvas/Canvas.tsx +++ b/client/src/features/canvas/Canvas.tsx @@ -5,6 +5,7 @@ import { ReactReduxContext } from "react-redux"; import Wrap3d from "./Wrap3d"; import classNames from "classnames"; +import useDimensions from "react-cool-dimensions"; const Canvas: React.FC<{ className?: string; @@ -12,11 +13,13 @@ const Canvas: React.FC<{ isGameboard?: boolean; }> = ({ children, className, isGameboard }) => { const ReduxProvider = useContextBridge(ReactReduxContext); + const { observe, width, height } = useDimensions(); return ( - {children} + {children} ); diff --git a/client/src/features/canvas/Wrap3d.tsx b/client/src/features/canvas/Wrap3d.tsx index 391b7b83..47ce44b0 100644 --- a/client/src/features/canvas/Wrap3d.tsx +++ b/client/src/features/canvas/Wrap3d.tsx @@ -46,14 +46,16 @@ const setZoom = ( interface Wrap3dProps { children: ReactNode; isGameboard: boolean | undefined; + width: number; + height: number; } -const Wrap3d = ({ children, isGameboard }: Wrap3dProps) => { +const Wrap3d = ({ children, isGameboard, width, height }: Wrap3dProps) => { extend({ TextGeometry }); const { progress } = useProgress(); const groupRef = useRef(); - const { width, height } = useThree((state) => state.size); + // const { width, height } = useThree((state) => state.size); const camera = useThree((state) => state.camera) as PerspectiveCamera; const [boundingBox] = useState(() => new Box3()); const invalidate = useThree((state) => state.invalidate); @@ -73,19 +75,19 @@ const Wrap3d = ({ children, isGameboard }: Wrap3dProps) => { invalidate(); } }); - setTimeout(() => { - if (groupRef.current) { - setZoom( - groupRef.current, - width, - height, - boundingBox, - camera, - isGameboard - ); - invalidate(); - } - }, 200); + // setTimeout(() => { + // if (groupRef.current) { + // setZoom( + // groupRef.current, + // width, + // height, + // boundingBox, + // camera, + // isGameboard + // ); + // invalidate(); + // } + // }, 200); } }, [ progress, diff --git a/client/src/features/game/GameBoard.tsx b/client/src/features/game/GameBoard.tsx index e80eeb95..57f51aaa 100644 --- a/client/src/features/game/GameBoard.tsx +++ b/client/src/features/game/GameBoard.tsx @@ -72,130 +72,128 @@ const GameBoard: React.FC = ({ id, game, userIndex }) => { }); return ( -
- - {/* */} - - - - } - > - - - - - - - + + {/* */} + + + + } + > + + + + + + - - -
- {selectedWord?.length && game.turn === userIndex ? ( - - ) : null} - {selectedWord?.length && game.turn === userIndex ? ( - - ) : null} -
+ + +
+ {selectedWord?.length && game.turn === userIndex ? ( +
- {selectedWord?.length && game.turn === userIndex ? ( - - ) : null} + + + ) : null} + {selectedWord?.length && game.turn === userIndex ? ( + + ) : null} +
+ + {replayLetters + ? R.take(replayProgress, replayLetters) + .join("") + .toUpperCase() + : selectedWord ?? ""} +
- -
- - {Object.keys(game.grid).map((coord: QRCoord) => { - const gridTile = game.grid[coord]; - return ( - - ); - })} - - - -
+ {selectedWord?.length && game.turn === userIndex ? ( + + ) : null} +
+ +
+ + {Object.keys(game.grid).map((coord: QRCoord) => { + const gridTile = game.grid[coord]; + return ( + + ); + })} + +
+
); }; diff --git a/client/src/features/game/MoveList.tsx b/client/src/features/game/MoveList.tsx new file mode 100644 index 00000000..44de803f --- /dev/null +++ b/client/src/features/game/MoveList.tsx @@ -0,0 +1,215 @@ +import React, { useCallback, useRef, useState } from "react"; +import * as R from "ramda"; +import { useSpring } from "@react-spring/three"; +import { toast } from "react-toastify"; +import { animated as a } from "@react-spring/web"; + +import { useAppDispatch, useAppSelector } from "../../app/hooks"; +import { nudgeGameById } from "./gameActions"; +import { clearReplay, toggleNudgeButton } from "./gameSlice"; +import { use100vh } from "react-div-100vh"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faGripLines, faPlayCircle } from "@fortawesome/free-solid-svg-icons"; +import { useDrag } from "@use-gesture/react"; +import classNames from "classnames"; +import MoveListItem from "./MoveListItem"; +import { isFullGame } from "../gamelist/gamelistSlice"; +import useDimensions from "react-cool-dimensions"; + +interface MoveListProps { + id: string; + mobileLayout: boolean; +} + +export function MoveList({ id, mobileLayout }: MoveListProps) { + const dispatch = useAppDispatch(); + const showingNudgeButton = useAppSelector( + (state) => state.game.showingNudgeButton + ); + const replayState = useAppSelector((state) => state.game.replay.move); + const replayIndex = useAppSelector( + (state) => state.game.replay.moveListIndex + ); + + const game = useAppSelector((state) => + id ? state.gamelist.games[id] : null + ); + + const [drawerIsOpen, setDrawerIsOpen] = useState(false); + + const onNudgeClick = useCallback(() => { + if (!id) { + return; + } + try { + dispatch(nudgeGameById(id)); + dispatch(toggleNudgeButton(false)); + } catch (e) { + toast(e.response?.data ?? e.response?.status ?? "Something went wrong", { + type: "error", + }); + } + }, [dispatch, id]); + + const onInitiateReplay = useCallback(() => { + setDrawerIsOpen(false); + }, []); + + const listRef = useRef(null); + + const { height: drawerHeight, observe } = useDimensions(); + // CQ: use cool-dimensions to measure game play area space? + const windowHeight = use100vh() ?? window.innerHeight; + const closedDrawerTop = windowHeight - (70 + 50); + const openDrawerTop = windowHeight - drawerHeight - 50; + + const drawerSpring = useSpring({ + top: drawerIsOpen ? openDrawerTop : closedDrawerTop, + config: { + clamp: true, + }, + }); + + const bind = useDrag( + ({ + down, + delta: [_dx, dy], + movement: [_mx, my], + distance: [_x, y], + velocity: [_vx, vy], + event, + }) => { + event.stopPropagation(); + if (down) { + // drawerSpring.top.pause(); + const dragPos = drawerSpring.top.get() + dy; + drawerSpring.top.set( + Math.max(Math.min(dragPos, closedDrawerTop), openDrawerTop) + ); + } else { + if (y < 50 && vy < 1) { + drawerSpring.top.start( + drawerIsOpen ? openDrawerTop : closedDrawerTop + ); + return; + } + if (my <= 0) { + setDrawerIsOpen(true); + } else { + setDrawerIsOpen(false); + } + } + } + ); + + if (!game || !id || !isFullGame(game)) { + return null; + } + + const moveListContent = ( + <> +

+ Moves +

+
+
+
    + {/* @ts-ignore */} + {R.reverse(game.moves).map((move, i) => { + const index = game.moves.length - i - 1; + return ( + + ); + })} +
+
+
+ + ); + + return ( + <> +
+ {!mobileLayout && moveListContent} +
+ {mobileLayout && ( + +
+ +
+ {replayState ? "Replaying: " : "Last move: "} +
+ {game.moves.length && ( + + )} +
+
+
+ {moveListContent} +
+ )} + + ); +} + +/*
*/ +{ + /* {showingNudgeButton && ( +
+

Looks like the AI opponent is taking a long time to move

+ +
+ )} */ +} +{ + /*
*/ +} +{ + /* */ +} +{ + /*
*/ +} diff --git a/client/src/features/play-route/MoveListItem.tsx b/client/src/features/game/MoveListItem.tsx similarity index 91% rename from client/src/features/play-route/MoveListItem.tsx rename to client/src/features/game/MoveListItem.tsx index 0d54b2f7..2f964f4e 100644 --- a/client/src/features/play-route/MoveListItem.tsx +++ b/client/src/features/game/MoveListItem.tsx @@ -6,7 +6,7 @@ import { faSpinner, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import axios, { AxiosError } from "axios"; +import axios from "axios"; import { Move } from "buzzwords-shared/Game"; import classNames from "classnames"; import React, { useCallback, useState } from "react"; @@ -15,15 +15,16 @@ import relativeDate from "tiny-relative-date"; import { useAppDispatch, useAppSelector } from "../../app/hooks"; import Button from "../../presentational/Button"; -import { initiateReplay } from "../game/gameActions"; -import { clearReplay } from "../game/gameSlice"; +import { initiateReplay } from "./gameActions"; +import { clearReplay } from "./gameSlice"; interface MoveListItemProps { move: Move; index: number; + onInitiateReplay: () => void; } -const MoveListItem: React.FC = ({ move, index }) => { +const MoveListItem: React.FC = ({ move, index, onInitiateReplay }) => { const dispatch = useAppDispatch(); const replayState = useAppSelector((state) => Boolean(state.game.replay.move) @@ -126,6 +127,8 @@ const MoveListItem: React.FC = ({ move, index }) => { return dispatch(clearReplay()); } dispatch(initiateReplay(index)); + setIsOpen(false); + onInitiateReplay(); }} > = ({ move, index }) => { onClickOutside={() => setIsOpen(false)} >
  • + {/* IS_MOBILE_BROWSER is temporary */} + {!window.ipc && !IS_MOBILE_BROWSER && ( +
    + + } + isOpen={appInfoOverlay} + onClickOutside={() => setAppInfoOverlay(false)} + > + + +
    + )} +
  • +
    + ); +} diff --git a/client/src/features/play-route/GameInviteOpponentPrompt.tsx b/client/src/features/play-route/GameInviteOpponentPrompt.tsx new file mode 100644 index 00000000..a264122e --- /dev/null +++ b/client/src/features/play-route/GameInviteOpponentPrompt.tsx @@ -0,0 +1,83 @@ +import { faShareSquare, faTrash } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React from "react"; +import { useNavigate } from "react-router-dom"; +import { useAppDispatch } from "../../app/hooks"; + +import Button from "../../presentational/Button"; +import CopyToClipboard from "../../presentational/CopyToClipboard"; +import { deleteGameById } from "../gamelist/gamelistActions"; + +interface GameInviteOpponentPromptProps { + id: string; + gameUrl: string; +} + +export default function GameInviteOpponentPrompt({ + gameUrl, + id, +}: GameInviteOpponentPromptProps) { + const dispatch = useAppDispatch(); + + const navigate = useNavigate(); + + return ( +
    +
    +

    + Invite an opponent to start the game +

    + they can use this link to join you +
    + {gameUrl} + +
    + + {navigator.share && ( + + )} + {id && ( + + )} +
    +
    +
    +

    Watch the tutorial

    + in the mean time + +
    +
    + ); +} diff --git a/client/src/features/play-route/Play.tsx b/client/src/features/play-route/Play.tsx index d24be67d..a18b47e5 100644 --- a/client/src/features/play-route/Play.tsx +++ b/client/src/features/play-route/Play.tsx @@ -1,46 +1,28 @@ -import React, { useCallback, useEffect, useState } from "react"; -import * as R from "ramda"; -import { Link, useNavigate, useParams, useLocation } from "react-router-dom"; -import { toast } from "react-toastify"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - faHistory, - faPlayCircle, - faShareSquare, - faTrash, - faInfoCircle, -} from "@fortawesome/free-solid-svg-icons"; -import { useSelector } from "react-redux"; +import React, { useEffect, useState } from "react"; +import { Link, useParams } from "react-router-dom"; import urljoin from "url-join"; -import classNames from "classnames"; +import * as R from "ramda"; -import { RootState } from "../../app/store"; -import { - clearReplay, - setCurrentGame, - toggleNudgeButton, -} from "../game/gameSlice"; +import { setCurrentGame, toggleNudgeButton } from "../game/gameSlice"; import { - deleteGameById, dequeueOrDismissGameStateModalForGame, fetchGameById, - joinGameById, markGameAsSeen, } from "../gamelist/gamelistActions"; import GameBoard from "../game/GameBoard"; -import CopyToClipboard from "../../presentational/CopyToClipboard"; import NicknameModal from "../user/NicknameModal"; import { useAppDispatch, useAppSelector } from "../../app/hooks"; -import { nudgeGameById } from "../game/gameActions"; -import Button from "../../presentational/Button"; import GameStateModal from "../game/GameStateModal"; -import MoveListItem from "./MoveListItem"; import { getOpponent } from "../user/userSelectors"; import { fetchOpponent } from "../user/userActions"; import { isFullGame } from "../gamelist/gamelistSlice"; import GameHeader from "./GameHeader"; -import { Popover } from "react-tiny-popover"; -import NativeAppAd from "../../presentational/NativeAppAd"; +import { MoveList } from "../game/MoveList"; +import GameInvitation from "./GameInvitation"; +import GameInviteOpponentPrompt from "./GameInviteOpponentPrompt"; +import useDimensions from "react-cool-dimensions"; +import { BREAKPOINTS } from "../../app/MainGameStructure"; +import classNames from "classnames"; export const getGameUrl = (id: string) => { if (import.meta.env.VITE_SHARE_BASEURL) { @@ -50,31 +32,26 @@ export const getGameUrl = (id: string) => { return window.location.toString(); }; +const PLAY_BREAKPOINTS = R.pick(["xs", "md"], BREAKPOINTS); + const Play: React.FC = () => { const dispatch = useAppDispatch(); - const location = useLocation(); const { id } = useParams(); - const navigate = useNavigate(); - const game = useSelector((state: RootState) => + const game = useAppSelector((state) => id ? state.gamelist.games[id] : null ); - const currentUser = useSelector((state: RootState) => state.user.user); - const replayState = useAppSelector((state) => - Boolean(state.game.replay.move) - ); + const currentUser = useAppSelector((state) => state.user.user); + const gameStateModal = useAppSelector((state) => state.game.gameStateModal); - const showingNudgeButton = useAppSelector( - (state) => state.game.showingNudgeButton - ); + const gameLoadingState = useAppSelector( (state) => id && state.gamelist.gamesLoading[id] ); const [fourohfour, setFourohfour] = useState(false); const [fetchedOpponentName, setFetchedOpponentName] = useState(false); - const [appInfoOverlay, setAppInfoOverlay] = useState(false); const userIndex = game && currentUser @@ -87,25 +64,6 @@ const Play: React.FC = () => { id ? getOpponent(state, id) : null ); - const joinGame = useCallback(() => { - if (id) { - dispatch(joinGameById(id)).then((joinedGame) => { - if (!joinedGame) { - setFourohfour(true); - } - }); - } - }, [id, dispatch]); - - const launchApp = useCallback(() => { - const path = location.pathname; - // @ts-ignore - window.location = urljoin( - `${import.meta.env.VITE_PRIVATE_SCHEME_NAME}://`, - path - ); - }, [location]); - useEffect(() => { if (id) { console.log("set current game", id); @@ -156,25 +114,15 @@ const Play: React.FC = () => { } }, [game, opponent, fetchedOpponentName, dispatch, isSpectating]); - const onNudgeClick = useCallback(() => { - if (!id) { - return; - } - try { - dispatch(nudgeGameById(id)); - dispatch(toggleNudgeButton(false)); - } catch (e) { - toast(e, { - type: "error", - }); - } - }, [dispatch, id]); - const nickModal = game && currentUser && !isSpectating && !currentUser.nickname ? ( ) : null; + const { observe, currentBreakpoint, width, height } = useDimensions({ + breakpoints: PLAY_BREAKPOINTS, + }); + if (!game || !id || !isFullGame(game)) { return null; } @@ -192,152 +140,44 @@ const Play: React.FC = () => { if (game.users.length === 1 && userIndex !== null && userIndex === -1) { return ( -
    -
    -

    - - {opponent?.nickname || "???"} - {" "} - has invited you to play Buzzwords -

    -
    -
    - - {!window.ipc && ( -
    - - } - isOpen={appInfoOverlay} - onClickOutside={() => setAppInfoOverlay(false)} - > - - -
    - )} -
    -
    + ); } if (game.users.length === 1 && userIndex !== null && userIndex > -1) { return ( -
    -
    -

    - Invite an opponent to start the game -

    - they can use this link to join you - - {getGameUrl(id)} - -
    - - {navigator.share && ( - - )} - {id && ( - - )} -
    -
    -
    -

    Watch the tutorial

    - in the mean time - -
    + <> + {nickModal} -
    + ); } return ( -
    +
    - {/* CQ: TODO: switch to cool-dimension to set flex direction based on measured available space instead of viewport size */} -
    - {userIndex !== null && ( - +
    - {showingNudgeButton && ( -
    -

    Looks like the AI opponent is taking a long time to move

    - -
    - )} -
    - -

    - Turns -

    + ref={observe} + > + {userIndex !== null && ( +
    +
    -
      - {R.reverse(game.moves).map((move, i) => { - const index = game.moves.length - i - 1; - return ; - })} -
    -
    + )} + {nickModal} {gameStateModal && ( { const preferredDarkTheme = useAppSelector( (state) => state.settings.preferredDarkTheme ); - const nickname = useAppSelector((state) => state.user.user.nickname); + const nickname = useAppSelector((state) => state.user.user?.nickname); const toggleTurnNotificationsMute = useCallback(() => { dispatch(setTurnNotificationsSetting(!turnNotificationsMuted)); diff --git a/client/src/features/topbar/TopBar.tsx b/client/src/features/topbar/TopBar.tsx index 2a059c86..dc79d18a 100644 --- a/client/src/features/topbar/TopBar.tsx +++ b/client/src/features/topbar/TopBar.tsx @@ -3,7 +3,6 @@ import { faBars, faCircle, faCog, - faQuestion, faSpinner, faSyncAlt, } from "@fortawesome/free-solid-svg-icons"; @@ -11,6 +10,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import classNames from "classnames"; import { useLocation } from "react-router"; import { Popover } from "react-tiny-popover"; +import { faCircleQuestion } from "@fortawesome/free-regular-svg-icons"; + import { useAppDispatch, useAppSelector } from "../../app/hooks"; import Button from "../../presentational/Button"; import NativeAppAd from "../../presentational/NativeAppAd"; @@ -20,7 +21,7 @@ import TutorialCard from "../gamelist/TutorialCard"; import { logout } from "../user/userActions"; import { isUserLoggedIn } from "../user/userSelectors"; import AuthPrompt from "./AuthPrompt"; -import { SettingsPage } from "../settings/SettingsPage"; +import { IS_MOBILE_BROWSER, SettingsPage } from "../settings/SettingsPage"; const PLATFORM = window.versions?.platform?.(); @@ -50,7 +51,7 @@ const TopBar: React.FC = () => { const [settingsPanel, setSettingsPanel] = useState(false); const [nativeAppAd, setNativeAppAd] = useState(false); - const showDownloadButton = !window.ipc && !location.pathname.match(/download/) + const showDownloadButton = !window.ipc && !location.pathname.match(/download/) && !IS_MOBILE_BROWSER; const isLoading = isRefreshing || (currentGame && gamesLoading[currentGame] === "loading"); @@ -91,7 +92,7 @@ const TopBar: React.FC = () => { > {/* CQ: this top param */} - + {hamburgerNotification} @@ -129,7 +130,7 @@ const TopBar: React.FC = () => { gamelistIsOpen && showTutorialCard && "hidden" )} > - +
    diff --git a/client/src/presentational/NativeAppAd.tsx b/client/src/presentational/NativeAppAd.tsx index 595d9109..6e359a84 100644 --- a/client/src/presentational/NativeAppAd.tsx +++ b/client/src/presentational/NativeAppAd.tsx @@ -4,9 +4,16 @@ import React from "react"; const NativeAppAd = React.forwardRef((props, ref) => { return ( -
    +
    - +

    The Buzzwords App

    @@ -32,7 +39,12 @@ const NativeAppAd = React.forwardRef((props, ref) => { href="https://chuckdries.itch.io/buzzwords" className="block text-center bg-darkbrown text-textInverse hover:bg-opacity-50 rounded-full p-2 m-1 transition-all active:transform active:scale-90 active:bg-opacity-100 inset-shadow" > - Download Now + Download Now{" "} +

    ); diff --git a/client/tailwind.config.js b/client/tailwind.config.js index 074841ee..c471bdfa 100644 --- a/client/tailwind.config.js +++ b/client/tailwind.config.js @@ -6,7 +6,9 @@ module.exports = { extend: { fontFamily: { fredoka: ["fredoka_oneregular", "sans"], - // sans: ['Patrick\\ Hand', 'sans'] + }, + boxShadow: { + 'upward': '0px -1px 5px 0px rgba(0,0,0,0.2)', }, colors: { lightbg: ({ opacityVariable, opacityValue }) => { diff --git a/yarn.lock b/yarn.lock index 49ff3f0a..1ba1e902 100644 --- a/yarn.lock +++ b/yarn.lock @@ -691,45 +691,45 @@ dependencies: tslib "2.4.0" -"@fortawesome/fontawesome-common-types@^0.2.36": - version "0.2.36" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz#b44e52db3b6b20523e0c57ef8c42d315532cb903" - integrity sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg== +"@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-svg-core@^1.2.36": - version "1.2.36" - resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz#4f2ea6f778298e0c47c6524ce2e7fd58eb6930e3" - integrity sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA== +"@fortawesome/fontawesome-svg-core@^6.1.2": + 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== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.36" + "@fortawesome/fontawesome-common-types" "6.1.2" -"@fortawesome/free-brands-svg-icons@^5.15.4": - version "5.15.4" - resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.4.tgz#ec8a44dd383bcdd58aa7d1c96f38251e6fec9733" - integrity sha512-f1witbwycL9cTENJegcmcZRYyawAFbm8+c6IirLmwbbpqz46wyjbQYLuxOc7weXFXfB7QR8/Vd2u5R3q6JYD9g== +"@fortawesome/free-brands-svg-icons@^6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.1.2.tgz#14160348b8ad5986b3805797dc4377a96e0014d9" + integrity sha512-b2eMfXQBsSxh52pcPtYchURQs6BWNh3zVTG8XH8Lv6V4kDhEg7D0kHN+K1SZniDiPb/e5tBlaygsinMUvetITA== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.36" + "@fortawesome/fontawesome-common-types" "6.1.2" -"@fortawesome/free-regular-svg-icons@^5.15.4": - version "5.15.4" - resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz#b97edab436954333bbeac09cfc40c6a951081a02" - integrity sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw== +"@fortawesome/free-regular-svg-icons@^6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.1.2.tgz#9f04009098addcc11d0d185126f058ed042c3099" + integrity sha512-xR4hA+tAwsaTHGfb+25H1gVU/aJ0Rzu+xIUfnyrhaL13yNQ7TWiI2RvzniAaB+VGHDU2a+Pk96Ve+pkN3/+TTQ== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.36" + "@fortawesome/fontawesome-common-types" "6.1.2" -"@fortawesome/free-solid-svg-icons@^5.15.4": - version "5.15.4" - resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz#2a68f3fc3ddda12e52645654142b9e4e8fbb6cc5" - integrity sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w== +"@fortawesome/free-solid-svg-icons@^6.1.2": + 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== dependencies: - "@fortawesome/fontawesome-common-types" "^0.2.36" + "@fortawesome/fontawesome-common-types" "6.1.2" -"@fortawesome/react-fontawesome@^0.1.16": - version "0.1.16" - resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.16.tgz#ce7665490214e20f929368d6b65f68884a99276a" - integrity sha512-aLmzDwC9rEOAJv2UJdMns89VZR5Ry4IHu5dQQh24Z/lWKEm44lfQr1UNalZlkUaQN8d155tNh+CS7ntntj1VMA== +"@fortawesome/react-fontawesome@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz#d90dd8a9211830b4e3c08e94b63a0ba7291ddcf4" + integrity sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw== dependencies: - prop-types "^15.7.2" + prop-types "^15.8.1" "@grpc/grpc-js@^1.3.7": version "1.5.1" @@ -4411,9 +4411,9 @@ camelcase@^6.2.0: integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== caniuse-lite@^1.0.30001272, caniuse-lite@^1.0.30001286: - version "1.0.30001294" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001294.tgz#4849f27b101fd59ddee3751598c663801032533d" - integrity sha512-LiMlrs1nSKZ8qkNhpUf5KD0Al1KCBE3zaT7OLOwEkagXMEDij98SiOovn9wxVGQpklk9vVC/pUSqgYmkmKOS8g== + version "1.0.30001379" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001379.tgz" + integrity sha512-zXf+qxuN8OJrK5Bl5HbJg8cc5/Zm01WNW4ooVWUh92YlKqQZW3fwN5lXLB+kI8wkP5vTWkIIN+rutZuJhf4ykw== chalk@2.4.1: version "2.4.1" @@ -8771,7 +8771,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.6.0, prop-types@^15.7.2: +prop-types@^15.6.0: version "15.8.0" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.0.tgz#d237e624c45a9846e469f5f31117f970017ff588" integrity sha512-fDGekdaHh65eI3lMi5OnErU6a8Ighg2KjcjQxO7m8VHyWjcPyj5kiOgV1LQDOOOgVy3+5FgjXvdSSX7B8/5/4g== @@ -8780,6 +8780,15 @@ prop-types@^15.6.0, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.13.1" +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== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + property-expr@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" @@ -8956,6 +8965,11 @@ react-composer@^5.0.3: dependencies: prop-types "^15.6.0" +react-cool-dimensions@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/react-cool-dimensions/-/react-cool-dimensions-2.0.7.tgz#2fe6657608f034cd7c89f149ed14e79cf1cb2d50" + integrity sha512-z1VwkAAJ5d8QybDRuYIXTE41RxGr5GYsv1bQhbOBE8cMfoZQZpcF0odL64vdgrQVzat2jayedj1GoYi80FWcbA== + react-div-100vh@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/react-div-100vh/-/react-div-100vh-0.7.0.tgz#b3bec03a833fa40e406f36ed2e23a35a59d1068f"