diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a3ac1dc63445e76c34c4ceeef16e91f65bb77154..a774a0e2fd4122c4695e8be3bcc5a3befb52866c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# How to contribute to LyonLivingLabEnergy? +# How to contribute to Ecolyo ? -Thank you for your interest in contributing to this Cozy application! There are many ways to contribute, and we appreciate all of them. +Thank you for your interest in contributing to this application! There are many ways to contribute, and we appreciate all of them. ## Security Issues @@ -18,7 +18,7 @@ Opening an issue is as easy as following [this link][issues] and filling out the - What did you try, step by step? - What did you expect? - What did happen instead? -- What is the version of the LyonLivingLabEnergy app? +- What is the version of the app? ## Pull Requests @@ -40,9 +40,9 @@ Pull requests are the primary mechanism we use to change Cozy. GitHub itself has Fork the project on GitHub and [check out your copy locally][forking]. ``` -$ git clone https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo.git -$ cd ecolyo -$ git remote add fork git://github.com/yourusername/ecolyo.git +git clone https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo.git +cd ecolyo +git remote add fork git://github.com/yourusername/ecolyo.git ``` #### Step 2: Branch @@ -50,7 +50,7 @@ $ git remote add fork git://github.com/yourusername/ecolyo.git Create a branch and start hacking: ``` -$ git checkout -b my-branch origin/master +git checkout -b my-branch origin/master ``` #### Step 3: Code @@ -62,8 +62,7 @@ Well, we think you know how to do that. Just be sure to follow the coding guidel Don't forget to add tests and be sure they are green: ``` -$ cd ecolyo -$ npm run test +yarn test ``` #### Step 5: Commit @@ -77,40 +76,36 @@ Please follow the [Conventional Commits Specification][conventional-commits] to Use `git rebase` (_not_ `git merge`) to sync your work from time to time. ``` -$ git fetch origin -$ git rebase origin/master my-branch +git fetch origin +git rebase origin/master my-branch ``` #### Step 7: Push ``` -$ git push -u fork my-branch +git push -u fork my-branch ``` -Go to https://github.com/yourusername/ecolyo and select your branch. Click the 'Pull Request' button and fill out the form. +Go to <https://github.com/yourusername/ecolyo> and select your branch. Click the 'Pull Request' button and fill out the form. Alternatively, you can use [hub] to open the pull request from your terminal: ``` -$ git pull-request -b master -m "My PR message" -o +git pull-request -b master -m "My PR message" -o ``` Pull requests are usually reviewed within a few days. If there are comments to address, apply your changes in a separate commit and push that to your branch. Post a comment in the pull request afterwards; GitHub doesn't send out notifications when you add commits. -## Writing documentation - -Documentation improvements are very welcome. We try to keep a good documentation in the `/docs` folder. But, you know, we are developers, we can forget to document important stuff that look obvious to us. And documentation can always be improved. - ## Translations -The LyonLivingLabEnergy is translated on a platform called [Transifex][tx]. [This tutorial][tx-start] can help you to learn how to make your first steps here. If you have any question, don't hesitate to ask us! +The Ecolyo is translated on a platform called [Transifex][tx]. [This tutorial][tx-start] can help you to learn how to make your first steps here. If you have any question, don't hesitate to ask us! ## Community You can help us by making our community even more vibrant. For example, you can write a blog post, take some videos, answer the questions on [the forum][forum], organize new meetups, and speak about what you like in Cozy! [conventional-commits]: https://conventionalcommits.org -[issues]: https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/issues/new +[issues]: https://support.grandlyon.com/ecolyo/ [pr]: https://help.github.com/categories/collaborating-with-issues-and-pull-requests/ [forking]: http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html [stdjs]: http://standardjs.com/ diff --git a/README.md b/README.md index 801cb647fb94e972a4af07903b6fbf010ae78946..d7c001cc9d082083d57eb945fe5471638618d406 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,32 @@ ## What's Ecolyo? -Ecolyo is a mobile-first app allowing citizen to visualise easily their energy consumption (electricity, gas, water ...). The app allow simple and more advanced data visualisation -but give also some tips on how to reduce energy consumption. +Ecolyo is a mobile-first app allowing citizens to visualise easily their energy consumption (electricity, gas, water ...). The app allows data visualisation but give also some tips on how to reduce energy consumption. -## Hack +## How does it works ? -_:pushpin: Note:_ we recommend to use [Yarn] instead of NPM for package management. Don't hesitate to [install][yarn-install] and use it for your Cozy projects, it's now our main node packages tool for Cozy official apps. +Ecolyo uses "konnectors" for fetching data. More on : -### Install +- [enedis konnector](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/enedis-sge-konnector) +- [grdf konnector](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/grdf-konnector) +- [egl konnector](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/egl-konnector) -Hacking the Cozy Ecolyo app requires you to [setup a dev environment][setup]. +## Development + +For a more complete look at our project, check our [Self Data Docs](https://doc.self-data.alpha.grandlyon.com/) + +_:pushpin: Note:_ we recommend to use [Yarn] instead of NPM for package management. + +Developing the Cozy Ecolyo app requires you to be familiar with [cozy's documentation](https://docs.cozy.io/en/). You can then clone the app repository and install dependencies: ```sh -$ git clone https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo.git -$ cd ecolyo -$ yarn install +git clone https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo.git +cd ecolyo +yarn ``` -:pushpin: If you use a node environment wrapper like [nvm] or [ndenv], don't forget to set your local node version before doing a `yarn install`. - Cozy's apps use a standard set of _npm scripts_ to run common tasks, like watch, lint, test, build… ### Run it inside a default Cozy using Docker @@ -30,18 +35,17 @@ Cozy's apps use a standard set of _npm scripts_ to run common tasks, like watch, First of all get cozy dev image : ```sh -$ docker pull cozy/cozy-app-dev +docker pull cozy/cozy-app-dev ``` Then you can run your application inside a Cozy thanks to the [cozy-stack docker image][cozy-stack-docker]: ```sh # in a terminal, run your app in watch mode with a docker running Cozy -$ cd ecolyo $ yarn start ``` -After the build and the stack launched, your app is now available at http://ecolyo.cozy.tools:8080. +After the build and the stack launched, your app is now available at <http://ecolyo.cozy.tools:8080>. ### Run it inside custom Docker @@ -52,13 +56,20 @@ For the project we have created our own docker container. For more information o Tests are run by [jest] under the hood. You can easily run the tests suite with: ```sh -$ cd ecolyo -$ yarn test +yarn test ``` :pushpin: Don't forget to update / create new tests when you contribute to code to keep the app the consistent. -## Models +### Open a Pull-Request + +If you want to work on Ecolyo and submit code modifications, feel free to open pull-requests! See the [contributing guide][contribute] for more information about how to properly open pull-requests. + +### Commits + +You must follow these rules to write your commit messages : [Conventional Commits Specification][conventional-commits] + +### Data storage The Cozy datastore stores documents, which can be seen as JSON objects. A `doctype` is simply a declaration of the fields in a given JSON object, to store similar objects in an homogeneous fashion. @@ -69,27 +80,19 @@ Whenever your app needs to use a given `doctype`, you should: - Check if this is a standard `doctype` defined in Cozy itself. If this is the case, you should add a model declaration in your app containing at least the fields listed in the [main fields list for this `doctype`][doctypes]. Note that you can extend the Cozy-provided `doctype` with your own customs fields. This is typically what is done in [Konnectors] for the [Bill `doctype`][bill-doctype]. - If no standards `doctypes` fit your needs, you should define your own `doctype` in your app. In this case, you do not have to put any field you want in your model, but you should crosscheck other cozy apps to try to homogeneize the names of your fields, so that your `doctype` data could be reused by other apps. This is typically the case for the [Konnector `doctype`][konnector-doctype] in [Konnectors]. -### Open a Pull-Request - -If you want to work on Ecolyo and submit code modifications, feel free to open pull-requests! See the [contributing guide][contribute] for more information about how to properly open pull-requests. - -### Commits - -You must follow these rules to write your commit messages : [Conventional Commits Specification][conventional-commits] - ## Community -### What's Cozy? - -<div align="center"> - <a href="https://cozy.io"> - <img src="https://cdn.rawgit.com/cozy/cozy-site/master/src/images/cozy-logo-name-horizontal-blue.svg" alt="cozy" height="48" /> - </a> - </div> - </br> +### What's Cozy?  [Cozy] is a platform that brings all your web services in the same private space. With it, your webapps and your devices can share data easily, providing you with a new experience. You can install Cozy on your own hardware where no one's tracking you. +You can reach the Cozy Community by: + +- Chatting with us on IRC [#cozycloud on Freenode][freenode] +- Posting on our [Forum][forum] +- Posting issues on the [Github repos][github] +- Say Hi! on [Twitter][twitter] + ### Localization Localization and translations are handled by [Transifex][tx], which is used by all Cozy's apps. @@ -100,29 +103,15 @@ As a _developer_, you must [configure the transifex client][tx-client], and clai ### Maintainer -The lead maintainer for Ecolyo is [hsubtil](https://forge.grandlyon.com/ext.sopra.husubtil), send him/her a :beers: to say hello! - -### Get in touch - -You can reach the Cozy Community by: - -- Chatting with us on IRC [#cozycloud on Freenode][freenode] -- Posting on our [Forum][forum] -- Posting issues on the [Github repos][github] -- Say Hi! on [Twitter][twitter] +Ecolyo is maintained by the [factory team](https://forge.grandlyon.com/groups/web-et-numerique/factory/-/group_members) feel free to get in touch ! ## License Ecolyo is developed by "La métropole de Lyon" and distributed under the [AGPL v3 license][agpl-3.0]. [conventional-commits]: https://conventionalcommits.org -[standard-version]: https://github.com/conventional-changelog/standard-version [cozy]: https://cozy.io 'Cozy Cloud' -[setup]: https://dev.cozy.io/#set-up-the-development-environment 'Cozy dev docs: Set up the Development Environment' [yarn]: https://yarnpkg.com/ -[yarn-install]: https://yarnpkg.com/en/docs/install -[cozy-ui]: https://github.com/cozy/cozy-ui -[cozy-client-js]: https://github.com/cozy/cozy-client-js/ [cozy-stack-docker]: https://github.com/cozy/cozy-stack/blob/master/docs/client-app-dev.md#with-docker [doctypes]: https://cozy.github.io/cozy-doctypes/ [bill-doctype]: https://github.com/cozy/cozy-konnector-libs/blob/master/models/bill.js @@ -138,6 +127,4 @@ Ecolyo is developed by "La métropole de Lyon" and distributed under the [AGPL v [forum]: https://forum.cozy.io/ [github]: https://github.com/cozy/ [twitter]: https://twitter.com/cozycloud -[nvm]: https://github.com/creationix/nvm -[ndenv]: https://github.com/riywo/ndenv [jest]: https://facebook.github.io/jest/ diff --git a/docs/code_review.md b/docs/code_review.md deleted file mode 100644 index 458b858ac4750b1dd07179042eec454e11c5de3e..0000000000000000000000000000000000000000 --- a/docs/code_review.md +++ /dev/null @@ -1,244 +0,0 @@ -Code Review -=========== - -Note : les points redondants entre différents fichiers ne sont pas forcément ré-expliqués. - -## App.jsx - -Redirection redondante dans la configuration du routeur : - -```jsx -<Redirect from="/" to="/home" /> -<Redirect from="*" to="/home" /> -``` - -## CardsContainer.tsx - -### Usage non recommandé de `any` : - -```tsx - const [elecCardProps, setElecCardProps] = useState<any>({ - consumptionValue: null, - weeklyCompareValue: null, - }) -``` - -Il vaut mieux définir un type commun dans `fluidService.ts` par exemple : - -```tsx -// fluidService.ts -export type ProcessedData = { - consumptionValue: number; - weeklyCompareValue: number; - euclideanComparator: string; -} - -// Card.tsx -export interface CardProps extends ProcessedData { - type: FluidType - t: any -} - -// CardsContainer.tsx -const [elecCardProps, setElecCardProps] = useState<ProcessedData|null>(null) - -// Dans le render : -{elecCardProps && <Card - type={FluidType.ELECTRICITY} - {...elecCardProps} -/>} -``` - -### Instanciation des services - -Il vaut mieux instancier les services dans les effets où on les utilise, pour éviter d'en instancier un de nouveau à chaque render, alors que l'effet l'utilisant n'est exécuté qu'une seule fois, au premier render, via le tableau de dépendances vide `[]` : - -```tsx -useEffect(() => { - const fluidService = new FluidService(client) - // ... -}, []) -``` - -### `async/await` dans un effet - -Un effet `async` n'est pas évident, car pendant qu'on attend un retour, il se peut que le composant soit re-render entre-temps, voire détruit. - -Du coup on aurait une `Promise` résolue, et les `set...CardProps` appelés alors que l'élément n'existe plus, généreront une erreur. - -Pour éviter cela il faut rajouter un flag mis à jour lors du unsubscribe de l'effet : - -```tsx -useEffect(() => { - const fluidService = new FluidService(client) - let subscribed = true - - async function getData() { - const elecData = await fluidService.getElectricityLastTwoWeeks() - const gasData = await fluidService.getGasLastTwoWeeks() - const waterData = await fluidService.getWaterLastTwoWeeks() - - if (subscribed) { - setElecCardProps(fluidService.processElectricityData(elecData.data)) - setGasCardProps(fluidService.processGasData(gasData.data)) - setWaterCardProps(fluidService.processWaterData(waterData.data)) - setIsLoading(false) - } - } - - getData() - - // Fonction unsubscribe, appelée avant le prochain render ou avant la destruction. - return () => { - subscribed = false - } -}, []) -``` - -### Multiples `await` - -Dernière chose pour optimiser les appels : quand on chaine des await, ils sont exécutés en séquence. - -Pour les appeler en parallèle, on peut utiliser `Promise.all()` couplé à de la deconstruction. - -Avant : - -```tsx -const elecData = await fluidService.getElectricityLastTwoWeeks() -const gasData = await fluidService.getGasLastTwoWeeks() -const waterData = await fluidService.getWaterLastTwoWeeks() -``` - -Après : - -```tsx -const [elecData, gasData, waterData] = await Promise.all([ - fluidService.getElectricityLastTwoWeeks(), - fluidService.getGasLastTwoWeeks(), - fluidService.getWaterLastTwoWeeks(), -]) -``` - -## KonnectorViewerList.tsx - -### Déclaration des fonctions utilisées par un effect - -De la même façon que les services doivent être instanciés dans l'effect, il vaut mieux déclarer les fonctions utilisées par un effect dans celui-ci, pour éviter de définir une nouvelle référence à chaque render. C'est le cas ici des fonctions `setAllKonnectors` et `setAllTriggers` : - -*Noter l'utilisation d'un flag subscribed comme expliqué précédemment.* - -```tsx - useEffect(() => { - let subscribed = true - - const setAllKonnectors = async () => { - const kss = new KonnectorStatusService(client) - const allKonnectors = await kss.getAllKonnectors() - - if (subscribed) { - setKonnectors(allKonnectors) - } - } - - const setAllTriggers = async () => { - const kss = new KonnectorStatusService(client) - const allTriggers = await kss.getAllTriggers() - - if (subscribed) { - setTriggers(allTriggers) - } - } - - setAllKonnectors() - setAllTriggers() - - return () => { - subscribed = false - } - }, []) -``` - -De même, la fonction `fetchKonnectorFromTrigger` pourrait être déplacée en dehors composant pour éviter de créer une nouvelle référence à chaque render. - -### Valeur utilisée pour la `key` lors d'un `.map()`` - -Idéalement, la prop `key` doit avoir une valeur associée à l'item courant, plutôt que son index dans le tableau. En effet le tableau pourrait changer, et un index pourrait pointer vers un autre item que celui du render précédent. - -La conséquence serait que le même élément serait utilisé pour render le nouvel item, sans qu'il soit détruit, générant potentiellement des effets de bords non souhaités (certains effect pourraient ne pas être exécutés par exemple). - -Pour éviter ça, on peut utiliser en `key` une propriété unique de l'item s'il y en a une : - -```tsx -<KonnectorViewer - key={item.id} - konnectorConfig={item} - trigger={getLastTrigger(item)} -/> -``` - -## KonnectorConfigForm.tsx - -### Ref - -Le hook `useRef` peut être utilisé pour stocker n'importe quelle valeur, pour s'en servir pour un node HTML, on peut le déclarer comme suit : - -Avant : - -```tsx -const content: React.MutableRefObject<null> = useRef<null>(null) -``` - -Après : - -```tsx -const content = useRef<HTMLDivElement>(null) -``` - -### Callbacks - -Dans le formulaire, on peut directement passer au `onSubmit` la callback comme la signature est identique : - -Avant : - -```tsx -onSubmit={(e: React.FormEvent<HTMLFormElement>) => handleSubmit(e)} -``` - -Après : - -```tsx -onSubmit={handleSubmit} -``` - -De plus, il est préférable quand on fourni des fonctions à des composants React (mais pas nécessaire lors des simples balises HTML) de mémoizer cette fonction pour fournir toujours la même fonction entre les renders. - -On peut faire ceci via le hook `useCallback`, et en refactorisant le stockage du couple login/password, on peut faire ceci : - -*Noter le tableau de dépendances, comme pour useEffect et useMemo, qui permet de préciser quand la référence de la fonction peut changer* - -```tsx -const [{login, password}, setCredentials] = useState({ - login: '', - password: '', -}) - -const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { - // Il nous faut utiliser cette syntaxe, avec une fonction, pour récupérer la valeur précédente. - // On ne peut utiliser les valeurs déconstruites plus haut car elles pourraient ne pas être à jour, React entassant une pile de callbacks avant de toutes les exécuter) - // Il faut aussi extraire les valeurs avant car la fonction est asynchrone, et l'event peut avoir été détruit entretemps - const {name, value} = e.target - - setCredentials(credentials => { - return { - ...credentials, - [name]: value, - } - }) -}, []) - -<Field - onChange={handleChange} - name="login" - value={login} -/> -```