Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,6 @@ module.exports = {
},
globals: {
__IS_DEV__: true,
__API__: '',
},
};
11 changes: 8 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
"i18n-ally.localesPaths": ["public/locales", "src/shared/config/i18n"],
"i18n-ally.keystyle": "nested",
"i18n-ally.sourceLanguage": "ru",
// "editor.defaultFormatter": "dbaeumer.vscode-eslint",
// "editor.formatOnSave": true,
// "eslint.format.enable": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit",
"source.fixAll.prettier": "explicit",
"source.organizeImports": "explicit"
},
"eslint.validate": ["javascript"],
"css.validate": false,
Expand All @@ -17,4 +15,11 @@
"editor.formatOnSave": true,
"jest.jestCommandLine": "npm run test:unit --",
"prettier.configPath": ".prettierrc",
"typescript.inlayHints.parameterNames.enabled": "all",
"typescript.inlayHints.parameterTypes.enabled": true,
"typescript.inlayHints.variableTypes.enabled": true,
"typescript.inlayHints.functionLikeReturnTypes.enabled": true,
"editor.inlayHints.fontSize": 13,
"editor.inlayHints.maximumLength": 55,
"editor.inlayHints.enabled": "onUnlessPressed"
}
3 changes: 2 additions & 1 deletion config/build/buildPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DefinePlugin, ProgressPlugin, WebpackPluginInstance } from 'webpack';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import { BuildOptions } from './types/config';

