diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/DocumentDecorations.scss | 81 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 67 | ||||
-rw-r--r-- | src/client/views/PropertiesView.scss | 70 | ||||
-rw-r--r-- | src/client/views/PropertiesView.tsx | 118 | ||||
-rw-r--r-- | src/client/views/StyleProvider.tsx | 7 | ||||
-rw-r--r-- | src/fields/util.ts | 4 |
6 files changed, 279 insertions, 68 deletions
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index ccac5ffe4..ce5378e69 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -112,6 +112,33 @@ $resizeHandler: 8px; } } + .documentDecorations-lockButton { + display: flex; + align-items: center; + justify-content: center; + background: grey; + border: solid 1.5px rgb(72, 71, 71); + color: grey; + transition: 0.1s ease; + opacity: 1; + pointer-events: all; + width: 20px; + height: 20px; + min-width: 20px; + border-radius: 100%; + opacity: 0.5; + cursor: pointer; + + &:hover { + color: rgb(72, 71, 71); + opacity: 1; + } + + > svg { + margin: 0; + } + } + .documentDecorations-minimizeButton { display: flex; align-items: center; @@ -186,6 +213,58 @@ $resizeHandler: 8px; } } + .documentDecorations-share { + background: none; + opacity: 1; + grid-column: 3; + pointer-events: auto; + overflow: hidden; + text-align: center; + width: 100%; + max-width: 120px; + display: flex; + height: 20px; + border-radius: 8px; + border-width: 10px; + opacity: 0.3; + &:hover { + opacity: 1; + } + .documentDecorations-shareNone, + .documentDecorations-shareAdmin{ + width: calc(100% + 10px); + background: grey; + color: rgb(71, 71, 71); + border-color: rgb(71, 71, 71); + } + .documentDecorations-shareEdit, + .documentDecorations-shareSelf-Edit{ + width: calc(100% + 10px); + background: rgb(235, 235, 145); + color: rgb(75, 75, 5); + border-color: rgb(75, 75, 5); + } + .documentDecorations-shareAugment{ + width: calc(100% + 10px); + background: rgb(160, 230, 160); + color:rgb(19, 80, 19); + border-color:rgb(19, 80, 19); + + } + .documentDecorations-shareView{ + width: calc(100% + 10px); + background: rgb(161, 161, 238); + color: rgb(25, 25, 101); + border-color: rgb(25, 25, 101);; + } + .documentDecorations-shareNot-Shared{ + width: calc(100% + 10px); + background: rgb(210, 143, 143); + color: rgb(146, 58, 58); + border-color: rgb(146, 58, 58);; + } + } + .documentDecorations-centerCont { grid-column: 2; background: none; @@ -264,7 +343,7 @@ $resizeHandler: 8px; .documentDecorations-lock { position: relative; background: black; - color: gray; + color: rgb(145, 144, 144); height: 14; width: 14; pointer-events: all; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 2982f8a99..b2b18b245 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,6 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; +import { StylesProvider, Tooltip } from '@material-ui/core'; import { IconButton } from 'browndash-components'; import { action, computed, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -11,7 +11,7 @@ import { Document } from '../../fields/documentSchemas'; import { InkField } from '../../fields/InkField'; import { ScriptField } from '../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../fields/Types'; -import { GetEffectiveAcl } from '../../fields/util'; +import { GetEffectiveAcl, SharingPermissions } from '../../fields/util'; import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents, Utils } from '../../Utils'; import { Docs } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; @@ -27,10 +27,11 @@ import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { InkStrokeProperties } from './InkStrokeProperties'; import { LightboxView } from './LightboxView'; -import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView, OpenWhereMod } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; import React = require('react'); +import { SharingManager } from '../util/SharingManager'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -283,12 +284,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc); Doc.deiconifyView(openDoc); } - selectedDocs[0].props.addDocTab(openDoc, OpenWhere.lightbox); - // LightboxView.SetLightboxDoc( - // openDoc, - // undefined, - // selectedDocs.slice(1).map(view => view.props.Document) - // ); + LightboxView.SetLightboxDoc( + openDoc, + undefined, + selectedDocs.slice(1).map(view => view.props.Document) + ); } } SelectionManager.DeselectAll(); @@ -735,6 +735,32 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P setTimeout(action(() => (this._showNothing = true))); return null; } + + // sharing + const docShareMode = seldocview.rootDoc["acl-Public"]; + const shareMode = StrCast(docShareMode); + var shareSymbolIcon = null; + switch(shareMode){ + case ("Edit"): + shareSymbolIcon = "⬢ "; + break; + case ("Self-Edit"): + shareSymbolIcon = "⬢ "; + break; + case ("Augment"): + shareSymbolIcon = "⬟ "; + break; + case ("View"): + shareSymbolIcon = "♦ "; + break; + case ("Not-Shared"): + shareSymbolIcon = "▲ "; + break; + default: + shareSymbolIcon = ""; + break; + } + // hide the decorations if the parent chooses to hide it or if the document itself hides it const hideDecorations = seldocview.props.hideDecorations || seldocview.rootDoc.hideDecorations; const hideResizers = hideDecorations || seldocview.props.hideResizeHandles || seldocview.rootDoc.hideResizeHandles || seldocview.rootDoc._isGroup || this._isRounding || this._isRotating; @@ -775,7 +801,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P undoBatch(e => click!(e)) )) }> - <FontAwesomeIcon icon={icon as any} /> + <FontAwesomeIcon icon={ icon as any} /> </div> </Tooltip> ); @@ -797,7 +823,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : ''; // Radius constants - const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; + const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox; const borderRadius = numberValue(StrCast(seldocview.rootDoc.borderRounding)); const docMax = Math.min(NumCast(seldocview.rootDoc.width) / 2, NumCast(seldocview.rootDoc.height) / 2); const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2); @@ -820,15 +846,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P ) : ( <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown}> <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${hideTitle ? '' : this.selectionTitle}`}</span> - {!useLock ? null : ( - <Tooltip key="lock" title={<div className="dash-tooltip">toggle ability to interact with document</div>} placement="top"> - <div className="documentDecorations-lock" style={{ color: seldocview.rootDoc._lockedPosition ? 'red' : undefined }} onPointerDown={this.onLockDown} onContextMenu={e => e.preventDefault()}> - <FontAwesomeIcon size="sm" icon="lock" /> - </div> - </Tooltip> - )} </div> ); + const sharingMenu = docShareMode ? ( + <div className={'documentDecorations-share'}> + <div className={`documentDecorations-share${shareMode}`}> + <span>{shareSymbolIcon + " " + shareMode}</span> + {/* <span>{shareMode}</span> */} + </div> + </div> ) : (<div/> + ); return ( <div className={`documentDecorations${colorScheme}`} style={{ opacity: this._showNothing ? 0.1 : undefined }}> <div @@ -862,6 +889,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P {hideDeleteButton ? <div /> : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} {hideResizers || hideDeleteButton ? <div /> : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} {hideTitle ? null : titleArea} + {sharingMenu} + {hideOpenButton ? <div /> : + seldocview.rootDoc._lockedPosition? topBtn('lock', 'lock', this.onLockDown, undefined, 'Toggle ability to interact with document') + : topBtn('lock', 'unlock', this.onLockDown, undefined, 'Toggle ability to interact with document')} {hideOpenButton ? <div /> : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')} </div> {hideResizers ? null : ( diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index 897be9a32..3edc7fea8 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -160,6 +160,50 @@ } } + .propertiesView-shareDropDown{ + margin-right: 10px; + width: 92%; + + & .propertiesView-shareDropDownNone, + .propertiesView-shareDropDownAdmin{ + padding: 5px; + background: grey; + color: rgb(71, 71, 71); + border-radius: 6px; + border: 1px solid rgb(71, 71, 71); + } + & .propertiesView-shareDropDownEdit, + .propertiesView-shareDropDownSelf-Edit{ + padding: 5px; + background: rgb(235, 235, 145); + color: rgb(75, 75, 5); + border-radius: 6px; + border: 1px solid rgb(75, 75, 5); + } + & .propertiesView-shareDropDownAugment{ + padding: 5px; + background: rgb(160, 230, 160); + color:rgb(19, 80, 19); + border-radius: 6px; + border: 1px solid rgb(19, 80, 19); + + } + & .propertiesView-shareDropDownView{ + padding: 5px; + background: rgb(161, 161, 238); + color: rgb(25, 25, 101); + border-radius: 6px; + border: 1px solid rgb(25, 25, 101); + } + & .propertiesView-shareDropDownNot-Shared{ + padding: 5px; + background: rgb(210, 143, 143); + color: rgb(138, 47, 47); + border-radius: 6px; + border: 1px solid rgb(138, 47, 47); + } + } + .propertiesView-filters { //border-bottom: 1px solid black; //padding: 8.5px; @@ -344,7 +388,6 @@ // display: inline-table; background-color: #ececec; max-height: 130px; - overflow-y: auto; width: 92%; .propertiesView-sharingTable-item { @@ -373,18 +416,6 @@ display: flex; align-items: flex-end; margin-left: auto; - - .permissions-select { - border: none; - background-color: inherit; - width: 87px; - text-align: justify; // for Edge - text-align-last: end; - - &:hover { - cursor: pointer; - } - } } &:last-child { @@ -393,6 +424,19 @@ } } + .propertiesView-permissions-select { + background-color: inherit; + background: inherit; + border: none; + background: inherit; + width: 87px; + text-align: justify; // for Edge + text-align-last: end; + &:hover { + cursor: pointer; + } + } + .propertiesView-fields { //border-bottom: 1px solid black; //padding: 8.5px; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index f3a5a5393..dfc43e6c8 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -7,7 +7,7 @@ import { intersection } from 'lodash'; import { action, computed, Lambda, observable } from 'mobx'; import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; -import { AclAdmin, AclSym, DataSym, Doc, Field, HeightSym, HierarchyMapping, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc'; +import { AclAdmin, AclSym, HierarchyMapping, DataSym, Doc, DocListCast, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { List } from '../../fields/List'; @@ -366,8 +366,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { */ @undoBatch changePermissions = (e: any, user: string) => { - const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym])); - SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs); + if (user=="Public"){ + this.selectedDoc!['acl-' +user] = e.currentTarget.value as SharingPermissions; + } + else{ + const docs = (SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(dv => dv.props.Document)).filter(doc => doc).map(doc => (this.layoutDocAcls ? doc : DocCast(doc)[DataSym])); + SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs); + } }; /** @@ -378,11 +383,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { if (permission === '-multiple-') dropdownValues.unshift(permission); if (user !== 'Override') dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1); return ( - <select className="permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}> + <select className="propertiesView-permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}> {dropdownValues .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)) .map(permission => ( - <option key={permission} value={permission}> + <option className="propertiesView-permisssions-select" key={permission} value={permission}> {' '} {permission}{' '} </option> @@ -435,8 +440,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)} > <div className="propertiesView-sharingTable-item-name" style={{ width: name !== 'Me' ? '85px' : '80px' }}> - {' '} - {name}{' '} + {' '}{name}{' '} </div> {/* {name !== "Me" ? this.notifyIcon : null} */} <div className="propertiesView-sharingTable-item-permission"> @@ -447,13 +451,52 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { ); } + publicACLDropDown(admin: boolean, permission: string, showExpansionIcon?: boolean){ + var dropDownText = ""; + switch(StrCast(permission)){ + case ("Edit"): + dropDownText = "⬢ "; + break; + case ("Self-Edit"): + dropDownText = "⬢ "; + break; + case ("Augment"): + dropDownText = "⬟ "; + break; + case ("View"): + dropDownText = "♦ "; + break; + case ("Not-Shared"): + dropDownText = "▲ "; + break; + } + return ( + <div> + <div className={'propertiesView-shareDropDown'}> + <div className={`propertiesView-shareDropDown${permission}`}> + <div className="propertiesView-shareDropDown"> + {' '}{dropDownText}{' '} + {admin && permission !== 'Owner' ? this.getPermissionsSelect("Public", permission) : permission} + {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null} + </div> + </div> + </div> + </div> + ); + } + /** * @returns the sharing and permissions panel. */ @computed get sharingTable() { + // const docToUse = this.selectedDocumentView?.rootDoc; + const docToUse = this.selectedDoc; + if (!docToUse){ + return null; + } // all selected docs const docs = - SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym])); + SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? docToUse : docToUse?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym])); const target = docs[0]; @@ -463,35 +506,45 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { // users in common between all docs const commonKeys: string[] = intersection(...docs.map(doc => doc?.[AclSym] && Object.keys(doc[AclSym]).filter(key => key !== 'acl-Me'))); - const tableEntries = []; // DocCastAsync(Doc.UserDoc().sidebarUsersDisplayed).then(sidebarUsersDisplayed => { - if (commonKeys.length) { - for (const key of commonKeys) { - const name = denormalizeEmail(key.substring(4)); - const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]); - if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) { - tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-')); - } - } - } - + // if (commonKeys.length) { + // for (const key of commonKeys) { + // const name = denormalizeEmail(key.substring(4)); + // const uniform = docs.every(doc => doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key]); + // if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) { + // tableEntries.push(this.sharingItem(name, showAdmin, uniform ? HierarchyMapping.get(target[AclSym][key])!.name : '-multiple-')); + // } + // } + // } const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author); // shifts the current user, owner, public to the top of the doc. // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-")); if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner')); - tableEntries.unshift(this.sharingItem('Public', showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-')); - tableEntries.unshift( - this.sharingItem( - 'Me', - showAdmin, - docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-', - !ownerSame - ) - ); + // tableEntries.unshift(this.sharingItem(target.author, showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === target['acl-Public']) ? target['acl-Public'] || SharingPermissions.None : '-multiple-')); + // tableEntries.unshift( + // this.sharingItem( + // 'Me', + // showAdmin, + // docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? HierarchyMapping.get(effectiveAcls[0])!.name : '-multiple-', + // !ownerSame + // ) + // ); + + SharingManager.Instance.users.forEach(eachUser => tableEntries.unshift(this.sharingItem(eachUser.user.email, false, "test", false))); - return <div className="propertiesView-sharingTable">{tableEntries}</div>; + docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, 'Owner', true))); + + return ( + <div> Sharing Mode + <div>{this.publicACLDropDown(true, StrCast(docToUse['acl-Public']), false)}</div> + <div> <br></br> Who has access to the Dashboard? </div> + <div className="propertiesView-sharingTable">{ + <div> {tableEntries}</div> + }</div> + </div> + ); } @computed get fieldsCheckbox() { @@ -1118,12 +1171,15 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { {!this.openSharing ? null : ( <div className="propertiesView-sharing-content"> <div className="propertiesView-buttonContainer"> - {!Doc.noviceMode ? ( + + + {/* {!Doc.noviceMode ? ( // what is the layout checkbox for? <div className="propertiesView-acls-checkbox"> <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <div className="propertiesView-acls-checkbox-text">Layout</div> </div> - ) : null} + ) : null} */} + {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}> <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}> <FontAwesomeIcon icon="redo-alt" color="white" size="1x" /> diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 096396a41..984b52ac8 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -302,6 +302,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}> <FontAwesomeIcon icon={lockedPosition() ? 'lock' : 'unlock'} style={{ color: lockedPosition() ? 'red' : undefined }} size="lg" /> </div> + // <div/> ) : null; } } @@ -331,9 +332,9 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps <> {DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => { doc.hidden = doc.hidden ? undefined : true; - if (!doc.hidden) { - DocFocusOrOpen(doc, props?.ContainingCollectionDoc); - } + // if (!doc.hidden) { + // DocFocusOrOpen(doc, props?.ContainingCollectionDoc); + // } })} </> ); diff --git a/src/fields/util.ts b/src/fields/util.ts index 70d9ed61f..eca4d1351 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -167,10 +167,10 @@ export enum SharingPermissions { Unset = 'None', Admin = 'Admin', Edit = 'Edit', - SelfEdit = 'Self Edit', + SelfEdit = 'Self-Edit', Augment = 'Augment', View = 'View', - None = 'Not Shared', + None = 'Not-Shared', } // return acl from cache or cache the acl and return. |