From ecefc52e9be114f1a7c562d025514ae9bb44f421 Mon Sep 17 00:00:00 2001 From: Arnaud Ambroselli <31724752+arnaudambro@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:44:24 +0200 Subject: [PATCH] =?UTF-8?q?fix(app):=20refraichit=20les=20donn=C3=A9es=20?= =?UTF-8?q?=C3=A0=20chaque=20foit=20que=20les=20actions/personnes/territoi?= =?UTF-8?q?res,=20ou=20qu'une=20personne,=20reviennent=20=C3=A0=20l'=C3=A9?= =?UTF-8?q?cran=20(#1638)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(app): refraichit les données à chaque foit que les actions/personnes/territoires, ou qu'une personne, reviennent à l'écran * update numbers and phrase --- README.md | 2 +- api/package.json | 2 +- api/src/controllers/utils.js | 4 +- app/android/app/build.gradle | 4 +- app/app.json | 4 +- app/package.json | 2 +- app/src/components/Loader.js | 52 +++++++++---------- app/src/scenes/Actions/ActionsList.js | 9 +++- app/src/scenes/Persons/Person.js | 14 +++-- app/src/scenes/Persons/PersonsList.js | 7 ++- app/src/scenes/Territories/TerritoriesList.js | 8 ++- website/package.json | 2 +- 12 files changed, 63 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 7248cc138..6b32e4aa0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Mano -![Mobile version](https://img.shields.io/badge/mobile%20app%20version-2.36.0-blue) +![Mobile version](https://img.shields.io/badge/mobile%20app%20version-2.36.1-blue) [![Maintainability](https://api.codeclimate.com/v1/badges/223e4185a3e13f1ef5d0/maintainability)](https://codeclimate.com/github/SocialGouv/mano/maintainability) Code source de [Mano](https://mano-app.fabrique.social.gouv.fr/), organisé en plusieurs services : diff --git a/api/package.json b/api/package.json index 48b6722c5..01cb50651 100644 --- a/api/package.json +++ b/api/package.json @@ -1,7 +1,7 @@ { "name": "api_mano", - "mobileAppVersion": "2.36.0", "version": "1.283.0", + "mobileAppVersion": "2.36.1", "description": "", "main": "index.js", "scripts": { diff --git a/api/src/controllers/utils.js b/api/src/controllers/utils.js index e6c04e631..3748bbec6 100644 --- a/api/src/controllers/utils.js +++ b/api/src/controllers/utils.js @@ -29,8 +29,8 @@ router.get("/version", async (req, res) => { `La nouvelle version ${MOBILE_APP_VERSION} de Mano est disponible !`, `Vous avez la version ${req.headers.version} actuellement sur votre téléphone. Cette nouvelle version : -- Permet la compatibilité avec la nouvelle organisation des dossiers sur le navigateur -- Affiche le nome de la personne concernée dans les commentaires d'actions des comptes-rendus" +- Rafraichit les données automatiquement plus souvent pour une meilleure cohérence des données entre différents utilisateurs simultanés +- Accélère le chargement initial des données `, [ { text: "Télécharger", link: `https://mano-app.fabrique.social.gouv.fr/download?ts=${Date.now()}` }, diff --git a/app/android/app/build.gradle b/app/android/app/build.gradle index 97c65ed22..1e71e0ed9 100644 --- a/app/android/app/build.gradle +++ b/app/android/app/build.gradle @@ -120,8 +120,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled true - versionCode 14 - versionName "2.36.0" + versionCode 15 + versionName "2.36.1" testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } diff --git a/app/app.json b/app/app.json index 364c6d96f..46c3ace30 100644 --- a/app/app.json +++ b/app/app.json @@ -2,8 +2,8 @@ "name": "mano", "displayName": "Mano", "version": { - "buildNumber": 14, - "buildName": "2.36.0" + "buildNumber": 15, + "buildName": "2.36.1" }, "bundle": { "android": "com.mano", diff --git a/app/package.json b/app/package.json index 53ce12cc3..9d127e163 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "mano", - "version": "2.36.0", + "version": "2.36.1", "private": true, "scripts": { "get-ip": "./get-ip.sh", diff --git a/app/src/components/Loader.js b/app/src/components/Loader.js index f2fb195b0..7a9977c6e 100644 --- a/app/src/components/Loader.js +++ b/app/src/components/Loader.js @@ -23,7 +23,6 @@ import { reportsState } from '../recoil/reports'; import { consultationsState, formatConsultation } from '../recoil/consultations'; import { medicalFileState } from '../recoil/medicalFiles'; import { treatmentsState } from '../recoil/treatments'; -import { sortByName } from '../utils/sortByName'; import { rencontresState } from '../recoil/rencontres'; import { passagesState } from '../recoil/passages'; import { groupsState } from '../recoil/groups'; @@ -69,7 +68,7 @@ export const DataLoader = () => { const setProgress = useSetRecoilState(progressState); const setFullScreen = useSetRecoilState(loaderFullScreenState); const [organisation, setOrganisation] = useRecoilState(organisationState); - const setUser = useSetRecoilState(userState); + const [user, setUser] = useRecoilState(userState); const organisationId = organisation?._id; const [persons, setPersons] = useRecoilState(personsState); @@ -93,6 +92,7 @@ export const DataLoader = () => { const refresh = async () => { const { showFullScreen, initialLoad } = refreshTrigger.options; + setLoading('Chargement...'); setFullScreen(showFullScreen); @@ -101,18 +101,29 @@ export const DataLoader = () => { and the latest user roles */ const userResponse = await API.get({ path: '/user/me' }); - setOrganisation(userResponse.user.organisation); - setUser(userResponse.user); + if (userResponse.ok) { + if (JSON.stringify(userResponse.user.organisation) !== JSON.stringify(organisation)) { + setOrganisation(userResponse.user.organisation); + } + if (JSON.stringify(userResponse.user) !== JSON.stringify(user)) { + setUser(userResponse.user); + } + } const serverDateResponse = await API.get({ path: '/now' }); const serverDate = serverDateResponse.data; - /* Get number of data to download to show the appropriate loading progress bar */ const response = await API.get({ path: '/organisation/stats', - query: { organisation: organisationId, after: lastRefresh, withDeleted: true }, + query: { + organisation: organisationId, + after: lastRefresh, + withDeleted: true, + // Medical data is never saved in cache so we always have to download all at every page reload. + withAllMedicalData: initialLoad, + }, }); if (!response.ok) { capture('error getting stats', { extra: response }); @@ -130,33 +141,18 @@ export const DataLoader = () => { response.data.territoryObservations + response.data.places + response.data.comments + + response.data.consultations + + response.data.treatments + + response.data.medicalFiles + response.data.passages + response.data.rencontres + response.data.reports + response.data.groups + response.data.relsPersonPlace; - // medical data is never saved in cache - // so we always have to download all at every page reload - const medicalDataResponse = await API.get({ - path: '/organisation/stats', - query: { organisation: organisationId, after: initialLoad ? 0 : lastRefresh, withDeleted: true }, - }); - if (!medicalDataResponse.ok) { - setRefreshTrigger({ - status: false, - options: { showFullScreen: false, initialLoad: false }, - }); - return; - } - - total = total + medicalDataResponse.data.consultations + medicalDataResponse.data.treatments + medicalDataResponse.data.medicalFiles; - if (initialLoad) { - const numberOfCollections = 9; - total = total + numberOfCollections; // for the progress bar to be beautiful + total = total + Object.keys(response.data).length; // for the progress bar to be beautiful } - /* Get persons */ @@ -191,7 +187,7 @@ export const DataLoader = () => { /* Get consultations */ - if (medicalDataResponse.data.consultations || initialLoad) { + if (response.data.consultations || initialLoad) { setLoading('Chargement des consultations'); const refreshedConsultations = await getData({ collectionName: 'consultation', @@ -206,7 +202,7 @@ export const DataLoader = () => { /* Get treatments */ - if (medicalDataResponse.data.treatments || initialLoad) { + if (response.data.treatments || initialLoad) { setLoading('Chargement des traitements'); const refreshedTreatments = await getData({ collectionName: 'treatment', @@ -221,7 +217,7 @@ export const DataLoader = () => { /* Get medicalFiles */ - if (medicalDataResponse.data.medicalFiles || initialLoad) { + if (response.data.medicalFiles || initialLoad) { setLoading('Chargement des informations médicales'); const refreshedMedicalFiles = await getData({ collectionName: 'medical-file', diff --git a/app/src/scenes/Actions/ActionsList.js b/app/src/scenes/Actions/ActionsList.js index 0dea8d51d..1c59ed962 100644 --- a/app/src/scenes/Actions/ActionsList.js +++ b/app/src/scenes/Actions/ActionsList.js @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import * as Sentry from '@sentry/react-native'; import { useRecoilState, useRecoilValue } from 'recoil'; @@ -11,7 +11,7 @@ import { MyText } from '../../components/MyText'; import { FlashListStyled } from '../../components/Lists'; import { TODO } from '../../recoil/actions'; import { actionsByStatusSelector, totalActionsByStatusSelector } from '../../recoil/selectors'; -import { useNavigation, useRoute } from '@react-navigation/native'; +import { useIsFocused, useNavigation, useRoute } from '@react-navigation/native'; import { refreshTriggerState, loadingState } from '../../components/Loader'; import Button from '../../components/Button'; import ConsultationRow from '../../components/ConsultationRow'; @@ -40,6 +40,11 @@ const ActionsList = ({ showActionSheetWithOptions }) => { setRefreshTrigger({ status: true, options: { showFullScreen: false, initialLoad: false } }); }, [setRefreshTrigger]); + const isFocused = useIsFocused(); + useEffect(() => { + if (isFocused && refreshTrigger.status !== true) onRefresh(); + }, [isFocused]); + const onPressFloatingButton = async () => { if (!user.healthcareProfessional) { navigation.navigate('NewActionForm', { fromRoute: 'ActionsList' }); diff --git a/app/src/scenes/Persons/Person.js b/app/src/scenes/Persons/Person.js index 8b2f91aaf..1651916fa 100644 --- a/app/src/scenes/Persons/Person.js +++ b/app/src/scenes/Persons/Person.js @@ -9,7 +9,7 @@ import ScreenTitle from '../../components/ScreenTitle'; import FoldersNavigator from './FoldersNavigator'; import Tabs from '../../components/Tabs'; import colors from '../../utils/colors'; -import { useFocusEffect } from '@react-navigation/native'; +import { useFocusEffect, useIsFocused } from '@react-navigation/native'; import { allowedFieldsInHistorySelector, personsState, @@ -40,7 +40,7 @@ const Person = ({ route, navigation }) => { const flattenedCustomFieldsPersons = useRecoilValue(flattenedCustomFieldsPersonsSelector); const allowedFieldsInHistory = useRecoilValue(allowedFieldsInHistorySelector); const preparePersonForEncryption = usePreparePersonForEncryption(); - const setRefreshTrigger = useSetRecoilState(refreshTriggerState); + const [refreshTrigger, setRefreshTrigger] = useRecoilState(refreshTriggerState); const [persons, setPersons] = useRecoilState(personsState); const actions = useRecoilValue(actionsState); const groups = useRecoilValue(groupsState); @@ -53,7 +53,14 @@ const Person = ({ route, navigation }) => { const relsPersonPlace = useRecoilValue(relsPersonPlaceState); const user = useRecoilValue(userState); - const [personDB, setPersonDB] = useState(() => persons.find((p) => p._id === route.params?.person?._id)); + const personDB = useMemo(() => persons.find((p) => p._id === route.params?.person?._id), [persons, route.params?.person?._id]); + + const isFocused = useIsFocused(); + useEffect(() => { + if (isFocused && refreshTrigger.status !== true) { + setRefreshTrigger({ status: true, options: { showFullScreen: false, initialLoad: false } }); + } + }, [isFocused]); const castToPerson = useCallback( (person = {}) => { @@ -155,7 +162,6 @@ const Person = ({ route, navigation }) => { }) ); setPerson(castToPerson(newPerson)); - setPersonDB(newPerson); if (alert) Alert.alert('Personne mise à jour !'); setUpdating(false); setEditable(false); diff --git a/app/src/scenes/Persons/PersonsList.js b/app/src/scenes/Persons/PersonsList.js index 48edac1a2..99617e0d3 100644 --- a/app/src/scenes/Persons/PersonsList.js +++ b/app/src/scenes/Persons/PersonsList.js @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Animated } from 'react-native'; import * as Sentry from '@sentry/react-native'; import SceneContainer from '../../components/SceneContainer'; @@ -13,6 +13,7 @@ import { arrayOfitemsGroupedByPersonSelector, itemsGroupedByPersonSelector } fro import { selector, selectorFamily, useRecoilState, useRecoilValue } from 'recoil'; import { loadingState, refreshTriggerState } from '../../components/Loader'; import { filterBySearch } from '../../utils/search'; +import { useIsFocused } from '@react-navigation/native'; const personsFilteredSelector = selectorFamily({ key: 'personsFilteredSelector', @@ -80,6 +81,10 @@ const PersonsList = ({ navigation, route }) => { setRefreshTrigger({ status: true, options: { showFullScreen: false, initialLoad: false } }); }; + const isFocused = useIsFocused(); + useEffect(() => { + if (isFocused && refreshTrigger.status !== true) onRefresh(); + }, [isFocused]); const onCreatePersonRequest = () => navigation.navigate('NewPersonForm', { toRoute: 'Person' }); const onFiltersPress = () => navigation.push('PersonsFilter', route.params); diff --git a/app/src/scenes/Territories/TerritoriesList.js b/app/src/scenes/Territories/TerritoriesList.js index 7d341fca1..ff5fc7787 100644 --- a/app/src/scenes/Territories/TerritoriesList.js +++ b/app/src/scenes/Territories/TerritoriesList.js @@ -1,4 +1,4 @@ -import React, { useMemo, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { Animated } from 'react-native'; import SceneContainer from '../../components/SceneContainer'; import ScreenTitle from '../../components/ScreenTitle'; @@ -11,7 +11,7 @@ import Row from '../../components/Row'; import { TerritoryIcon } from '../../icons'; import { useRecoilState, useRecoilValue } from 'recoil'; import { territoriesSearchSelector } from '../../recoil/selectors'; -import { useNavigation } from '@react-navigation/native'; +import { useIsFocused, useNavigation } from '@react-navigation/native'; import { refreshTriggerState, loadingState } from '../../components/Loader'; const TerritoriesList = () => { @@ -26,6 +26,10 @@ const TerritoriesList = () => { const onRefresh = async () => { setRefreshTrigger({ status: true, options: { showFullScreen: false, initialLoad: false } }); }; + const isFocused = useIsFocused(); + useEffect(() => { + if (isFocused && refreshTrigger.status !== true) onRefresh(); + }, [isFocused]); const onCreateTerritoryRequest = () => navigation.navigate('NewTerritoryForm'); diff --git a/website/package.json b/website/package.json index 630d52cb5..0ff00b187 100644 --- a/website/package.json +++ b/website/package.json @@ -1,7 +1,7 @@ { "name": "website", - "mobileAppVersion": "2.36.0", "version": "1.283.0", + "mobileAppVersion": "2.36.1", "private": true, "engines": { "npm": "please-use-yarn",