1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 21:06:42 +07:00

auth: refactored sagas to use try-catch

This commit is contained in:
Fedor Katurov 2021-03-02 14:17:27 +07:00
parent 7d2511e7e9
commit c36494c3f9
22 changed files with 400 additions and 424 deletions

View file

@ -1,55 +1,46 @@
import { api, configWithToken, errorMiddleware, resultMiddleware } from '~/utils/api';
import { api, cleanResult, errorMiddleware, resultMiddleware } from '~/utils/api';
import { API } from '~/constants/api';
import { INotification, IResultWithStatus } from '~/redux/types';
import { userLoginTransform } from '~/redux/auth/transforms';
import { ISocialAccount, IUser } from './types';
import { IResultWithStatus } from '~/redux/types';
import {
ApiAttachSocialRequest,
ApiAttachSocialResult,
ApiAuthGetUpdatesRequest,
ApiAuthGetUpdatesResult,
ApiAuthGetUserProfileRequest,
ApiAuthGetUserProfileResult,
ApiAuthGetUserResult,
ApiCheckRestoreCodeRequest,
ApiCheckRestoreCodeResult,
ApiDropSocialRequest,
ApiDropSocialResult,
ApiGetSocialsResult,
ApiLoginWithSocialRequest,
ApiLoginWithSocialResult,
ApiRestoreCodeRequest,
ApiRestoreCodeResult,
ApiUpdateUserRequest,
ApiUpdateUserResult,
ApiUserLoginRequest,
ApiUserLoginResult,
} from './types';
export const apiUserLogin = ({
username,
password,
}: {
username: string;
password: string;
}): Promise<IResultWithStatus<{ token: string; status?: number }>> =>
export const apiUserLogin = ({ username, password }: ApiUserLoginRequest) =>
api
.post(API.USER.LOGIN, { username, password })
.then(resultMiddleware)
.catch(errorMiddleware)
.then(userLoginTransform);
.post<ApiUserLoginResult>(API.USER.LOGIN, { username, password })
.then(cleanResult);
export const apiAuthGetUser = ({ access }): Promise<IResultWithStatus<{ user: IUser }>> =>
api
.get(API.USER.ME, configWithToken(access))
.then(resultMiddleware)
.catch(errorMiddleware);
export const apiAuthGetUser = () => api.get<ApiAuthGetUserResult>(API.USER.ME).then(cleanResult);
export const apiAuthGetUserProfile = ({
access,
username,
}): Promise<IResultWithStatus<{ user: IUser }>> =>
api
.get(API.USER.PROFILE(username), configWithToken(access))
.then(resultMiddleware)
.catch(errorMiddleware);
export const apiAuthGetUserProfile = ({ username }: ApiAuthGetUserProfileRequest) =>
api.get<ApiAuthGetUserProfileResult>(API.USER.PROFILE(username)).then(cleanResult);
export const apiAuthGetUpdates = ({
access,
exclude_dialogs,
last,
}): Promise<IResultWithStatus<{
notifications: INotification[];
boris: { commented_at: string };
}>> =>
export const apiAuthGetUpdates = ({ exclude_dialogs, last }: ApiAuthGetUpdatesRequest) =>
api
.get(API.USER.GET_UPDATES, configWithToken(access, { params: { exclude_dialogs, last } }))
.then(resultMiddleware)
.catch(errorMiddleware);
.get<ApiAuthGetUpdatesResult>(API.USER.GET_UPDATES, { params: { exclude_dialogs, last } })
.then(cleanResult);
export const apiUpdateUser = ({ access, user }): Promise<IResultWithStatus<{ user: IUser }>> =>
api
.patch(API.USER.ME, user, configWithToken(access))
.then(resultMiddleware)
.catch(errorMiddleware);
export const apiUpdateUser = ({ user }: ApiUpdateUserRequest) =>
api.patch<ApiUpdateUserResult>(API.USER.ME, user).then(cleanResult);
export const apiRequestRestoreCode = ({ field }): Promise<IResultWithStatus<{}>> =>
api
@ -57,75 +48,26 @@ export const apiRequestRestoreCode = ({ field }): Promise<IResultWithStatus<{}>>
.then(resultMiddleware)
.catch(errorMiddleware);
export const apiCheckRestoreCode = ({ code }): Promise<IResultWithStatus<{}>> =>
api
.get(API.USER.REQUEST_CODE(code))
.then(resultMiddleware)
.catch(errorMiddleware);
export const apiCheckRestoreCode = ({ code }: ApiCheckRestoreCodeRequest) =>
api.get<ApiCheckRestoreCodeResult>(API.USER.REQUEST_CODE(code)).then(cleanResult);
export const apiRestoreCode = ({ code, password }): Promise<IResultWithStatus<{}>> =>
export const apiRestoreCode = ({ code, password }: ApiRestoreCodeRequest) =>
api
.post(API.USER.REQUEST_CODE(code), { password })
.then(resultMiddleware)
.catch(errorMiddleware);
.post<ApiRestoreCodeResult>(API.USER.REQUEST_CODE(code), { password })
.then(cleanResult);
export const apiGetSocials = ({
access,
}: {
access: string;
}): Promise<IResultWithStatus<{
accounts: ISocialAccount[];
}>> =>
api
.get(API.USER.GET_SOCIALS, configWithToken(access))
.then(resultMiddleware)
.catch(errorMiddleware);
export const apiGetSocials = () =>
api.get<ApiGetSocialsResult>(API.USER.GET_SOCIALS).then(cleanResult);
export const apiDropSocial = ({
access,
id,
provider,
}: {
access: string;
id: string;
provider: string;
}): Promise<IResultWithStatus<{
accounts: ISocialAccount[];
}>> =>
api
.delete(API.USER.DROP_SOCIAL(provider, id), configWithToken(access))
.then(resultMiddleware)
.catch(errorMiddleware);
export const apiDropSocial = ({ id, provider }: ApiDropSocialRequest) =>
api.delete<ApiDropSocialResult>(API.USER.DROP_SOCIAL(provider, id)).then(cleanResult);
export const apiAttachSocial = ({
access,
token,
}: {
access: string;
token: string;
}): Promise<IResultWithStatus<{
account: ISocialAccount;
}>> =>
export const apiAttachSocial = ({ token }: ApiAttachSocialRequest) =>
api
.post(API.USER.ATTACH_SOCIAL, { token }, configWithToken(access))
.then(resultMiddleware)
.catch(errorMiddleware);
.post<ApiAttachSocialResult>(API.USER.ATTACH_SOCIAL, { token })
.then(cleanResult);
export const apiLoginWithSocial = ({
token,
username,
password,
}: {
token: string;
username?: string;
password?: string;
}): Promise<IResultWithStatus<{
token: string;
error: string;
errors: Record<string, string>;
needs_register: boolean;
}>> =>
export const apiLoginWithSocial = ({ token, username, password }: ApiLoginWithSocialRequest) =>
api
.post(API.USER.LOGIN_WITH_SOCIAL, { token, username, password })
.then(resultMiddleware)
.catch(errorMiddleware);
.post<ApiLoginWithSocialResult>(API.USER.LOGIN_WITH_SOCIAL, { token, username, password })
.then(cleanResult);

