diff --git a/package.json b/package.json index 41af203323..acb4171d7d 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,8 @@ "@sentry/react": "7.119.0", "comlink": "4.4.1", "cozy-client": "^51.6.0", - "cozy-dataproxy-lib": "^1.9.0", - "cozy-device-helper": "2.7.0", + "cozy-dataproxy-lib": "^2.3.0", + "cozy-device-helper": "3.7.1", "cozy-devtools": "^1.2.1", "cozy-doctypes": "1.83.8", "cozy-flags": "4.0.0", diff --git a/src/components/Applications.jsx b/src/components/Applications.jsx index 797d9ae83a..8758e2b3ed 100644 --- a/src/components/Applications.jsx +++ b/src/components/Applications.jsx @@ -1,4 +1,4 @@ -import React, { memo, useEffect, useRef } from 'react' +import React, { memo } from 'react' import memoize from 'lodash/memoize' import uniqBy from 'lodash/uniqBy' import { useQuery } from 'cozy-client' @@ -60,17 +60,19 @@ const getApplicationsList = data => { } } -export const Applications = ({ onAppsFetched }) => { +export const Applications = () => { const showLogout = !!flag('home.mainlist.show-logout') const { t } = useI18n() - const { data } = useQuery(appsConn.query, appsConn) + const { data: apps } = useQuery(appsConn.query, appsConn) const homeMagicFolderConn = mkHomeMagicFolderConn(t) + const magicHomeFolder = useQuery( homeMagicFolderConn.query, homeMagicFolderConn ) + const magicHomeFolderId = magicHomeFolder?.data?.[0]?._id const homeShortcutsConn = mkHomeShorcutsConn(magicHomeFolderId) const { data: shortcuts } = useQuery(homeShortcutsConn.query, { @@ -78,24 +80,12 @@ export const Applications = ({ onAppsFetched }) => { enabled: !!magicHomeFolderId }) - const didLoad = useRef(false) - - useEffect(() => { - const isReady = - didLoad.current === false && onAppsFetched && isValidData(data) - - if (isReady) { - onAppsFetched(data) - didLoad.current = true - } - }, [data, onAppsFetched]) - return (
- {getApplicationsList(data)} + {getApplicationsList(apps)} {shortcuts && shortcuts.map((shortcut, index) => ( diff --git a/src/components/FooterLogo/FooterLogo.jsx b/src/components/FooterLogo/FooterLogo.jsx index 938937d560..6df10ff2d2 100644 --- a/src/components/FooterLogo/FooterLogo.jsx +++ b/src/components/FooterLogo/FooterLogo.jsx @@ -1,6 +1,6 @@ import React from 'react' import { useClient, useQuery } from 'cozy-client' -import { buildContextQuery } from 'queries' +import { makeContextQuery } from 'queries' import Divider from 'cozy-ui/transpiled/react/Divider' import { useCozyTheme } from 'cozy-ui/transpiled/react/providers/CozyTheme' @@ -9,8 +9,10 @@ export const FooterLogo = () => { const { type } = useCozyTheme() const rootURL = client.getStackClient().uri - const contextQuery = buildContextQuery() - const { data } = useQuery(contextQuery.definition, contextQuery.options) + const { data } = useQuery( + makeContextQuery.definition, + makeContextQuery.options + ) const logos = data?.logos?.home?.light || [] const secondaries = logos.filter(logos => logos.type === 'secondary') diff --git a/src/components/Home.jsx b/src/components/Home.jsx index 9d77b1b04c..cdbba62565 100644 --- a/src/components/Home.jsx +++ b/src/components/Home.jsx @@ -18,7 +18,7 @@ import { Announcements } from 'components/Announcements/Announcements' import styles from './styles.styl' -const Home = ({ setAppsReady, wrapper }) => { +const Home = ({ wrapper }) => { const { pathname } = useLocation() const { isMobile } = useBreakpoints() @@ -38,7 +38,7 @@ const Home = ({ setAppsReady, wrapper }) => { getFlagshipMetadata().immersive })} > - + {flag('home.detailed-services-dev') ? ( diff --git a/src/components/Sections/SectionView.tsx b/src/components/Sections/SectionView.tsx index 5975b5eca3..ccb5fbd78a 100644 --- a/src/components/Sections/SectionView.tsx +++ b/src/components/Sections/SectionView.tsx @@ -1,6 +1,7 @@ import React, { useState, useRef } from 'react' import cx from 'classnames' +import { useQuery } from 'cozy-client' import type { IOCozyKonnector } from 'cozy-client/types/types' import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints' import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n' @@ -17,6 +18,7 @@ import { } from 'components/Sections/SectionsTypes' import { useSections } from './SectionsContext' import CandidateServiceTile from 'components/CandidateServiceTile' +import { makeTriggersWithJobStatusQuery } from 'queries' export const SectionBody = ({ section }: SectionViewProps): JSX.Element => { const { isMobile } = useBreakpoints() @@ -26,6 +28,13 @@ export const SectionBody = ({ section }: SectionViewProps): JSX.Element => { const shouldOpenStoreModal = section.type === 'category' && section.pristine const { isRunning, isInMaintenance } = useSections() + // This query is useful to fetch the triggers' current_state, used to display + // the konnector icon. The data is fetched from the store in KonnectorTile + useQuery( + makeTriggersWithJobStatusQuery.definition(), + makeTriggersWithJobStatusQuery.options + ) + return (
{ +const App = () => { const { isMobile } = useBreakpoints() const [contentWrapper, setContentWrapper] = useState(undefined) - const [isFetching, setIsFetching] = useState( - [accounts, konnectors, triggers].some(collection => - ['pending', 'loading'].includes(collection.fetchStatus) - ) - ) - const [hasError, setHasError] = useState(false) - const [isReady, setIsReady] = useState(false) - const [appsReady, setAppsReady] = useState(false) + + const [didInit, setDidInit] = useState(false) const webviewIntent = useWebviewIntent() const theme = useCozyTheme() @@ -93,48 +86,28 @@ const App = ({ accounts, konnectors, triggers }) => { const shortcutsDirectories = canHaveShortcuts ? formatShortcuts(folders, customHomeShortcuts) : null - const context = useQuery(contextQuery.definition, contextQuery.options) + + const { isFetching, hasError } = useFetchInitialData() const showAssistantForMobile = isFlagshipApp() ? flag('cozy.searchbar.enabled-for-flagship') : flag('cozy.searchbar.enabled') && isMobile - useEffect(() => { - setIsFetching( - [accounts, konnectors, triggers, context].some(collection => - ['pending', 'loading'].includes(collection.fetchStatus) - ) - ) - setHasError( - [accounts, konnectors, triggers, context].find( - collection => collection.fetchStatus === 'failed' - ) - ) - }, [accounts, konnectors, triggers, context]) - - if (context?.attributes?.features) { - const flags = toFlagNames(context.attributes.features) - enableFlags(flags) - } - - useEffect(() => { - setIsReady( - appsReady && - !hasError && - !isFetching && - shortcutsDirectories !== undefined - ) - }, [appsReady, hasError, isFetching, shortcutsDirectories]) - - useEffect(() => { - if (isReady && webviewIntent) { + if ( + !didInit && + !hasError && + !isFetching && + shortcutsDirectories !== undefined + ) { + if (webviewIntent) { webviewIntent.call('setTheme', theme.variant) webviewIntent.call('hideSplashScreen') } - if (isReady && !webviewIntent && __SIMULATE_FLAGSHIP__) { + if (!webviewIntent && __SIMULATE_FLAGSHIP__) { document.getElementById('splashscreen').style.display = 'none' } - }, [isReady, theme, webviewIntent]) + setDidInit(true) + } return ( <> @@ -145,7 +118,7 @@ const App = ({ accounts, konnectors, triggers }) => {
setContentWrapper(div) : null} + ref={didInit ? div => setContentWrapper(div) : null} > @@ -167,7 +140,6 @@ const App = ({ accounts, konnectors, triggers }) => { element={ setAppsReady(true)} shortcutsDirectories={shortcutsDirectories} /> } @@ -206,4 +178,4 @@ const App = ({ accounts, konnectors, triggers }) => { ) } -export default appEntryPoint(App) +export default App diff --git a/src/containers/ReloadFocus.jsx b/src/containers/ReloadFocus.jsx index ffc222ecb0..9011819148 100644 --- a/src/containers/ReloadFocus.jsx +++ b/src/containers/ReloadFocus.jsx @@ -1,7 +1,11 @@ import React from 'react' import PropTypes from 'prop-types' - -import { withClient, Q } from 'cozy-client' +import { + makeTriggersWithJobStatusQuery, + makeAppsQuery, + makeJobsQuery +} from 'queries' +import { withClient } from 'cozy-client' class RealoadFocus extends React.Component { static contextTypes = { store: PropTypes.object @@ -9,13 +13,12 @@ class RealoadFocus extends React.Component { componentDidMount() { const client = this.props.client window.addEventListener('focus', () => { - client.query(Q('io.cozy.jobs')) + client.query(makeJobsQuery.definition(), makeJobsQuery.options) client.query( - Q('io.cozy.triggers').where({ - worker: { $in: ['client', 'konnector'] } - }) + makeTriggersWithJobStatusQuery.definition(), + makeTriggersWithJobStatusQuery.options ) - client.query(Q('io.cozy.apps')) + client.query(makeAppsQuery.definition(), makeAppsQuery.options) }) } diff --git a/src/dataproxy/DataProxyProvider.jsx b/src/dataproxy/DataProxyProvider.jsx deleted file mode 100644 index 1209e3580d..0000000000 --- a/src/dataproxy/DataProxyProvider.jsx +++ /dev/null @@ -1,113 +0,0 @@ -import * as Comlink from 'comlink' -import React, { useContext, useState, useEffect } from 'react' - -import { useClient } from 'cozy-client' -import { isFlagshipApp } from 'cozy-device-helper' -import flag from 'cozy-flags' -import Minilog from 'cozy-minilog' - -const log = Minilog('👷‍♂️ [DataProxyProvider]') - -export const DataProxyContext = React.createContext() - -export const useDataProxy = () => { - const context = useContext(DataProxyContext) - - return context -} - -export const DataProxyProvider = React.memo(({ children }) => { - const client = useClient() - const [iframeUrl, setIframeUrl] = useState() - const [dataProxy, setDataProxy] = useState() - const [dataProxyServicesAvailable, setDataProxyServicesAvailable] = - useState(undefined) - - useEffect(() => { - if (!client) return - - const initIframe = async () => { - try { - if (!flag('cozy.search.enabled')) { - log.log( - 'Dataproxy features will be disabled due to missing feature flags' - ) - setDataProxyServicesAvailable(false) - return - } - - log.log('Initializing DataProxy intent') - const result = await client.stackClient.fetchJSON('POST', '/intents', { - data: { - type: 'io.cozy.intents', - attributes: { - action: 'OPEN', - type: 'io.cozy.dataproxy', - permissions: ['GET'] - } - } - }) - - if (!result.data?.attributes?.services?.[0]?.href) { - log.log( - 'No dataproxy intent available, dataproxy features will be disabled' - ) - setDataProxyServicesAvailable(false) - return - } - - setIframeUrl(result.data.attributes.services[0]?.href) - setDataProxyServicesAvailable(true) - } catch (error) { - setDataProxyServicesAvailable(false) - log.error( - 'Error whild initializing Search intent, dataproxy features will be disabled', - error - ) - } - } - - if (isFlagshipApp()) { - setDataProxyServicesAvailable(false) - } else { - initIframe() - } - }, [client]) - - const onIframeLoaded = () => { - const ifr = document.getElementById('DataProxy') - const remote = Comlink.wrap(Comlink.windowEndpoint(ifr.contentWindow)) - setDataProxy(() => remote) - } - - const search = async search => { - log.log('Send search query to DataProxy iframe') - - const result = await dataProxy.search(search) - - return result - } - - const value = { - dataProxyServicesAvailable, - search - } - - return ( - - {children} - {iframeUrl ? ( - - ) : undefined} - - ) -}) - -DataProxyProvider.displayName = 'DataProxyProvider' diff --git a/src/hooks/useFetchInitialData.jsx b/src/hooks/useFetchInitialData.jsx new file mode 100644 index 0000000000..24ab223aaa --- /dev/null +++ b/src/hooks/useFetchInitialData.jsx @@ -0,0 +1,61 @@ +import { useEffect, useRef } from 'react' +import { useQuery } from 'cozy-client' +import { + makeAccountsQuery, + makeAppsQuery, + makeKonnectorsQuery, + makeTriggersQuery +} from 'queries' +import log from 'cozy-logger' + +export const useFetchInitialData = () => { + const accountsQuery = useQuery( + makeAccountsQuery.definition, + makeAccountsQuery.options + ) + const konnectorsQuery = useQuery( + makeKonnectorsQuery.definition, + makeKonnectorsQuery.options + ) + const appsQuery = useQuery(makeAppsQuery.definition, makeAppsQuery.options) + const triggersQuery = useQuery( + makeTriggersQuery.definition, + makeTriggersQuery.options + ) + + // eslint-disable-next-line react-hooks/exhaustive-deps + const allQueries = [accountsQuery, konnectorsQuery, appsQuery, triggersQuery] + + const startTime = useRef(null) + const loggedTime = useRef(false) + + useEffect(() => { + if (startTime.current === null) { + startTime.current = performance.now() + } + + const allFetched = allQueries.every(query => query.fetchStatus === 'loaded') + + if (allFetched && !loggedTime.current) { + const endTime = performance.now() + log('info', `Required data fetched in ${endTime - startTime.current} ms`) + loggedTime.current = true + } + }, [allQueries]) + + const isFetching = allQueries.some( + query => query.fetchStatus === 'loading' || query.fetchStatus === 'pending' + ) + const hasError = allQueries.some(query => query.fetchStatus === 'failed') + + return { + isFetching, + hasError, + data: { + accounts: accountsQuery.data, + konnectors: konnectorsQuery.data, + apps: appsQuery.data, + triggers: triggersQuery.data + } + } +} diff --git a/src/queries.js b/src/queries.js index c59b1e320a..8b2b58984f 100644 --- a/src/queries.js +++ b/src/queries.js @@ -14,16 +14,54 @@ export const konnectorsConn = { fetchPolicy: defaultFetchPolicy } +export const makeJobsQuery = { + definition: () => Q('io.cozy.jobs'), + options: { + as: 'io.cozy.jobs', + fetchPolicy: defaultFetchPolicy + } +} + +export const makeAppsQuery = { + definition: () => Q('io.cozy.apps'), + options: { + as: 'io.cozy.apps', + fetchPolicy: defaultFetchPolicy + } +} + +export const makeKonnectorsQuery = { + definition: () => Q('io.cozy.konnectors'), + options: { + as: 'io.cozy.konnectors', + fetchPolicy: defaultFetchPolicy + } +} + export const makeTriggersQuery = { definition: () => { return Q('io.cozy.triggers') .partialIndex({ - worker: 'konnector' + worker: { $in: ['client', 'konnector'] } // client is for CLISK }) .limitBy(1000) }, options: { - as: 'io.cozy.triggers/worker=konnector', + as: 'io.cozy.triggers/worker=client-or-konnector', + fetchPolicy: defaultFetchPolicy + } +} + +export const makeTriggersWithJobStatusQuery = { + definition: () => { + return Q('io.cozy.triggers') + .where({ + worker: { $in: ['client', 'konnector'] } // client is for CLISK + }) + .limitBy(1000) + }, + options: { + as: 'io.cozy.triggers/worker=client-or-konnector/withjobstatus', fetchPolicy: defaultFetchPolicy } } @@ -133,14 +171,14 @@ export const mkHomeCustomShorcutsConn = foldersId => { } } -export const buildContextQuery = () => ({ +export const makeContextQuery = { definition: () => Q('io.cozy.settings').getById('io.cozy.settings.context'), options: { as: 'io.cozy.settings/io.cozy.settings.context', fetchPolicy: defaultFetchPolicy, singleDocData: true } -}) +} export const buildExistingTimeseriesGeojsonQuery = () => ({ definition: Q('io.cozy.timeseries.geojson') @@ -153,12 +191,3 @@ export const buildExistingTimeseriesGeojsonQuery = () => ({ fetchPolicy: CozyClient.fetchPolicies.olderThan(60 * 60 * 24 * 365 * 1000) } }) - -export const contextQuery = { - definition: Q('io.cozy.settings').getById('io.cozy.settings.context'), - options: { - as: 'io.cozy.settings/io.cozy.settings.context', - fetchPolicy: defaultFetchPolicy, - singleDocData: true - } -} diff --git a/yarn.lock b/yarn.lock index 0195232897..9f76971038 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6079,14 +6079,13 @@ cozy-client@^51.6.0: sift "^6.0.0" url-search-params-polyfill "^8.0.0" -cozy-dataproxy-lib@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/cozy-dataproxy-lib/-/cozy-dataproxy-lib-1.9.0.tgz#5e43d49f2ac86e5d6a96bb74260e2567392626eb" - integrity sha512-ij9a3x20THxCRjvqcG8yR3hJXZhidDWawaTsfQaCT/eQUS5vwGx4Ob2p6Fur4G8GqayMSmbMsrfkGXv4JlEb/g== +cozy-dataproxy-lib@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cozy-dataproxy-lib/-/cozy-dataproxy-lib-2.3.0.tgz#c702c36a674c61edffd31e10d3c9e3665f64c4ef" + integrity sha512-rDYd3yOykArExr8Iz/hoP+2jI57NX0Z/LIDsPmH3W9vifXnb1SVBfekxE4vNXj9vJKLSZ8vfKgl3OuqYbOKVow== dependencies: classnames "2.5.1" comlink "4.4.1" - cozy-device-helper "^3.7.1" cozy-pouch-link "^50.3.1" flexsearch "0.7.43" lodash "4.17.21" @@ -6094,7 +6093,14 @@ cozy-dataproxy-lib@^1.9.0: react-type-animation "3.2.0" rooks "7.14.1" -cozy-device-helper@2.7.0, cozy-device-helper@^2.1.0: +cozy-device-helper@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/cozy-device-helper/-/cozy-device-helper-3.7.1.tgz#59f11ab3ab92335525a767e78f983b07eeaf4eea" + integrity sha512-D0zkEFynUrICNhQixyyYGhRHTwgJL+pf7XtmTwjKePIn2pNHKw5V64IhGg9uLYf6tI9uivkUKvZvFYuKzEvlqA== + dependencies: + lodash "^4.17.19" + +cozy-device-helper@^2.1.0: version "2.7.0" resolved "https://registry.yarnpkg.com/cozy-device-helper/-/cozy-device-helper-2.7.0.tgz#573749997f18e5a1f11f720faec8c9bf2406beeb" integrity sha512-jMzW7s4IDuMivbsP8fo1IWW1z5l0wJ0u440E0fQmdsi+Zm/L9GXFKthLuuceYqXlH6c/APUCfpozJqjFMMHU1A== @@ -6115,13 +6121,6 @@ cozy-device-helper@^3.1.2: dependencies: lodash "^4.17.19" -cozy-device-helper@^3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/cozy-device-helper/-/cozy-device-helper-3.7.1.tgz#59f11ab3ab92335525a767e78f983b07eeaf4eea" - integrity sha512-D0zkEFynUrICNhQixyyYGhRHTwgJL+pf7XtmTwjKePIn2pNHKw5V64IhGg9uLYf6tI9uivkUKvZvFYuKzEvlqA== - dependencies: - lodash "^4.17.19" - cozy-devtools@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/cozy-devtools/-/cozy-devtools-1.2.1.tgz#680976ab69ebdf2d2727cffc07243705794d1a83"