From 62666034718378f4d970331665019b23659c1f3d Mon Sep 17 00:00:00 2001 From: srichman333 Date: Wed, 22 Mar 2023 17:01:17 -0400 Subject: visual changes to collaboration on doc and in PropertiesView --- src/client/views/DocumentDecorations.tsx | 64 +++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 18 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 2982f8a99..d87c13ce6 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,29 @@ 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; + } + // 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 +798,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P undoBatch(e => click!(e)) )) }> - + ); @@ -797,7 +820,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 +843,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P ) : (
{`${hideTitle ? '' : this.selectionTitle}`} - {!useLock ? null : ( - toggle ability to interact with document
} placement="top"> -
e.preventDefault()}> - -
- - )} ); + const sharingMenu = docShareMode ? ( +
+
+ {shareSymbolIcon + " " + shareMode} + {/* {shareMode} */} +
+
) : (
+ ); return (
: topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} {hideResizers || hideDeleteButton ?
: topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} {hideTitle ? null : titleArea} + {sharingMenu} + {hideOpenButton ?
: + 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 ?
: topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')}
{hideResizers ? null : ( -- cgit v1.2.3-70-g09d2 From 4265feca9e63cad6067055497ecabd354ead84f4 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Wed, 22 Mar 2023 17:45:32 -0400 Subject: collaboration icons --- src/client/views/DocumentDecorations.tsx | 3 +++ src/client/views/PropertiesView.tsx | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d87c13ce6..b2b18b245 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -756,6 +756,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P 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 diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index c34760d13..e58558aea 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -504,25 +504,22 @@ export class PropertiesView extends React.Component { // 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(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( @@ -532,7 +529,10 @@ export class PropertiesView extends React.Component { // !ownerSame // ) // ); - docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, doc['acl-Public'], true))); + + SharingManager.Instance.users.forEach(eachUser => tableEntries.unshift(this.sharingItem(eachUser.user.email, false, "test", false))); + + docs.map(doc => tableEntries.unshift(this.sharingItem(doc.author, showAdmin, 'Owner', true))); return (
Sharing Mode -- cgit v1.2.3-70-g09d2 From d1e4d4a9daae4f037cf8b22e1011d97680a01e27 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Tue, 2 May 2023 17:44:00 -0400 Subject: update properties view, document decorations, and sharing manager show + use correct acl --- package-lock.json | 6 - src/client/documents/Documents.ts | 2 +- src/client/util/SharingManager.scss | 8 +- src/client/util/SharingManager.tsx | 126 ++++++++++++++++++--- src/client/views/DocumentDecorations.tsx | 60 ++++++---- src/client/views/PropertiesView.scss | 7 +- src/client/views/PropertiesView.tsx | 124 ++++++++++++++------ .../views/nodes/formattedText/FormattedTextBox.tsx | 18 +-- src/fields/Doc.ts | 7 +- src/fields/util.ts | 47 ++++++-- 10 files changed, 292 insertions(+), 113 deletions(-) (limited to 'src/client/views/DocumentDecorations.tsx') diff --git a/package-lock.json b/package-lock.json index 42a7babb6..d74073e7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22164,12 +22164,6 @@ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", "dev": true }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 3e89c8347..f5510d7e1 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -12,7 +12,7 @@ import { SchemaHeaderField } from '../../fields/SchemaHeaderField'; import { ComputedField, ScriptField } from '../../fields/ScriptField'; import { Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types'; import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from '../../fields/URLField'; -import { inheritParentAcls, SharingPermissions } from '../../fields/util'; +import { inheritParentAcls, normalizeEmail, SharingPermissions } from '../../fields/util'; import { Upload } from '../../server/SharedMediaTypes'; import { aggregateBounds, OmitKeys, Utils } from '../../Utils'; import { YoutubeBox } from '../apis/youtube/YoutubeBox'; diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 932e94664..f47c48805 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -6,7 +6,7 @@ transform: translate(-20px, -20px); } - select { + .select { text-align: justify; text-align-last: end } @@ -212,9 +212,9 @@ cursor: pointer; } - &:hover .padding { - white-space: unset; - } + // &:hover .padding { + // white-space: unset; + // } .padding { padding: 0 10px 0 0; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 3c05af4bb..5e61f6d3c 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -12,6 +12,7 @@ import { Cast, NumCast, PromiseValue, StrCast } from '../../fields/Types'; import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util'; import { Utils } from '../../Utils'; import { DocServer } from '../DocServer'; +import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { CollectionView } from '../views/collections/CollectionView'; import { DictationOverlay } from '../views/DictationOverlay'; import { MainViewModal } from '../views/MainViewModal'; @@ -157,21 +158,42 @@ export class SharingManager extends React.Component<{}> { const acl = `acl-${normalizeEmail(user.email)}`; const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`; const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1; + + // var docs: Doc[] = []; + // const dashboardList = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); + // const dashboard = dashboardList[0] + // docs.push(dashboard!) + // const tabs = DocListCast(dashboard?.data) + // tabs.forEach(tab => { + // docs.push(tab) + // var newDocs = DocListCast(tab.data) + // newDocs.forEach(newDoc => + // docs.push(newDoc)) + // }) + + // if (!docs){ + // return null; + // } const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); + + //var docs: Doc[] = []; + docs.push(targetDoc!) + + // ! ensures it returns true if document has been shared successfully, false otherwise return !docs .map(doc => (this.layoutDocAcls ? doc : doc[DataSym])) .map(doc => { doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard); - + if (permission === SharingPermissions.None) { if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1; } else { if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1; } + doc = targetDoc distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard); - this.setDashboardBackground(doc, permission as SharingPermissions); if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc); @@ -190,8 +212,18 @@ export class SharingManager extends React.Component<{}> { const acl = `acl-${key}`; const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1; - const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document); - + var docs: Doc[] = []; + const dashboardList = SelectionManager.Views().length < 2 ? [this.targetDoc] : SelectionManager.Views().map(docView => docView.props.Document); + const dashboard = dashboardList[0] + const tabs = DocListCast(dashboard?.data) + tabs.forEach(tab => { + var newDocs = DocListCast(tab.data) + newDocs.forEach(newDoc => docs.push(newDoc)) + }) + + if (!docs){ + return null; + } // ! ensures it returns true if document has been shared successfully, false otherwise return !docs .map(doc => (this.layoutDocAcls ? doc : doc[DataSym])) @@ -334,13 +366,20 @@ export class SharingManager extends React.Component<{}> { // targetDoc["acl-" + PublicKey] = permission; // } - // private get sharingUrl() { - // if (!this.targetDoc) { - // return undefined; - // } - // const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]); - // return `${baseUrl}?sharing=true`; - // } + private get sharingUrl() { + if (!this.targetDoc) { + return undefined; + } + const baseUrl = Utils.prepend("/doc/" + this.targetDoc[Id]); + return `${baseUrl}?sharing=true`; + } + + /** + * Copies the Public sharing url to the user's clipboard. + */ + private copyURL = (e: any) => { + Utils.CopyText(this.sharingUrl!) + } // copy = action(() => { // if (this.sharingUrl) { @@ -357,7 +396,7 @@ export class SharingManager extends React.Component<{}> { if (!uniform) dropdownValues.unshift('-multiple-'); if (!override) dropdownValues.splice(dropdownValues.indexOf(SharingPermissions.Unset), 1); return dropdownValues - .filter(permission => !Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any)) + .filter(permission => permission != SharingPermissions.SelfEdit && (!Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any))) .map(permission => (