Skip to content

Commit

Permalink
WIP 3
Browse files Browse the repository at this point in the history
  • Loading branch information
gabriellsh committed May 22, 2020
1 parent 7fc4af0 commit c0b9fcb
Show file tree
Hide file tree
Showing 6 changed files with 428 additions and 72 deletions.
102 changes: 102 additions & 0 deletions client/admin/apps/AppDetailsPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, { useState, useEffect } from 'react';
import { Button, ButtonGroup, Icon, Avatar, Box, Divider, Chip, Margins } from '@rocket.chat/fuselage';

import Page from '../../components/basic/Page';
import { useMethod } from '../../contexts/ServerContext';
import PriceDisplay from './PriceDisplay';
import { AppStatus } from './AppStatus';
import { useTranslation } from '../../contexts/TranslationContext';
import { Apps } from '../../../app/apps/client/orchestrator';

const objectFit = { objectFit: 'contain' };

const useSpecificApp = (id) => {
const [data, setData] = useState({});
useEffect(() => {
(async () => {
const app = await Apps.getAppFromMarketplace(id);

setData(app);
})();
}, [id]);

return data;
};

export default function AppDetailsPage({ id }) {
const t = useTranslation();
const data = useSpecificApp(id);
const [modal, setModal] = useState(null);

const {
iconFileData = '',
name,
author: { name: authorName, homepage, support } = {},
description,
categories = [],
version,
price,
purchaseType,
pricingPlans,
} = data;

const getLoggedInCloud = useMethod('cloud:checkUserLoggedIn');
const isLoggedIn = getLoggedInCloud();

return <><Page flexDirection='column'>
<Page.Header title={t('App_Details')}>
<ButtonGroup>
<Button primary disabled>
{t('Save_changes')}
</Button>
<Button>
<Icon name='back'/>
{t('Back')}
</Button>
</ButtonGroup>
</Page.Header>
<Page.Content maxWidth='x600' w='full' alignSelf='center'>
<Box display='flex' flexDirection='row' mbe='x20'>
<Avatar style={objectFit} size='x120' mie='x20' url={`data:image/png;base64,${ iconFileData }`}/>
<Box display='flex' flexDirection='column' justifyContent='space-between'>
<Box fontScale='h1' fontWeight='500'>{name}</Box>
<Box display='flex' flexDirection='row' color='hint' alignItems='center'>
<Box fontScale='p2' mie='x4'>{`${ t('By') } ${ authorName }`}</Box>
|
<Box mis= 'x4'>{`${ t('Version') } ${ version }`}</Box>
</Box>
<Box display='flex' flexDirection='row' alignItems='center'>
<AppStatus app={data} setModal={setModal} isLoggedIn={isLoggedIn}/>
<PriceDisplay mis='x12' purchaseType={purchaseType} pricingPlans={pricingPlans} price={price} showType={false}/>
</Box>
</Box>
</Box>
<Divider />
<Box display='flex' flexDirection='column'>

<Margins block='x12'>
<Box fontScale='h1' textTransform='uppercase'>{t('Categories')}</Box>
<Box display='flex' flexDirection='row'>
{categories && categories.map((current) => <Chip key={current} textTransform='uppercase' mie='x8'><Box color='hint'>{current}</Box></Chip>)}
</Box>

<Box fontScale='h1' textTransform='uppercase'>{t('Contact')}</Box>
<Box display='flex' flexDirection='row'>
<Box display='flex' flexDirection='column' mie='x12'>
<Box fontScale='s1' color='hint' textTransform='uppercase'>{t('Author_Site')}</Box>
<Box withRichContent><a href={homepage}>{homepage}</a></Box>
</Box>
<Box display='flex' flexDirection='column'>
<Box fontScale='s1' color='hint' textTransform='uppercase'>{t('Support')}</Box>
<Box withRichContent><a href={support}>{support}</a></Box>
</Box>
</Box>

<Box fontScale='h1' textTransform='uppercase'>{t('Details')}</Box>
<Box display='flex' flexDirection='row'>{description}</Box>
</Margins>

</Box>
</Page.Content>
</Page>{modal}</>;
}
108 changes: 108 additions & 0 deletions client/admin/apps/AppStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { useCallback, useEffect, useState } from 'react';
import { Box, Button, Icon, Throbber } from '@rocket.chat/fuselage';

import { Modal } from '../../components/basic/Modal';
import { useTranslation } from '../../contexts/TranslationContext';
import { appButtonProps, appStatusSpanProps, handleAPIError, warnStatusChange } from '../../../app/apps/client/admin/helpers';
import { Apps } from '../../../app/apps/client/orchestrator';

const iframeMsgListener = (confirm, cancel) => (e) => {
let data;
try {
data = JSON.parse(e.data);
} catch (e) {
return;
}

if (data.result) {
confirm(data);
} else {
cancel();
}
};

const IframeModal = ({ url, confirm, cancel, ...props }) => {
useEffect(() => {
const listener = iframeMsgListener(confirm, cancel);
window.addEventListener('message', listener);

return () => window.removeEventListener('message', listener);
}, []);
return <Modal padding='x12' minHeight='x400' {...props}>
<iframe style={{ border: 'none', height: '100%', width: '100%' }} src={url}/>
</Modal>;
};

