Redux toolkit (#115)

* add @redux/toolkit

* bump react version to allow for hooks

* add remote-redux devtools to help track extension state

* add remote-redux server to allow for remote state viewing

* setup react-redux for current user

* setup watch mode for running redux remote dev watch to options

* move screenshots
This commit is contained in:
Vu Nguyen
2021-01-22 16:37:59 -08:00
committed by GitHub
parent 0c39fbbc79
commit 8ade367b3f
20 changed files with 1859 additions and 22 deletions

View File

@@ -1,6 +1,6 @@
import React from 'react';
import classNames from 'classnames';
import { SuccessOrFailType } from '../config';
import { SuccessOrFailType } from '../config/config';
interface AlertProps {
text: string;
type: SuccessOrFailType;

View File

@@ -2,7 +2,7 @@ import axios, { AxiosResponse } from 'axios';
import moment from 'moment';
import { Tabs } from 'webextension-polyfill-ts';
import { User } from '../types/user';
import config from '../config';
import config from '../config/config';
import { SummariesPayload, GrandTotal } from '../types/summaries';
class WakaTimeCore {

View File

@@ -1,14 +1,21 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import createStore from './stores/createStore';
import checkCurrentUser from './utils/checkCurrentUser';
const container = document.getElementById('wakatime');
const store = createStore('WakaTime-Options');
checkCurrentUser(store)(30 * 1000);
const openOptions = async (): Promise<void> => {
await browser.runtime.openOptionsPage();
};
ReactDOM.render(
<>
<Provider store={store}>
<h1>POPUP GO HERE</h1>
<div onClick={openOptions}>Open options</div>
</>,
</Provider>,
container,
);

View File

@@ -0,0 +1,37 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios, { AxiosResponse } from 'axios';
import { User, UserPayload } from '../types/user';
import config from '../config/config';
type NameType = 'currentUser';
export const name: NameType = 'currentUser';
export const fetchCurrentUser = createAsyncThunk<User, undefined>(`[${name}]`, async () => {
const userPayload: AxiosResponse<UserPayload> = await axios.get(config.currentUserApiUrl);
return userPayload.data.data;
});
export interface CurrentUser {
error?: unknown;
pending?: boolean;
user?: User;
}
export const initialState: CurrentUser = {};
const currentUser = createSlice({
extraReducers: (builder) => {
builder.addCase(fetchCurrentUser.fulfilled, (state, { payload }) => {
state.user = payload;
});
builder.addCase(fetchCurrentUser.rejected, (state, { error }) => {
state.user = undefined;
state.error = error;
});
},
initialState,
name,
reducers: {},
});
export const actions = currentUser.actions;
export default currentUser.reducer;

41
src/stores/createStore.ts Normal file
View File

@@ -0,0 +1,41 @@
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 isProd from '../utils/isProd';
export interface RootState {
currentUser: CurrentUser;
}
const preloadedState: RootState = {
currentUser: InitalCurrentUser,
};
export type RootStore = Store<RootState>;
export default (appName: string): RootStore => {
const enhancers = [reduxBatch];
if (!isProd()) {
enhancers.push(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
devToolsEnhancer({ hostname: 'localhost', name: appName, port: 8000, realtime: true }),
);
}
const store = configureStore({
devTools: true,
enhancers,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
preloadedState,
reducer: {
currentUser: currentUserReducer,
},
});
return store;
};

View File

@@ -1,5 +1,5 @@
import { browser } from 'webextension-polyfill-ts';
import config from '../config';
import config from '../config/config';
type ColorIconTypes = 'gray' | 'red' | 'white' | '';

View File

@@ -1,4 +1,4 @@
import config, { ApiStates } from '../config';
import config, { ApiStates } from '../config/config';
import changeExtensionIcon from './changeExtensionIcon';
import changeExtensionTooltip from './changeExtensionTooltip';

View File

@@ -1,5 +1,5 @@
import { browser } from 'webextension-polyfill-ts';
import config from '../config';
import config from '../config/config';
/**
* It changes the extension title

View File

@@ -0,0 +1,16 @@
import { RootStore } from '../stores/createStore';
import { fetchCurrentUser } from '../reducers/currentUser';
type unsub = () => void;
export default (store: RootStore) => (time: number): unsub => {
const fetchUser = () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
store.dispatch(fetchCurrentUser());
};
fetchUser();
const timeout = setInterval(fetchUser, time);
return () => {
clearInterval(timeout);
};
};

1
src/utils/isProd.ts Normal file
View File

@@ -0,0 +1 @@
export default (): boolean => process.env.NODE_ENV !== 'development';