diff --git a/package.json b/package.json index 0078db7d5..0ada602be 100644 --- a/package.json +++ b/package.json @@ -62,16 +62,16 @@ "@sentry/react": "^8.5.0", "cozy-app-publish": "^0.27.2", "cozy-bar": "^15.0.0", - "cozy-client": "^48.25.0", + "cozy-client": "^49.3.0", "cozy-device-helper": "^2.1.0", "cozy-doctypes": "1.85.0", "cozy-flags": "^4.0.0", - "cozy-intent": "^2.0.2", + "cozy-intent": "^2.23.0", "cozy-interapp": "0.9.0", "cozy-logger": "1.9.0", "cozy-minilog": "^3.3.1", "cozy-realtime": "3.14.4", - "cozy-stack-client": "^48.16.0", + "cozy-stack-client": "^49.0.0", "cozy-ui": "^111.13.0", "emoji-js": "3.7.0", "focus-trap-react": "4.0.1", diff --git a/src/ducks/AlternativeStore/tests/transformData.spec.ts b/src/ducks/AlternativeStore/tests/transformData.spec.ts index ba52c4962..88883bc9f 100644 --- a/src/ducks/AlternativeStore/tests/transformData.spec.ts +++ b/src/ducks/AlternativeStore/tests/transformData.spec.ts @@ -8,12 +8,12 @@ describe('transformData', () => { { id: '01904ab1-f1fd-7243-b39b-37fe73b5579a', metadata: { + description: 'Description for I-Paf', target: { - category: 'perso', - description: 'Description for I-Paf' + category: 'perso' }, externalDataSource: { - source: 'Developer A' + creator: 'Developer A' } }, path: '/Settings/Home/Barfoo/I-Paf.url', @@ -22,12 +22,12 @@ describe('transformData', () => { { id: '01904ab2-20a9-7243-9972-df18c9c671d3', metadata: { + description: 'Description for Gestion des dossiers publidoc', target: { - category: 'foobaz', - description: 'Description for Gestion des dossiers publidoc' + category: 'foobaz' }, externalDataSource: { - source: 'Developer B' + creator: 'Developer B' } }, path: '/Settings/Home/Foobaz/Gestion des dossiers publidoc.url', @@ -36,12 +36,12 @@ describe('transformData', () => { { id: '01904ab2-2633-7243-9774-dc81fb0bc46a', metadata: { + description: 'Description for M@gistère', target: { - category: 'quxbaz', - description: 'Description for M@gistère' + category: 'quxbaz' }, externalDataSource: { - source: 'Developer C' + creator: 'Developer C' } }, path: '/Settings/Home/Barfoo/M@gistère.url', @@ -53,12 +53,12 @@ describe('transformData', () => { { id: '01904ab1-f1fd-7243-b39b-37fe73b5579a', metadata: { + description: 'Description for I-Paf', target: { - category: 'perso', - description: 'Description for I-Paf' + category: 'perso' }, externalDataSource: { - source: 'Developer A' + creator: 'Developer A' } }, path: '/Settings/Home/Barfoo/I-Paf.url', @@ -72,12 +72,12 @@ describe('transformData', () => { { id: '01904ab2-20a9-7243-9972-df18c9c671d3', metadata: { + description: 'Description for Gestion des dossiers publidoc', target: { - category: 'foobaz', - description: 'Description for Gestion des dossiers publidoc' + category: 'foobaz' }, externalDataSource: { - source: 'Developer B' + creator: 'Developer B' } }, path: '/Settings/Home/Foobaz/Gestion des dossiers publidoc.url', @@ -91,12 +91,12 @@ describe('transformData', () => { { id: '01904ab2-2633-7243-9774-dc81fb0bc46a', metadata: { + description: 'Description for M@gistère', target: { - category: 'quxbaz', - description: 'Description for M@gistère' + category: 'quxbaz' }, externalDataSource: { - source: 'Developer C' + creator: 'Developer C' } }, path: '/Settings/Home/Barfoo/M@gistère.url', @@ -118,12 +118,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c89c', metadata: { + description: 'Description for quxbaz', target: { - category: 'foobaz', - description: 'Description for quxbaz' + category: 'foobaz' }, externalDataSource: { - source: 'Developer D' + creator: 'Developer D' } }, path: '/Settings/Home/Applications Foobar/Store Foobar/quxbaz.url', @@ -132,12 +132,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c89d', metadata: { + description: 'Description for OtherFile', target: { - category: 'other', - description: 'Description for OtherFile' + category: 'other' }, externalDataSource: { - source: 'Developer E' + creator: 'Developer E' } }, path: '/Settings/Home/Applications Foobar/Store Foobar/OtherFile.url', @@ -149,12 +149,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c89c', metadata: { + description: 'Description for quxbaz', target: { - category: 'foobaz', - description: 'Description for quxbaz' + category: 'foobaz' }, externalDataSource: { - source: 'Developer D' + creator: 'Developer D' } }, path: '/Settings/Home/Applications Foobar/Store Foobar/quxbaz.url', @@ -168,12 +168,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c89d', metadata: { + description: 'Description for OtherFile', target: { - category: 'other', - description: 'Description for OtherFile' + category: 'other' }, externalDataSource: { - source: 'Developer E' + creator: 'Developer E' } }, path: '/Settings/Home/Applications Foobar/Store Foobar/OtherFile.url', @@ -195,11 +195,9 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c890', metadata: { - target: { - description: 'Description for NoCategory' - }, + description: 'Description for NoCategory', externalDataSource: { - source: 'Developer F' + creator: 'Developer F' } }, path: '/Settings/Home/Foobaz/NoCategory.url', @@ -208,12 +206,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c891', metadata: { + description: 'Description for UndefinedCategory', target: { - category: undefined, - description: 'Description for UndefinedCategory' + category: undefined }, externalDataSource: { - source: 'Developer G' + creator: 'Developer G' } }, path: @@ -226,11 +224,9 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c890', metadata: { - target: { - description: 'Description for NoCategory' - }, + description: 'Description for NoCategory', externalDataSource: { - source: 'Developer F' + creator: 'Developer F' } }, path: '/Settings/Home/Foobaz/NoCategory.url', @@ -244,12 +240,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c891', metadata: { + description: 'Description for UndefinedCategory', target: { - category: undefined, - description: 'Description for UndefinedCategory' + category: undefined }, externalDataSource: { - source: 'Developer G' + creator: 'Developer G' } }, path: @@ -272,12 +268,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c892', metadata: { + description: 'Description for Unknown', target: { - category: 'unknown', - description: 'Description for Unknown' + category: 'unknown' }, externalDataSource: { - source: 'Developer H' + creator: 'Developer H' } }, path: '/ultra/bogus/path', @@ -296,12 +292,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c89c', metadata: { + description: 'Description for quxbaz', target: { - category: 'foobaz', - description: 'Description for quxbaz' + category: 'foobaz' }, externalDataSource: { - source: 'Developer I' + creator: 'Developer I' } }, path: '/Settings/Home/Applications Foobar/Store Foobar/quxbaz.url', @@ -310,11 +306,9 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c890', metadata: { - target: { - description: 'Description for NoType' - }, + description: 'Description for NoType', externalDataSource: { - source: 'Developer J' + creator: 'Developer J' } }, path: '/Settings/Home/Foobaz/NoType.url', @@ -323,12 +317,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c892', metadata: { + description: 'Description for Unknown', target: { - category: 'unknown', - description: 'Description for Unknown' + category: 'unknown' }, externalDataSource: { - source: 'Developer K' + creator: 'Developer K' } }, path: '/Settings/Home/Applications Foobar/Store Foobar/Unknown.url', @@ -340,12 +334,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c89c', metadata: { + description: 'Description for quxbaz', target: { - category: 'foobaz', - description: 'Description for quxbaz' + category: 'foobaz' }, externalDataSource: { - source: 'Developer I' + creator: 'Developer I' } }, path: '/Settings/Home/Applications Foobar/Store Foobar/quxbaz.url', @@ -359,11 +353,9 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c890', metadata: { - target: { - description: 'Description for NoType' - }, + description: 'Description for NoType', externalDataSource: { - source: 'Developer J' + creator: 'Developer J' } }, path: '/Settings/Home/Foobaz/NoType.url', @@ -377,12 +369,12 @@ describe('transformData', () => { { id: '01908039-3b2c-7852-b4bd-1d768199c892', metadata: { + description: 'Description for Unknown', target: { - category: 'unknown', - description: 'Description for Unknown' + category: 'unknown' }, externalDataSource: { - source: 'Developer K' + creator: 'Developer K' } }, path: '/Settings/Home/Applications Foobar/Store Foobar/Unknown.url', diff --git a/src/ducks/AlternativeStore/transformData.ts b/src/ducks/AlternativeStore/transformData.ts index 86fce1a46..70995e667 100644 --- a/src/ducks/AlternativeStore/transformData.ts +++ b/src/ducks/AlternativeStore/transformData.ts @@ -59,12 +59,12 @@ export const transformData = ( return { ...file, name: file.name.replace('.url', ''), - long_description: file.metadata.target?.description, + long_description: file.metadata.description, installed: !isStorePath, categories: [category], slug: file.id, // This is much easier than refactoring the whole app developer: { - name: file.metadata.externalDataSource?.source + name: file.metadata.externalDataSource?.creator } } }) diff --git a/src/ducks/AlternativeStore/useAlternativeStore.ts b/src/ducks/AlternativeStore/useAlternativeStore.ts index 65bab19b5..74ac32be8 100644 --- a/src/ducks/AlternativeStore/useAlternativeStore.ts +++ b/src/ducks/AlternativeStore/useAlternativeStore.ts @@ -4,10 +4,10 @@ import { AlternativeShortcut, AlternativeStoreConfig } from 'ducks/AlternativeStore/types' -import { buildShortcutsQuery } from 'ducks/queries' +import { buildFileByPathsQuery, buildFilesByDirIdsQuery } from 'ducks/queries' import { useMemo } from 'react' -import { useQuery } from 'cozy-client' +import { useQueryAll } from 'cozy-client' import { IOCozyFile } from 'cozy-client/types/types' import flag from 'cozy-flags' import { useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n' @@ -16,18 +16,28 @@ export const useAlternativeStore = (): { alternativeApps: AlternativeShortcut[] installedAlternativeApps: AlternativeShortcut[] } => { - const shortcutsQuery = buildShortcutsQuery() - const { data, fetchStatus } = useQuery( + const config = flag('store.alternative-source') + const categoriesPath = Object.values(config?.categories ?? {}) + const paths = [...categoriesPath, config?.store] + + const foldersQuery = buildFileByPathsQuery(paths) + const foldersResult = useQueryAll( + foldersQuery.definition, + foldersQuery.options + ) as { data?: IOCozyFile[] } + + const folderIds = foldersResult.data?.map(folder => folder._id) + const shortcutsQuery = buildFilesByDirIdsQuery(folderIds) + const { data, fetchStatus } = useQueryAll( shortcutsQuery.definition, shortcutsQuery.options - ) as { data: IOCozyFile[]; fetchStatus: string } - const config = flag('store.alternative-source') + ) as { data?: IOCozyFile[]; fetchStatus: string } const i18nConfig = generateI18nConfig(config?.categories) useExtendI18n(i18nConfig) const alternativeApps = useMemo(() => { - if (fetchStatus !== 'loaded' || !config) return [] + if (fetchStatus !== 'loaded' || !config || !data) return [] return transformData(data, config) }, [data, fetchStatus, config]) diff --git a/src/ducks/apps/components/ApplicationPage/Header.jsx b/src/ducks/apps/components/ApplicationPage/Header.jsx index 35e6ea1be..8849dec15 100644 --- a/src/ducks/apps/components/ApplicationPage/Header.jsx +++ b/src/ducks/apps/components/ApplicationPage/Header.jsx @@ -1,137 +1,45 @@ -import cozySmileIcon from 'assets/icons/icon-cozy-smile.svg' import { isShortcutFile } from 'ducks/AlternativeStore/helpers' -import { APP_TYPE, getAppIconProps, openApp } from 'ducks/apps' -import { - hasPendingUpdate, - isUnderMaintenance, - isInstalledAndNothingToReport -} from 'ducks/apps/appStatus' -import AsyncButton from 'ducks/components/AsyncButton' -import compose from 'lodash/flowRight' +import { getAppIconProps } from 'ducks/apps' import React from 'react' import { connect } from 'react-redux' -import { Link, useLocation } from 'react-router-dom' -import { withClient, useFetchShortcut } from 'cozy-client' -import { isFlagshipApp } from 'cozy-device-helper' -import { useWebviewIntent } from 'cozy-intent' import AppIcon from 'cozy-ui/transpiled/react/AppIcon' -import Button from 'cozy-ui/transpiled/react/deprecated/Button' -import withBreakpoints from 'cozy-ui/transpiled/react/helpers/withBreakpoints' -import { translate } from 'cozy-ui/transpiled/react/providers/I18n' -import { handleIntent } from './helpers' +import { HeaderActions } from './HeaderActions' +import { HeaderShortcutActions } from './HeaderShortcutActions' + export const Header = ({ - t, app, namePrefix, name, description, parent, isInstalling, - breakpoints = {}, - client, intentData -}) => { - const { search } = useLocation() - const webviewIntent = useWebviewIntent() - const { slug, installed, type, uninstallable } = app - const { isMobile } = breakpoints - const isCurrentAppInstalling = isInstalling === slug - const { shortcutInfos } = useFetchShortcut(client, app.id) - - const handleClick = () => { - if (shortcutInfos) { - const url = shortcutInfos.data?.attributes?.url - - if (!url) return - - return window.open(url, '_blank') - } - - openApp(webviewIntent, app) - } - - const openConnector = async () => { - if (isFlagshipApp()) { - return webviewIntent.call('openApp', app.related, app) - } else { - handleIntent(client, intentData, app) - } - } - - const isKonnector = type === APP_TYPE.KONNECTOR - const isInstallDisabled = !!isUnderMaintenance(app) || isInstalling - const isUninstallDisabled = !uninstallable || isCurrentAppInstalling - const appOrKonnectorLabel = isKonnector - ? t('app_page.webapp.open') - : t('app_page.konnector.open') - - return ( -
-
- -
-
-

- {namePrefix ? `${namePrefix} ${name}` : name} -

-

{description}

- {isInstalledAndNothingToReport(app) && !isCurrentAppInstalling ? ( - isKonnector ? ( - - ) : ( -
+}) => ( +
+
+ +
+
+

+ {namePrefix ? `${namePrefix} ${name}` : name} +

+

{description}

+ {isShortcutFile(app) ? ( + + ) : ( + + )}
- ) -} +
+) -export default compose( - connect(state => ({ - isInstalling: state.apps.isInstalling - })), - withBreakpoints(), - translate(), - withClient -)(Header) +export default connect(state => ({ + isInstalling: state.apps.isInstalling +}))(Header) diff --git a/src/ducks/apps/components/ApplicationPage/HeaderActions.jsx b/src/ducks/apps/components/ApplicationPage/HeaderActions.jsx new file mode 100644 index 000000000..326df72e9 --- /dev/null +++ b/src/ducks/apps/components/ApplicationPage/HeaderActions.jsx @@ -0,0 +1,96 @@ +import cozySmileIcon from 'assets/icons/icon-cozy-smile.svg' +import { APP_TYPE, openApp } from 'ducks/apps' +import { + hasPendingUpdate, + isUnderMaintenance, + isInstalledAndNothingToReport +} from 'ducks/apps/appStatus' +import AsyncButton from 'ducks/components/AsyncButton' +import React from 'react' +import { Link, useLocation } from 'react-router-dom' + +import { useClient } from 'cozy-client' +import { isFlagshipApp } from 'cozy-device-helper' +import { useWebviewIntent } from 'cozy-intent' +import Button from 'cozy-ui/transpiled/react/deprecated/Button' +import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints' +import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n' + +import { handleIntent } from './helpers' + +const HeaderActions = ({ app, intentData, parent, isInstalling }) => { + const client = useClient() + const { t } = useI18n() + const { isMobile } = useBreakpoints() + + const isCurrentAppInstalling = isInstalling === app.slug + const { search } = useLocation() + const webviewIntent = useWebviewIntent() + + const isKonnector = app.type === APP_TYPE.KONNECTOR + const isInstallDisabled = !!isUnderMaintenance(app) || isInstalling + const isUninstallDisabled = !app.uninstallable || isCurrentAppInstalling + const appOrKonnectorLabel = isKonnector + ? t('app_page.webapp.open') + : t('app_page.konnector.open') + + const handleClick = () => { + openApp(webviewIntent, app) + } + + const openConnector = async () => { + if (isFlagshipApp()) { + return webviewIntent?.call('openApp', app.related, app) + } else { + handleIntent(client, intentData, app) + } + } + + return ( + <> + {isInstalledAndNothingToReport(app) && !isCurrentAppInstalling ? ( + isKonnector ? ( + + ) : ( +