From 6dfa40e0267f2b49483071064e5c8f614b18541f Mon Sep 17 00:00:00 2001 From: Sebastian Velez Date: Fri, 13 Jan 2023 14:03:04 -0500 Subject: [PATCH] chore: store apiKey in redux store --- src/components/NavBar.tsx | 34 ++++++++++++----- src/components/WakaTime.tsx | 72 +++++++++++++++++------------------ src/core/WakaTimeCore.ts | 1 - src/reducers/apiKey.ts | 25 ++++++++++++ src/reducers/currentUser.ts | 32 ++++++++++------ src/stores/createStore.ts | 8 ++-- src/types/store.ts | 10 +++++ src/types/user.ts | 6 +++ src/utils/checkCurrentUser.ts | 4 +- 9 files changed, 128 insertions(+), 64 deletions(-) create mode 100644 src/reducers/apiKey.ts create mode 100644 src/types/store.ts diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index cb19edc..41dd363 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -1,13 +1,15 @@ import React, { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { setValue } from '../reducers/apiKey'; +import { ReduxSelector } from '../types/store'; +import { User } from '../types/user'; import config from '../config/config'; import apiKeyInvalid from '../utils/apiKey'; +import WakaTimeCore from '../core/WakaTimeCore'; +import { setUser } from '../reducers/currentUser'; +import changeExtensionState from '../utils/changeExtensionState'; -type Props = { - loggedIn: boolean; - user: any; -}; - -export default function NavBar({ loggedIn, user }: Props) { +export default function NavBar(): JSX.Element { const [state, setState] = useState({ apiKey: '', apiKeyError: '', @@ -22,8 +24,13 @@ export default function NavBar({ loggedIn, user }: Props) { fetch(); }, []); + const dispatch = useDispatch(); + const user: User | undefined = useSelector( + (selector: ReduxSelector) => selector.currentUser.user, + ); + const signedInAs = () => { - if (loggedIn === true) { + if (user) { return (

Signed in as {user.full_name} @@ -35,7 +42,7 @@ export default function NavBar({ loggedIn, user }: Props) { }; const customRules = () => { - if (loggedIn === true) { + if (user) { return (

  • @@ -50,7 +57,7 @@ export default function NavBar({ loggedIn, user }: Props) { }; const dashboard = () => { - if (loggedIn === true) { + if (user) { return (
  • @@ -151,6 +158,15 @@ export default function NavBar({ loggedIn, user }: Props) { if (state.apiKeyError === '' && state.apiKey !== '') { setState({ ...state, loading: true }); await browser.storage.sync.set({ apiKey: state.apiKey }); + dispatch(setValue(state.apiKey)); + + try { + const data = await WakaTimeCore.checkAuth(state.apiKey); + dispatch(setUser(data)); + } catch (err: unknown) { + dispatch(setUser(undefined)); + await changeExtensionState('notSignedIn'); + } setState({ ...state, loading: false }); } }} diff --git a/src/components/WakaTime.tsx b/src/components/WakaTime.tsx index 3cf5a7d..d1434bd 100644 --- a/src/components/WakaTime.tsx +++ b/src/components/WakaTime.tsx @@ -1,4 +1,8 @@ import React, { useEffect, useState } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { setValue } from '../reducers/apiKey'; +import { ReduxSelector } from '../types/store'; +import { setUser } from '../reducers/currentUser'; import WakaTimeCore from '../core/WakaTimeCore'; import config from '../config/config'; import changeExtensionState from '../utils/changeExtensionState'; @@ -7,58 +11,54 @@ import MainList from './MainList'; const API_KEY = 'waka_3766d693-bff3-4c63-8bf5-b439f3e12301'; -export default function WakaTime() { +export default function WakaTime(): JSX.Element { + const dispatch = useDispatch(); + const defaultState = { - apiKey: '', - loading: true, loggedIn: false, loggingEnabled: config.loggingEnabled, totalTimeLoggedToday: '0 minutes', - user: { - email: '', - full_name: '', - photo: '', - }, }; const [state, setState] = useState(defaultState); + const apiKeyFromRedux: string = useSelector((selector: ReduxSelector) => selector.apiKey.value); - const fetchUserData = async () => { + const fetchUserData = async (): Promise => { // await browser.storage.sync.set({ apiKey: API_KEY }); - const { apiKey } = await browser.storage.sync.get({ apiKey: config.apiKey }); + let apiKey = ''; + if (!apiKeyFromRedux) { + const storage = await browser.storage.sync.get({ + apiKey: config.apiKey, + }); + apiKey = storage.apiKey as string; + dispatch(setValue(apiKey)); + } if (!apiKey) { - changeExtensionState('notSignedIn'); + await changeExtensionState('notSignedIn'); } try { - const data = await WakaTimeCore.checkAuth(apiKey as string); + const data = await WakaTimeCore.checkAuth(apiKey); + dispatch(setUser(data)); const items = await browser.storage.sync.get({ loggingEnabled: config.loggingEnabled }); if (items.loggingEnabled === true) { - changeExtensionState('allGood'); + await changeExtensionState('allGood'); } else { - changeExtensionState('notLogging'); + await changeExtensionState('notLogging'); } - const totalTimeLoggedToday = await WakaTimeCore.getTotalTimeLoggedToday(apiKey as string); + const totalTimeLoggedToday = await WakaTimeCore.getTotalTimeLoggedToday(apiKey); setState({ ...state, - apiKey, - loading: false, loggedIn: true, - loggingEnabled: items.loggingEnabled, + loggingEnabled: items.loggingEnabled as boolean, totalTimeLoggedToday: totalTimeLoggedToday.text, - user: { - email: data.email, - full_name: data.full_name, - photo: data.photo, - }, }); await WakaTimeCore.recordHeartbeat(); - } catch (err) { - changeExtensionState('notSignedIn'); - setState({ ...defaultState, loading: false }); + } catch (err: unknown) { + await changeExtensionState('notSignedIn'); } }; @@ -66,28 +66,28 @@ export default function WakaTime() { fetchUserData(); }, []); - const disableLogging = () => { + const disableLogging = async () => { setState({ ...state, loggingEnabled: false, }); - changeExtensionState('notLogging'); + await changeExtensionState('notLogging'); - browser.storage.sync.set({ + await browser.storage.sync.set({ loggingEnabled: false, }); }; - const enableLogging = () => { + const enableLogging = async () => { setState({ ...state, loggingEnabled: true, }); - changeExtensionState('allGood'); + await changeExtensionState('allGood'); - browser.storage.sync.set({ + await browser.storage.sync.set({ loggingEnabled: true, }); }; @@ -97,16 +97,12 @@ export default function WakaTime() { setState(defaultState); - changeExtensionState('notSignedIn'); + await changeExtensionState('notSignedIn'); }; - // if (state.loading === true) { - // return
    Loading
    - // } - return (
    - +
    diff --git a/src/core/WakaTimeCore.ts b/src/core/WakaTimeCore.ts index 5616bf6..e66dd1d 100644 --- a/src/core/WakaTimeCore.ts +++ b/src/core/WakaTimeCore.ts @@ -31,7 +31,6 @@ class WakaTimeCore { } async checkAuth(api_key = ''): Promise { - console.log('api_keyapi_keyapi_keyapi_key', api_key); const userPayload: AxiosResponse = await axios.get( config.currentUserApiUrl, { params: { api_key } }, diff --git a/src/reducers/apiKey.ts b/src/reducers/apiKey.ts new file mode 100644 index 0000000..442d20d --- /dev/null +++ b/src/reducers/apiKey.ts @@ -0,0 +1,25 @@ +import { createSlice } from '@reduxjs/toolkit'; + +interface setValueAction { + payload: string; + type: string; +} + +export interface ApiKey { + value: string; +} +export const initialState: ApiKey = { value: '' }; + +const apiKeySlice = createSlice({ + initialState, + name: 'spiKey', + reducers: { + setValue: (state, action: setValueAction) => { + state.value = action.payload; + }, + }, +}); + +export const actions = apiKeySlice.actions; +export const { setValue } = apiKeySlice.actions; +export default apiKeySlice.reducer; diff --git a/src/reducers/currentUser.ts b/src/reducers/currentUser.ts index 6d3a1e0..e5bb3c4 100644 --- a/src/reducers/currentUser.ts +++ b/src/reducers/currentUser.ts @@ -1,21 +1,26 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import axios, { AxiosResponse } from 'axios'; -import { User, UserPayload } from '../types/user'; +import { CurrentUser, User, UserPayload } from '../types/user'; import config from '../config/config'; +interface setUserAction { + payload: User | undefined; + type: string; +} + type NameType = 'currentUser'; export const name: NameType = 'currentUser'; -export const fetchCurrentUser = createAsyncThunk(`[${name}]`, async () => { - const userPayload: AxiosResponse = await axios.get(config.currentUserApiUrl); - return userPayload.data.data; -}); +export const fetchCurrentUser = createAsyncThunk( + `[${name}]`, + async (api_key = '') => { + const userPayload: AxiosResponse = await axios.get(config.currentUserApiUrl, { + params: { api_key }, + }); + return userPayload.data.data; + }, +); -export interface CurrentUser { - error?: unknown; - pending?: boolean; - user?: User; -} export const initialState: CurrentUser = {}; const currentUser = createSlice({ @@ -30,8 +35,13 @@ const currentUser = createSlice({ }, initialState, name, - reducers: {}, + reducers: { + setUser: (state, action: setUserAction) => { + state.user = action.payload; + }, + }, }); export const actions = currentUser.actions; +export const { setUser } = currentUser.actions; export default currentUser.reducer; diff --git a/src/stores/createStore.ts b/src/stores/createStore.ts index 1ec6584..5825ddc 100644 --- a/src/stores/createStore.ts +++ b/src/stores/createStore.ts @@ -2,11 +2,10 @@ import { configureStore, Store } from '@reduxjs/toolkit'; import { logger } from 'redux-logger'; import { reduxBatch } from '@manaflair/redux-batch'; import devToolsEnhancer from 'remote-redux-devtools'; -import currentUserReducer, { - initialState as InitalCurrentUser, - CurrentUser, -} from '../reducers/currentUser'; +import currentUserReducer, { initialState as InitalCurrentUser } from '../reducers/currentUser'; +import apiKeyReducer from '../reducers/apiKey'; import isProd from '../utils/isProd'; +import { CurrentUser } from '../types/user'; export interface RootState { currentUser: CurrentUser; @@ -31,6 +30,7 @@ export default (appName: string): RootStore => { middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger), preloadedState, reducer: { + apiKey: apiKeyReducer, currentUser: currentUserReducer, }, }); diff --git a/src/types/store.ts b/src/types/store.ts new file mode 100644 index 0000000..a472505 --- /dev/null +++ b/src/types/store.ts @@ -0,0 +1,10 @@ +import { CurrentUser } from './user'; + +export interface ApiKeyReducer { + value: string; +} + +export interface ReduxSelector { + apiKey: ApiKeyReducer; + currentUser: CurrentUser; +} diff --git a/src/types/user.ts b/src/types/user.ts index d3431c1..4c93212 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -46,3 +46,9 @@ export interface User { weekday_start: number; writes_only: boolean; } + +export interface CurrentUser { + error?: unknown; + pending?: boolean; + user?: User; +} diff --git a/src/utils/checkCurrentUser.ts b/src/utils/checkCurrentUser.ts index c5c8f01..c2a06a1 100644 --- a/src/utils/checkCurrentUser.ts +++ b/src/utils/checkCurrentUser.ts @@ -1,13 +1,15 @@ import { RootStore } from '../stores/createStore'; import { fetchCurrentUser } from '../reducers/currentUser'; +import { ReduxSelector } from '../types/store'; type unsub = () => void; export default (store: RootStore) => (time: number): unsub => { const fetchUser = () => { + const apiKey: string = (store.getState() as ReduxSelector).apiKey.value; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - store.dispatch(fetchCurrentUser()); + store.dispatch(fetchCurrentUser(apiKey)); }; fetchUser(); const timeout = setInterval(fetchUser, time);