From 5f0b5c66928d932309fcd4ccb51344d91ebd15b7 Mon Sep 17 00:00:00 2001 From: Meg G <146496794+stardustmeg@users.noreply.github.com> Date: Thu, 9 May 2024 18:21:27 +0300 Subject: [PATCH] feat(RSS-ECOMM-3_29): pages lazy loading (#210) * feat: implement lazy loading for pages * fix: add a catch block --- src/app/App/model/AppModel.ts | 60 ++++++++++++++----- src/app/Router/model/RouterModel.ts | 14 ++--- .../Navigation/model/NavigationModel.ts | 10 +++- src/pages/LoginPage/model/LoginPageModel.ts | 25 ++++++-- .../model/RegistrationPageModel.ts | 10 +++- src/widgets/Header/model/HeaderModel.ts | 28 ++++++--- 6 files changed, 107 insertions(+), 40 deletions(-) diff --git a/src/app/App/model/AppModel.ts b/src/app/App/model/AppModel.ts index b7150741..b7a0ac7c 100644 --- a/src/app/App/model/AppModel.ts +++ b/src/app/App/model/AppModel.ts @@ -1,10 +1,6 @@ import type { Page } from '@/shared/types/common.ts'; import RouterModel from '@/app/Router/model/RouterModel.ts'; -import LoginPageModel from '@/pages/LoginPage/model/LoginPageModel.ts'; -import MainPageModel from '@/pages/MainPage/model/MainPageModel.ts'; -import NotFoundPageModel from '@/pages/NotFoundPage/model/NotFoundPageModel.ts'; -import RegistrationPageModel from '@/pages/RegistrationPage/model/RegistrationPageModel.ts'; import { PAGE_ID } from '@/shared/constants/pages.ts'; import FooterModel from '@/widgets/Footer/model/FooterModel.ts'; import HeaderModel from '@/widgets/Header/model/HeaderModel.ts'; @@ -17,22 +13,54 @@ class AppModel { private router = new RouterModel(); constructor() { + this.initialize() + .then() + .catch(() => { + throw new Error('AppModel initialization error'); + }); + } + + private createRoutes(): Promise Promise>> { + const routesMap = { + [PAGE_ID.DEFAULT_PAGE]: async (): Promise => { + const { default: MainPageModel } = await import('@/pages/MainPage/model/MainPageModel.ts'); + return new MainPageModel(this.appView.getHTML()); + }, + [PAGE_ID.LOGIN_PAGE]: async (): Promise => { + const { default: LoginPageModel } = await import('@/pages/LoginPage/model/LoginPageModel.ts'); + return new LoginPageModel(this.appView.getHTML(), this.router); + }, + [PAGE_ID.MAIN_PAGE]: async (): Promise => { + const { default: MainPageModel } = await import('@/pages/MainPage/model/MainPageModel.ts'); + return new MainPageModel(this.appView.getHTML()); + }, + [PAGE_ID.NOT_FOUND_PAGE]: async (): Promise => { + const { default: NotFoundPageModel } = await import('@/pages/NotFoundPage/model/NotFoundPageModel.ts'); + return new NotFoundPageModel(this.appView.getHTML(), this.router); + }, + [PAGE_ID.REGISTRATION_PAGE]: async (): Promise => { + const { default: RegistrationPageModel } = await import( + '@/pages/RegistrationPage/model/RegistrationPageModel.ts' + ); + return new RegistrationPageModel(this.appView.getHTML(), this.router); + }, + }; + + const routes = new Map Promise>(); + Object.entries(routesMap).forEach(([key, value]) => { + routes.set(key, value); + }); + + return Promise.resolve(routes); + } + + private async initialize(): Promise { document.body.append(this.appView.getHTML()); this.appView.getHTML().insertAdjacentElement('beforebegin', new HeaderModel(this.router).getHTML()); this.appView.getHTML().insertAdjacentElement('afterend', new FooterModel(this.router).getHTML()); - this.router.setRoutes(this.createRoutes()); - } - private createRoutes(): Map Page> { - return new Map( - Object.entries({ - [PAGE_ID.DEFAULT_PAGE]: () => new MainPageModel(this.appView.getHTML()), - [PAGE_ID.LOGIN_PAGE]: () => new LoginPageModel(this.appView.getHTML(), this.router), - [PAGE_ID.MAIN_PAGE]: () => new MainPageModel(this.appView.getHTML()), - [PAGE_ID.NOT_FOUND_PAGE]: () => new NotFoundPageModel(this.appView.getHTML(), this.router), - [PAGE_ID.REGISTRATION_PAGE]: () => new RegistrationPageModel(this.appView.getHTML(), this.router), - }), - ); + const routes = await this.createRoutes(); + this.router.setRoutes(routes); } public start(): boolean { diff --git a/src/app/Router/model/RouterModel.ts b/src/app/Router/model/RouterModel.ts index 61bd9d32..1095a859 100644 --- a/src/app/Router/model/RouterModel.ts +++ b/src/app/Router/model/RouterModel.ts @@ -8,15 +8,15 @@ const PATH_SEGMENTS_TO_KEEP = import.meta.env.VITE_APP_PATH_SEGMENTS_TO_KEEP; const PROJECT_TITLE = import.meta.env.VITE_APP_PROJECT_TITLE; class RouterModel { - private routes: Map Page> = new Map(); + private routes: Map Promise> = new Map(); constructor() { - document.addEventListener('DOMContentLoaded', () => { + document.addEventListener('DOMContentLoaded', async () => { const currentPath = window.location.pathname .split(DEFAULT_SEGMENT) .slice(PATH_SEGMENTS_TO_KEEP + NEXT_SEGMENT) .join(DEFAULT_SEGMENT); - this.navigateTo(currentPath); + await this.navigateTo(currentPath); }); } @@ -25,7 +25,7 @@ class RouterModel { document.title = title; } - public navigateTo(path: string): void { + public async navigateTo(path: string): Promise { const pathnameApp = window.location.pathname .split(DEFAULT_SEGMENT) .slice(NEXT_SEGMENT, PATH_SEGMENTS_TO_KEEP + NEXT_SEGMENT) @@ -38,14 +38,14 @@ class RouterModel { this.changeAppTitle(pathParts[1], hasRoute); if (!hasRoute) { - this.routes.get(PAGE_ID.NOT_FOUND_PAGE)?.(); + await this.routes.get(PAGE_ID.NOT_FOUND_PAGE)?.(); return; } - this.routes.get(pathParts[1])?.(); + await this.routes.get(pathParts[1])?.(); } - public setRoutes(routes: Map Page>): Map Page> { + public setRoutes(routes: Map Promise>): Map Promise> { this.routes = routes; return this.routes; } diff --git a/src/entities/Navigation/model/NavigationModel.ts b/src/entities/Navigation/model/NavigationModel.ts index 746c0b3b..9e535b15 100644 --- a/src/entities/Navigation/model/NavigationModel.ts +++ b/src/entities/Navigation/model/NavigationModel.ts @@ -1,7 +1,9 @@ import type RouterModel from '@/app/Router/model/RouterModel'; +import serverMessageModel from '@/shared/ServerMessage/model/ServerMessageModel.ts'; import getStore from '@/shared/Store/Store.ts'; import observeStore, { selectCurrentPage, selectCurrentUser } from '@/shared/Store/observer.ts'; +import { MESSAGE_STATUS, SERVER_MESSAGE } from '@/shared/constants/messages.ts'; import { PAGE_ID } from '@/shared/constants/pages.ts'; import NavigationView from '../view/NavigationView.ts'; @@ -43,9 +45,13 @@ class NavigationModel { private setNavigationLinksHandlers(): boolean { const navigationLinks = this.view.getNavigationLinks(); navigationLinks.forEach((link, route) => { - link.getHTML().addEventListener('click', (event) => { + link.getHTML().addEventListener('click', async (event) => { event.preventDefault(); - this.router.navigateTo(route); + try { + await this.router.navigateTo(route); + } catch { + serverMessageModel.showServerMessage(SERVER_MESSAGE.BAD_REQUEST, MESSAGE_STATUS.ERROR); + } }); }); diff --git a/src/pages/LoginPage/model/LoginPageModel.ts b/src/pages/LoginPage/model/LoginPageModel.ts index 295d2a4e..93a1663c 100644 --- a/src/pages/LoginPage/model/LoginPageModel.ts +++ b/src/pages/LoginPage/model/LoginPageModel.ts @@ -2,9 +2,11 @@ import type RouterModel from '@/app/Router/model/RouterModel.ts'; import type { Page } from '@/shared/types/common.ts'; import type { User } from '@/shared/types/user.ts'; +import serverMessageModel from '@/shared/ServerMessage/model/ServerMessageModel.ts'; import getStore from '@/shared/Store/Store.ts'; import { setCurrentPage } from '@/shared/Store/actions.ts'; import observeStore, { selectCurrentUser } from '@/shared/Store/observer.ts'; +import { MESSAGE_STATUS, SERVER_MESSAGE } from '@/shared/constants/messages.ts'; import { PAGE_ID, PAGE_LINK_TEXT, PAGE_LINK_TEXT_KEYS } from '@/shared/constants/pages.ts'; import observeCurrentLanguage from '@/shared/utils/observeCurrentLanguage.ts'; import LoginFormModel from '@/widgets/LoginForm/model/LoginFormModel.ts'; @@ -24,12 +26,17 @@ class LoginPageModel implements Page { this.init(); } - private checkAuthUser(): User | null { + private async checkAuthUser(): Promise { const { currentUser } = getStore().getState(); if (currentUser) { - this.router.navigateTo(PAGE_ID.MAIN_PAGE); - return currentUser; + try { + await this.router.navigateTo(PAGE_ID.MAIN_PAGE); + return currentUser; + } catch (error) { + serverMessageModel.showServerMessage(SERVER_MESSAGE.BAD_REQUEST, MESSAGE_STATUS.ERROR); + return null; + } } return null; @@ -37,7 +44,9 @@ class LoginPageModel implements Page { private init(): boolean { getStore().dispatch(setCurrentPage(PAGE_ID.LOGIN_PAGE)); - this.checkAuthUser(); + this.checkAuthUser().catch(() => { + serverMessageModel.showServerMessage(SERVER_MESSAGE.BAD_REQUEST, MESSAGE_STATUS.ERROR); + }); this.view.getAuthWrapper().append(this.loginForm.getHTML()); this.loginForm.getFirstInputField().getView().getInput().getHTML().focus(); this.setRegisterLinkHandler(); @@ -45,9 +54,13 @@ class LoginPageModel implements Page { return true; } - private registerLinkHandler(event: Event): void { + private async registerLinkHandler(event: Event): Promise { event.preventDefault(); - this.router.navigateTo(PAGE_ID.REGISTRATION_PAGE); + try { + await this.router.navigateTo(PAGE_ID.REGISTRATION_PAGE); + } catch (error) { + serverMessageModel.showServerMessage(SERVER_MESSAGE.BAD_REQUEST, MESSAGE_STATUS.ERROR); + } } private setRegisterLinkHandler(): void { diff --git a/src/pages/RegistrationPage/model/RegistrationPageModel.ts b/src/pages/RegistrationPage/model/RegistrationPageModel.ts index 0b67429d..3c1f4f52 100644 --- a/src/pages/RegistrationPage/model/RegistrationPageModel.ts +++ b/src/pages/RegistrationPage/model/RegistrationPageModel.ts @@ -1,9 +1,11 @@ import type RouterModel from '@/app/Router/model/RouterModel.ts'; import type { Page } from '@/shared/types/common.ts'; +import serverMessageModel from '@/shared/ServerMessage/model/ServerMessageModel.ts'; import getStore from '@/shared/Store/Store.ts'; import { setCurrentPage } from '@/shared/Store/actions.ts'; import observeStore, { selectIsUserLoggedIn } from '@/shared/Store/observer.ts'; +import { MESSAGE_STATUS, SERVER_MESSAGE } from '@/shared/constants/messages.ts'; import { PAGE_ID, PAGE_LINK_TEXT, PAGE_LINK_TEXT_KEYS } from '@/shared/constants/pages.ts'; import observeCurrentLanguage from '@/shared/utils/observeCurrentLanguage.ts'; import RegisterFormModel from '@/widgets/RegistrationForm/model/RegistrationFormModel.ts'; @@ -31,9 +33,13 @@ class RegistrationPageModel implements Page { this.setLoginLinkHandler(); } - private loginLinkHandler(event: Event): void { + private async loginLinkHandler(event: Event): Promise { event.preventDefault(); - this.router.navigateTo(PAGE_ID.LOGIN_PAGE); + try { + await this.router.navigateTo(PAGE_ID.LOGIN_PAGE); + } catch { + serverMessageModel.showServerMessage(SERVER_MESSAGE.BAD_REQUEST, MESSAGE_STATUS.ERROR); + } } private setLoginLinkHandler(): void { diff --git a/src/widgets/Header/model/HeaderModel.ts b/src/widgets/Header/model/HeaderModel.ts index b90a35c8..ee4bac8c 100644 --- a/src/widgets/Header/model/HeaderModel.ts +++ b/src/widgets/Header/model/HeaderModel.ts @@ -2,9 +2,11 @@ import type RouterModel from '@/app/Router/model/RouterModel.ts'; import NavigationModel from '@/entities/Navigation/model/NavigationModel.ts'; import getCustomerModel from '@/shared/API/customer/model/CustomerModel.ts'; +import serverMessageModel from '@/shared/ServerMessage/model/ServerMessageModel.ts'; import getStore from '@/shared/Store/Store.ts'; import { setCurrentUser } from '@/shared/Store/actions.ts'; import observeStore, { selectCurrentUser } from '@/shared/Store/observer.ts'; +import { MESSAGE_STATUS, SERVER_MESSAGE } from '@/shared/constants/messages.ts'; // import { LANGUAGE_CHOICE } from '@/shared/constants/buttons.ts'; import { PAGE_ID } from '@/shared/constants/pages.ts'; @@ -44,11 +46,15 @@ class HeaderModel { return true; } - private logoutHandler(): boolean { + private async logoutHandler(): Promise { localStorage.clear(); getStore().dispatch(setCurrentUser(null)); - getCustomerModel().logout(); - this.router.navigateTo(PAGE_ID.LOGIN_PAGE); + try { + getCustomerModel().logout(); + await this.router.navigateTo(PAGE_ID.LOGIN_PAGE); + } catch { + serverMessageModel.showServerMessage(SERVER_MESSAGE.BAD_REQUEST, MESSAGE_STATUS.ERROR); + } return true; } @@ -72,17 +78,25 @@ class HeaderModel { private setLogoHandler(): boolean { const logo = this.view.getLinkLogo().getHTML(); - logo.addEventListener('click', (event) => { + logo.addEventListener('click', async (event) => { event.preventDefault(); - this.router.navigateTo(PAGE_ID.DEFAULT_PAGE); + try { + await this.router.navigateTo(PAGE_ID.DEFAULT_PAGE); + } catch { + serverMessageModel.showServerMessage(SERVER_MESSAGE.BAD_REQUEST, MESSAGE_STATUS.ERROR); + } }); return true; } private setLogoutButtonHandler(): boolean { const logoutButton = this.view.getLogoutButton(); - logoutButton.getHTML().addEventListener('click', () => { - this.logoutHandler(); + logoutButton.getHTML().addEventListener('click', async () => { + try { + await this.logoutHandler(); + } catch { + serverMessageModel.showServerMessage(SERVER_MESSAGE.BAD_REQUEST, MESSAGE_STATUS.ERROR); + } logoutButton.setDisabled(); }); return true;