From fc9efed3e265be1e1c3e5ed48127db230a948fed Mon Sep 17 00:00:00 2001 From: talksik Date: Fri, 13 May 2022 06:20:08 -0500 Subject: [PATCH] auth and socket connections linear and slow progress but cleaner? --- packages/api/sockets/index.ts | 316 +++++++----------- packages/desktop/package.json | 1 + packages/desktop/src/api/NirvanaApi.tsx | 91 +++++ .../desktop/src/providers/AuthProvider.tsx | 47 ++- .../desktop/src/providers/SocketProvider.tsx | 53 ++- packages/desktop/src/tree/electronApp.tsx | 25 +- .../src/tree/protected/ProtectedRoute.tsx | 6 +- .../src/tree/protected/login/Login.tsx | 30 +- yarn.lock | 186 ++++++++++- 9 files changed, 535 insertions(+), 220 deletions(-) create mode 100644 packages/desktop/src/api/NirvanaApi.tsx diff --git a/packages/api/sockets/index.ts b/packages/api/sockets/index.ts index c42cd15..92a4d63 100644 --- a/packages/api/sockets/index.ts +++ b/packages/api/sockets/index.ts @@ -15,20 +15,20 @@ import { UntuneFromLineRequest, UserStartedBroadcastingResponse, UserStoppedBroadcastingResponse, -} from "@nirvana/core/sockets/channels"; +} from '@nirvana/core/sockets/channels'; -import GetAllSocketClients from "@nirvana/core/sockets/getAllActiveSocketClients"; -import { JwtClaims } from "../middleware/auth"; -import { LineMemberState } from "@nirvana/core/models/line.model"; -import { LineService } from "../services/line.service"; -import ReceiveSignal from "@nirvana/core/sockets/receiveSignal"; -import SendSignal from "@nirvana/core/sockets/sendSignal"; -import { UserService } from "../services/user.service"; -import { UserStatus } from "@nirvana/core/models/user.model"; -import { client } from "../services/database.service"; -import { loadConfig } from "../config"; +import GetAllSocketClients from '@nirvana/core/sockets/getAllActiveSocketClients'; +import { JwtClaims } from '../middleware/auth'; +import { LineMemberState } from '@nirvana/core/models/line.model'; +import { LineService } from '../services/line.service'; +import ReceiveSignal from '@nirvana/core/sockets/receiveSignal'; +import SendSignal from '@nirvana/core/sockets/sendSignal'; +import { UserService } from '../services/user.service'; +import { UserStatus } from '@nirvana/core/models/user.model'; +import { client } from '../services/database.service'; +import { loadConfig } from '../config'; -const jwt = require("jsonwebtoken"); +const jwt = require('jsonwebtoken'); const config = loadConfig(); @@ -41,14 +41,15 @@ const userIdsToSocketIds: { } = {}; export default function InitializeWs(io: any) { - console.log("initializing web sockets"); + console.log('initializing web sockets'); return io .use(function (socket: any, next: any) { - console.log("authenticating user..."); + console.log('authenticating user...'); try { const { token } = socket.handshake.query; + console.log(token); // verify jwt token with our api secret var decoded: JwtClaims = jwt.verify(token, config.JWT_TOKEN_SECRET); @@ -58,171 +59,132 @@ export default function InitializeWs(io: any) { next(); } catch (error) { console.error(error); - next(new Error("WS Authentication Error")); + next(new Error('WS Authentication Error')); } }) - .on("connection", function (socket: any) { + .on('connection', function (socket: any) { const userInfo: JwtClaims = socket.userInfo; socketIdsToUserIds[socket.id] = userInfo.userId.toString(); userIdsToSocketIds[userInfo.userId.toString()] = socket.id; - console.log( - `a user connected | user Id: ${userInfo.userId} and name: ${userInfo.name}` - ); + console.log(`a user connected | user Id: ${userInfo.userId} and name: ${userInfo.name}`); - socket.on("test", () => { - console.log("asdf"); + socket.on('test', () => { + console.log('asdf'); }); // ?verification that user is in a particular line to be tuned into it or just generally in it? /** CONNECT | User wants to subscribe to live emissions of a line */ - socket.on( - ServerRequestChannels.CONNECT_TO_LINE, - (req: ConnectToLineRequest) => { - // add this user to the room - console.log( - `${socket.id} user CONNECTED room for line ${Object.keys( - socket.rooms - )}` - ); + socket.on(ServerRequestChannels.CONNECT_TO_LINE, (req: ConnectToLineRequest) => { + // add this user to the room + console.log(`${socket.id} user CONNECTED room for line ${Object.keys(socket.rooms)}`); - const roomName = `connectedLine:${req.lineId}`; - socket.join(roomName); + const roomName = `connectedLine:${req.lineId}`; + socket.join(roomName); - console.log(`${socket.id} now in rooms ${socket.rooms}`); + console.log(`${socket.id} now in rooms ${socket.rooms}`); - const clientUserIdsInRoom = [ - ...(io.sockets.adapter.rooms.get(roomName) ?? []), - ].map( - (otherUserSocketId: string) => socketIdsToUserIds[otherUserSocketId] - ); + const clientUserIdsInRoom = [...(io.sockets.adapter.rooms.get(roomName) ?? [])].map( + (otherUserSocketId: string) => socketIdsToUserIds[otherUserSocketId], + ); - io.in(roomName).emit( - ServerResponseChannels.SOMEONE_CONNECTED_TO_LINE, - new SomeoneConnectedResponse( - req.lineId, - userInfo.userId, - clientUserIdsInRoom - ) - ); - } - ); + io.in(roomName).emit( + ServerResponseChannels.SOMEONE_CONNECTED_TO_LINE, + new SomeoneConnectedResponse(req.lineId, userInfo.userId, clientUserIdsInRoom), + ); + }); /** * TODO: handle when user wants to completely leave a line (delete or removed from one) */ - socket.on(ServerRequestChannels.DISCONNECT_FROM_LINE, () => - console.log("not implemented") - ); + socket.on(ServerRequestChannels.DISCONNECT_FROM_LINE, () => console.log('not implemented')); /** TUNE | User tunes into the line either temporarily or toggled in */ - socket.on( - ServerRequestChannels.TUNE_INTO_LINE, - async (req: TuneToLineRequest) => { - console.log( - `${socket.id} user TUNED into room for line ${req.lineId}` + socket.on(ServerRequestChannels.TUNE_INTO_LINE, async (req: TuneToLineRequest) => { + console.log(`${socket.id} user TUNED into room for line ${req.lineId}`); + + const roomName = `tunedLine:${req.lineId}`; + socket.join(roomName); + + console.log(`${socket.id} now in rooms ${Object.keys(socket.rooms)}`); + + // persist tuning in if user is toggle tuning in + if (req.keepTunedIn) { + await LineService.updateLineMemberState( + req.lineId, + userInfo.userId, + LineMemberState.TUNED, ); - - const roomName = `tunedLine:${req.lineId}`; - socket.join(roomName); - - console.log(`${socket.id} now in rooms ${Object.keys(socket.rooms)}`); - - // persist tuning in if user is toggle tuning in - if (req.keepTunedIn) { - await LineService.updateLineMemberState( - req.lineId, - userInfo.userId, - LineMemberState.TUNED - ); - } else { - await LineService.updateLineMemberState( - req.lineId, - userInfo.userId, - LineMemberState.INBOX - ); - } - - const clientUserIdsInRoom = [ - ...(io.sockets.adapter.rooms.get(roomName) ?? []), - ].map( - (otherUserSocketId: string) => socketIdsToUserIds[otherUserSocketId] - ); - - // we want to notify everyone connected to the line even if they are not tuned in - const connectedLineRoomName = `connectedLine:${req.lineId}`; - - io.in(connectedLineRoomName).emit( - ServerResponseChannels.SOMEONE_TUNED_INTO_LINE, - new SomeoneTunedResponse( - req.lineId, - userInfo.userId, - clientUserIdsInRoom, - req.keepTunedIn - ) + } else { + await LineService.updateLineMemberState( + req.lineId, + userInfo.userId, + LineMemberState.INBOX, ); } - ); + + const clientUserIdsInRoom = [...(io.sockets.adapter.rooms.get(roomName) ?? [])].map( + (otherUserSocketId: string) => socketIdsToUserIds[otherUserSocketId], + ); + + // we want to notify everyone connected to the line even if they are not tuned in + const connectedLineRoomName = `connectedLine:${req.lineId}`; + + io.in(connectedLineRoomName).emit( + ServerResponseChannels.SOMEONE_TUNED_INTO_LINE, + new SomeoneTunedResponse( + req.lineId, + userInfo.userId, + clientUserIdsInRoom, + req.keepTunedIn, + ), + ); + }); /** * Notify all connected users when someone UNTUNES from a room * ?might not be needed, all users' memory of tuned in users is irrelevant? don't need real time? but UI will show # of users tuned in? */ - socket.on( - ServerRequestChannels.UNTUNE_FROM_LINE, - async (req: UntuneFromLineRequest) => { - const roomName = `tunedLine:${req.lineId}`; - socket.leave(roomName); + socket.on(ServerRequestChannels.UNTUNE_FROM_LINE, async (req: UntuneFromLineRequest) => { + const roomName = `tunedLine:${req.lineId}`; + socket.leave(roomName); - console.log("someone left room"); + console.log('someone left room'); - const clientUserIdsInRoom = [ - ...(io.sockets.adapter.rooms.get(roomName) ?? []), - ].map( - (otherUserSocketId: string) => socketIdsToUserIds[otherUserSocketId] - ); + const clientUserIdsInRoom = [...(io.sockets.adapter.rooms.get(roomName) ?? [])].map( + (otherUserSocketId: string) => socketIdsToUserIds[otherUserSocketId], + ); - // we want to notify everyone connected to the line even if they are not tuned in - const connectedLineRoomName = `connectedLine:${req.lineId}`; + // we want to notify everyone connected to the line even if they are not tuned in + const connectedLineRoomName = `connectedLine:${req.lineId}`; - io.in(connectedLineRoomName).emit( - ServerResponseChannels.SOMEONE_UNTUNED_FROM_LINE, - new SomeoneUntunedFromLineResponse( - req.lineId, - userInfo.userId, - clientUserIdsInRoom - ) - ); - } - ); + io.in(connectedLineRoomName).emit( + ServerResponseChannels.SOMEONE_UNTUNED_FROM_LINE, + new SomeoneUntunedFromLineResponse(req.lineId, userInfo.userId, clientUserIdsInRoom), + ); + }); // TODO: use same pattern as tuning and untuning and send updated fresh list of current broadcasters but using another namespace/room for broadcasters in a line /** BROADCAST UPDATE | tell all connected, not just tuned into, that there is an update to someone broadcasting */ - socket.on( - ServerRequestChannels.BROADCAST_TO_LINE, - (req: StartBroadcastingRequest) => { - const roomName = `connectedLine:${req.lineId}`; + socket.on(ServerRequestChannels.BROADCAST_TO_LINE, (req: StartBroadcastingRequest) => { + const roomName = `connectedLine:${req.lineId}`; - io.in(roomName).emit( - ServerResponseChannels.SOMEONE_STARTED_BROADCASTING, - new UserStartedBroadcastingResponse(req.lineId, userInfo.userId) - ); - } - ); + io.in(roomName).emit( + ServerResponseChannels.SOMEONE_STARTED_BROADCASTING, + new UserStartedBroadcastingResponse(req.lineId, userInfo.userId), + ); + }); - socket.on( - ServerRequestChannels.STOP_BROADCAST_TO_LINE, - (req: StopBroadcastingRequest) => { - const roomName = `connectedLine:${req.lineId}`; + socket.on(ServerRequestChannels.STOP_BROADCAST_TO_LINE, (req: StopBroadcastingRequest) => { + const roomName = `connectedLine:${req.lineId}`; - io.in(roomName).emit( - ServerResponseChannels.SOMEONE_STOPPED_BROADCASTING, - new UserStoppedBroadcastingResponse(req.lineId, userInfo.userId) - ); - } - ); + io.in(roomName).emit( + ServerResponseChannels.SOMEONE_STOPPED_BROADCASTING, + new UserStoppedBroadcastingResponse(req.lineId, userInfo.userId), + ); + }); // socket.on(SocketChannels.SEND_SIGNAL, async (payload: SendSignal) => { // console.log(payload); @@ -240,29 +202,23 @@ export default function InitializeWs(io: any) { // }); // tell the proper other user to create a local peer object for the one on one mesh connection - socket.on( - ServerRequestChannels.RTC_CALL_REQUEST, - (req: RtcCallRequest) => { - const userSocketId = userIdsToSocketIds[req.userIdToCall]; + socket.on(ServerRequestChannels.RTC_CALL_REQUEST, (req: RtcCallRequest) => { + const userSocketId = userIdsToSocketIds[req.userIdToCall]; - io.to(userSocketId).emit( - `${ServerResponseChannels.RTC_NEW_USER_JOINED_RESPONSE_PREFIX}:${req.lineId}`, - new RtcNewUserResponse(userInfo.userId, req.simplePeerSignal) - ); - } - ); + io.to(userSocketId).emit( + `${ServerResponseChannels.RTC_NEW_USER_JOINED_RESPONSE_PREFIX}:${req.lineId}`, + new RtcNewUserResponse(userInfo.userId, req.simplePeerSignal), + ); + }); - socket.on( - ServerRequestChannels.RTC_ANSWER_REQUEST, - (req: RtcAnswerRequest) => { - const userSocketId = userIdsToSocketIds[req.userIdToCall]; + socket.on(ServerRequestChannels.RTC_ANSWER_REQUEST, (req: RtcAnswerRequest) => { + const userSocketId = userIdsToSocketIds[req.userIdToCall]; - io.to(userSocketId).emit( - `${ServerResponseChannels.RTC_RECEIVING_ANSWER_RESPONSE_PREFIX}:${req.lineId}`, - new RtcReceiveAnswerResponse(userInfo.userId, req.simplePeerSignal) - ); - } - ); + io.to(userSocketId).emit( + `${ServerResponseChannels.RTC_RECEIVING_ANSWER_RESPONSE_PREFIX}:${req.lineId}`, + new RtcReceiveAnswerResponse(userInfo.userId, req.simplePeerSignal), + ); + }); // TODO: not complete socket.on(ServerRequestChannels.GOING_INTO_FLOW_STATE, () => { @@ -272,17 +228,12 @@ export default function InitializeWs(io: any) { for (const roomName of socket.rooms) { if (roomName !== socket.id) { - const lineId = roomName.split(":")[1]; - if (roomName.includes("connectedLine")) { + const lineId = roomName.split(':')[1]; + if (roomName.includes('connectedLine')) { // get fresh list of tuned in folks without me - const clientUserIdsInRoom = [ - ...(io.sockets.adapter.rooms.get(roomName) ?? []), - ] + const clientUserIdsInRoom = [...(io.sockets.adapter.rooms.get(roomName) ?? [])] .filter((mappedSocketId) => mappedSocketId !== socket.id) - .map( - (otherUserSocketId: string) => - socketIdsToUserIds[otherUserSocketId] - ); + .map((otherUserSocketId: string) => socketIdsToUserIds[otherUserSocketId]); // io.in(roomName).emit( // ServerResponseChannels.SOMEONE_GOING_INTO_FLOW_STATE, @@ -300,33 +251,24 @@ export default function InitializeWs(io: any) { // tell all connected people that I am disconnecting // tell tuned in folks that I am leaving the room // tell the tuned in folks the new list of - socket.on("disconnecting", (reason: any) => { + socket.on('disconnecting', (reason: any) => { console.log(reason); console.log(socket.rooms); for (const roomName of socket.rooms) { if (roomName !== socket.id) { - const lineId = roomName.split(":")[1]; - if (roomName.includes("tunedLine")) { + const lineId = roomName.split(':')[1]; + if (roomName.includes('tunedLine')) { // get fresh list of tuned in folks without me - const clientUserIdsInRoom = [ - ...(io.sockets.adapter.rooms.get(roomName) ?? []), - ] + const clientUserIdsInRoom = [...(io.sockets.adapter.rooms.get(roomName) ?? [])] .filter((mappedSocketId) => mappedSocketId !== socket.id) - .map( - (otherUserSocketId: string) => - socketIdsToUserIds[otherUserSocketId] - ); + .map((otherUserSocketId: string) => socketIdsToUserIds[otherUserSocketId]); io.in(roomName).emit( ServerResponseChannels.SOMEONE_UNTUNED_FROM_LINE, - new SomeoneUntunedFromLineResponse( - lineId, - userInfo.userId, - clientUserIdsInRoom - ) + new SomeoneUntunedFromLineResponse(lineId, userInfo.userId, clientUserIdsInRoom), ); - } else if (roomName.includes("connectedLine")) { + } else if (roomName.includes('connectedLine')) { //TODO: p3: client doesn't really to know right now in our flow as this list is not really used // io.in(roomName).emit(ServerResponseChannels.SOMEONE_UNTUNED_FROM_LINE, new SomeoneDisconnected(lineId, userInfo.userId, clientUserIdsInRoom)); } @@ -335,17 +277,15 @@ export default function InitializeWs(io: any) { }); // ==== DISCONNECT ==== - socket.on("disconnect", () => { + socket.on('disconnect', () => { delete socketIdsToUserIds[socket.id]; delete userIdsToSocketIds[userInfo.userId]; // get all of the rooms of this socket // notify everyone of this disconnection - console.log("user disconnected"); - console.log( - `list of sockets mappings in memory: ${socketIdsToUserIds}` - ); + console.log('user disconnected'); + console.log(`list of sockets mappings in memory: ${socketIdsToUserIds}`); console.log(socket.rooms); diff --git a/packages/desktop/package.json b/packages/desktop/package.json index ccf7f9b..9345c3e 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -115,6 +115,7 @@ "react-hotkeys": "^2.0.0", "react-icons": "^4.3.1", "react-query": "^3.34.16", + "react-use": "^17.3.2", "recoil": "^0.6.1", "sass": "^1.51.0", "simple-peer": "^9.11.1", diff --git a/packages/desktop/src/api/NirvanaApi.tsx b/packages/desktop/src/api/NirvanaApi.tsx new file mode 100644 index 0000000..61beebd --- /dev/null +++ b/packages/desktop/src/api/NirvanaApi.tsx @@ -0,0 +1,91 @@ +import axios, { AxiosRequestConfig, AxiosResponse, Method } from 'axios'; + +import CreateLineRequest from '@nirvana/core/requests/createLine.request'; +import GetUserLinesResponse from '@nirvana/core/responses/getUserLines.response'; +import { Line } from '@nirvana/core/models/line.model'; +import LoginResponse from '../../../core/responses/login.response'; +import MasterLineData from '@nirvana/core/models/masterLineData.model'; +import NirvanaResponse from '../../../core/responses/nirvanaResponse'; +import { User } from '@nirvana/core/models'; +import UserDetailsResponse from '../../../core/responses/userDetails.response'; +import UserSearchResponse from '../../../core/responses/userSearch.response'; + +// export const localHost = process.env.REACT_APP_API_DOMAIN; + +export const localHost = 'http://localhost:5000/api'; + +export default class NirvanaApi { + // auth token from google that our backend will use + static _jwtToken?: string; + + static async fetch(url: string, method: Method, privateRoute = false, body: object = null) { + // use the auth token if it's a private route + // error if no auth token and it's a private route + // throw error and show message on anything that is an error from the backend + + const fullUrl = localHost + url; + + let res; + if (privateRoute && !this._jwtToken) throw Error('No jwt token available!'); + + if (privateRoute && this._jwtToken) { + res = await fetch(fullUrl, { + method: method, + headers: { + Authorization: this._jwtToken, + 'Content-Type': 'application/json', + }, + body: body ? JSON.stringify(body) : null, + }); + } else { + res = await fetch(fullUrl); + } + + if (!res.ok) { + if (res.status === 401) throw new Error('You are not authorized here'); + + throw new Error('Something went wrong'); + } + + return await res.json(); + } +} + +export async function serverCheck(): Promise { + return await NirvanaApi.fetch(`/status`, 'GET', false); +} + +export async function login(reqLoginTokens: { + accessToken: string; + idToken: string; +}): Promise { + return await NirvanaApi.fetch( + `/user/login?access_token=${reqLoginTokens.accessToken}&id_token=${reqLoginTokens.idToken}`, + 'GET', + false, + ); +} + +export async function authCheck(): Promise { + return await NirvanaApi.fetch(`/user/authcheck`, 'GET', true); +} + +export async function getUserDetails(): Promise { + return await NirvanaApi.fetch(`/user`, 'GET', true); +} + +export async function userSearch(searchQuery: string): Promise { + return await NirvanaApi.fetch(`/search/users?query=${searchQuery}`, 'GET', true); +} + +export async function getUserLines(): Promise> { + return await NirvanaApi.fetch>(`/lines`, 'GET', true); +} + +export async function getDmByUserId(otherUserId: string): Promise { + return await NirvanaApi.fetch(`/lines/dm/${otherUserId}`, 'GET', true); +} + +export async function createLine(request: CreateLineRequest): Promise> { + return await NirvanaApi.fetch(`/lines`, 'POST', true, request); +} diff --git a/packages/desktop/src/providers/AuthProvider.tsx b/packages/desktop/src/providers/AuthProvider.tsx index 01293e7..125b947 100644 --- a/packages/desktop/src/providers/AuthProvider.tsx +++ b/packages/desktop/src/providers/AuthProvider.tsx @@ -1,18 +1,53 @@ import React, { useContext, useState, useEffect } from 'react'; import { User } from '@nirvana/core/models/user.model'; +import { useAsyncFn } from 'react-use'; +import NirvanaApi, { getUserDetails } from '../api/NirvanaApi'; +import toast from 'react-hot-toast'; +import { useCallback } from 'react'; interface IAuthProvider { - user: User; + user?: User; + jwtToken?: string; + setJwtToken?: (jwtToken: string) => void; } -const AuthContext = React.createContext({ - user: null, -}); +const AuthContext = React.createContext({}); export function AuthProvider({ children }: { children: React.ReactNode }) { - if (true) return <>; + const [jwtToken, setJwtToken] = useState(); + const [userFetchState, fetchUser] = useAsyncFn(getUserDetails); - return {children}; + useEffect(() => { + if (jwtToken) { + console.warn(jwtToken); + + // ?should this be set here? what's a better way of + NirvanaApi._jwtToken = jwtToken; + + fetchUser() + .then((response) => { + toast.success('authenticated user'); + }) + .catch(() => { + toast.error('problem in authenticating jwt token'); + }); + } + }, [jwtToken, fetchUser]); + + const handleSetJwtToken = useCallback( + (newJwtToken: string) => { + setJwtToken(newJwtToken); + }, + [setJwtToken], + ); + + return ( + + {children} + + ); } export default function useAuth() { diff --git a/packages/desktop/src/providers/SocketProvider.tsx b/packages/desktop/src/providers/SocketProvider.tsx index dd4dd10..a563ab8 100644 --- a/packages/desktop/src/providers/SocketProvider.tsx +++ b/packages/desktop/src/providers/SocketProvider.tsx @@ -1,5 +1,7 @@ import React, { useState, useEffect, useContext } from 'react'; -import { Socket } from 'socket.io-client'; +import toast from 'react-hot-toast'; +import { io, Socket } from 'socket.io-client'; +import useAuth from './AuthProvider'; interface ISocketProvider { $ws: Socket; @@ -10,9 +12,17 @@ const SocketContext = React.createContext({ }); export function SocketProvider({ children }: { children: React.ReactNode }) { + const { jwtToken } = useAuth(); + const [$ws, set$ws] = useState(null); useEffect(() => { + if (!jwtToken) { + toast.error('no jwt token!!!'); + + return; + } + const socketConnection = io('http://localhost:5000', { query: { token: jwtToken }, transports: ['websocket'], @@ -21,10 +31,45 @@ export function SocketProvider({ children }: { children: React.ReactNode }) { reconnection: false, // ! TESTING THE PROBLEM WITH RECONNECTION CLIENT LISTENERS NOT ACTIVATING }); - set$ws(socketConnection); - }, [set$ws]); + socketConnection.on('connect', () => { + toast.success('sockets connected'); + }); - if (!$ws) return please wait for a solid connection; + socketConnection.io.on('close', () => { + console.error( + 'SOCKET | there was a problem with your app...connection closed likely due to idling or manual disconnect', + ); + + toast.error('sorry...this is our bad...please refresh with cmd + r'); + }); + + // client-side errors + socketConnection.on('connect_error', (err) => { + console.error(`SOCKETS | ${err.message}`); + toast.error('sorry...this is our bad...please refresh with cmd + r'); + }); + + set$ws(socketConnection); + + return () => { + socketConnection.disconnect(); + }; + }, [set$ws, jwtToken]); + + if (!$ws) { + return ( +
+ + { + "Please wait for a solid connection. If that doesn't work, please click below or restart the application." + } + + +
+ ); + } return {children}; } diff --git a/packages/desktop/src/tree/electronApp.tsx b/packages/desktop/src/tree/electronApp.tsx index 3470b70..141e52b 100644 --- a/packages/desktop/src/tree/electronApp.tsx +++ b/packages/desktop/src/tree/electronApp.tsx @@ -1,14 +1,29 @@ import React from 'react'; - +import { Toaster } from 'react-hot-toast'; +import { AuthProvider } from '../providers/AuthProvider'; import { ElectronProvider } from '../providers/ElectronProvider'; -import SocketProvider from '../providers/SocketProvider'; +import { SocketProvider } from '../providers/SocketProvider'; +import ProtectedRoute from './protected/ProtectedRoute'; export default function ElectronApp() { return ( - -
this is the bottom of the tree
-
+ + + yo its connected dawg + + + +
); } diff --git a/packages/desktop/src/tree/protected/ProtectedRoute.tsx b/packages/desktop/src/tree/protected/ProtectedRoute.tsx index 0661c2c..d4edd54 100644 --- a/packages/desktop/src/tree/protected/ProtectedRoute.tsx +++ b/packages/desktop/src/tree/protected/ProtectedRoute.tsx @@ -1,9 +1,11 @@ import React from 'react'; import useAuth from '../../providers/AuthProvider'; +import Login from './login/Login'; export default function ProtectedRoute({ children }: { children: React.ReactNode }) { const { user } = useAuth(); - if (user) return; - return <>hey; + if (!user) return ; + + return <>{children}; } diff --git a/packages/desktop/src/tree/protected/login/Login.tsx b/packages/desktop/src/tree/protected/login/Login.tsx index 251666e..ed285e7 100644 --- a/packages/desktop/src/tree/protected/login/Login.tsx +++ b/packages/desktop/src/tree/protected/login/Login.tsx @@ -1,16 +1,18 @@ -import Channels, { STORE_ITEMS } from '../../electron/constants'; -import { useEffect, useState } from 'react'; -import { useRecoilState, useSetRecoilState } from 'recoil'; +import React, { useEffect, useState } from 'react'; -import { $jwtToken } from '../../controller/recoil'; import { FcGoogle } from 'react-icons/fc'; -import Logo from '../../components/Logo'; -import { useLogin } from '../../controller/index'; +// import Logo from '../../components/Logo'; +import { login } from '../../../api/NirvanaApi'; +import { useAsyncFn } from 'react-use'; +import Channels from '../../../electron/constants'; +import useAuth from '../../../providers/AuthProvider'; export default function Login() { - const [isLoading, setIsLoading] = useState(false); + const { setJwtToken } = useAuth(); - const setJwtToken = useSetRecoilState($jwtToken); + const [loginState, loginApiHandler] = useAsyncFn(login); + + const [isLoading, setIsLoading] = useState(false); useEffect(() => { window.electronAPI.once( @@ -19,15 +21,16 @@ export default function Login() { console.log('got tokens', tokens); setIsLoading(true); - // todo: implement refresh token procedure in api layer by sending refresh_token and such - - const loginResponse = await mutateAsync({ + const loginResponse = await loginApiHandler({ accessToken: tokens.access_token, idToken: tokens.id_token, }); const { jwtToken, userDetails } = loginResponse; + setJwtToken(jwtToken); + + setIsLoading(false); }, ); @@ -35,8 +38,9 @@ export default function Login() { // return () => { // window.electronAPI.removeAllListeners(Channels.GOOGLE_AUTH_TOKENS); // }; - }, []); + }, [loginApiHandler, setJwtToken]); + // take user to browser to complete authentication const continueAuth = () => { setIsLoading(true); @@ -50,7 +54,7 @@ export default function Login() { h-screen w-screen bg-zinc-700" > - + {/* */} {/* ! TESTING PURPOSES */}
diff --git a/yarn.lock b/yarn.lock index b4551db..76e0729 100644 --- a/yarn.lock +++ b/yarn.lock @@ -65,6 +65,13 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" +"@babel/runtime@^7.1.2": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" + integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4": version "7.17.8" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2" @@ -783,6 +790,11 @@ dependencies: "@types/node" "*" +"@types/js-cookie@^2.2.6": + version "2.2.7" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3" + integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA== + "@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.9" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz" @@ -1210,6 +1222,11 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.1.tgz#0de2875ac31b46b6c5bb1ae0a7d7f0ba5678dffe" integrity sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw== +"@xobotyi/scrollbar-width@^1.9.5": + version "1.9.5" + resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" + integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" @@ -2384,7 +2401,7 @@ cookie@0.4.2, cookie@~0.4.1: resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -copy-to-clipboard@^3.2.0: +copy-to-clipboard@^3.2.0, copy-to-clipboard@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae" integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw== @@ -2480,6 +2497,14 @@ crypto-random-string@^2.0.0: resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +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== + dependencies: + hyphenate-style-name "^1.0.2" + isobject "^3.0.1" + css-loader@^6.0.0: version "6.7.1" resolved "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz" @@ -2505,6 +2530,14 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" +css-tree@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + css-what@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz" @@ -2515,7 +2548,7 @@ cssesc@^3.0.0: resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -csstype@^3.0.2: +csstype@^3.0.2, csstype@^3.0.6: version "3.0.11" resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz" integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== @@ -3179,6 +3212,13 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.7.tgz#b0c6e2ce27d0495cf78ad98715e0cad1219abb57" + integrity sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA== + dependencies: + stackframe "^1.1.1" + es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz" @@ -3727,6 +3767,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== + fast-text-encoding@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz" @@ -3737,6 +3782,11 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== +fastest-stable-stringify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" + integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q== + fastq@^1.6.0: version "1.13.0" resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" @@ -4649,6 +4699,11 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +hyphenate-style-name@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" + integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" @@ -4762,6 +4817,13 @@ ini@^1.3.4, ini@~1.3.0: resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +inline-style-prefixer@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz#c5c0e43ba8831707afc5f5bbfd97edf45c1fa7ae" + integrity sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ== + dependencies: + css-in-js-utils "^2.0.0" + inquirer@^8.0.0: version "8.2.1" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.1.tgz#e00022e3e8930a92662f760f020686530a84671d" @@ -5101,6 +5163,11 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + js-sha3@0.8.0: version "0.8.0" resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" @@ -5605,6 +5672,11 @@ matcher@^3.0.0: dependencies: escape-string-regexp "^4.0.0" +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" @@ -5898,6 +5970,20 @@ nan@^2.4.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== +nano-css@^5.3.1: + version "5.3.4" + resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.4.tgz#40af6a83a76f84204f346e8ccaa9169cdae9167b" + integrity sha512-wfcviJB6NOxDIDfr7RFn/GlaN7I/Bhe4d39ZRCJ3xvZX60LVe2qZ+rDqM49nm4YT81gAjzS+ZklhKP/Gnfnubg== + dependencies: + css-tree "^1.1.2" + csstype "^3.0.6" + fastest-stable-stringify "^2.0.2" + inline-style-prefixer "^6.0.0" + rtl-css-js "^1.14.0" + sourcemap-codec "^1.4.8" + stacktrace-js "^2.0.2" + stylis "^4.0.6" + nano-time@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz" @@ -7331,6 +7417,31 @@ react-toastify@^8.2.0: dependencies: clsx "^1.1.1" +react-universal-interface@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" + integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== + +react-use@^17.3.2: + version "17.3.2" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.3.2.tgz#448abf515f47c41c32455024db28167cb6e53be8" + integrity sha512-bj7OD0/1wL03KyWmzFXAFe425zziuTf7q8olwCYBfOeFHY1qfO1FAMjROQLsLZYwG4Rx63xAfb7XAbBrJsZmEw== + dependencies: + "@types/js-cookie" "^2.2.6" + "@xobotyi/scrollbar-width" "^1.9.5" + copy-to-clipboard "^3.3.1" + fast-deep-equal "^3.1.3" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.3.1" + react-universal-interface "^0.6.2" + resize-observer-polyfill "^1.5.1" + screenfull "^5.1.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^3.0.1" + ts-easing "^0.2.0" + tslib "^2.1.0" + react@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" @@ -7642,6 +7753,13 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" +rtl-css-js@^1.14.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.15.0.tgz#680ed816e570a9ebccba9e1cd0f202c6a8bb2dc0" + integrity sha512-99Cu4wNNIhrI10xxUaABHsdDqzalrSRTie4GeCmbGVuehm4oj+fIy8fTzB+16pmKe8Bv9rl+hxIBez6KxExTew== + dependencies: + "@babel/runtime" "^7.1.2" + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -7743,6 +7861,11 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.0.0" +screenfull@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" + integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== + scroll-into-view-if-needed@^2.2.25: version "2.2.29" resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885" @@ -7859,6 +7982,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" @@ -8034,6 +8162,11 @@ source-map-support@^0.5.13, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" @@ -8044,6 +8177,11 @@ source-map@~0.7.2: resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + sparse-bitfield@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" @@ -8132,6 +8270,35 @@ ssri@^8.0.0, ssri@^8.0.1: dependencies: minipass "^3.1.1" +stack-generator@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.5.tgz#fb00e5b4ee97de603e0773ea78ce944d81596c36" + integrity sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q== + dependencies: + stackframe "^1.1.1" + +stackframe@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.1.tgz#1033a3473ee67f08e2f2fc8eba6aef4f845124e1" + integrity sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg== + +stacktrace-gps@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz#7688dc2fc09ffb3a13165ebe0dbcaf41bcf0c69a" + integrity sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg== + dependencies: + source-map "0.5.6" + stackframe "^1.1.1" + +stacktrace-js@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" + integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== + dependencies: + error-stack-parser "^2.0.6" + stack-generator "^2.0.5" + stacktrace-gps "^3.0.4" + "statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" @@ -8277,6 +8444,11 @@ styled-jsx@5.0.1: resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.1.tgz#78fecbbad2bf95ce6cd981a08918ce4696f5fc80" integrity sha512-+PIZ/6Uk40mphiQJJI1202b+/dYeTVd9ZnMPR80pgiWbjIwvN2zIp4r9et0BgqBuShh48I0gttPlAXA7WVvBxw== +stylis@^4.0.6: + version "4.1.1" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.1.tgz#e46c6a9bbf7c58db1e65bb730be157311ae1fe12" + integrity sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ== + sudo-prompt@^9.1.1: version "9.2.1" resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" @@ -8398,6 +8570,11 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +throttle-debounce@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" + integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== + throttleit@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" @@ -8530,6 +8707,11 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + ts-loader@^9.2.2: version "9.2.8" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.8.tgz#e89aa32fa829c5cad0a1d023d6b3adecd51d5a48"