RocketChat/Rocket.Chat

View on GitHub
apps/meteor/client/views/marketplace/AppDetailsPage/AppDetailsPage.tsx

Summary

Maintainability
A
1 hr
Test Coverage
import type { ISetting } from '@rocket.chat/apps-engine/definition/settings';
import type { App } from '@rocket.chat/core-typings';
import { Button, ButtonGroup, Box } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useTranslation, useRouteParameter, useToastMessageDispatch, usePermission, useRouter } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React, { useMemo, useCallback } from 'react';
import { FormProvider, useForm } from 'react-hook-form';

import { AppClientOrchestratorInstance } from '../../../apps/orchestrator';
import { Page, PageFooter, PageHeader, PageScrollableContentWithShadow } from '../../../components/Page';
import { handleAPIError } from '../helpers/handleAPIError';
import { useAppInfo } from '../hooks/useAppInfo';
import AppDetailsPageHeader from './AppDetailsPageHeader';
import AppDetailsPageLoading from './AppDetailsPageLoading';
import AppDetailsPageTabs from './AppDetailsPageTabs';
import AppDetails from './tabs/AppDetails';
import AppLogs from './tabs/AppLogs';
import AppReleases from './tabs/AppReleases';
import AppRequests from './tabs/AppRequests/AppRequests';
import AppSecurity from './tabs/AppSecurity/AppSecurity';
import AppSettings from './tabs/AppSettings';

const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => {
    const t = useTranslation();
    const router = useRouter();
    const dispatchToastMessage = useToastMessageDispatch();
    const isAdminUser = usePermission('manage-apps');

    const tab = useRouteParameter('tab');
    const context = useRouteParameter('context');
    const appData = useAppInfo(id, context || '');

    const handleReturn = useMutableCallback((): void => {
        if (!context) {
            return;
        }

        router.navigate({
            name: 'marketplace',
            params: { context, page: 'list' },
        });
    });

    const { installed, settings, privacyPolicySummary, permissions, tosLink, privacyLink, name } = appData || {};
    const isSecurityVisible = Boolean(privacyPolicySummary || permissions || tosLink || privacyLink);

    const saveAppSettings = useCallback(
        async (data) => {
            try {
                await AppClientOrchestratorInstance.setAppSettings(
                    id,
                    (Object.values(settings || {}) as ISetting[]).map((setting) => ({
                        ...setting,
                        value: data[setting.id],
                    })),
                );

                dispatchToastMessage({ type: 'success', message: `${name} settings saved succesfully` });
            } catch (e: any) {
                handleAPIError(e);
            }
        },
        [dispatchToastMessage, id, name, settings],
    );

    const reducedSettings = useMemo(() => {
        return Object.values(settings || {}).reduce((ret, { id, value, packageValue }) => ({ ...ret, [id]: value ?? packageValue }), {});
    }, [settings]);

    const methods = useForm({ values: reducedSettings });
    const {
        handleSubmit,
        reset,
        formState: { isDirty, isSubmitting, isSubmitted },
    } = methods;

    return (
        <Page flexDirection='column' h='full'>
            <PageHeader title={t('App_Info')} onClickBack={handleReturn} />
            <PageScrollableContentWithShadow pi={24} pbs={24} pbe={0} h='full'>
                <Box w='full' alignSelf='center' h='full' display='flex' flexDirection='column'>
                    {!appData && <AppDetailsPageLoading />}
                    {appData && (
                        <>
                            <AppDetailsPageHeader app={appData} />
                            <AppDetailsPageTabs
                                context={context || ''}
                                installed={installed}
                                isSecurityVisible={isSecurityVisible}
                                settings={settings}
                                tab={tab}
                            />
                            {Boolean(!tab || tab === 'details') && <AppDetails app={appData} />}
                            {tab === 'requests' && <AppRequests id={id} isAdminUser={isAdminUser} />}
                            {tab === 'security' && isSecurityVisible && (
                                <AppSecurity
                                    privacyPolicySummary={privacyPolicySummary}
                                    appPermissions={permissions}
                                    tosLink={tosLink}
                                    privacyLink={privacyLink}
                                />
                            )}
                            {tab === 'releases' && <AppReleases id={id} />}
                            {Boolean(tab === 'settings' && settings && Object.values(settings).length) && (
                                <FormProvider {...methods}>
                                    <AppSettings settings={settings || {}} />
                                </FormProvider>
                            )}
                            {tab === 'logs' && <AppLogs id={id} />}
                        </>
                    )}
                </Box>
            </PageScrollableContentWithShadow>
            <PageFooter isDirty={isDirty}>
                <ButtonGroup>
                    <Button onClick={() => reset()}>{t('Cancel')}</Button>
                    {installed && isAdminUser && (
                        <Button primary loading={isSubmitting || isSubmitted} onClick={handleSubmit(saveAppSettings)}>
                            {t('Save_changes')}
                        </Button>
                    )}
                </ButtonGroup>
            </PageFooter>
        </Page>
    );
};

export default AppDetailsPage;