refactor: remove legacy code and move redesign code to src

VAN-670
This commit is contained in:
Waheed Ahmed
2021-07-28 14:27:04 +05:00
parent 0cc2191658
commit 32300db8dd
254 changed files with 28 additions and 16186 deletions

View File

@@ -0,0 +1,33 @@
import { getConfig } from '@edx/frontend-platform';
import { applyMiddleware, createStore, compose } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import { createLogger } from 'redux-logger';
import createSagaMiddleware from 'redux-saga';
import createRootReducer from './reducers';
import rootSaga from './sagas';
const sagaMiddleware = createSagaMiddleware();
function composeMiddleware() {
if (getConfig().ENVIRONMENT === 'development') {
const loggerMiddleware = createLogger({
collapsed: true,
});
return composeWithDevTools(applyMiddleware(thunkMiddleware, sagaMiddleware, loggerMiddleware));
}
return compose(applyMiddleware(thunkMiddleware, sagaMiddleware));
}
export default function configureStore(initialState = {}) {
const store = createStore(
createRootReducer(),
initialState,
composeMiddleware(),
);
sagaMiddleware.run(rootSaga);
return store;
}

33
src/data/constants.js Normal file
View File

@@ -0,0 +1,33 @@
// URL Paths
export const LOGIN_PAGE = '/login';
export const REGISTER_PAGE = '/register';
export const RESET_PAGE = '/reset';
export const WELCOME_PAGE = '/welcome';
export const DEFAULT_REDIRECT_URL = '/dashboard';
export const PASSWORD_RESET_CONFIRM = '/password_reset_confirm/:token/';
export const PAGE_NOT_FOUND = '/notfound';
export const ENTERPRISE_LOGIN_URL = '/enterprise/login';
// Constants
export const SUPPORTED_ICON_CLASSES = ['apple', 'facebook', 'google', 'microsoft'];
// Error Codes
export const INTERNAL_SERVER_ERROR = 'internal-server-error';
export const API_RATELIMIT_ERROR = 'api-ratelimit-error';
// States
export const DEFAULT_STATE = 'default';
export const PENDING_STATE = 'pending';
export const COMPLETE_STATE = 'complete';
// Regex
export const VALID_EMAIL_REGEX = '(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*'
+ '|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"'
+ ')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+)(?:[A-Z0-9-]{2,63})'
+ '|\\[(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\]$';
export const LETTER_REGEX = /[a-zA-Z]/;
export const NUMBER_REGEX = /\d/;
// Query string parameters that can be passed to LMS to manage
// things like auto-enrollment upon login and registration.
export const AUTH_PARAMS = ['course_id', 'enrollment_action', 'course_mode', 'email_opt_in', 'purchase_workflow', 'next'];

37
src/data/reducers.js Executable file
View File

@@ -0,0 +1,37 @@
import { combineReducers } from 'redux';
import {
reducer as loginReducer,
storeName as loginStoreName,
} from '../login';
import {
reducer as registerReducer,
storeName as registerStoreName,
} from '../register';
import {
reducer as commonComponentsReducer,
storeName as commonComponentsStoreName,
} from '../common-components';
import {
reducer as forgotPasswordReducer,
storeName as forgotPasswordStoreName,
} from '../forgot-password';
import {
reducer as resetPasswordReducer,
storeName as resetPasswordStoreName,
} from '../reset-password';
import {
reducer as welcomePageReducers,
storeName as welcomePageStoreName,
} from '../welcome';
const createRootReducer = () => combineReducers({
[loginStoreName]: loginReducer,
[registerStoreName]: registerReducer,
[commonComponentsStoreName]: commonComponentsReducer,
[forgotPasswordStoreName]: forgotPasswordReducer,
[resetPasswordStoreName]: resetPasswordReducer,
[welcomePageStoreName]: welcomePageReducers,
});
export default createRootReducer;

19
src/data/sagas.js Normal file
View File

@@ -0,0 +1,19 @@
import { all } from 'redux-saga/effects';
import { saga as registrationSaga } from '../register';
import { saga as loginSaga } from '../login';
import { saga as commonComponentsSaga } from '../common-components';
import { saga as forgotPasswordSaga } from '../forgot-password';
import { saga as resetPasswordSaga } from '../reset-password';
import { saga as welcomePageSaga } from '../welcome';
export default function* rootSaga() {
yield all([
loginSaga(),
registrationSaga(),
commonComponentsSaga(),
forgotPasswordSaga(),
resetPasswordSaga(),
welcomePageSaga(),
]);
}

21
src/data/utils/cookies.js Normal file
View File

@@ -0,0 +1,21 @@
import Cookies from 'universal-cookie';
import { getConfig } from '@edx/frontend-platform';
export function setCookie(cookieName, cookieValue, cookieExpiry) {
const cookies = new Cookies();
const options = { domain: getConfig().COOKIE_DOMAIN, path: '/' };
if (cookieExpiry) {
options.expires = cookieExpiry;
}
cookies.set(cookieName, cookieValue, options);
}
export default function setSurveyCookie(surveyType) {
const cookieName = getConfig().USER_SURVEY_COOKIE_NAME;
if (cookieName) {
const signupTimestamp = (new Date()).getTime();
// set expiry to exactly 24 hours from now
const cookieExpiry = new Date(signupTimestamp + 1 * 864e5);
setCookie(cookieName, surveyType, cookieExpiry);
}
}

View File

