Skip to content
Snippets Groups Projects
Unverified Commit 84664c60 authored by Douglas Fabris's avatar Douglas Fabris Committed by GitHub
Browse files

Chore: Apps/Marketplace code organization (#27061)

parent dca6e5be
No related branches found
No related tags found
No related merge requests found
Showing
with 111 additions and 86 deletions
......@@ -19,9 +19,9 @@ import React, { memo, ReactElement, useMemo } from 'react';
import { RoomManager } from '../../app/ui-utils/client';
import { UiTextContext } from '../../definition/IRoomTypeConfig';
import { GenericModalDoNotAskAgain } from '../components/GenericModal';
import WarningModal from '../components/WarningModal';
import { useDontAskAgain } from '../hooks/useDontAskAgain';
import { roomCoordinator } from '../lib/rooms/roomCoordinator';
import WarningModal from '../views/admin/apps/WarningModal';
const fields: Fields = {
f: true,
......
import { ISetting } from '@rocket.chat/apps-engine/definition/settings';
import { App } from '@rocket.chat/core-typings';
import { Button, ButtonGroup, Box, Throbber, Tabs } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useTranslation, useCurrentRoute, useRoute, useRouteParameter } from '@rocket.chat/ui-contexts';
import React, { useState, useCallback, useRef, FC } from 'react';
import { ISettings } from '../../../../app/apps/client/@types/IOrchestrator';
import { Apps } from '../../../../app/apps/client/orchestrator';
import Page from '../../../components/Page';
import AppDetails from './AppDetails';
import AppDetailsHeader from './AppDetailsHeader';
import AppLogs from './AppLogs';
import AppReleases from './AppReleases';
import AppSecurity from './AppSecurity';
import LoadingDetails from './LoadingDetails';
import SettingsDisplay from './SettingsDisplay';
import { handleAPIError } from './helpers';
import { useAppInfo } from './hooks/useAppInfo';
const AppDetailsPage: FC<{ id: string }> = function AppDetailsPage({ id }) {
import React, { useState, useCallback, useRef, ReactElement } from 'react';
import { ISettings } from '../../../../../app/apps/client/@types/IOrchestrator';
import { Apps } from '../../../../../app/apps/client/orchestrator';
import Page from '../../../../components/Page';
import { handleAPIError } from '../helpers';
import { useAppInfo } from '../hooks/useAppInfo';
import AppDetailsPageHeader from './AppDetailsPageHeader';
import AppDetailsPageLoading from './AppDetailsPageLoading';
import AppDetails from './tabs/AppDetails';
import AppLogs from './tabs/AppLogs';
import AppReleases from './tabs/AppReleases';
import AppSecurity from './tabs/AppSecurity';
import AppSettings from './tabs/AppSettings';
const AppDetailsPage = ({ id }: { id: App['id'] }): ReactElement => {
const t = useTranslation();
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
......@@ -81,11 +82,10 @@ const AppDetailsPage: FC<{ id: string }> = function AppDetailsPage({ id }) {
</Page.Header>
<Page.ScrollableContentWithShadow padding='x24'>
<Box w='full' alignSelf='center'>
{!appData && <LoadingDetails />}
{!appData && <AppDetailsPageLoading />}
{appData && (
<>
<AppDetailsHeader app={appData} />
<AppDetailsPageHeader app={appData} />
<Tabs>
<Tabs.Item onClick={(): void => handleTabClick('details')} selected={!tab || tab === 'details'}>
{t('Details')}
......@@ -111,9 +111,7 @@ const AppDetailsPage: FC<{ id: string }> = function AppDetailsPage({ id }) {
</Tabs.Item>
)}
</Tabs>
{Boolean(!tab || tab === 'details') && <AppDetails app={appData} />}
{tab === 'security' && isSecurityVisible && (
<AppSecurity
privacyPolicySummary={privacyPolicySummary}
......@@ -122,17 +120,14 @@ const AppDetailsPage: FC<{ id: string }> = function AppDetailsPage({ id }) {
privacyLink={privacyLink}
/>
)}
{tab === 'releases' && <AppReleases id={id} />}
{Boolean(tab === 'settings' && settings && Object.values(settings).length) && (
<SettingsDisplay
<AppSettings
settings={settings || ({} as ISettings)}
setHasUnsavedChanges={setHasUnsavedChanges}
settingsRef={settingsRef}
/>
)}
{tab === 'logs' && <AppLogs id={id} />}
</>
)}
......
import type { App } from '@rocket.chat/core-typings';
import { Box } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import moment from 'moment';
import React, { ReactElement } from 'react';
import AppAvatar from '../../../components/avatar/AppAvatar';
import AppMenu from './AppMenu';
import AppStatus from './AppStatus';
import BundleChips from './BundleChips';
import { App } from './types';
import AppAvatar from '../../../../components/avatar/AppAvatar';
import AppMenu from '../AppMenu';
import BundleChips from '../BundleChips';
import AppStatus from './tabs/AppStatus';
const AppDetailsHeader = ({ app }: { app: App }): ReactElement => {
const AppDetailsPageHeader = ({ app }: { app: App }): ReactElement => {
const t = useTranslation();
const { iconFileData, name, author, version, iconFileContent, installed, isSubscribed, modifiedAt, bundledIn } = app;
const lastUpdated = modifiedAt && moment(modifiedAt).fromNow();
......@@ -26,9 +26,7 @@ const AppDetailsHeader = ({ app }: { app: App }): ReactElement => {
</Box>
{app?.shortDescription && <Box mbe='x16'>{app.shortDescription}</Box>}
<Box display='flex' flexDirection='row' alignItems='center' mbe='x16'>
<Box display='flex' flexDirection='row' alignItems='center'>
<AppStatus app={app} installed={installed} isAppDetailsPage={true} isSubscribed={isSubscribed} />
</Box>
<AppStatus app={app} installed={installed} isAppDetailsPage={true} isSubscribed={isSubscribed} />
{(installed || isSubscribed) && <AppMenu app={app} mis='x8' />}
</Box>
<Box display='flex' flexDirection='row' color='hint' alignItems='center'>
......@@ -53,4 +51,4 @@ const AppDetailsHeader = ({ app }: { app: App }): ReactElement => {
);
};
export default AppDetailsHeader;
export default AppDetailsPageHeader;
import { Box, Skeleton } from '@rocket.chat/fuselage';
import React, { FC } from 'react';
const LoadingDetails: FC = () => (
const AppDetailsPageLoading: FC = () => (
<Box display='flex' flexDirection='row' mbe='x20' w='full'>
<Skeleton variant='rect' w='x120' h='x120' mie='x20' />
<Box display='flex' flexDirection='column' justifyContent='space-between' flexGrow={1}>
......@@ -12,4 +12,4 @@ const LoadingDetails: FC = () => (
</Box>
);
export default LoadingDetails;
export default AppDetailsPageLoading;
export { default } from './AppDetailsPage';
import { Box, ButtonGroup, Callout, Chip, Margins } from '@rocket.chat/fuselage';
import { ExternalLink } from '@rocket.chat/ui-client';
import { TranslationKey, useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC } from 'react';
import React, { ReactElement } from 'react';
import APIsDisplay from './APIsDisplay';
import ScreenshotCarouselAnchor from './components/ScreenshotCarouselAnchor';
import { AppInfo } from './definitions/AppInfo';
import ScreenshotCarouselAnchor from '../../../components/ScreenshotCarouselAnchor';
import { AppInfo } from '../../../definitions/AppInfo';
import AppDetailsAPIs from './AppDetailsAPIs';
type AppDetailsProps = {
app: AppInfo;
};
const AppDetails: FC<AppDetailsProps> = ({ app }) => {
const AppDetails = ({ app }: { app: AppInfo }): ReactElement => {
const t = useTranslation();
const {
author: { homepage, support },
detailedDescription,
......@@ -21,8 +18,6 @@ const AppDetails: FC<AppDetailsProps> = ({ app }) => {
apis,
} = app;
const t = useTranslation();
const isMarkdown = detailedDescription && Object.keys(detailedDescription).length !== 0 && detailedDescription.rendered;
const isCarouselVisible = screenshots && Boolean(screenshots.length);
......@@ -90,7 +85,7 @@ const AppDetails: FC<AppDetailsProps> = ({ app }) => {
{apis?.length ? (
<Box is='section'>
<APIsDisplay apis={apis || []} />
<AppDetailsAPIs apis={apis || []} />
</Box>
) : null}
</Margins>
......
......@@ -3,13 +3,13 @@ import { Box } from '@rocket.chat/fuselage';
import { useAbsoluteUrl, useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC, Fragment } from 'react';
import { apiCurlGetter } from './helpers';
import { apiCurlGetter } from '../../../helpers';
type APIsDisplayProps = {
type AppDetailsAPIsProps = {
apis: IApiEndpointMetadata[];
};
const APIsDisplay: FC<APIsDisplayProps> = ({ apis }) => {
const AppDetailsAPIs: FC<AppDetailsAPIsProps> = ({ apis }) => {
const t = useTranslation();
const absoluteUrl = useAbsoluteUrl();
const getApiCurl = apiCurlGetter(absoluteUrl);
......@@ -48,4 +48,4 @@ const APIsDisplay: FC<APIsDisplayProps> = ({ apis }) => {
);
};
export default APIsDisplay;
export default AppDetailsAPIs;
export { default } from './AppDetails';
......@@ -2,10 +2,10 @@ import { Accordion, Box } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { ReactElement } from 'react';
import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime';
import AccordionLoading from './AccordionLoading';
import LogItem from './LogItem';
import { useLogs } from './hooks/useLogs';
import { useFormatDateAndTime } from '../../../../../../hooks/useFormatDateAndTime';
import AccordionLoading from '../../../AccordionLoading';
import { useLogs } from '../../../hooks/useLogs';
import AppLogsItem from './AppLogsItem';
const AppLogs = ({ id }: { id: string }): ReactElement => {
const t = useTranslation();
......@@ -23,7 +23,7 @@ const AppLogs = ({ id }: { id: string }): ReactElement => {
{isSuccess && (
<Accordion width='100%' alignSelf='center'>
{data?.logs?.map((log) => (
<LogItem
<AppLogsItem
key={log._createdAt}
title={`${formatDateAndTime(log._createdAt)}: "${log.method}" (${log.totalTime}ms)`}
instanceId={log.instanceId}
......
......@@ -3,15 +3,15 @@ import { Box, Accordion } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC } from 'react';
import LogEntry from './LogEntry';
import AppLogsItemEntry from './AppLogsItemEntry';
type LogItemProps = {
type AppLogsItemProps = {
entries: ILogEntry[];
instanceId: string;
title: string;
};
const LogItem: FC<LogItemProps> = ({ entries, instanceId, title, ...props }) => {
const AppLogsItem: FC<AppLogsItemProps> = ({ entries, instanceId, title, ...props }) => {
const t = useTranslation();
return (
......@@ -22,10 +22,10 @@ const LogItem: FC<LogItemProps> = ({ entries, instanceId, title, ...props }) =>
</Box>
)}
{entries.map(({ severity, timestamp, caller, args }, i) => (
<LogEntry key={i} severity={severity} timestamp={timestamp} caller={caller} args={args} />
<AppLogsItemEntry key={i} severity={severity} timestamp={timestamp} caller={caller} args={args} />
))}
</Accordion.Item>
);
};
export default LogItem;
export default AppLogsItem;
......@@ -2,16 +2,16 @@ import { Box } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC } from 'react';
import { useHighlightedCode } from '../../../hooks/useHighlightedCode';
import { useHighlightedCode } from '../../../../../../hooks/useHighlightedCode';
type LogEntryProps = {
type AppLogsItemEntryProps = {
severity: string;
timestamp: string;
caller: string;
args: unknown;
};
const LogEntry: FC<LogEntryProps> = ({ severity, timestamp, caller, args }) => {
const AppLogsItemEntry: FC<AppLogsItemEntryProps> = ({ severity, timestamp, caller, args }) => {
const t = useTranslation();
return (
......@@ -32,4 +32,4 @@ const LogEntry: FC<LogEntryProps> = ({ severity, timestamp, caller, args }) => {
);
};
export default LogEntry;
export default AppLogsItemEntry;
export { default } from './AppLogs';
import { App } from '@rocket.chat/core-typings';
import { Accordion } from '@rocket.chat/fuselage';
import React from 'react';
import React, { ReactElement } from 'react';
import { useEndpointData } from '../../../hooks/useEndpointData';
import { AsyncStatePhase } from '../../../lib/asyncState/AsyncStatePhase';
import AccordionLoading from './AccordionLoading';
import ReleaseItem from './ReleaseItem';
import { useEndpointData } from '../../../../../../hooks/useEndpointData';
import { AsyncStatePhase } from '../../../../../../lib/asyncState/AsyncStatePhase';
import AccordionLoading from '../../../AccordionLoading';
import AppReleasesItem from './AppReleasesItem';
const AppReleases = ({ id }: { id: string }): JSX.Element => {
// TODO: replace useEndpointData
const AppReleases = ({ id }: { id: App['id'] }): ReactElement => {
const result = useEndpointData(`/apps/${id}/versions`);
return (
......@@ -16,7 +18,7 @@ const AppReleases = ({ id }: { id: string }): JSX.Element => {
{result.phase === AsyncStatePhase.RESOLVED && (
<>
{result.value.apps.map((release) => (
<ReleaseItem release={release} key={release.version} />
<AppReleasesItem release={release} key={release.version} />
))}
</>
)}
......
import { Accordion, Box } from '@rocket.chat/fuselage';
import React from 'react';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { ReactElement } from 'react';
import { useTimeAgo } from '../../../hooks/useTimeAgo';
import { useTimeAgo } from '../../../../../../hooks/useTimeAgo';
type release = {
type IRelease = {
version: string;
createdDate: string;
detailedChangelog: {
......@@ -13,10 +14,11 @@ type release = {
};
type ReleaseItemProps = {
release: release;
release: IRelease;
};
const ReleaseItem = ({ release, ...props }: ReleaseItemProps): JSX.Element => {
const AppReleasesItem = ({ release, ...props }: ReleaseItemProps): ReactElement => {
const t = useTranslation();
const formatDate = useTimeAgo();
const title = (
......@@ -35,10 +37,10 @@ const ReleaseItem = ({ release, ...props }: ReleaseItemProps): JSX.Element => {
{release.detailedChangelog?.rendered ? (
<Box dangerouslySetInnerHTML={{ __html: release.detailedChangelog?.rendered }} />
) : (
'No release information provided'
t('No_release_information_provided')
)}
</Accordion.Item>
);
};
export default ReleaseItem;
export default AppReleasesItem;
export { default } from './AppReleases';
......@@ -3,13 +3,14 @@ import { ISettingBase, SettingValue } from '@rocket.chat/core-typings';
import { useRouteParameter, useTranslation } from '@rocket.chat/ui-contexts';
import React, { useMemo, useCallback, ReactElement } from 'react';
import MarkdownText from '../../../components/MarkdownText';
import MemoizedSetting from '../settings/MemoizedSetting';
import MarkdownText from '../../../../../../components/MarkdownText';
import MemoizedSetting from '../../../../settings/MemoizedSetting';
type AppTranslationFunction = {
(key: string, ...replaces: unknown[]): string;
has: (key: string | undefined) => boolean;
};
const useAppTranslation = (appId: string): AppTranslationFunction => {
const t = useTranslation();
......@@ -57,7 +58,7 @@ type AppSettingProps = {
onChange: (value: SettingValue) => void;
value: SettingValue;
};
function AppSetting({ appSetting, onChange, value, ...props }: AppSettingProps): ReactElement {
const AppSetting = ({ appSetting, onChange, value, ...props }: AppSettingProps): ReactElement => {
const appId = useRouteParameter('id');
const tApp = useAppTranslation(appId || '');
......@@ -94,6 +95,6 @@ function AppSetting({ appSetting, onChange, value, ...props }: AppSettingProps):
{...props}
/>
);
}
};
export default AppSetting;
......@@ -4,23 +4,23 @@ import { Box } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC, useMemo, useEffect, MutableRefObject } from 'react';
import { ISettings } from '../../../../app/apps/client/@types/IOrchestrator';
import { useForm } from '../../../hooks/useForm';
import { ISettings } from '../../../../../../../app/apps/client/@types/IOrchestrator';
import { useForm } from '../../../../../../hooks/useForm';
import AppSettingsAssembler from './AppSettingsAssembler';
type SettingsDisplayProps = {
type AppSettingsProps = {
settings: ISettings;
setHasUnsavedChanges: (hasUnsavedChanges: boolean) => void;
settingsRef: MutableRefObject<Record<string, ISetting['value']>>;
};
const SettingsDisplay: FC<SettingsDisplayProps> = ({ settings, setHasUnsavedChanges, settingsRef }) => {
const AppSettings: FC<AppSettingsProps> = ({ settings, setHasUnsavedChanges, settingsRef }) => {
const t = useTranslation();
const stringifiedSettings = JSON.stringify(settings);
const reducedSettings = useMemo(() => {
const settings: SettingsDisplayProps['settings'] = JSON.parse(stringifiedSettings);
const settings: AppSettingsProps['settings'] = JSON.parse(stringifiedSettings);
return Object.values(settings).reduce((ret, { id, value, packageValue }) => ({ ...ret, [id]: value ?? packageValue }), {});
}, [stringifiedSettings]);
......@@ -49,4 +49,4 @@ const SettingsDisplay: FC<SettingsDisplayProps> = ({ settings, setHasUnsavedChan
);
};
export default SettingsDisplay;
export default AppSettings;
......@@ -3,7 +3,7 @@ import { Box } from '@rocket.chat/fuselage';
import { capitalize } from '@rocket.chat/string-helpers';
import React, { ReactElement } from 'react';
import { ISettings } from '../../../../app/apps/client/@types/IOrchestrator';
import { ISettings } from '../../../../../../../app/apps/client/@types/IOrchestrator';
import AppSetting from './AppSetting';
type AppSettingsAssemblerProps = {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment