chore: store apiKey in redux store

This commit is contained in:
Sebastian Velez
2023-01-13 14:03:04 -05:00
parent def9fe1243
commit 6dfa40e026
9 changed files with 128 additions and 64 deletions

View File

@@ -1,13 +1,15 @@
import React, { useEffect, useState } from 'react'; 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 config from '../config/config';
import apiKeyInvalid from '../utils/apiKey'; import apiKeyInvalid from '../utils/apiKey';
import WakaTimeCore from '../core/WakaTimeCore';
import { setUser } from '../reducers/currentUser';
import changeExtensionState from '../utils/changeExtensionState';
type Props = { export default function NavBar(): JSX.Element {
loggedIn: boolean;
user: any;
};
export default function NavBar({ loggedIn, user }: Props) {
const [state, setState] = useState({ const [state, setState] = useState({
apiKey: '', apiKey: '',
apiKeyError: '', apiKeyError: '',
@@ -22,8 +24,13 @@ export default function NavBar({ loggedIn, user }: Props) {
fetch(); fetch();
}, []); }, []);
const dispatch = useDispatch();
const user: User | undefined = useSelector(
(selector: ReduxSelector) => selector.currentUser.user,
);
const signedInAs = () => { const signedInAs = () => {
if (loggedIn === true) { if (user) {
return ( return (
<p className="navbar-text"> <p className="navbar-text">
Signed in as <b>{user.full_name}</b> Signed in as <b>{user.full_name}</b>
@@ -35,7 +42,7 @@ export default function NavBar({ loggedIn, user }: Props) {
}; };
const customRules = () => { const customRules = () => {
if (loggedIn === true) { if (user) {
return ( return (
<li> <li>
<a target="_blank" href="https://wakatime.com/settings/rules" rel="noreferrer"> <a target="_blank" href="https://wakatime.com/settings/rules" rel="noreferrer">
@@ -50,7 +57,7 @@ export default function NavBar({ loggedIn, user }: Props) {
}; };
const dashboard = () => { const dashboard = () => {
if (loggedIn === true) { if (user) {
return ( return (
<li> <li>
<a target="_blank" href="https://wakatime.com/dashboard" rel="noreferrer"> <a target="_blank" href="https://wakatime.com/dashboard" rel="noreferrer">
@@ -151,6 +158,15 @@ export default function NavBar({ loggedIn, user }: Props) {
if (state.apiKeyError === '' && state.apiKey !== '') { if (state.apiKeyError === '' && state.apiKey !== '') {
setState({ ...state, loading: true }); setState({ ...state, loading: true });
await browser.storage.sync.set({ apiKey: state.apiKey }); 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 }); setState({ ...state, loading: false });
} }
}} }}

View File

@@ -1,4 +1,8 @@
import React, { useEffect, useState } from 'react'; 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 WakaTimeCore from '../core/WakaTimeCore';
import config from '../config/config'; import config from '../config/config';
import changeExtensionState from '../utils/changeExtensionState'; import changeExtensionState from '../utils/changeExtensionState';
@@ -7,58 +11,54 @@ import MainList from './MainList';
const API_KEY = 'waka_3766d693-bff3-4c63-8bf5-b439f3e12301'; const API_KEY = 'waka_3766d693-bff3-4c63-8bf5-b439f3e12301';
export default function WakaTime() { export default function WakaTime(): JSX.Element {
const dispatch = useDispatch();
const defaultState = { const defaultState = {
apiKey: '',
loading: true,
loggedIn: false, loggedIn: false,
loggingEnabled: config.loggingEnabled, loggingEnabled: config.loggingEnabled,
totalTimeLoggedToday: '0 minutes', totalTimeLoggedToday: '0 minutes',
user: {
email: '',
full_name: '',
photo: '',
},
}; };
const [state, setState] = useState(defaultState); const [state, setState] = useState(defaultState);
const apiKeyFromRedux: string = useSelector((selector: ReduxSelector) => selector.apiKey.value);
const fetchUserData = async () => { const fetchUserData = async (): Promise<void> => {
// await browser.storage.sync.set({ apiKey: API_KEY }); // 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) { if (!apiKey) {
changeExtensionState('notSignedIn'); await changeExtensionState('notSignedIn');
} }
try { 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 }); const items = await browser.storage.sync.get({ loggingEnabled: config.loggingEnabled });
if (items.loggingEnabled === true) { if (items.loggingEnabled === true) {
changeExtensionState('allGood'); await changeExtensionState('allGood');
} else { } else {
changeExtensionState('notLogging'); await changeExtensionState('notLogging');
} }
const totalTimeLoggedToday = await WakaTimeCore.getTotalTimeLoggedToday(apiKey as string); const totalTimeLoggedToday = await WakaTimeCore.getTotalTimeLoggedToday(apiKey);
setState({ setState({
...state, ...state,
apiKey,
loading: false,
loggedIn: true, loggedIn: true,
loggingEnabled: items.loggingEnabled, loggingEnabled: items.loggingEnabled as boolean,
totalTimeLoggedToday: totalTimeLoggedToday.text, totalTimeLoggedToday: totalTimeLoggedToday.text,
user: {
email: data.email,
full_name: data.full_name,
photo: data.photo,
},
}); });
await WakaTimeCore.recordHeartbeat(); await WakaTimeCore.recordHeartbeat();
} catch (err) { } catch (err: unknown) {
changeExtensionState('notSignedIn'); await changeExtensionState('notSignedIn');
setState({ ...defaultState, loading: false });
} }
}; };
@@ -66,28 +66,28 @@ export default function WakaTime() {
fetchUserData(); fetchUserData();
}, []); }, []);
const disableLogging = () => { const disableLogging = async () => {
setState({ setState({
...state, ...state,
loggingEnabled: false, loggingEnabled: false,
}); });
changeExtensionState('notLogging'); await changeExtensionState('notLogging');
browser.storage.sync.set({ await browser.storage.sync.set({
loggingEnabled: false, loggingEnabled: false,
}); });
}; };
const enableLogging = () => { const enableLogging = async () => {
setState({ setState({
...state, ...state,
loggingEnabled: true, loggingEnabled: true,
}); });
changeExtensionState('allGood'); await changeExtensionState('allGood');
browser.storage.sync.set({ await browser.storage.sync.set({
loggingEnabled: true, loggingEnabled: true,
}); });
}; };
@@ -97,16 +97,12 @@ export default function WakaTime() {
setState(defaultState); setState(defaultState);
changeExtensionState('notSignedIn'); await changeExtensionState('notSignedIn');
}; };
// if (state.loading === true) {
// return <div>Loading</div>
// }
return ( return (
<div> <div>
<NavBar user={state.user} loggedIn={state.loggedIn} /> <NavBar />
<div className="container"> <div className="container">
<div className="row"> <div className="row">
<div className="col-md-12"> <div className="col-md-12">

View File

@@ -31,7 +31,6 @@ class WakaTimeCore {
} }
async checkAuth(api_key = ''): Promise<User> { async checkAuth(api_key = ''): Promise<User> {
console.log('api_keyapi_keyapi_keyapi_key', api_key);
const userPayload: AxiosResponse<AxiosUserResponse> = await axios.get( const userPayload: AxiosResponse<AxiosUserResponse> = await axios.get(
config.currentUserApiUrl, config.currentUserApiUrl,
{ params: { api_key } }, { params: { api_key } },

25
src/reducers/apiKey.ts Normal file
View File

@@ -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;

View File

@@ -1,21 +1,26 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios, { AxiosResponse } from 'axios'; import axios, { AxiosResponse } from 'axios';
import { User, UserPayload } from '../types/user'; import { CurrentUser, User, UserPayload } from '../types/user';
import config from '../config/config'; import config from '../config/config';
interface setUserAction {
payload: User | undefined;
type: string;
}
type NameType = 'currentUser'; type NameType = 'currentUser';
export const name: NameType = 'currentUser'; export const name: NameType = 'currentUser';
export const fetchCurrentUser = createAsyncThunk<User, undefined>(`[${name}]`, async () => { export const fetchCurrentUser = createAsyncThunk<User, string>(
const userPayload: AxiosResponse<UserPayload> = await axios.get(config.currentUserApiUrl); `[${name}]`,
async (api_key = '') => {
const userPayload: AxiosResponse<UserPayload> = await axios.get(config.currentUserApiUrl, {
params: { api_key },
});
return userPayload.data.data; return userPayload.data.data;
}); },
);
export interface CurrentUser {
error?: unknown;
pending?: boolean;
user?: User;
}
export const initialState: CurrentUser = {}; export const initialState: CurrentUser = {};
const currentUser = createSlice({ const currentUser = createSlice({
@@ -30,8 +35,13 @@ const currentUser = createSlice({
}, },
initialState, initialState,
name, name,
reducers: {}, reducers: {
setUser: (state, action: setUserAction) => {
state.user = action.payload;
},
},
}); });
export const actions = currentUser.actions; export const actions = currentUser.actions;
export const { setUser } = currentUser.actions;
export default currentUser.reducer; export default currentUser.reducer;

View File

@@ -2,11 +2,10 @@ import { configureStore, Store } from '@reduxjs/toolkit';
import { logger } from 'redux-logger'; import { logger } from 'redux-logger';
import { reduxBatch } from '@manaflair/redux-batch'; import { reduxBatch } from '@manaflair/redux-batch';
import devToolsEnhancer from 'remote-redux-devtools'; import devToolsEnhancer from 'remote-redux-devtools';
import currentUserReducer, { import currentUserReducer, { initialState as InitalCurrentUser } from '../reducers/currentUser';
initialState as InitalCurrentUser, import apiKeyReducer from '../reducers/apiKey';
CurrentUser,
} from '../reducers/currentUser';
import isProd from '../utils/isProd'; import isProd from '../utils/isProd';
import { CurrentUser } from '../types/user';
export interface RootState { export interface RootState {
currentUser: CurrentUser; currentUser: CurrentUser;
@@ -31,6 +30,7 @@ export default (appName: string): RootStore => {
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger), middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
preloadedState, preloadedState,
reducer: { reducer: {
apiKey: apiKeyReducer,
currentUser: currentUserReducer, currentUser: currentUserReducer,
}, },
}); });

10
src/types/store.ts Normal file
View File

@@ -0,0 +1,10 @@
import { CurrentUser } from './user';
export interface ApiKeyReducer {
value: string;
}
export interface ReduxSelector {
apiKey: ApiKeyReducer;
currentUser: CurrentUser;
}

View File

@@ -46,3 +46,9 @@ export interface User {
weekday_start: number; weekday_start: number;
writes_only: boolean; writes_only: boolean;
} }
export interface CurrentUser {
error?: unknown;
pending?: boolean;
user?: User;
}

View File

@@ -1,13 +1,15 @@
import { RootStore } from '../stores/createStore'; import { RootStore } from '../stores/createStore';
import { fetchCurrentUser } from '../reducers/currentUser'; import { fetchCurrentUser } from '../reducers/currentUser';
import { ReduxSelector } from '../types/store';
type unsub = () => void; type unsub = () => void;
export default (store: RootStore) => export default (store: RootStore) =>
(time: number): unsub => { (time: number): unsub => {
const fetchUser = () => { const fetchUser = () => {
const apiKey: string = (store.getState() as ReduxSelector).apiKey.value;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
store.dispatch(fetchCurrentUser()); store.dispatch(fetchCurrentUser(apiKey));
}; };
fetchUser(); fetchUser();
const timeout = setInterval(fetchUser, time); const timeout = setInterval(fetchUser, time);