@@ -0,0 +1,86 @@
// Utility functions
import * as QueryString from 'query-string';
import { AUTH_PARAMS } from '../constants';
export default function processLink(link) {
let matches;
link.replace(/(.*?)<a href=["']([^"']*).*?>([^<]+)<\/a>(.*)/g, function () { // eslint-disable-line func-names
matches = Array.prototype.slice.call(arguments, 1, 5); // eslint-disable-line prefer-rest-params
});
return matches;
}
export const getTpaProvider = (tpaHintProvider, primaryProviders, secondaryProviders) => {
let tpaProvider = null;
let skipHintedLogin = false;
[...primaryProviders, ...secondaryProviders].forEach((provider) => {
if (provider.id === tpaHintProvider) {
tpaProvider = provider;
if (provider.skipHintedLogin) {
skipHintedLogin = true;
}
}
});
return { provider: tpaProvider, skipHintedLogin };
};
export const getTpaHint = () => {
const params = QueryString.parse(window.location.search);
let tpaHint = null;
tpaHint = params.tpa_hint;
if (!tpaHint) {
const { next } = params;
if (next) {
const index = next.indexOf('tpa_hint=');
if (index !== -1) {
tpaHint = next.substring(index + 'tpa_hint='.length, next.length);
}
}
}
return tpaHint;
};
export const updatePathWithQueryParams = (path) => {
let queryParams = window.location.search;
if (!queryParams) {
return path;
}
if (queryParams.indexOf('track=pwreset') > -1) {
queryParams = queryParams.replace(
'?track=pwreset&', '?',
).replace('?track=pwreset', '').replace('&track=pwreset', '').replace('?&', '?');
}
return `${path}${queryParams}`;
};
export const getAllPossibleQueryParam = () => {
const urlParams = QueryString.parse(window.location.search);
const params = {};
Object.entries(urlParams).forEach(([key, value]) => {
if (AUTH_PARAMS.indexOf(key) > -1) {
params[key] = value;
}
});
return params;
};
export const getActivationStatus = () => {
const params = QueryString.parse(window.location.search);
return params.account_activation_status;
};
export const isScrollBehaviorSupported = () => 'scrollBehavior' in document.documentElement.style;
export const windowScrollTo = (options) => {
if (isScrollBehaviorSupported()) {
return window.scrollTo(options);
}
return window.scrollTo(options.top, options.left);
};

View File

@@ -0,0 +1,32 @@
import { LOGIN_PAGE } from '../constants';
import processLink, { updatePathWithQueryParams } from './dataUtils';
describe('processLink', () => {
it('should use the provided processLink function to', () => {
const expectedHref = 'http://test.server.com/';
const expectedText = 'test link';
const link = `<a href="${expectedHref}">${expectedText}</a>`;
const matches = processLink(link);
expect(matches[1]).toEqual(expectedHref);
expect(matches[2]).toEqual(expectedText);
});
});
describe('updatePathWithQueryParams', () => {
it('should append query params into the path', () => {
const params = '?course_id=testCourseId';
const expectedPath = `${LOGIN_PAGE}${params}`;
Object.defineProperty(window, 'location', {
value: {
href: 'http://localhost/',
search: params,
},
});
const updatedPath = updatePathWithQueryParams(LOGIN_PAGE);
expect(updatedPath).toEqual(expectedPath);
});
});

11
src/data/utils/index.js Normal file
View File

@@ -0,0 +1,11 @@
export {
default,
getTpaProvider,
getTpaHint,
updatePathWithQueryParams,
getAllPossibleQueryParam,
getActivationStatus,
windowScrollTo,
} from './dataUtils';
export { default as AsyncActionType } from './reduxUtils';
export { default as setSurveyCookie, setCookie } from './cookies';

View File

@@ -0,0 +1,34 @@
/**
* Helper class to save time when writing out action types for asynchronous methods. Also helps
* ensure that actions are namespaced.
*/
export default class AsyncActionType {
constructor(topic, name) {
this.topic = topic;
this.name = name;
}
get BASE() {
return `${this.topic}__${this.name}`;
}
get BEGIN() {
return `${this.topic}__${this.name}__BEGIN`;
}
get SUCCESS() {
return `${this.topic}__${this.name}__SUCCESS`;
}
get FAILURE() {
return `${this.topic}__${this.name}__FAILURE`;
}
get RESET() {
return `${this.topic}__${this.name}__RESET`;
}
get FORBIDDEN() {
return `${this.topic}__${this.name}__FORBIDDEN`;
}
}

View File

@@ -0,0 +1,14 @@
import AsyncActionType from './reduxUtils';
describe('AsyncActionType', () => {
it('should return well formatted action strings', () => {
const actionType = new AsyncActionType('HOUSE_CATS', 'START_THE_RACE');
expect(actionType.BASE).toBe('HOUSE_CATS__START_THE_RACE');
expect(actionType.BEGIN).toBe('HOUSE_CATS__START_THE_RACE__BEGIN');
expect(actionType.SUCCESS).toBe('HOUSE_CATS__START_THE_RACE__SUCCESS');
expect(actionType.FAILURE).toBe('HOUSE_CATS__START_THE_RACE__FAILURE');
expect(actionType.RESET).toBe('HOUSE_CATS__START_THE_RACE__RESET');
expect(actionType.FORBIDDEN).toBe('HOUSE_CATS__START_THE_RACE__FORBIDDEN');
});
});