chore: store apiKey in redux store
This commit is contained in:
@@ -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 });
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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
25
src/reducers/apiKey.ts
Normal 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;
|
||||||
@@ -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}]`,
|
||||||
return userPayload.data.data;
|
async (api_key = '') => {
|
||||||
});
|
const userPayload: AxiosResponse<UserPayload> = 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 = {};
|
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;
|
||||||
|
|||||||
@@ -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
10
src/types/store.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { CurrentUser } from './user';
|
||||||
|
|
||||||
|
export interface ApiKeyReducer {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReduxSelector {
|
||||||
|
apiKey: ApiKeyReducer;
|
||||||
|
currentUser: CurrentUser;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user