const installApp = async ({ id, name, version }, callback) => {
try {
const { status } = await Apps.installApp(id, version);
warnStatusChange(name, status);
} catch (error) {
handleAPIError(error);
} finally {
callback();
}
};

const actions = {
purchase: installApp,
install: installApp,
update: async ({ id, name, version }, callback) => {
try {
const { status } = await Apps.updateApp(id, version);
warnStatusChange(name, status);
} catch (error) {
handleAPIError(error);
} finally {
callback();
}
},
};

export const AppStatus = React.memo(({ app, show = true, setModal, isLoggedIn, ...props }) => {
const t = useTranslation();
const [loading, setLoading] = useState();

const button = appButtonProps(app);
const status = !button && appStatusSpanProps(app);

const { id } = app;

const confirmAction = () => {
actions[button.action](app, () => {
setLoading(false);
setModal(null);
});
};

const openModal = async () => {
setLoading(true);
try {
const data = await Apps.buildExternalUrl(app.id, app.purchaseType, false);

setModal(() => <IframeModal url={data.url} cancel={() => setModal(null)} confirm={confirmAction}/>);
} catch (error) {
handleAPIError(error);
}
};

const openLoginPrompt = () => setModal();

const handleClick = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
isLoggedIn ? openModal() : openLoginPrompt();
}, [id, isLoggedIn]);

return <Box display={show || status || loading ? 'box' : 'none'}>
{button && <Button primary disabled={loading} onClick={handleClick}>
{loading && <Throbber />}
{!loading && button.icon && <Icon name={button.icon} />}
{!loading && t(button.label)}
</Button>}
{status && <Box color={status.label === 'Disabled' ? 'warning' : 'hint'}>
<Icon size='x20' name={status.icon} mie='x4'/>
{t(status.label)}
</Box>}
</Box>;
});
10 changes: 9 additions & 1 deletion client/admin/apps/AppsRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import React, { useState, useEffect } from 'react';
import { Box } from '@rocket.chat/fuselage';

import { Apps } from '../../../app/apps/client/orchestrator';
import { useRoute, useRouteParameter } from '../../contexts/RouterContext';
import { usePermission } from '../../contexts/AuthorizationContext';
import { useTranslation } from '../../contexts/TranslationContext';
import NotAuthorizedPage from '../NotAuthorizedPage';
import AppDetailsPage from './AppDetailsPage';
import MarketplacePage from './MarketplacePage';

export default function AppsRoute() {
Expand All @@ -13,6 +15,9 @@ export default function AppsRoute() {
const canViewAppsAndMarketplace = usePermission('manage-apps');
const [isEnabled, setEnabled] = useState(false);

const context = useRouteParameter('context');
const id = useRouteParameter('id');

useEffect(() => {
(async () => setEnabled(await Apps.isEnabled()))();
}, []);
Expand All @@ -25,5 +30,8 @@ export default function AppsRoute() {
return <Box>{t('Apps_disabled')}</Box>;
}

return <MarketplacePage />;
return <>
{!context && <MarketplacePage />}
{context === 'details' && <AppDetailsPage id={id}/>}
</>;
}
29 changes: 7 additions & 22 deletions client/admin/apps/MarketplacePage.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
import { Button, ButtonGroup, Icon, Tabs } from '@rocket.chat/fuselage';
import React, { useEffect, useCallback } from 'react';
import { Button, ButtonGroup, Icon } from '@rocket.chat/fuselage';
import React, { useState } from 'react';

import Page from '../../components/basic/Page';
import { Apps } from '../../../app/apps/client/orchestrator';
import { useTranslation } from '../../contexts/TranslationContext';
import { useRoute, useRouteParameter } from '../../contexts/RouterContext';
import { useRoute } from '../../contexts/RouterContext';
import { useMethod } from '../../contexts/ServerContext';
import MarketplaceTable from './MarketplaceTable';

function MarketplacePage() {
const t = useTranslation();
const [modal, setModal] = useState(null);

const cloudRouter = useRoute('cloud');

// const handleNewButtonClick = useCallback(() => {
// router.push({ context: 'new', type: 'incoming' });
// }, []);

// const context = useRouteParameter('context');
// useEffect(() => {
// if (!context) {
// router.push({ context: 'webhook-incoming' });
// }
// }, [context]);

const getLoggedInCloud = useMethod('cloud:checkUserLoggedIn');
const isLoggedInCloud = getLoggedInCloud();

useEffect(() => {
(async () => console.log(await Apps.getAppsFromMarketplace()))();
}, []);

return <Page flexDirection='column'>
return <><Page flexDirection='column'>
<Page.Header title={t('Marketplace')}>
{isLoggedInCloud && <ButtonGroup>
<Button onClick={() => { cloudRouter.push({}); }}>
Expand All @@ -40,9 +25,9 @@ function MarketplacePage() {
</ButtonGroup>}
</Page.Header>
<Page.Content>
{/* <MarketplaceTable /> */}
<MarketplaceTable setModal={setModal}/>
</Page.Content>
</Page>;
</Page>{ modal }</>;
}

export default MarketplacePage;
Loading

0 comments on commit c0b9fcb

Please sign in to comment.