diff --git a/web/apps/app-listing.react.js b/web/apps/app-listing.react.js new file mode 100644 --- /dev/null +++ b/web/apps/app-listing.react.js @@ -0,0 +1,78 @@ +// @flow + +import { faCheckCircle } from '@fortawesome/free-regular-svg-icons'; +import { faPlusCircle } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import classnames from 'classnames'; +import * as React from 'react'; +import { useDispatch } from 'react-redux'; + +import { + disableAppActionType, + enableAppActionType, +} from 'lib/reducers/enabled-apps-reducer'; +import type { SupportedApps } from 'lib/types/enabled-apps'; + +import SWMansionIcon from '../SWMansionIcon.react'; +import css from './apps.css'; + +type Props = { + +id: SupportedApps | 'chat', + +readOnly: boolean, + +enabled: boolean, + +name: string, + +icon: 'message-square' | 'calendar', + +copy: string, +}; + +function AppListing(props: Props): React.Node { + const { id, readOnly, enabled, name, icon, copy } = props; + const dispatch = useDispatch(); + + const enableApp = React.useCallback( + () => dispatch({ type: enableAppActionType, payload: id }), + [dispatch, id], + ); + + const disableApp = React.useCallback(() => { + dispatch({ type: disableAppActionType, payload: id }); + }, [dispatch, id]); + + const actionButton = React.useMemo(() => { + const switchIcon = enabled ? faCheckCircle : faPlusCircle; + if (readOnly) { + const readOnlyIconClasses = classnames( + css.appListingIconState, + css.iconReadOnly, + ); + return ( +
+ +
+ ); + } + const iconClasses = classnames(css.appListingIconState, { + [css.iconEnabled]: enabled, + [css.iconDisabled]: !enabled, + }); + return ( +
+ +
+ ); + }, [readOnly, disableApp, enableApp, enabled]); + return ( +
+
+ +
+
+
{name}
+ {copy} +
+ {actionButton} +
+ ); +} + +export default AppListing; diff --git a/web/apps/apps-directory.react.js b/web/apps/apps-directory.react.js --- a/web/apps/apps-directory.react.js +++ b/web/apps/apps-directory.react.js @@ -1,9 +1,56 @@ // @flow import * as React from 'react'; +import { useSelector } from 'react-redux'; + +import AppListing from './app-listing.react'; +import css from './apps.css'; + +const APP_DIRECTORY_DATA = [ + { + id: 'chat', + defaultEnabled: true, + readOnly: true, + name: 'Chat', + icon: 'message-square', + copy: 'Keep in touch with your community', + }, + { + id: 'calendar', + defaultEnabled: true, + readOnly: false, + name: 'Calendar', + icon: 'calendar', + copy: 'Shared calendar for your community', + }, +]; function AppsDirectory(): React.Node { - return
Apps directory
; + const enabledApps = useSelector(state => state.enabledApps); + + const appData = React.useMemo( + () => + APP_DIRECTORY_DATA.map(app => { + const { defaultEnabled, ...result } = { + ...app, + enabled: enabledApps[app.id] ?? app.defaultEnabled, + }; + return result; + }), + [enabledApps], + ); + + const appItems = React.useMemo( + () => appData.map(item => ), + [appData], + ); + + return ( +
+

Choose Apps

+
{appItems}
+
+ ); } export default AppsDirectory; diff --git a/web/apps/apps.css b/web/apps/apps.css new file mode 100644 --- /dev/null +++ b/web/apps/apps.css @@ -0,0 +1,67 @@ +div.appsDirectoryContainer { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +h4.appsHeader { + color: var(--fg); + padding: 20px 0px 40px 40px; + font-weight: var(--semi-bold); +} + +div.appsDirectoryList { + margin-left: 20px; + display: flex; + flex-direction: column; + row-gap: 10px; +} + +div.appListingContainer { + color: white; + display: flex; + flex-direction: row; + align-items: center; +} + +div.appListingTextContainer { + display: flex; + flex-direction: column; + flex: 1; +} + +h5.appName { + font-weight: var(--semi-bold); + margin-bottom: 4px; +} + +small.appCopy { + font-size: var(--xs-font-12); +} + +div.appListingIcon { + padding: 0 20px; + align-self: stretch; + display: flex; + align-items: center; +} + +div.appListingIconState { + padding: 0 20px; + align-self: stretch; + display: flex; + align-items: center; + font-size: var(--xl-font-20); +} + +div.iconReadOnly { + color: var(--app-list-icon-read-only-color); +} + +div.iconEnabled { + color: var(--app-list-icon-enabled-color); +} + +div.iconDisabled { + color: var(--app-list-icon-disabled-color); +} diff --git a/web/theme.css b/web/theme.css --- a/web/theme.css +++ b/web/theme.css @@ -101,4 +101,7 @@ --thread-ancestor-color-dark: var(--shades-black-100); --text-message-default-background: var(--shades-black-80); --message-action-tooltip-bg: var(--shades-black-90); + --app-list-icon-read-only-color: var(--shades-black-60); + --app-list-icon-enabled-color: var(--success-primary); + --app-list-icon-disabled-color: var(--shades-white-80); }