export function buildPlugins({ paths, isDev }: BuildOptions): WebpackPluginInstance[] {
export function buildPlugins({ paths, isDev, apiUrl }: BuildOptions): WebpackPluginInstance[] {
const plugins = [
new HtmlWebpackPlugin({
template: paths.html,
Expand All @@ -16,6 +16,7 @@ export function buildPlugins({ paths, isDev }: BuildOptions): WebpackPluginInsta
}),
new DefinePlugin({
__IS_DEV__: JSON.stringify(isDev),
__API__: JSON.stringify(apiUrl),
}),
];

Expand Down
24 changes: 13 additions & 11 deletions config/build/types/config.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
export type BuildMode = 'production' | 'development'
export type BuildMode = 'production' | 'development';

export interface BuildPaths {
entry: string,
build: string,
html: string,
src: string,
entry: string;
build: string;
html: string;
src: string;
}

export interface BuildEnv {
port: number,
mode: BuildMode,
port: number;
mode: BuildMode;
apiUrl: string;
}

export interface BuildOptions {
mode: BuildMode,
paths: BuildPaths,
isDev: boolean,
port: number,
mode: BuildMode;
paths: BuildPaths;
isDev: boolean;
port: number;
apiUrl: string;
}
1 change: 1 addition & 0 deletions config/jest/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export default {
// A set of global variables that need to be available in all test environments
globals: {
__IS_DEV__: true,
__API__: '',
},
transformIgnorePatterns: ['node_modules/(?!axios)'],

Expand Down
5 changes: 3 additions & 2 deletions config/storybook/webpack.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import webpack, { DefinePlugin } from 'webpack';
import path from 'path';
import webpack, { DefinePlugin } from 'webpack';
import { buildCssLoader } from '../build/loaders/buildCssLoader';
import { BuildPaths } from '../build/types/config';

Expand Down Expand Up @@ -31,7 +31,8 @@ export default ({ config }: {config: webpack.Configuration}) => {
config.module!.rules!.push(buildCssLoader(true));

config.plugins!.push(new DefinePlugin({
__IS_DEV__: true,
__IS_DEV__: JSON.stringify(true),
__API__: JSON.stringify(''),
}));

return config;
Expand Down
29 changes: 24 additions & 5 deletions src/app/providers/StoreProvider/config/StateSchema.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import {
AnyAction, CombinedState, EnhancedStore, Reducer, ReducersMapObject,
AnyAction,
CombinedState,
EnhancedStore,
Reducer,
ReducersMapObject,
} from '@reduxjs/toolkit';
import { AxiosInstance } from 'axios';
import type { CounterSchema } from 'entities/Counter';
import { ProfileSchema } from 'entities/Profile';
import { UserSchema } from 'entities/User';
import { LoginSchema } from 'features/AuthByUsername';

export interface StateSchema {
counter: CounterSchema,
user: UserSchema,
counter: CounterSchema;
user: UserSchema;

// Async reducers
login?: LoginSchema,
profile?: ProfileSchema,
login?: LoginSchema;
profile?: ProfileSchema;
}

export type StateSchemaKey = keyof StateSchema;
Expand All @@ -27,3 +32,17 @@ export interface ReducerManager {
export interface ReduxStoreWithManager extends EnhancedStore<StateSchema> {
reducerManager: ReducerManager;
}

/** Тип для extraArgument миддлвара thunk в createReduxStore() */
export interface ThunkApiArg {
api: AxiosInstance;
}

/**
* Тип конфига для всех thunk, включает тип для данных при ошибке и тип для прокинутого extraArgument.
* @template T - Тип данных, которые могут быть возвращены в случае отказа (reject).
*/
export interface ThunkConfig<T> {
rejectValue: T;
extra: ThunkApiArg;
}
7 changes: 5 additions & 2 deletions src/app/providers/StoreProvider/config/store.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { configureStore, ReducersMapObject } from '@reduxjs/toolkit';
import { counterReducer } from 'entities/Counter';
import { userReducer } from 'entities/User';
import { $api } from 'shared/api/api';
import { createReducerManager } from './reducerManager';
import type { StateSchema } from './StateSchema';

export function createReduxStore(
initialState?: StateSchema,
asyncReducers?: ReducersMapObject<StateSchema>,
asyncReducers?: ReducersMapObject<StateSchema>
) {
const rootReducer: ReducersMapObject<StateSchema> = {
...asyncReducers,
Expand All @@ -16,10 +17,12 @@ export function createReduxStore(

const reducerManager = createReducerManager(rootReducer);

const store = configureStore<StateSchema>({
const store = configureStore({
reducer: reducerManager.reduce,
devTools: __IS_DEV__,
preloadedState: initialState,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ thunk: { extraArgument: { api: $api } } }),
});

// @ts-ignore
Expand Down
8 changes: 6 additions & 2 deletions src/app/providers/StoreProvider/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export type { StateSchema, ReduxStoreWithManager } from 'app/providers/StoreProvider/config/StateSchema';
export type {
ReduxStoreWithManager,
StateSchema,
ThunkConfig,
} from 'app/providers/StoreProvider/config/StateSchema';
export { AppDispatch, createReduxStore } from './config/store';
export { StoreProvider } from './ui/StoreProvider';
export { createReduxStore, AppDispatch } from './config/store';
1 change: 1 addition & 0 deletions src/app/types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ declare module '*.svg' {
}

declare const __IS_DEV__: boolean;
declare const __API__: string;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
import { ThunkConfig } from 'app/providers/StoreProvider';
import { User, userActions } from 'entities/User';
import { USER_LOCAL_STORAGE_KEY } from 'shared/const/localStorage';

Expand All @@ -8,19 +8,21 @@ interface LoginByUsernameProps {
password: string;
}

export const loginByUsername = createAsyncThunk<User, LoginByUsernameProps, {rejectValue: string}>(
'login/loginByUsername',
async (authData, { rejectWithValue, dispatch }) => {
try {
const response = await axios.post('http://localhost:8000/login', authData);
if (!response.data) {
throw new Error();
}
localStorage.setItem(USER_LOCAL_STORAGE_KEY, JSON.stringify(response.data));
dispatch(userActions.setAuthData(response.data));
return response.data;
} catch (error) {
return rejectWithValue('error');
export const loginByUsername = createAsyncThunk<
User,
LoginByUsernameProps,
ThunkConfig<string>
>('login/loginByUsername', async (authData, thunkApi) => {
const { rejectWithValue, dispatch, extra } = thunkApi;
try {
const response = await extra.api.post('/login', authData);
if (!response.data) {
throw new Error();
}
},
);
localStorage.setItem(USER_LOCAL_STORAGE_KEY, JSON.stringify(response.data));
dispatch(userActions.setAuthData(response.data));
return response.data;
} catch (error) {
return rejectWithValue('error');
}
});
7 changes: 7 additions & 0 deletions src/shared/api/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import axios from 'axios';
import { USER_LOCAL_STORAGE_KEY } from 'shared/const/localStorage';

export const $api = axios.create({
baseURL: __API__,
headers: { authorization: localStorage.getItem(USER_LOCAL_STORAGE_KEY) },
});
3 changes: 3 additions & 0 deletions webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ export default (env: BuildEnv) => {

const mode = env.mode || 'development';
const PORT = env.port || 3001;
const apiUrl = env.apiUrl || 'http://localhost:8100';

const isDev = mode === 'development';

const config: Configuration = buildWebpackConfig({
mode,
paths,
isDev,
apiUrl,
port: PORT,
});

Expand Down
Loading