View file

@ -53,26 +53,26 @@ export const USER_ROLES = {
};
export const EMPTY_TOKEN: IToken = {
access: null,
refresh: null,
access: '',
refresh: '',
};
export const EMPTY_USER: IUser = {
id: null,
id: 0,
role: USER_ROLES.GUEST,
email: null,
name: null,
username: null,
photo: null,
cover: null,
email: '',
name: '',
username: '',
photo: undefined,
cover: undefined,
is_activated: false,
is_user: false,
fullname: null,
description: null,
fullname: '',
description: '',
last_seen: null,
last_seen_messages: null,
last_seen_boris: null,
last_seen: '',
last_seen_messages: '',
last_seen_boris: '',
};
export interface IApiUser {

View file

@ -8,17 +8,17 @@ const HANDLERS = {
};
const INITIAL_STATE: IAuthState = {
token: null,
token: '',
user: { ...EMPTY_USER },
updates: {
last: null,
last: '',
notifications: [],
boris_commented_at: null,
boris_commented_at: '',
},
login: {
error: null,
error: '',
is_loading: false,
is_registering: true,
},
@ -27,7 +27,7 @@ const INITIAL_STATE: IAuthState = {
tab: 'profile',
is_loading: true,
user: null,
user: undefined,
patch_errors: {},
socials: {
@ -39,20 +39,19 @@ const INITIAL_STATE: IAuthState = {
restore: {
code: '',
user: null,
user: undefined,
is_loading: false,
is_succesfull: false,
error: null,
error: '',
},
register_social: {
errors: {
username: 'and this',
password: 'dislike this',
username: '',
password: '',
},
error: 'dont like this one',
token:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJEYXRhIjp7IlByb3ZpZGVyIjoiZ29vZ2xlIiwiSWQiOiJma2F0dXJvdkBpY2Vyb2NrZGV2LmNvbSIsIkVtYWlsIjoiZmthdHVyb3ZAaWNlcm9ja2Rldi5jb20iLCJUb2tlbiI6InlhMjkuYTBBZkg2U01EeXFGdlRaTExXckhsQm1QdGZIOFNIVGQteWlSYTFKSXNmVXluY2F6MTZ5UGhjRmxydTlDMWFtTEg0aHlHRzNIRkhrVGU0SXFUS09hVVBEREdqR2JQRVFJbGpPME9UbUp2T2RrdEtWNDVoUGpJcTB1cHVLc003UWJLSm1oRWhkMEFVa3YyejVHWlNSMjhaM2VOZVdwTEVYSGV0MW1yNyIsIkZldGNoZWQiOnsiUHJvdmlkZXIiOiJnb29nbGUiLCJJZCI6OTIyMzM3MjAzNjg1NDc3NTgwNywiTmFtZSI6IkZlZG9yIEthdHVyb3YiLCJQaG90byI6Imh0dHBzOi8vbGg2Lmdvb2dsZXVzZXJjb250ZW50LmNvbS8ta1VMYXh0VV9jZTAvQUFBQUFBQUFBQUkvQUFBQUFBQUFBQUEvQU1adXVjbkEycTFReU1WLUN0RUtBclRhQzgydE52NTM2QS9waG90by5qcGcifX0sIlR5cGUiOiJvYXV0aF9jbGFpbSJ9.r1MY994BC_g4qRDoDoyNmwLs0qRzBLx6_Ez-3mHQtwg',
error: '',
token: '',
is_loading: false,
},
};

View file

@ -1,5 +1,5 @@
import { call, delay, put, select, takeEvery, takeLatest } from 'redux-saga/effects';
import { AUTH_USER_ACTIONS, EMPTY_USER, USER_ERRORS, USER_ROLES } from '~/redux/auth/constants';
import { AUTH_USER_ACTIONS, EMPTY_USER, USER_ROLES } from '~/redux/auth/constants';
import {
authAttachSocial,
authDropSocial,
@ -48,49 +48,37 @@ import {
selectAuthRestore,
selectAuthUpdates,
selectAuthUser,
selectToken,
} from './selectors';
import { IResultWithStatus, OAUTH_EVENT_TYPES, Unwrap } from '../types';
import { IAuthState, IUser } from './types';
import { OAUTH_EVENT_TYPES, Unwrap } from '../types';
import { IAuthState } from './types';
import { REHYDRATE, RehydrateAction } from 'redux-persist';
import { selectModal } from '~/redux/modal/selectors';
import { IModalState } from '~/redux/modal';
import { DIALOGS } from '~/redux/modal/constants';
import { ERRORS } from '~/constants/errors';
import { messagesSet } from '~/redux/messages/actions';
import { SagaIterator } from 'redux-saga';
export function* reqWrapper(requestAction, props = {}): ReturnType<typeof requestAction> {
const access = yield select(selectToken);
const result = yield call(requestAction, { access, ...props });
if (result && result.status === 401) {
return { error: USER_ERRORS.UNAUTHORIZED, data: {} };
}
return result;
function* setTokenSaga({ token }: ReturnType<typeof authSetToken>) {
localStorage.setItem('token', token);
}
function* sendLoginRequestSaga({ username, password }: ReturnType<typeof userSendLoginRequest>) {
if (!username || !password) return;
const {
error,
data: { token, user },
}: IResultWithStatus<{ token: string; user: IUser }> = yield call(apiUserLogin, {
username,
password,
});
try {
const { token, user }: Unwrap<typeof apiUserLogin> = yield call(apiUserLogin, {
username,
password,
});
if (error) {
yield put(userSetLoginError(error));
return;
yield put(authSetToken(token));
yield put(authSetUser({ ...user, is_user: true }));
yield put(authLoggedIn());
yield put(modalSetShown(false));
} catch (e) {
yield put(userSetLoginError(e));
}
yield put(authSetToken(token));
yield put(authSetUser({ ...user, is_user: true }));
yield put(authLoggedIn());
yield put(modalSetShown(false));
}
function* refreshUser() {
@ -98,23 +86,18 @@ function* refreshUser() {
if (!token) return;
const {
error,
data: { user },
}: IResultWithStatus<{ user: IUser }> = yield call(reqWrapper, apiAuthGetUser);
try {
const { user }: Unwrap<typeof apiAuthGetUser> = yield call(apiAuthGetUser);
if (error) {
yield put(authSetUser({ ...user, is_user: true }));
} catch (e) {
yield put(
authSetUser({
...EMPTY_USER,
is_user: false,
})
);
return;
}
yield put(authSetUser({ ...user, is_user: true }));
}
function* checkUserSaga({ key }: RehydrateAction) {
@ -126,44 +109,43 @@ function* gotPostMessageSaga({ token }: ReturnType<typeof gotAuthPostMessage>) {
yield put(authSetToken(token));
yield call(refreshUser);
const { is_shown, dialog }: IModalState = yield select(selectModal);
const { is_shown, dialog }: ReturnType<typeof selectModal> = yield select(selectModal);
if (is_shown && dialog === DIALOGS.LOGIN) yield put(modalSetShown(false));
}
function* logoutSaga() {
yield put(authSetToken(null));
yield put(authSetToken(''));
yield put(authSetUser({ ...EMPTY_USER }));
yield put(
authSetUpdates({
last: null,
last: '',
notifications: [],
})
);
}
function* loadProfile({ username }: ReturnType<typeof authLoadProfile>) {
function* loadProfile({ username }: ReturnType<typeof authLoadProfile>): SagaIterator<boolean> {
yield put(authSetProfile({ is_loading: true }));
const {
error,
data: { user },
} = yield call(reqWrapper, apiAuthGetUserProfile, { username });
try {
const { user }: Unwrap<typeof apiAuthGetUserProfile> = yield call(apiAuthGetUserProfile, {
username,
});
if (error || !user) {
yield put(authSetProfile({ is_loading: false, user }));
yield put(messagesSet({ messages: [] }));
return true;
} catch (error) {
return false;
}
yield put(authSetProfile({ is_loading: false, user }));
yield put(messagesSet({ messages: [] }));
return true;
}
function* openProfile({ username, tab = 'profile' }: ReturnType<typeof authOpenProfile>) {
yield put(modalShowDialog(DIALOGS.PROFILE));
yield put(authSetProfile({ tab }));
const success: boolean = yield call(loadProfile, authLoadProfile(username));
const success: Unwrap<typeof loadProfile> = yield call(loadProfile, authLoadProfile(username));
if (!success) {
return yield put(modalSetShown(false));
@ -171,42 +153,39 @@ function* openProfile({ username, tab = 'profile' }: ReturnType<typeof authOpenP
}
function* getUpdates() {
const user: ReturnType<typeof selectAuthUser> = yield select(selectAuthUser);
try {
const user: ReturnType<typeof selectAuthUser> = yield select(selectAuthUser);
if (!user || !user.is_user || user.role === USER_ROLES.GUEST || !user.id) return;
if (!user || !user.is_user || user.role === USER_ROLES.GUEST || !user.id) return;
const modal: IModalState = yield select(selectModal);
const profile: IAuthState['profile'] = yield select(selectAuthProfile);
const { last, boris_commented_at }: IAuthState['updates'] = yield select(selectAuthUpdates);
const exclude_dialogs =
modal.is_shown && modal.dialog === DIALOGS.PROFILE && profile.user.id ? profile.user.id : null;
const modal: ReturnType<typeof selectModal> = yield select(selectModal);
const profile: ReturnType<typeof selectAuthProfile> = yield select(selectAuthProfile);
const { last, boris_commented_at }: IAuthState['updates'] = yield select(selectAuthUpdates);
const exclude_dialogs =
modal.is_shown && modal.dialog === DIALOGS.PROFILE && profile.user?.id ? profile.user.id : 0;
const { error, data }: Unwrap<ReturnType<typeof apiAuthGetUpdates>> = yield call(
reqWrapper,
apiAuthGetUpdates,
{ exclude_dialogs, last: last || user.last_seen_messages }
);
const data: Unwrap<typeof apiAuthGetUpdates> = yield call(apiAuthGetUpdates, {
exclude_dialogs,
last: last || user.last_seen_messages,
});
if (error || !data) {
return;
}
if (data.notifications && data.notifications.length) {
yield put(
authSetUpdates({
last: data.notifications[0].created_at,
notifications: data.notifications,
})
);
}
if (data.notifications && data.notifications.length) {
yield put(
authSetUpdates({
last: data.notifications[0].created_at,
notifications: data.notifications,
})
);
}
if (data.boris && data.boris.commented_at && boris_commented_at !== data.boris.commented_at) {
yield put(
authSetUpdates({
boris_commented_at: data.boris.commented_at,
})
);
}
if (data.boris && data.boris.commented_at && boris_commented_at !== data.boris.commented_at) {
yield put(
authSetUpdates({
boris_commented_at: data.boris.commented_at,
})
);
}
} catch (error) {}
}
function* startPollingSaga() {
@ -219,148 +198,136 @@ function* startPollingSaga() {
function* setLastSeenMessages({ last_seen_messages }: ReturnType<typeof authSetLastSeenMessages>) {
if (!Date.parse(last_seen_messages)) return;
yield call(reqWrapper, apiUpdateUser, { user: { last_seen_messages } });
yield call(apiUpdateUser, { user: { last_seen_messages } });
}
function* patchUser({ user }: ReturnType<typeof authPatchUser>) {
const me = yield select(selectAuthUser);
function* patchUser(payload: ReturnType<typeof authPatchUser>) {
const me: ReturnType<typeof selectAuthUser> = yield select(selectAuthUser);
const { error, data } = yield call(reqWrapper, apiUpdateUser, { user });
try {
const { user, errors }: Unwrap<typeof apiUpdateUser> = yield call(apiUpdateUser, {
user: payload.user,
});
if (error || !data.user || data.errors) {
return yield put(authSetProfile({ patch_errors: data.errors }));
if (errors && Object.keys(errors).length) {
yield put(authSetProfile({ patch_errors: errors }));
return;
}
yield put(authSetUser({ ...me, ...user }));
yield put(authSetProfile({ user: { ...me, ...user }, tab: 'profile' }));
} catch (error) {
return;
}
yield put(authSetUser({ ...me, ...data.user }));
yield put(authSetProfile({ user: { ...me, ...data.user }, tab: 'profile' }));
}
function* requestRestoreCode({ field }: ReturnType<typeof authRequestRestoreCode>) {
if (!field) return;
yield put(authSetRestore({ error: null, is_loading: true }));
const { error, data } = yield call(apiRequestRestoreCode, { field });
try {
yield put(authSetRestore({ error: '', is_loading: true }));
yield call(apiRequestRestoreCode, {
field,
});
if (data.error || error) {
return yield put(authSetRestore({ is_loading: false, error: data.error || error }));
yield put(authSetRestore({ is_loading: false, is_succesfull: true }));
} catch (error) {
return yield put(authSetRestore({ is_loading: false, error }));
}
yield put(authSetRestore({ is_loading: false, is_succesfull: true }));
}
function* showRestoreModal({ code }: ReturnType<typeof authShowRestoreModal>) {
if (!code && !code.length) {
return yield put(authSetRestore({ error: ERRORS.CODE_IS_INVALID, is_loading: false }));
try {
if (!code && !code.length) {
return yield put(authSetRestore({ error: ERRORS.CODE_IS_INVALID, is_loading: false }));
}
yield put(authSetRestore({ user: undefined, is_loading: true }));
const data: Unwrap<typeof apiCheckRestoreCode> = yield call(apiCheckRestoreCode, { code });
yield put(authSetRestore({ user: data.user, code, is_loading: false }));
yield put(modalShowDialog(DIALOGS.RESTORE_PASSWORD));
} catch (error) {
yield put(authSetRestore({ is_loading: false, error: error || ERRORS.CODE_IS_INVALID }));
yield put(modalShowDialog(DIALOGS.RESTORE_PASSWORD));
}
yield put(authSetRestore({ user: null, is_loading: true }));
const { error, data } = yield call(apiCheckRestoreCode, { code });
if (data.error || error || !data.user) {
yield put(
authSetRestore({ is_loading: false, error: data.error || error || ERRORS.CODE_IS_INVALID })
);
return yield put(modalShowDialog(DIALOGS.RESTORE_PASSWORD));
}
yield put(authSetRestore({ user: data.user, code, is_loading: false }));
yield put(modalShowDialog(DIALOGS.RESTORE_PASSWORD));
}
function* restorePassword({ password }: ReturnType<typeof authRestorePassword>) {
if (!password) return;
try {
if (!password) return;
yield put(authSetRestore({ is_loading: true }));
const { code } = yield select(selectAuthRestore);
yield put(authSetRestore({ is_loading: true }));
const { code } = yield select(selectAuthRestore);
if (!code) {
return yield put(authSetRestore({ error: ERRORS.CODE_IS_INVALID, is_loading: false }));
if (!code) {
return yield put(authSetRestore({ error: ERRORS.CODE_IS_INVALID, is_loading: false }));
}
const data: Unwrap<typeof apiRestoreCode> = yield call(apiRestoreCode, { code, password });
yield put(authSetToken(data.token));
yield put(authSetUser(data.user));
yield put(authSetRestore({ is_loading: false, is_succesfull: true, error: '' }));
yield call(refreshUser);
} catch (error) {
return yield put(authSetRestore({ is_loading: false, error: error || ERRORS.CODE_IS_INVALID }));
}
const { error, data } = yield call(apiRestoreCode, { code, password });
if (data.error || error || !data.user || !data.token) {
return yield put(
authSetRestore({ is_loading: false, error: data.error || error || ERRORS.CODE_IS_INVALID })
);
}
yield put(authSetToken(data.token));
yield put(authSetUser(data.user));
yield put(authSetRestore({ is_loading: false, is_succesfull: true, error: null }));
yield call(refreshUser);
}
function* getSocials() {
yield put(authSetSocials({ is_loading: true, error: '' }));
try {
const { data, error }: Unwrap<ReturnType<typeof apiGetSocials>> = yield call(
reqWrapper,
apiGetSocials,
{}
);
if (error) {
throw new Error(error);
}
yield put(authSetSocials({ is_loading: false, accounts: data.accounts, error: '' }));
} catch (e) {
yield put(authSetSocials({ is_loading: false, error: e.toString() }));
yield put(authSetSocials({ is_loading: true, error: '' }));
const data: Unwrap<typeof apiGetSocials> = yield call(apiGetSocials);
yield put(authSetSocials({ accounts: data.accounts }));
} catch (error) {
yield put(authSetSocials({ error }));
} finally {
yield put(authSetSocials({ is_loading: false }));
}
}
// TODO: start from here
function* dropSocial({ provider, id }: ReturnType<typeof authDropSocial>) {
try {
yield put(authSetSocials({ error: '' }));
const { error }: Unwrap<ReturnType<typeof apiDropSocial>> = yield call(
reqWrapper,
apiDropSocial,
{ id, provider }
);
if (error) {
throw new Error(error);
}
yield call(apiDropSocial, {
id,
provider,
});
yield call(getSocials);
} catch (e) {
yield put(authSetSocials({ error: e.message }));
} catch (error) {
yield put(authSetSocials({ error }));
}
}
function* attachSocial({ token }: ReturnType<typeof authAttachSocial>) {
if (!token) return;
try {
if (!token) return;
yield put(authSetSocials({ error: '', is_loading: true }));
const { data, error }: Unwrap<ReturnType<typeof apiAttachSocial>> = yield call(
reqWrapper,
apiAttachSocial,
{ token }
);
if (error) {
throw new Error(error);
}
const data: Unwrap<typeof apiAttachSocial> = yield call(apiAttachSocial, {
token,
});
const {
socials: { accounts },
}: ReturnType<typeof selectAuthProfile> = yield select(selectAuthProfile);
if (accounts.some(it => it.id === data.account.id && it.provider === data.account.provider)) {
yield put(authSetSocials({ is_loading: false }));
} else {
yield put(authSetSocials({ is_loading: false, accounts: [...accounts, data.account] }));
return;
}
yield put(authSetSocials({ accounts: [...accounts, data.account] }));
} catch (e) {
yield put(authSetSocials({ is_loading: false, error: e.message }));
yield put(authSetSocials({ error: e.message }));
} finally {
yield put(authSetSocials({ is_loading: false }));
}
}
@ -368,10 +335,9 @@ function* loginWithSocial({ token }: ReturnType<typeof authLoginWithSocial>) {
try {
yield put(userSetLoginError(''));
const {
data,
error,
}: Unwrap<ReturnType<typeof apiLoginWithSocial>> = yield call(apiLoginWithSocial, { token });
const data: Unwrap<typeof apiLoginWithSocial> = yield call(apiLoginWithSocial, {
token,
});
// Backend asks us for account registration
if (data?.needs_register) {
@ -380,18 +346,14 @@ function* loginWithSocial({ token }: ReturnType<typeof authLoginWithSocial>) {
return;
}
if (error) {
throw new Error(error);
}
if (data.token) {
yield put(authSetToken(data.token));
yield call(refreshUser);
yield put(modalSetShown(false));
return;
}
} catch (e) {
yield put(userSetLoginError(e.message));
} catch (error) {
yield put(userSetLoginError(error));
}
}
@ -414,23 +376,19 @@ function* authRegisterSocial({ username, password }: ReturnType<typeof authSendR
try {
yield put(authSetRegisterSocial({ error: '' }));
const { token }: Unwrap<ReturnType<typeof selectAuthRegisterSocial>> = yield select(
const { token }: Unwrap<typeof selectAuthRegisterSocial> = yield select(
selectAuthRegisterSocial
);
const { data, error }: Unwrap<ReturnType<typeof apiLoginWithSocial>> = yield call(
apiLoginWithSocial,
{
token,
username,
password,
}
);
const data: Unwrap<typeof apiLoginWithSocial> = yield call(apiLoginWithSocial, {
token,
username,
password,
});
if (data?.errors) {
yield put(authSetRegisterSocialErrors(data.errors));
} else if (data?.error) {
throw new Error(error);
return;
}
if (data.token) {
@ -449,6 +407,7 @@ function* authSaga() {
yield takeLatest([REHYDRATE, AUTH_USER_ACTIONS.LOGGED_IN], startPollingSaga);
yield takeLatest(AUTH_USER_ACTIONS.LOGOUT, logoutSaga);
yield takeLatest(AUTH_USER_ACTIONS.SET_TOKEN, setTokenSaga);
yield takeLatest(AUTH_USER_ACTIONS.SEND_LOGIN_REQUEST, sendLoginRequestSaga);
yield takeLatest(AUTH_USER_ACTIONS.GOT_AUTH_POST_MESSAGE, gotPostMessageSaga);
yield takeLatest(AUTH_USER_ACTIONS.OPEN_PROFILE, openProfile);

View file

@ -5,7 +5,7 @@ export const selectUser = (state: IState) => state.auth.user;
export const selectToken = (state: IState) => state.auth.token;
export const selectAuthLogin = (state: IState) => state.auth.login;
export const selectAuthProfile = (state: IState) => state.auth.profile;
export const selectAuthProfileUsername = (state: IState) => state.auth.profile.user.username;
export const selectAuthProfileUsername = (state: IState) => state.auth.profile.user?.username;
export const selectAuthUser = (state: IState) => state.auth.user;
export const selectAuthUpdates = (state: IState) => state.auth.updates;
export const selectAuthRestore = (state: IState) => state.auth.restore;

View file

@ -1,13 +1,18 @@
import { IResultWithStatus } from '~/redux/types';
import { HTTP_RESPONSES } from '~/utils/api';
export const userLoginTransform = ({ status, data, error }: IResultWithStatus<any>): IResultWithStatus<any> => {
export const userLoginTransform = ({
status,
data,
error,
}: IResultWithStatus<any>): IResultWithStatus<any> => {
switch (true) {
case (status === HTTP_RESPONSES.UNAUTHORIZED || !data.token) && status !== HTTP_RESPONSES.CONNECTION_REFUSED:
case (status === HTTP_RESPONSES.UNAUTHORIZED || !data.token) &&
status !== HTTP_RESPONSES.CONNECTION_REFUSED:
return { status, data, error: 'Пользователь не найден' };
case status === 200:
return { status, data, error: null };
return { status, data, error: '' };
default:
return { status, data, error: error || 'Неизвестная ошибка' };

View file

@ -1,4 +1,4 @@
import { IFile, INotification } from '../types';
import { IFile, INotification, IResultWithStatus } from '../types';
export interface IToken {
access: string;
@ -10,8 +10,8 @@ export interface IUser {
username: string;
email: string;
role: string;
photo: IFile;
cover: IFile;
photo?: IFile;
cover?: IFile;
name: string;
fullname: string;
description: string;
@ -53,7 +53,7 @@ export type IAuthState = Readonly<{
tab: 'profile' | 'messages' | 'settings';
is_loading: boolean;
user: IUser;
user?: IUser;
patch_errors: Record<string, string>;
socials: {
@ -65,7 +65,7 @@ export type IAuthState = Readonly<{
restore: {
code: string;
user: Pick<IUser, 'username' | 'photo'>;
user?: Pick<IUser, 'username' | 'photo'>;
is_loading: boolean;
is_succesfull: boolean;
error: string;
@ -81,3 +81,52 @@ export type IAuthState = Readonly<{
is_loading: boolean;
};
}>;
export type ApiWithTokenRequest = { access: string };
export type ApiUserLoginRequest = Record<'username' | 'password', string>;
export type ApiUserLoginResult = { token: string; user: IUser };
export type ApiAuthGetUserRequest = {};
export type ApiAuthGetUserResult = { user: IUser };
export type ApiUpdateUserRequest = { user: Partial<IUser> };
export type ApiUpdateUserResult = { user: IUser; errors: Record<Partial<keyof IUser>, string> };
export type ApiAuthGetUserProfileRequest = { username: string };
export type ApiAuthGetUserProfileResult = { user: IUser };
export type ApiAuthGetUpdatesRequest = {
exclude_dialogs: number;
last: string;
};
export type ApiAuthGetUpdatesResult = {
notifications: INotification[];
boris: { commented_at: string };
};
export type ApiCheckRestoreCodeRequest = { code: string };
export type ApiCheckRestoreCodeResult = { user: IUser };
export type ApiRestoreCodeRequest = { code: string; password: string };
export type ApiRestoreCodeResult = { token: string; user: IUser };
export type ApiGetSocialsResult = { accounts: ISocialAccount[] };
export type ApiDropSocialRequest = { id: string; provider: string };
export type ApiDropSocialResult = { accounts: ISocialAccount[] };
export type ApiAttachSocialRequest = { token: string };
export type ApiAttachSocialResult = { account: ISocialAccount };
export type ApiLoginWithSocialRequest = {
token: string;
username?: string;
password?: string;
};
export type ApiLoginWithSocialResult = {
token: string;
errors: Record<string, string>;
needs_register: boolean;
};