build new permissions modal (#532)

build new permissions modal

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
Co-authored-by: Erik Michelson <github@erik.michelson.eu>
Co-authored-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
Philip Molares 2020-09-08 18:16:13 +02:00 committed by GitHub
parent d500ebcd19
commit ac00bc98c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 350 additions and 27 deletions

View file

@ -1,23 +0,0 @@
import React, { Fragment, useState } from 'react'
import { Modal } from 'react-bootstrap'
import { CommonModal } from '../../../common/modals/common-modal'
import { TranslatedIconButton } from '../../../common/icon-button/translated-icon-button'
export const PermissionButton: React.FC = () => {
const [showReadOnly, setShowReadOnly] = useState(false)
return (
<Fragment>
<TranslatedIconButton size={'sm'} className={'mx-1'} icon={'lock'} variant={'light'} onClick={() => setShowReadOnly(true)} i18nKey={'editor.documentBar.permissions'}/>
<CommonModal
show={showReadOnly}
onHide={() => setShowReadOnly(false)}
closeButton={true}
titleI18nKey={'editor.modal.permissions.title'}>
<Modal.Body>
<img className={'w-100'} src={'https://thumbs.gfycat.com/ImpassionedDeliriousIndianpalmsquirrel-size_restricted.gif'} alt={'Placeholder'}/>
</Modal.Body>
</CommonModal>
</Fragment>
)
}

View file

@ -1,13 +1,13 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import { PinToHistoryButton } from './buttons/pin-to-history-button'
import { ShareLinkButton } from './buttons/share-link-button'
import { ConnectionIndicator } from './connection-indicator/connection-indicator'
import { DocumentInfoButton } from './document-info/document-info-button'
import { EditorMenu } from './menus/editor-menu'
import { ExportMenu } from './menus/export-menu'
import { ImportMenu } from './menus/import-menu'
import { PermissionButton } from './buttons/permission-button'
import { PinToHistoryButton } from './buttons/pin-to-history-button'
import { ShareLinkButton } from './buttons/share-link-button'
import { PermissionButton } from './permissions/permission-button'
import { RevisionButton } from './revisions/revision-button'
export interface DocumentBarProps {

View file

@ -0,0 +1,14 @@
import React, { Fragment, useState } from 'react'
import { TranslatedIconButton } from '../../../common/icon-button/translated-icon-button'
import { PermissionModal } from './permission-modal'
export const PermissionButton: React.FC = () => {
const [showPermissionModal, setShowPermissionModal] = useState(false)
return (
<Fragment>
<TranslatedIconButton size={'sm'} className={'mx-1'} icon={'lock'} variant={'light'} onClick={() => setShowPermissionModal(true)} i18nKey={'editor.documentBar.permissions'}/>
<PermissionModal show={showPermissionModal} onChangeShow={setShowPermissionModal}/>
</Fragment>
)
}

View file

@ -0,0 +1,57 @@
import React from 'react'
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
export interface PermissionGroupEntryProps {
title: string
editMode: GroupMode
onChangeEditMode: (newMode: GroupMode) => void
}
export enum GroupMode {
NONE,
VIEW,
EDIT,
}
export const PermissionGroupEntry: React.FC<PermissionGroupEntryProps> = ({ title, editMode, onChangeEditMode }) => {
const { t } = useTranslation()
return (
<li className={'list-group-item d-flex flex-row justify-content-between align-items-center'}>
<Trans i18nKey={title}/>
<ToggleButtonGroup
type='radio'
name='edit-mode'
value={editMode}
onChange={onChangeEditMode}
>
<ToggleButton
title={ t('editor.modal.permissions.denyGroup', { name: t(title) })}
variant={'light'}
className={'text-secondary'}
value={GroupMode.NONE}
>
<ForkAwesomeIcon icon='ban'/>
</ToggleButton>
<ToggleButton
title={ t('editor.modal.permissions.viewOnlyGroup', { name: t(title) })}
variant={'light'}
className={'text-secondary'}
value={GroupMode.VIEW}
>
<ForkAwesomeIcon icon='eye'/>
</ToggleButton>
<ToggleButton
title={t('editor.modal.permissions.editGroup', { name: t(title) })}
variant={'light'}
className={'text-secondary'}
value={GroupMode.EDIT}
>
<ForkAwesomeIcon icon='pencil'/>
</ToggleButton>
</ToggleButtonGroup>
</li>
)
}

View file

@ -0,0 +1,98 @@
import React, { ReactElement, useState } from 'react'
import { Button, FormControl, InputGroup, ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
import { Principal } from './permission-modal'
export interface PermissionListProps {
list: Principal[]
identifier: (entry: Principal) => ReactElement
changeEditMode: (id: Principal['id'], canEdit: Principal['canEdit']) => void
removeEntry: (id: Principal['id']) => void
createEntry: (name: Principal['name']) => void
editI18nKey: string
viewI18nKey: string
removeI18nKey: string
addI18nKey: string
}
export enum EditMode {
VIEW,
EDIT
}
export const PermissionList: React.FC<PermissionListProps> = ({ list, identifier, changeEditMode, removeEntry, createEntry, editI18nKey, viewI18nKey, removeI18nKey, addI18nKey }) => {
const { t } = useTranslation()
const [newEntry, setNewEntry] = useState('')
const addEntry = () => {
createEntry(newEntry)
setNewEntry('')
}
return (
<ul className={'list-group'}>
{list.map(entry => (
<li key={entry.id} className={'list-group-item d-flex flex-row justify-content-between align-items-center'}>
{identifier(entry)}
<div>
<Button
variant='light'
className={'text-danger mr-2'}
title={t(removeI18nKey, { name: entry.name })}
onClick={() => removeEntry(entry.id)}
>
<ForkAwesomeIcon icon={'times'}/>
</Button>
<ToggleButtonGroup
type='radio'
name='edit-mode'
value={entry.canEdit ? EditMode.EDIT : EditMode.VIEW}
onChange={(value: EditMode) => changeEditMode(entry.id, value === EditMode.EDIT)}
>
<ToggleButton
title={ t(viewI18nKey, { name: entry.name })}
variant={'light'}
className={'text-secondary'}
value={EditMode.VIEW}
>
<ForkAwesomeIcon icon='eye'/>
</ToggleButton>
<ToggleButton
title={t(editI18nKey, { name: entry.name })}
variant={'light'}
className={'text-secondary'}
value={EditMode.EDIT}
>
<ForkAwesomeIcon icon='pencil'/>
</ToggleButton>
</ToggleButtonGroup>
</div>
</li>
))}
<li className={'list-group-item'}>
<form onSubmit={event => {
event.preventDefault()
addEntry()
}}>
<InputGroup className={'mr-1 mb-1'}>
<FormControl
value={newEntry}
placeholder={t(addI18nKey)}
aria-label={t(addI18nKey)}
onChange={event => setNewEntry(event.currentTarget.value)}
/>
<Button
variant='light'
className={'text-secondary ml-2'}
title={t(addI18nKey)}
onClick={addEntry}
>
<ForkAwesomeIcon icon={'plus'}/>
</Button>
</InputGroup>
</form>
</li>
</ul>
)
}

View file

@ -0,0 +1,163 @@
import React, { useEffect, useState } from 'react'
import { Alert, Modal } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { getUserById } from '../../../../api/users'
import { CommonModal } from '../../../common/modals/common-modal'
import { ShowIf } from '../../../common/show-if/show-if'
import { UserAvatar, UserAvatarProps } from '../../../common/user-avatar/user-avatar'
import { GroupMode, PermissionGroupEntry } from './permission-group-entry'
import { PermissionList } from './permission-list'
export interface PermissionsModalProps {
show: boolean,
onChangeShow: (newShow: boolean) => void
}
export interface Principal {
id: string
name: string
photo: string
canEdit: boolean
}
interface NotePermissions {
owner: string
sharedTo: {
username: string
canEdit: boolean
}[],
sharedToGroup: {
id: string
canEdit: boolean
}[]
}
export const EVERYONE_GROUP_ID = '1'
export const EVERYONE_LOGGED_IN_GROUP_ID = '2'
const permissionsApiResponse: NotePermissions = {
owner: 'dermolly',
sharedTo: [{
username: 'emcrx',
canEdit: true
}, {
username: 'mrdrogdrog',
canEdit: false
}],
sharedToGroup: [{
id: EVERYONE_GROUP_ID,
canEdit: true
}, {
id: EVERYONE_LOGGED_IN_GROUP_ID,
canEdit: false
}]
}
export const PermissionModal: React.FC<PermissionsModalProps> = ({ show, onChangeShow }) => {
useTranslation()
const [error, setError] = useState(false)
const [userList, setUserList] = useState<Principal[]>([])
const [owner, setOwner] = useState<UserAvatarProps>()
const [allUserPermissions, setAllUserPermissions] = useState(GroupMode.NONE)
const [allLoggedInUserPermissions, setAllLoggedInUserPermissions] = useState(GroupMode.NONE)
useEffect(() => {
// set owner
getUserById(permissionsApiResponse.owner).then(response => {
setOwner({
name: response.name,
photo: response.photo
})
}).catch(() => setError(true))
// set user List
permissionsApiResponse.sharedTo.forEach(shareUser => {
getUserById(shareUser.username).then(response => {
setUserList(list => list.concat([{
id: response.id,
name: response.name,
photo: response.photo,
canEdit: shareUser.canEdit
}]))
}).catch(() => setError(true))
})
// set group List
permissionsApiResponse.sharedToGroup.forEach(sharedGroup => {
if (sharedGroup.id === EVERYONE_GROUP_ID) {
setAllUserPermissions(sharedGroup.canEdit ? GroupMode.EDIT : GroupMode.VIEW)
} else if (sharedGroup.id === EVERYONE_LOGGED_IN_GROUP_ID) {
setAllLoggedInUserPermissions(sharedGroup.canEdit ? GroupMode.EDIT : GroupMode.VIEW)
}
})
}, [])
const changeUserMode = (userId: Principal['id'], canEdit: Principal['canEdit']) => {
setUserList(list =>
list
.map(user => {
if (user.id === userId) {
user.canEdit = canEdit
}
return user
}))
}
const removeUser = (userId: Principal['id']) => {
setUserList(list => list.filter(user => user.id !== userId))
}
const addUser = (name: Principal['name']) => {
setUserList(list => list.concat({
id: name,
photo: '/avatar.png',
name: name,
canEdit: false
}))
}
return (
<CommonModal
show={show}
onHide={() => onChangeShow(false)}
closeButton={true}
titleI18nKey={'editor.modal.permissions.title'}>
<Modal.Body>
<h5 className={'mb-3'}><Trans i18nKey={'editor.modal.permissions.owner'}/></h5>
<ShowIf condition={error}>
<Alert variant='danger'>
<Trans i18nKey='editor.modal.permissions.error'/>
</Alert>
</ShowIf>
<ul className={'list-group'}>
<li className={'list-group-item d-flex flex-row align-items-center'}>
<UserAvatar name={owner?.name ?? ''} photo={owner?.photo ?? ''}/>
</li>
</ul>
<h5 className={'my-3'}><Trans i18nKey={'editor.modal.permissions.sharedWithUsers'}/></h5>
<PermissionList
list={userList}
identifier={entry => (<UserAvatar name={entry.name} photo={entry.photo}/>)}
changeEditMode={changeUserMode}
removeEntry={removeUser}
createEntry={addUser}
editI18nKey={'editor.modal.permissions.editUser'}
viewI18nKey={'editor.modal.permissions.viewOnlyUser'}
removeI18nKey={'editor.modal.permissions.removeUser'}
addI18nKey={'editor.modal.permissions.addUser'}
/>
<h5 className={'my-3'}><Trans i18nKey={'editor.modal.permissions.sharedWithGroups'}/></h5>
<ul className={'list-group'}>
<PermissionGroupEntry
title={'editor.modal.permissions.allUser'}
editMode={allUserPermissions}
onChangeEditMode={setAllUserPermissions}
/>
<PermissionGroupEntry
title={'editor.modal.permissions.allLoggedInUser'}
editMode={allLoggedInUserPermissions}
onChangeEditMode={setAllLoggedInUserPermissions}
/>
</ul>
</Modal.Body>
</CommonModal>
)
}