Mezon is great for playing games and chilling with friends, or even building a worldwide community. Customize your own space to talk, play, and hang out.
https://mezon.ai/invite/1840696977034055680
https://mezon.ai/invite/1840680133686464512
https://mezon.ai/invite/1840677412925345792
npm install --global nx@latest
- using
Git Bashto run the commands - using
VSCodeas the code editor
- Node.js 18.17.0
- yarn 1.22.17
- git bash
- Clone the repository
- Run
yarnto install the dependencies
- Create a
.envfile in theapps/chatdirectory
To start the development server run yarn dev:chat. Open your browser and navigate to http://localhost:4200/. Happy coding!
- Run
yarn lintto lint the codebase - Run
yarn lint:fixto fix the linting issues
- Run
yarn formatto format the codebase - Run
yarn format:fixto fix the formatting issues
We are using monorepo architecture to manage the codebase. The workspace is divided into multiple applications and libraries. Each application is a standalone application and each library is a reusable codebase.
Workspace will be managed by Nx which is a smart, fast and extensible build system.
All applications are located in the apps directory. Each application is a standalone React application and has its own codebase.
chat: Chat applicationadmin: Admin application
Currently, we only focus on the chat application.
All libraries are located in the libs directory. Each library is a reusable codebase and can be used by multiple applications.
ui: UI elements library, the components arestatelessanddumbcomponents: Shared components library, the components arestatefulandsmartperform some logic throughcontextandhookscore: Core library, contains the core logic of the application, could be reused by multiple applications e.g. web, mobile, desktoptransports: Transport layer library, contains the logic to communicate with the server throughmezon-jslibrarystore: State management library, contains the logic to manage the state of the application usingreduxandredux-toolkitassets: Assets library, contains the assets used by the applications and librarieslogger: Logger library, contains the logic to log the messagesutils: Utility functions library
Codebase is divided into multiple dependencies which are managed by Nx. There are 2 types of modules:
- apps: standalone applications
- libs: reusable codebase
the libraries are shared by multiple applications and could be reused by multiple applications.
There are several types of libraries:
ui: UI elements library, the components arestatelessanddumbcomponents: Shared components library, the components arestatefulandsmartperform some logic throughcontextandhookslogic: Logic library, contains the core logic of the application, could be reused by multiple applications e.g. web, mobile, desktoputils: Utility functions library
The dependencies are depend on each other based on the following rules (<- = depend on):
apps<-libsβlibs<-libsβcomponents<-uiβcomponents<-storeβstore<-transportsβstore<-utilsβ
Bad dependencies:
apps<-appsβlibs<-appsβcomponents<-appsβui<-componentsβstore<-componentsβtransports<-storeβutils<-storeβutils<-transportsβmobile libs<-web libsβweb libs<-desktop libsβ
The dependency graph is managed by Nx and could be visualized by running the following command:
npx nx graphthe output will be the dependency graph of the workspace.
how to read the dependency graph:
- dependencies are represented by the arrows
- the
appsare the standalone applications - the
libsare the reusable codebase - the bad dependencies are the dependencies that are not allowed, for example,
apps<-apps,libs<-apps,ui<-components, etc. - in summary, all dependencies should be in top -> bottom direction, if a dependency is in the bottom -> top direction, it is a bad dependency
- Cycle dependencies are not allowed, a cycle dependency is a dependency that forms a cycle, to remove the cycle dependency, we need to move the shared code to the shared library or create a new library to manage the shared code
for example, we have bad dependencies between components and apps which are not allowed.
We are using one-way data flow architecture to manage the data flow of the application. The data flow is unidirectional follow the Redux pattern.
See more about the Redux pattern here.
The core concepts are one-way data flow and single source of truth.
The application data flow is managed by some packages:
-
mezon-js: The core package to communicate with the server throughWebSocketandRESTAPIWebSocket: send and listen to the messages from the serverREST: send and receive the messages from the server
-
store: The state management package to manage the state of the application. store is divided into multiple slices, each slice is a standalone slice and has its own reducer, action, and selector.slice: A standalone slice of the store, contains the reducer, action, and selectorreducer: A function to manage the state of the applicationaction: A function to dispatch the action to the reducerselector: A function to select the state from the store
-
routing: The routing package to manage the routing of the application. The routing is managed byreact-router-dompackage.- loader: The loader to load the component dynamically
- route: The route to navigate to the component
- page: The page to render the component
- When the application starts, based on the initial route, the application will load the components and pages
- Before render components and pages, the application will trigger
loaderto load the initial data - The
loaderwill trigger theactionto fetch the data from the server - The component will render the data based on the state of the store
- The dispatched action will trigger an
asyncThunkto fetch the data from the server usingmezon-jspackage - The
asyncThunkreturns the data from the server and updates the state of the store by anextraReducersfunction - The component or hook will select the data from the store using
useSelector - the selectors will select the data from the store based on the state of the store
- When user interacts with the component, the component will dispatch the action to update the state of the store
- We could group the data and logic into a custom hook to manage the data and logic of the component
- The component could use the custom hook to manage the data and logic of the component
- in voice context, we add room voice creat function createVoiceConnection.
- when someone join to voice room, voicecontext will send to chat server and trigger onVoiceJoined (notify all) in mezon-js.
- we create a slice in FE to manage state when onVoiceJoined trigger and update number of joined participate in channel.
how to layout the components and pages
We have sevaral layout components to handle layout based on the route:
/-AppLayout: The layout for the application/chat-[logged in]-MainLayout: The layout for the main pageMain: The main page to render the global components/chat/server/:id-ClanLayout: The layout for the server page/chat/server/:id/channel/:id-ChannelLayout: The layout for the channel page- routes are defined in the ./apps/chat/src/app/routes/index.tsx file
- We are using
react-routerv6 to manage the routing of the application, see more about thereact-routerv6 here
Access control is managed by the policies slice. each user has it own permissions to access the resources. The permission is managed by the policies slice.
There are several ways to manage the access control:
- using
policiesslice andselectAllPermissionsUserto get the permissions of the user - using
useSelector(selectAllPermissionsUser)to get the permissions of the user - using
UserRestrictionZoneto control displaying the components based on the user permissions - using
useUserRestrictionto get the user restrictions based on the user permissions
Toast notification is managed by the toasts slice. each toast has it own message and type. The toast is managed by the toasts slice.
Actions
addToast: add a toast to the listremoveToast: remove a toast from the list
Toast are displayed in the <ToastContainer /> component.
There are several ways to manage the toast notification:
- dispatch the
addToastaction to add the toast to the list - dispatch any action with
withToastmeta
// add toast notification to any action
return thunkAPI.fulfillWithValue(
value,
withToast({
message: 'Clan changed',
type: 'success',
})
);
// dispatch the addToast action directly
thunkAPI.dispatch(
addToast({
message: 'Clan changed',
type: 'success',
});
);Error handling is managed by the errors slice. each error has it own message and code. The error is managed by the errors slice.
By default, the error is displayed as toast notification. in case you want to disable the toast notification, you could set the toast meta to false.
// No toast notification
return thunkAPI.rejectWithValue(
error,
withError({
toast: false
})
);
// toast with custom message
return thunkAPI.rejectWithValue(error, withError('Custom error message'));
// fully custom error
return thunkAPI.rejectWithValue(
error,
withError({
toast: {
message: 'Custom error message',
type: 'error',
theme: 'dark'
}
})
);For the desktop application, we are using electron to build the application. The application's dependencies are managed by the apps/desktop/package.json file. When building the application, the dependencies are installed in the apps/desktop/node_modules directory.
The application performance is mostly affected by these factors:
- The routing structure: we keep the routing straitforward and simple, make sure that one route is only re-render when the route changes
- Unnecessary re-render: we use
memoanduseMemoto prevent unnecessary re-render - Memory leak: we use
useEffectandclear functionto prevent memory leak - Function changes reference: we use
useCallbackto prevent function changes reference - Api calls: we use
storeandmemoizeeto cache the api calls - Wrong level of abstraction: we use
custom hookto manage the data and logic of the component, make sure that the custom hook group data of same level of abstraction, and not re-render the component when the unrelated data changes
We use several tools to measure the performance of the application:
React DevTools: to measure the performance of the applicationChrome DevTools: to measure the performance of the application
Using Prettier and ESLint to format the codebase. The codebase should be formatted before committing the code.
PascalCasefor the components and pagescamelCasefor the functions and variables
See more about the naming convention here
- Stick with the architecture
- Do not cause blocks to other team members
- Identify and clarify to the team what and where you are going to add to the repo
- Do self-test for your own codes
- Contact 2 team members to review your changes
- 1, First build and package the app as usual
- 2, Run the app with
./dist/executables/win-unpacked/mezon.exe --remote-debugging-port=8315 - 3, Open Chrome and navigate to
chrome://inspect - 4, Click on
Configure...and addlocalhost:8315to the list - 5, Click on
inspectto open the DevTools