You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
recodex-wiki/Web-application.md

156 lines
16 KiB
Markdown

8 years ago
# Web application
## Description
8 years ago
The *WebApp* is the default user interface of the ReCodEx project. It is a Single Page Application (SPA) which users access through their web browsers.
This user interface allows students to join *groups* corresponding to their school classes, submit new *solutions* and see the results as soon as the solution is evaluated by the backend.
Teachers can manage their *groups* by adding and removing *students*, assigning *exercises* and examining the *automatically evaluated solutions* of their students and their total score gained throughout the semester.
The actions performed by the user are sent to the *API server* over the HTTPS protocol and the web application does not modify the database or access files on their server directly and all the inputs sent
from the web application are additionally *validated* on the *API server*.
8 years ago
The *WebApp* is written in *ECMAScript* (often referred to as JavaScript) and HTML5. The [React](https://facebook.github.io/react) library developed by Facebook is used to manage the user interface and the [Redux](http://redux.js.org/) framework
created by Dan Abramov is used to manage the application state. Both of these frameworks are industry standards used in production by many companies worldwide. Many other smaller libraries and components are used, notably the [Immutable.js](https://facebook.github.io/immutable-js) and [Format.js](http://formatjs.io/) by Yahoo via *redact-intl* integration library.
### Multiplatform approach
Since the runtime environment of *WebApp* is the user's web browser it does not need any installation on the enduser's device and does not require any particular platform or non-standard software installed on the user's device.
This application is designed and implemented in a way which should be suitable for modern web browsers on both desktop and mobile devices.
8 years ago
## Architecture
8 years ago
The whole project is written using the next generation of JavaScript referred to as *ECMAScript 6* (also known as *ES6*, *ES.next*, or *Harmony*). Since not all the features introduced in this standard are implemented in today's modern web browsers (like classes and spread operators) and hardly any are implemented in older versions of web browsers which are currently still in use, the source code is transpiled into the older standard *ES5* using [Babel.js](https://babeljs.io/) transpiler and bundled into a single script file using the [webpack](https://webpack.github.io/) moudle bundler. The need for a transpiler also arises from the usage of the *JSX* syntax for declaring React components. To read more about these these tools and their usage please refer to the [installation section](#Installation). The whole bundling process takes place at deployment and is not repeated afterwards.
### Routing and server side rendering
The routing mechanism uses the [react-router](https://github.com/ReactTraining/react-router) and [react-router-redux](https://github.com/reactjs/react-router-redux) libraries. The routing mechanism works both on the server and the client.
The routes are defined in the `/src/pages/routes.js` file.
### Server rendering and the 'loadAsync' convention
The server matches the URL of the request with the router and pre-renders the contents of the page. The components, which are selected by the router based on the URL, are given the parameters and are attached to the component tree.
The server script examines all the components matched with the route and looks for a static function called `loadAsync` and if it is found, it will be executed with the params extracted from the URL, the store's dispatch method and the ID of the logged in user if he is logged in. It is expected that this function will return an instance of `Promise`. Server then waits until all the promises are resolved and only after then it goes on and sends a HTTP response to the client.
### Client
Each time the user clicks on a link to a different location of the application the page is not reloaded but the router rather changes the location of the browser to the new URL and replaces the components in the component tree with the ones matched against the new URL the same way the server did. Components should call the `loadAsync` methods themselves in their lifecycle methods.
### Redux middleware and helpers
The redux [store](https://github.com/ReCodEx/web-app/blob/master/src/redux/store.js) is extended with several middlewares - [redux-thunk](https://github.com/gaearon/redux-thunk), [redux-promise-middleware](https://github.com/pburtchaell/redux-promise-middleware), router middleware, accessTokenMiddleware, and apiMiddleware.
#### apiMiddleware
8 years ago
The API middleware catches all the `CALL_API` actions and turns them into a HTTP request via the [fetch API](https://developer.mozilla.org/en/docs/Web/API/Fetch_API). The middleware deeply converts data in the *body* part of the request to `FormData` and sets correctly the headers and creates endpoints for the API server before each request is sent. The middleware also handle the responses - converts them to the format expected by the reducers and catches requests' failures.
#### accessTokenMiddleware
[Access token middleware](https://github.com/ReCodEx/web-app/blob/master/src/redux/middleware/accessTokenMiddleware.js) stores and keeps the access token once the user authenticates and receives a token from the API server. The middleware listens for dispatched API actions and inserts the token to the request automatically.
#### Modules
Modules (sometimes called "ducks") contain a reducer and related action types and action creators. There are many separate reducers and actions for each individual resource and task to make the store tree as flat as possible. Vast majority of the modules is derived from the *resource manager* and are very similar to each other. Modules are placed in the `/src/redux/modules` directory.
#### Selectors
Selectors are used from the *containers* to return a specific information from the global redux state. It would be very inconvenient if the containers needed to know the whole structure of the state, so they can rely on the functions provided by the selectors for each module. The [reselect](https://github.com/reactjs/reselect) library is used for creating efficient selectors. Selectors are placed in the `/src/redux/selectors` directory.
#### Resource manager
The [resource manager](https://github.com/ReCodEx/web-app/blob/master/src/redux/helpers/resourceManager/index.js) is a set of factory functions which was created to extract the repeating functionality of obtaining and managing the loading phases of a resource or multiple resources at once.
The factory methods create the reducer, action types, and action creators. It strongly relies on the *redux-promise-middleware*. It was inspired by the [Reddit API example](http://redux.js.org/docs/advanced/ExampleRedditAPI.html) from the Redux library official website.
Resource manager is placed in the `/src/redux/helpers/resourceManager` directory.
### Declarative UI components
8 years ago
The implementation of *WebApp* is split across more than a hundred small components which are composed together. There are two basic types of React components - presentational and stateful components.
8 years ago
- **Presentational components** are in fact pure functions which yield their output based solely on the input *props* and application's *context* (e. g. current language has effect on URLs). These components are preferred and should be as simple as possible. These components are placed in the subfolders of `src/components` directory.
8 years ago
- **Stateful components** are *connected* to the *redux store* and yield information based on the either the *props* and *context*, but also on the current state of the application. These components are more complicated and often make use of React's components' life cycle methods. *Stateful components* are often referred to as *containers* and the source code files are placed in a separate directories called `src/containers` and `src/pages`.
8 years ago
- There is no technical difference between *containers* and *pages*. *Pages* are used as "root" components for different pages and are registered in the app's [router](https://github.com/ReactTraining/react-router).
As was mentioned earlier in the text, there is over a hundred components in the whole application and most of them are very simple. Some of the most important and widely used components are described in the following paragraphs.
#### App
[App](https://github.com/ReCodEx/web-app/blob/master/src/containers/App/App.js) container is meant to be the root of the *react-router* tree. It checks whether a user is logged in and loads his profile and settings from the API. App container also handles access token refreshing.
#### LayoutContainer
[LayoutContainer](https://github.com/ReCodEx/web-app/blob/master/src/containers/LayoutContainer/LayoutContainer.js) handles the dependency injection of the localized links based on the current language stated in the URL. It also controls the state of the sidebar - collapsing and showing the sidebar.
##### Layout, Header, Sidebar, Footer
The [Layout](https://github.com/ReCodEx/web-app/blob/master/src/components/Layout/Layout.js), [Header](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Header/Header.js), [Header](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Footer/Footer.js), and [Sidebar](https://github.com/ReCodEx/web-app/blob/master/src/components/Sidebar/Sidebar/Sidebar.js) components hold the main HTML structure of the page which is displayed to the user and hold links to different parts of the application.
#### PageContent
The [PageContent](https://github.com/ReCodEx/web-app/blob/master/src/components/PageContent/PageContent.js) component holds the main content of a page with the common structure for all pages - the *title*, *description*, *[breadcrumbs](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Breadcrumbs/Breadcrumbs.js),*, *content*. The component passes the title and description to the [Helmet](https://github.com/nfl/react-helmet) library which reflects these into the `<head>` section of the HTML document.
#### ResourceRenderer
[ResourceRenderer](https://github.com/ReCodEx/web-app/blob/master/src/components/ResourceRenderer/ResourceRenderer.js) component is given a *resource* managed by the *resourceManager* as a *prop* and displays different content based on the state of the given *resource* - still loading, loading failed, fully loaded.
Passing content for the *loading* and *failed* states though *props* is optional; however, the content for the *loaded* state is required and must be passed as a child to the `ResourceManager`.
Multiple resources can be passed as an array to the component and it will wait in the *loading* state until some of the resources are still loading. If one of the resources fails to load the component will switch to the *failed* state. When all the files are fully loaded then the component displays the content for the *loaded* state.
It is worth mentioning that the component is completely stateless and "switching of the states" is the effect of several renderings of the component over time.
#### Page
[Page](https://github.com/ReCodEx/web-app/blob/master/src/components/Page/Page.js) combines the two previously described components, since they are often used together for displaying a page content which is dependant on a resource.
#### Pages
The main pages of the application are placed in the `/src/pages/` directory. These pages are referenced from *the router* and are responsible mainly for initiating loading of the main resources when the component will be mounted to the DOM.
#### EvaluationProgressContainer
#### CommentThreadContainer
[CommentThreadContainer](https://github.com/ReCodEx/web-app/blob/master/src/containers/CommentThreadContainer/CommentThreadContainer.js) can be used on any page of the *webapp* to create a discussion thread. A unique identification must be assigned to every thread so the comments in the distinct threads do not interleave.
8 years ago
#### Bootstrap and AdminLTE theme
8 years ago
The UI of the application is built using the stylesheets from the [Bootstrap framework](http://getbootstrap.com/) from Twitter and the [AdminLTE](https://almsaeedstudio.com/) theme from Abdullah Almsaeed. None of the JavaScript plugins from these libraries is used.
A package [react-bootstrap](https://react-bootstrap.github.io/) is used. Components specific to the *AdminLTE* theme are implemented and stored in the `src/components/AdminLTE` folder and its subfolders.
##### Box, FormBox
[Box](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Box/Box.js) is a frequently used component for bounding other components like text paragraphs or tables inside. It is in fact a re-styled `Panel` component from Bootstrap. It can be collapsable and can be displayed in different colors and types. [FormBox](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/FormBox/FormBox.js) is just a wrapper of `Box` adjusted for holding forms inside of the body.
##### CommentThread
[CommentThread](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/CommentThread/CommentThread.js) component is used directly by the earlier described `CommentThreadContainer` and uses the HTML markup introduced by the theme author to achieve nice comment threads.
##### Avatar, LoadingAvatar, FailedAvatar
[Avatar](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Avatar/Avatar.js) component is used on many places for displaying a round profile picture of a user. `LoadingAvatar` and `FailedAvatar` are used to mock the visual appearance of the avatar while the image is being downloaded or if the download failed for some reason.
#### Icons
The free [FontAwesome](http://fontawesome.io/) icon pack is used in the application via the [react-fontawesome](https://github.com/danawoodman/react-fontawesome) library. Many types of [icons](https://github.com/ReCodEx/web-app/blob/master/src/components/AdminLTE/Header/Header.js) are defined as components (e. g. `LoadingIcon`, `WarningIcon`, `SuccessIcon`, `DeleteIcon`, and many others) to be used throughout the application so the same symbols are used for the same purposes.
8 years ago
#### Forms
The [redux-form](http://redux-form.com/) library is used for the management of forms' states. Several of the implemented components can be used as form fields, namely the [CheckboxField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/CheckboxField.js), [MarkdownTextAreaField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/MarkdownTextAreaField.js), [SourceCodeField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/SourceCodeField.js), [DatetimeField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/DatetimeField.js), [TabbedArrayField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/TabbedArrayField.js), [LanguageSelectField](https://github.com/ReCodEx/web-app/blob/master/src/components/Forms/Fields/LanguageSelectField.js), and a few others.
### Localization and globalization
The whole application is prepared for localization. All the text can be exported from the user interface and translated into several languages. The numbers, dates, and time values are also formatted with respect to the selected language. The [react-intl](https://github.com/yahoo/react-intl) and [Moment.js](http://momentjs.com/) libraries are used to achieve this.
All the strings can be extracted from the application using a command:
```
$ npm run exportStrings
```
This will create *JSON* files with the exported strings for the *'en'* and *'cs'* locale. If you want to export strings for more languages, you must edit the `/manageTranslations.js` script. The exported strings are placed in the `/src/locales` directory.