Skip to content

Commit

Permalink
feat(RSS-ECOMM-2_28): add router component (#111)
Browse files Browse the repository at this point in the history
* feat: add Router component

* feat: add environment variables

* feat: add EventMediator component
  • Loading branch information
Kleostro authored Apr 29, 2024
1 parent f88e92f commit 12885ea
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ VITE_APP_CTP_REGION=europe-west1
VITE_APP_CTP_AUTH_URL=https://auth.europe-west1.gcp.commercetools.com
VITE_APP_CTP_API_URL=https://api.europe-west1.gcp.commercetools.com
VITE_APP_CTP_SCOPES=manage_orders:green-shop manage_payments:green-shop manage_types:green-shop view_shopping_lists:green-shop manage_customers:green-shop manage_my_orders:green-shop view_orders:green-shop manage_quotes:green-shop view_discount_codes:green-shop view_quote_requests:green-shop manage_order_edits:green-shop manage_products:green-shop manage_quote_requests:green-shop view_quotes:green-shop view_api_clients:green-shop view_order_edits:green-shop manage_shipping_methods:green-shop manage_cart_discounts:green-shop manage_my_shopping_lists:green-shop view_products:green-shop view_categories:green-shop manage_my_payments:green-shop manage_my_profile:green-shop manage_discount_codes:green-shop manage_categories:green-shop manage_shopping_lists:green-shop manage_extensions:green-shop
VITE_APP_DEFAULT_SEGMENT='/'
VITE_APP_NEXT_SEGMENT=0
VITE_APP_PATH_SEGMENTS_TO_KEEP=0
4 changes: 4 additions & 0 deletions .env.github
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

VITE_APP_DEFAULT_SEGMENT='/'
VITE_APP_NEXT_SEGMENT=1
VITE_APP_PATH_SEGMENTS_TO_KEEP=2
33 changes: 33 additions & 0 deletions src/app/App/model/AppModel.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,41 @@
import type { PageInterface } from '@/shared/types/interfaces.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 { PAGES_IDS } from '@/shared/constants/enums.ts';

import AppView from '../view/AppView.ts';

class AppModel {
private appView: AppView = new AppView();

private router = new RouterModel();

constructor() {
this.router.setPages(this.initPages());
}

private initPages(): Map<string, PageInterface> {
const root = this.getHTML();
const loginPage = new LoginPageModel(root);
const mainPage = new MainPageModel(root);
const registrationPage = new RegistrationPageModel(root);
const notFoundPage = new NotFoundPageModel(root);
const pages: Map<string, PageInterface> = new Map(
Object.entries({
[PAGES_IDS.DEFAULT_PAGE]: mainPage,
[PAGES_IDS.LOGIN_PAGE]: loginPage,
[PAGES_IDS.MAIN_PAGE]: mainPage,
[PAGES_IDS.NOT_FOUND_PAGE]: notFoundPage,
[PAGES_IDS.REGISTRATION_PAGE]: registrationPage,
}),
);
return pages;
}

public getHTML(): HTMLDivElement {
return this.appView.getHTML();
}
Expand Down
Empty file removed src/app/Router/.gitkeep
Empty file.
64 changes: 64 additions & 0 deletions src/app/Router/model/RouterModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { PageInterface } from '@/shared/types/interfaces.ts';

import EventMediatorModel from '@/shared/EventMediator/model/EventMediatorModel.ts';
import { EVENT_NAMES, MEDIATOR_EVENTS, PAGES_IDS } from '@/shared/constants/enums.ts';

const DEFAULT_SEGMENT = import.meta.env.VITE_APP_DEFAULT_SEGMENT;
const NEXT_SEGMENT = import.meta.env.VITE_APP_NEXT_SEGMENT;
const PATH_SEGMENTS_TO_KEEP = import.meta.env.VITE_APP_PATH_SEGMENTS_TO_KEEP;

class RouterModel {
private eventMediator = EventMediatorModel.getInstance();

private pages: Map<string, PageInterface> = new Map();

constructor() {
document.addEventListener(EVENT_NAMES.DOM_CONTENT_LOADED, () => {
const currentPath = window.location.pathname
.split(DEFAULT_SEGMENT)
.slice(PATH_SEGMENTS_TO_KEEP + NEXT_SEGMENT)
.join(DEFAULT_SEGMENT);
this.handleRequest(currentPath);
this.eventMediator.notify(MEDIATOR_EVENTS.CHANGE_PAGE, currentPath.split(DEFAULT_SEGMENT).join());
});

window.addEventListener(EVENT_NAMES.POPSTATE, () => {
const currentPath = window.location.pathname
.split(DEFAULT_SEGMENT)
.slice(PATH_SEGMENTS_TO_KEEP + NEXT_SEGMENT)
.join(DEFAULT_SEGMENT);
this.handleRequest(currentPath);
});
}

private handleRequest(path: string): null | string {
const pathParts = path.split(DEFAULT_SEGMENT);
const hasRoute = this.pages.has(pathParts.join(''));
if (!hasRoute) {
this.navigateTo(PAGES_IDS.NOT_FOUND_PAGE);
return null;
}

this.eventMediator.notify(MEDIATOR_EVENTS.CHANGE_PAGE, pathParts.join());
return pathParts.join('');
}

public navigateTo(route: string): History {
this.handleRequest(route);

const pathnameApp = window.location.pathname
.split(DEFAULT_SEGMENT)
.slice(NEXT_SEGMENT, PATH_SEGMENTS_TO_KEEP + NEXT_SEGMENT)
.join(DEFAULT_SEGMENT);
const url = `/${pathnameApp}/${route}`;
window.history.pushState({}, '', url);
return window.history;
}

public setPages(pages: Map<string, PageInterface>): Map<string, PageInterface> {
this.pages = pages;
return this.pages;
}
}

export default RouterModel;
3 changes: 3 additions & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ interface ImportMeta {
VITE_APP_CTP_PROJECT_KEY: string;
VITE_APP_CTP_REGION: string;
VITE_APP_CTP_SCOPES: string;
VITE_APP_DEFAULT_SEGMENT: string;
VITE_APP_NEXT_SEGMENT: number;
VITE_APP_PATH_SEGMENTS_TO_KEEP: number;
};
}
4 changes: 2 additions & 2 deletions src/pages/RegistrationPage/model/RegistrationPageModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { PageInterface } from '@/shared/types/interfaces.ts';

import RegistrationPageView from '../view/RegistrationPageView.ts';

class LoginPageModel implements PageInterface {
class RegistrationPageModel implements PageInterface {
private view: RegistrationPageView;

constructor(parent: HTMLDivElement) {
Expand All @@ -14,4 +14,4 @@ class LoginPageModel implements PageInterface {
}
}

export default LoginPageModel;
export default RegistrationPageModel;
Empty file removed src/shared/EventMediator/.gitkeep
Empty file.
46 changes: 46 additions & 0 deletions src/shared/EventMediator/model/EventMediatorModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { ListenerCallback } from '@/shared/types/types';

class EventMediatorModel<T> {
private listeners: Map<string, Array<ListenerCallback<T>>> = new Map();

private static mediator = new EventMediatorModel();

public static getInstance(): EventMediatorModel<unknown> {
return EventMediatorModel.mediator;
}

public notify(eventName: string, params: T): void {
const eventListeners = this.listeners.get(eventName);
if (eventListeners) {
eventListeners.forEach((listener) => listener(params));
}
}

public subscribe(eventName: string, listener: ListenerCallback<T>): void {
if (this.listeners.has(eventName)) {
const listeners = this.listeners.get(eventName);
listeners?.push(listener);
} else {
const newListeners = [];
newListeners.push(listener);
this.listeners.set(eventName, newListeners);
}
}

public unsubscribe(eventName: string, listener: ListenerCallback<T>): void {
if (this.listeners.has(eventName)) {
const listeners = this.listeners.get(eventName);
const index = listeners?.findIndex((l) => l.toString() === listener.toString());

if (index !== undefined && index !== -1) {
listeners?.splice(index, 1);

if (listeners) {
this.listeners.set(eventName, listeners);
}
}
}
}
}

export default EventMediatorModel;
12 changes: 12 additions & 0 deletions src/shared/constants/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,15 @@ export const EVENT_NAMES = {
SUBMIT: 'submit',
TRANSITIONEND: 'transitionend',
} as const;

export const PAGES_IDS = {
DEFAULT_PAGE: '',
LOGIN_PAGE: 'login',
MAIN_PAGE: 'main',
NOT_FOUND_PAGE: '404',
REGISTRATION_PAGE: 'registration',
} as const;

export const MEDIATOR_EVENTS = {
CHANGE_PAGE: 'changePage',
} as const;
2 changes: 1 addition & 1 deletion src/shared/types/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type ButtonActionType from './types.ts';
import type { ButtonActionType } from './types.ts';

export interface ButtonAttributesInterface {
action?: ButtonActionType;
Expand Down
4 changes: 2 additions & 2 deletions src/shared/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ type ActionsType =
| 'transitionrun'
| 'transitionstart';

type ButtonActionType = { key: ActionsType; value: () => void };
export default ButtonActionType;
export type ButtonActionType = { key: ActionsType; value: () => void };
export type ListenerCallback<T> = (params: T) => void;

0 comments on commit 12885ea

Please sign in to comment.