diff --git a/.vscode/settings.json b/.vscode/settings.json index 7908240a6..35a6d34f1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { "typescript.tsdk": "node_modules/typescript/lib", - "editor.formatOnSave": true, - "eslint.validate": [{ "language": "typescript", "autoFix": true }, { "language": "typescriptreact", "autoFix": true }] + "editor.formatOnSave": true } diff --git a/apps/sensenet/src/components/AddButton.tsx b/apps/sensenet/src/components/AddButton.tsx index cb3fee1bb..7136b464a 100644 --- a/apps/sensenet/src/components/AddButton.tsx +++ b/apps/sensenet/src/components/AddButton.tsx @@ -1,23 +1,49 @@ -import Button from '@material-ui/core/Button' -import Fab from '@material-ui/core/Fab' -import SwipeableDrawer from '@material-ui/core/SwipeableDrawer' -import Tooltip from '@material-ui/core/Tooltip' -import Typography from '@material-ui/core/Typography' import Add from '@material-ui/icons/Add' import CloudUpload from '@material-ui/icons/CloudUpload' import { GenericContent, Schema } from '@sensenet/default-content-types' import React, { useContext, useEffect, useState } from 'react' import { CurrentContentContext, useLogger, useRepository } from '@sensenet/hooks-react' +import { + Button, + createStyles, + IconButton, + ListItem, + ListItemIcon, + ListItemText, + makeStyles, + SwipeableDrawer, + Theme, + Tooltip, + Typography, +} from '@material-ui/core' import { useLocalization } from '../hooks' import { Icon } from './Icon' import { useDialog } from './dialogs' +const useStyles = makeStyles((theme: Theme) => { + return createStyles({ + addButton: { + width: '32px', + height: '32px', + minHeight: 0, + padding: 0, + margin: '0.5rem 0.5rem', + backgroundColor: theme.palette.primary.main, + '&:hover': { + backgroundColor: theme.palette.primary.dark, + }, + }, + }) +}) + export interface AddButtonProps { parent?: GenericContent - allowedTypes?: string[] + isOpened?: boolean + path: string } export const AddButton: React.FunctionComponent = props => { + const classes = useStyles() const repo = useRepository() const { openDialog } = useDialog() const parentContext = useContext(CurrentContentContext) @@ -26,6 +52,7 @@ export const AddButton: React.FunctionComponent = props => { const [allowedChildTypes, setAllowedChildTypes] = useState([]) const localization = useLocalization().addButton const logger = useLogger('AddButton') + const [isAvailable, setAvailable] = useState(false) useEffect(() => { props.parent && setParent(props.parent) @@ -36,33 +63,83 @@ export const AddButton: React.FunctionComponent = props => { }, [parentContext, props.parent]) useEffect(() => { - if (props.allowedTypes && props.allowedTypes.length > 0) { - setAllowedChildTypes(props.allowedTypes.map(type => repo.schemas.getSchemaByName(type))) - } else if (showSelectType) { - repo - .getAllowedChildTypes({ idOrPath: parent.Id }) - .then(types => setAllowedChildTypes(types.d.results.map(t => repo.schemas.getSchemaByName(t.Name)))) - .catch(error => { - logger.error({ - message: localization.errorGettingAllowedContentTypes, - data: { - details: { error }, - }, - }) + const getActions = async () => { + try { + const actions = await repo.getActions({ idOrPath: props.parent ? parent.Id : props.path }) + const isActionFound = actions.d.Actions.some(action => action.Name === 'Add' || action.Name === 'Upload') + setAvailable(isActionFound) + } catch (error) { + logger.error({ + message: localization.errorGettingActions, + data: { + details: { error }, + }, + }) + } + } + + if (props.parent || props.path !== '') { + getActions() + } else { + setAvailable(false) + } + }, [localization.errorGettingActions, logger, parent, props.parent, props.path, repo]) + + useEffect(() => { + const getAllowedChildTypes = async () => { + try { + const allowedChildTypesFromRepo = await repo.getAllowedChildTypes({ + idOrPath: props.parent ? parent.Id : props.path, + }) + const allowedChildTypeList = allowedChildTypesFromRepo.d.results.map(t => repo.schemas.getSchemaByName(t.Name)) + setAllowedChildTypes(allowedChildTypeList) + } catch (error) { + logger.error({ + message: localization.errorGettingAllowedContentTypes, + data: { + details: { error }, + }, }) + } + } + + if (showSelectType) { + props.parent || props.path !== '' ? getAllowedChildTypes() : setAllowedChildTypes([]) } - }, [localization.errorGettingAllowedContentTypes, logger, parent.Id, props.allowedTypes, repo, showSelectType]) + }, [localization.errorGettingAllowedContentTypes, logger, parent.Id, props.parent, props.path, repo, showSelectType]) return ( -
- - setShowSelectType(true)}> - - - +
+ {!props.isOpened ? ( + + + setShowSelectType(true)} disabled={!isAvailable}> + + + + + ) : ( + setShowSelectType(true)} + disabled={!isAvailable}> + + + + + + )} setShowSelectType(false)} diff --git a/apps/sensenet/src/components/content/Commander.tsx b/apps/sensenet/src/components/content/Commander.tsx index f5c31a0c7..d17f1449e 100644 --- a/apps/sensenet/src/components/content/Commander.tsx +++ b/apps/sensenet/src/components/content/Commander.tsx @@ -10,7 +10,6 @@ import { useRepository, } from '@sensenet/hooks-react' import { useSelectionService } from '../../hooks' -import { AddButton } from '../AddButton' import { CollectionComponent } from '../content-list' import { useDialog } from '../dialogs' @@ -29,7 +28,6 @@ export const CommanderComponent: React.FunctionComponent(null) const [_rightPanelRef, setRightPanelRef] = useState(null) @@ -141,8 +139,6 @@ export const CommanderComponent: React.FunctionComponent - - {activeParent ? : null}
) } diff --git a/apps/sensenet/src/components/content/Explore.tsx b/apps/sensenet/src/components/content/Explore.tsx index b4f04a119..f8592675a 100644 --- a/apps/sensenet/src/components/content/Explore.tsx +++ b/apps/sensenet/src/components/content/Explore.tsx @@ -8,7 +8,6 @@ import { LoadSettingsContextProvider, } from '@sensenet/hooks-react' import { useSelectionService } from '../../hooks' -import { AddButton } from '../AddButton' import { ContentBreadcrumbs } from '../ContentBreadcrumbs' import { CollectionComponent } from '../content-list' import { Tree } from '../tree/index' @@ -70,8 +69,6 @@ export const Explore: React.FunctionComponent = props => fieldsToDisplay={props.fieldsToDisplay} onActiveItemChange={item => selectionService.activeContent.setValue(item)} /> - -
diff --git a/apps/sensenet/src/components/content/Simple.tsx b/apps/sensenet/src/components/content/Simple.tsx index cd863ab38..08231cfac 100644 --- a/apps/sensenet/src/components/content/Simple.tsx +++ b/apps/sensenet/src/components/content/Simple.tsx @@ -6,7 +6,6 @@ import { LoadSettingsContextProvider, } from '@sensenet/hooks-react' import { useSelectionService } from '../../hooks' -import { AddButton } from '../AddButton' import { CollectionComponent, CollectionComponentProps } from '../content-list' export interface SimpleListComponentProps { @@ -36,7 +35,6 @@ export const SimpleList: React.FunctionComponent = pro onActiveItemChange={item => selectionService.activeContent.setValue(item)} {...props.collectionComponentProps} /> - diff --git a/apps/sensenet/src/components/drawer/PermanentDrawer.tsx b/apps/sensenet/src/components/drawer/PermanentDrawer.tsx index 6d1048ed7..dd9d1d7c0 100644 --- a/apps/sensenet/src/components/drawer/PermanentDrawer.tsx +++ b/apps/sensenet/src/components/drawer/PermanentDrawer.tsx @@ -12,21 +12,24 @@ import Settings from '@material-ui/icons/Settings' import { PathHelper } from '@sensenet/client-utils' import React, { useContext, useEffect, useState } from 'react' import { withRouter } from 'react-router' -import { Link, matchPath, NavLink, RouteComponentProps } from 'react-router-dom' +import { matchPath, NavLink, RouteComponentProps } from 'react-router-dom' import { useRepository, useSession } from '@sensenet/hooks-react' import { ResponsiveContext, ResponsivePersonalSetttings } from '../../context' -import { useDrawerItems, useLocalization, usePersonalSettings, useTheme } from '../../hooks' +import { useDrawerItems, useLocalization, usePersonalSettings, useSelectionService, useTheme } from '../../hooks' import { LogoutButton } from '../LogoutButton' import { UserAvatar } from '../UserAvatar' +import { AddButton } from '../AddButton' const PermanentDrawer: React.FunctionComponent = props => { const settings = useContext(ResponsivePersonalSetttings) + const selectionService = useSelectionService() const personalSettings = usePersonalSettings() const theme = useTheme() const session = useSession() const repo = useRepository() const device = useContext(ResponsiveContext) - + const [currentComponent, setCurrentComponent] = useState(selectionService.activeContent.getValue()) + const [currentPath, setCurrentPath] = useState('') const [opened, setOpened] = useState(settings.drawer.type === 'permanent') const items = useDrawerItems() const localization = useLocalization().drawer @@ -35,13 +38,21 @@ const PermanentDrawer: React.FunctionComponent = props => { personalSettings.repositories.find(r => r.url === PathHelper.trimSlashes(repo.configuration.repositoryUrl)), ) - useEffect( - () => - setCurrentRepoEntry( - personalSettings.repositories.find(r => r.url === PathHelper.trimSlashes(repo.configuration.repositoryUrl)), - ), - [personalSettings, repo], - ) + useEffect(() => { + const activeComponentObserve = selectionService.activeContent.subscribe(newActiveComponent => + setCurrentComponent(newActiveComponent), + ) + + return function cleanup() { + activeComponentObserve.dispose() + } + }, [selectionService.activeContent]) + + useEffect(() => { + setCurrentRepoEntry( + personalSettings.repositories.find(r => r.url === PathHelper.trimSlashes(repo.configuration.repositoryUrl)), + ) + }, [personalSettings, repo]) if (!settings.drawer.enabled) { return null @@ -63,28 +74,18 @@ const PermanentDrawer: React.FunctionComponent = props => { transition: 'width 100ms ease-in-out', }}>
+ {items.map((item, index) => { - const isActive = matchPath(props.location.pathname, `/:repositoryId${item.url}`) - return isActive ? ( - - - {item.primaryText}
{item.secondaryText} - - } - placement="right"> - {item.icon} -
- {opened ? : null} -
- ) : ( + return ( - + key={index} + onClick={() => setCurrentPath(item.root ? item.root : '')}> + @@ -121,11 +122,11 @@ const PermanentDrawer: React.FunctionComponent = props => { /> {device === 'mobile' ? null : ( - + - + )} @@ -134,10 +135,10 @@ const PermanentDrawer: React.FunctionComponent = props => { ) : ( <> + key="personalSettings" + onClick={() => setCurrentPath('')}> (decodeQueryData(props.match.params.queryData)) - const [activeParent, setActiveParent] = useState(null as any) - - useEffect(() => { - if (queryData.parentPath && queryData.parentPath.length > 0) { - setActiveParent({ Path: queryData.parentPath } as GenericContent) - } - }, [queryData.parentPath]) - + const selectionService = useSelectionService() const localization = useLocalization().search const [scrollToken, setScrollToken] = useState(Math.random()) const [scrollLock] = useState(new Semaphore(1)) @@ -220,10 +212,11 @@ const Search: React.FunctionComponent { /** */ }} + onSelectionChange={sel => { + selectionService.selection.setValue(sel) + }} + onActiveItemChange={item => selectionService.activeContent.setValue(item)} /> - {queryData.showAddButton && queryData.parentPath && queryData.parentPath.length > 0 ? ( - - ) : null} diff --git a/apps/sensenet/src/hooks/use-drawer-items.tsx b/apps/sensenet/src/hooks/use-drawer-items.tsx index 732c4eceb..6b1124416 100644 --- a/apps/sensenet/src/hooks/use-drawer-items.tsx +++ b/apps/sensenet/src/hooks/use-drawer-items.tsx @@ -29,6 +29,7 @@ export interface DrawerItem { url: string icon: JSX.Element requiredGroupPath: string + root?: string } export const useDrawerItems = () => { @@ -157,6 +158,7 @@ export const useDrawerItems = () => { name: setting.itemType, requiredGroupPath: '', url: getUrlFromSetting(setting), + root: setting.settings?.root, } return drawerItem }, diff --git a/apps/sensenet/src/localization/default.ts b/apps/sensenet/src/localization/default.ts index 9b4effce2..caacd3fbd 100644 --- a/apps/sensenet/src/localization/default.ts +++ b/apps/sensenet/src/localization/default.ts @@ -12,11 +12,13 @@ const values = { addButton: { tooltip: 'Create or upload content', new: 'New...', + addNew: 'Add new', dialogTitle: 'Create new {0}', upload: 'Upload', errorGettingAllowedContentTypes: 'There was an error while fetching the allowed content types.', contentCreatedNotification: `The content '{0}' has been created succesfully.`, errorPostingContentNotification: 'There was an error during content creation', + errorGettingActions: 'There was an error while fetching the actions.', }, commandPalette: { title: 'Show Command Palette', diff --git a/apps/sensenet/src/localization/hungarian.ts b/apps/sensenet/src/localization/hungarian.ts index 456630a74..2efb59ede 100644 --- a/apps/sensenet/src/localization/hungarian.ts +++ b/apps/sensenet/src/localization/hungarian.ts @@ -3,6 +3,7 @@ import { DeepPartial } from '@sensenet/client-utils' const values: DeepPartial = { addButton: { new: 'Új...', + addNew: 'Új hozzáadása', tooltip: 'Tartalom létrehozása vagy feltöltése', upload: 'Feltöltés', dialogTitle: 'Új {0} létrehozása', diff --git a/apps/sensenet/src/services/PersonalSettings.ts b/apps/sensenet/src/services/PersonalSettings.ts index 73f13daa0..0be6b361b 100644 --- a/apps/sensenet/src/services/PersonalSettings.ts +++ b/apps/sensenet/src/services/PersonalSettings.ts @@ -301,25 +301,29 @@ export const defaultSettings: PersonalSettingsType = { }, { itemType: 'Users and groups', - settings: undefined, + settings: { root: '/Root/IMS/Public' }, permissions: [{ path: '/Root/IMS/Public', action: 'Add' }], }, { itemType: 'Trash', - settings: undefined, + settings: { root: '/Root/Trash' }, permissions: [{ path: '/Root/Trash', action: 'Edit' }], }, { itemType: 'Content Types', - settings: undefined, + settings: { root: '/Root/System/Schema/ContentTypes' }, permissions: [{ path: '/Root/System/Schema/ContentTypes', action: 'Add' }], }, { itemType: 'Localization', - settings: undefined, + settings: { root: '/Root/Localization' }, permissions: [{ path: '/Root/Localization', action: 'Add' }], }, - { itemType: 'Setup', settings: undefined, permissions: [{ path: '/Root/System/Settings', action: 'Browse' }] }, + { + itemType: 'Setup', + settings: { root: '/Root/System/Settings' }, + permissions: [{ path: '/Root/System/Settings', action: 'Browse' }], + }, ], }, commandPalette: { enabled: true, wrapQuery: '{0} .AUTOFILTERS:OFF' },