diff options
author | Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com> | 2022-09-22 13:10:58 -0400 |
---|---|---|
committer | Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com> | 2022-09-22 13:10:58 -0400 |
commit | 1ec9505775315d0ba41b114942ff69d8ceb93e04 (patch) | |
tree | 3dde9bb324814b419b4b5a27d9be3c30dd81d993 | |
parent | 604b3e566eaf762ede05f4a31c459c20d0df020b (diff) | |
parent | 40cc7455e853d306ee2750c51308d095dc2970ef (diff) |
Merge branch 'master' of https://github.com/brown-dash/Dash-Web
-rw-r--r-- | src/client/Network.ts | 11 | ||||
-rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
-rw-r--r-- | src/client/goldenLayout.js | 13 | ||||
-rw-r--r-- | src/client/views/ContextMenu.scss | 2 | ||||
-rw-r--r-- | src/client/views/DashboardView.tsx | 7 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 25 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/global/globalCssVariables.scss | 2 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.scss | 25 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 27 | ||||
-rw-r--r-- | src/client/views/topbar/TopBar.tsx | 87 | ||||
-rw-r--r-- | src/fields/Doc.ts | 2 | ||||
-rw-r--r-- | src/server/ApiManagers/UploadManager.ts | 12 |
14 files changed, 86 insertions, 133 deletions
diff --git a/src/client/Network.ts b/src/client/Network.ts index a222b320f..996eb35d8 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -30,6 +30,17 @@ export namespace Networking { if (!files.length) { return []; } + const maxFileSize = 50000000; + if (files.some(f => f.size > maxFileSize)) { + return new Promise<any>(res => + res([ + { + source: { name: '', type: '', size: 0, toJson: () => ({ name: '', type: '' }) }, + result: { name: '', message: `max file size (${maxFileSize / 1000000}MB) exceeded` }, + }, + ]) + ); + } files.forEach(file => formData.append(Utils.GenerateGuid(), file)); } else { formData.append(Utils.GenerateGuid(), files); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4f652b6e4..029c3033d 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1864,7 +1864,7 @@ export namespace DocUtils { const { source: { name, type }, result, - } = upfiles.lastElement(); + } = upfiles.lastElement() ?? { source: { name: '', type: '' }, result: { message: 'upload failed' } }; if ((result as any).message) { if (overwriteDoc) { overwriteDoc.isLoading = false; diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index e5cd50de2..dd11e6466 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -3263,11 +3263,6 @@ const canDelete = rowOrCol && !rowOrCol.isRoot && (rowOrCol.contentItems.length > 1 || (parRowOrCol && parRowOrCol.contentItems.length > 1)); // bcz: added test for last stack if (canDelete) { rowOrCol.removeChild(stack); - if (rowOrCol.contentItems.length === 1 && parRowOrCol.contentItems.length === 1 && !parRowOrCol.isRoot) { - saveScrollTops(rowOrCol.contentItems[0].element); - parRowOrCol.replaceChild(rowOrCol, rowOrCol.contentItems[0]); - restoreScrollTops(rowOrCol.contentItems[0].element); - } } } }, @@ -4061,6 +4056,14 @@ lm.items.AbstractContentItem.prototype.removeChild.call(this, contentItem, keepChild); if (this.contentItems.length === 1 && this.config.isClosable === true) { + if (["row","column"].includes(this.contentItems[0].type) || ["row","column"].includes(this.parent.type)) { + let parent = this.parent; + let correctRowOrCol = this.contentItems[0]; + saveScrollTops(correctRowOrCol.element); + parent.replaceChild(this, correctRowOrCol); + restoreScrollTops(correctRowOrCol.element); + } + // bcz: this has the effect of removing children from the DOM and then re-adding them above where they were before. // in the case of things like an iFrame with a YouTube video, the video will reload for now reason. So let's try leaving these "empty" rows alone. // childItem = this.contentItems[0]; diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 1e6a377de..cbe14060a 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -3,7 +3,7 @@ .contextMenu-cont { position: absolute; display: flex; - z-index: 100000; + z-index: $contextMenu-zindex; box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%); flex-direction: column; background: whitesmoke; diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 911b53945..11bb0c6a8 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -370,11 +370,12 @@ export class DashboardView extends React.Component { system: true, explainer: 'All of the trails that you have created will appear here.', }; - dashboardDoc.myTrails = new PrefetchProxy(DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' })); + const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' }); + dashboardDoc.myTrails = new PrefetchProxy(myTrails); const contextMenuScripts = [reqdBtnScript.onClick]; - if (Cast(Doc.MyTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { - Doc.MyTrails.contextMenuScripts = new List<ScriptField>(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); + if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { + myTrails.contextMenuScripts = new List<ScriptField>(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 797730c10..4675571c2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -31,6 +31,7 @@ import { ImageBox } from './nodes/ImageBox'; import React = require('react'); import { IconButton } from 'browndash-components'; import { FaUndo } from 'react-icons/fa'; +import { VideoBox } from './nodes/VideoBox'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -678,7 +679,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight)); // Rotation constants: Only allow rotation on ink and images - const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox; + const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox || seldoc.ComponentView instanceof VideoBox; const rotation = NumCast(seldoc.rootDoc._jitterRotation); const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : ''; @@ -721,12 +722,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px', }}> - {hideResizers ? null : (<div className="documentDecorations-topbar"> - {hideDeleteButton ? <div /> : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} - {hideDeleteButton ? <div /> : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} - {titleArea} - {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Tab (ctrl: as alias, shift: in new collection)')} - </div>)} + {hideResizers ? null : ( + <div className="documentDecorations-topbar"> + {hideDeleteButton ? <div /> : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} + {hideDeleteButton ? <div /> : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} + {titleArea} + {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Tab (ctrl: as alias, shift: in new collection)')} + </div> + )} {hideResizers ? null : ( <> <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} /> @@ -743,9 +746,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P </> )} - {useRotation && <div key="rot" className={`documentDecorations-rotation`} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}> - <IconButton icon={<FaUndo/>} isCircle={true} hoverStyle={"lighten"} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY}/> - </div>} + {useRotation && ( + <div key="rot" className={`documentDecorations-rotation`} onPointerDown={this.onRotateDown} onContextMenu={e => e.preventDefault()}> + <IconButton icon={<FaUndo />} isCircle={true} hoverStyle={'lighten'} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY} /> + </div> + )} {useRounding && ( <div diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 25fe5fe43..c6ac5ee0c 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -477,6 +477,7 @@ export class CollectionDockingView extends CollectionSubView() { }; stackCreated = (stack: any) => { + stack = stack.header ? stack : stack.origin; stack.header?.element.on('mousedown', (e: any) => { const dashboard = Doc.ActiveDashboard; if (dashboard && e.target === stack.header?.element[0] && e.button === 2) { @@ -499,7 +500,7 @@ export class CollectionDockingView extends CollectionSubView() { .click( action(() => { //if (confirm('really close this?')) { - if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) { + if ((!stack.parent.isRoot && !stack.parent.parent.isRoot) || stack.parent.contentItems.length > 1) { stack.remove(); } else { alert('cant delete the last stack'); diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss index ac2c04aaa..e68f9abe3 100644 --- a/src/client/views/global/globalCssVariables.scss +++ b/src/client/views/global/globalCssVariables.scss @@ -47,7 +47,7 @@ $topbar-height: 37px; $antimodemenu-height: 36px; // dragged items -$contextMenu-zindex: 100000; // context menu shows up over everything +$contextMenu-zindex: 100002; // context menu shows up over everything $radialMenu-zindex: 100000; // context menu shows up over everything // borders diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 4bcd79641..f7f558bb4 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -405,6 +405,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp } if (Number.isFinite(this.player!.duration)) { this.rawDuration = this.player!.duration; + this.dataDoc[this.fieldKey + '-duration'] = this.rawDuration; } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + '-duration']); }); diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index a0a2dd4f8..34df415ac 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -1,4 +1,4 @@ -@import "../../global/globalCssVariables"; +@import '../../global/globalCssVariables'; .presBox-cont { cursor: auto; @@ -231,8 +231,7 @@ margin-top: 10px; } - @media screen and (-webkit-min-device-pixel-ratio:0) { - + @media screen and (-webkit-min-device-pixel-ratio: 0) { .multiThumb-slider { display: grid; background-color: $white; @@ -289,6 +288,7 @@ height: 10px; -webkit-appearance: none; margin-top: -1px; + background: transparent; } .toolbar-slider::-webkit-slider-thumb { @@ -330,8 +330,6 @@ } } - - .slider-headers { position: relative; display: grid; @@ -382,7 +380,6 @@ border-bottom: solid 2px $medium-gray; } - .ribbon-textInput { border-radius: 2px; height: 20px; @@ -467,7 +464,6 @@ font-weight: 500; position: relative; - .ribbon-final-button { cursor: pointer; position: relative; @@ -687,7 +683,6 @@ max-width: 200px; overflow: visible; - .presBox-dropdownOption { cursor: pointer; font-size: 11; @@ -716,7 +711,7 @@ width: 85%; min-width: max-content; display: block; - background: #FFFFFF; + background: #ffffff; border: 0.5px solid #979797; box-sizing: border-box; box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); @@ -741,7 +736,7 @@ padding-top: 5px; padding-bottom: 5px; border: solid 1px $black; - // overflow: auto; + // overflow: auto; ::-webkit-scrollbar { -webkit-appearance: none; @@ -953,8 +948,6 @@ min-width: 150px; } - - select { background: $dark-gray; color: $white; @@ -999,8 +992,6 @@ } } - - .collectionViewBaseChrome-viewPicker { min-width: 50; width: 5%; @@ -1080,7 +1071,7 @@ position: absolute; top: 0; left: 0; - opacity: 0.1; + opacity: 0.5; transition: all 0.4s; color: $white; width: 100%; @@ -1165,8 +1156,6 @@ .presPanel-button-text:hover { background-color: $medium-gray; } - - } // .miniPres { @@ -1241,4 +1230,4 @@ // background-color: #5a5a5a; // } // } -// }
\ No newline at end of file +// } diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 40cda4f7e..de19f831d 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -234,8 +234,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time // TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions // No more frames in current doc and next slide is defined, therefore move to next slide - nextSlide = (activeNext: Doc) => { - let nextSelected = this.itemIndex + 1; + nextSlide = (slideNum?: number) => { + let nextSelected = slideNum ?? this.itemIndex + 1; this.gotoDocument(nextSelected, this.activeItem); for (nextSelected = nextSelected + 1; nextSelected < this.childDocs.length; nextSelected++) { if (this.childDocs[nextSelected].groupWithUp) { @@ -261,10 +261,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this.nextInternalFrame(targetDoc, activeItem); } else if (this.childDocs[this.itemIndex + 1] !== undefined) { // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide - this.nextSlide(activeNext); + const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]); + const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex; + this.nextSlide(curLast + 1); } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) { // Case 3: Last slide and presLoop is toggled ON or it is in Edit mode - this.gotoDocument(0, this.activeItem); + this.nextSlide(0); } }; @@ -280,8 +282,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { let prevSelected = this.itemIndex; // Functionality for group with up let didZoom = activeItem.presMovement; - for (; !didZoom && prevSelected > 0 && this.childDocs[prevSelected].groupButton; prevSelected--) { - didZoom = this.childDocs[prevSelected].presMovement; + for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].groupWithUp; prevSelected--) { + didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presMovement : didZoom; } if (lastFrame !== undefined && curFrame >= 1) { // Case 1: There are still other frames and should go through all frames before going to previous slide @@ -289,7 +291,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } else if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) { // Case 2: There are no other frames so it should go to the previous slide prevSelected = Math.max(0, prevSelected - 1); - this.gotoDocument(prevSelected, activeItem); + this.nextSlide(prevSelected); + this.rootDoc._itemIndex = prevSelected; if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc._currentFrame = NumCast(prevTargetDoc.lastFrame); } else if (this.childDocs[this.itemIndex - 1] === undefined && this.layoutDoc.presLoop) { // Case 3: Pres loop is on so it should go to the last slide @@ -1427,7 +1430,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; const clipStart: number = NumCast(activeItem.clipStart); - const clipEnd: number = NumCast(activeItem.clipEnd); + const clipEnd: number = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '-duration'])); const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc); if (activeItem && targetDoc) { return ( @@ -2458,12 +2461,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} - onClick={() => { + onClick={e => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } + e.stopPropagation(); }}> <FontAwesomeIcon icon={'arrow-left'} /> </div> @@ -2475,18 +2479,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} - onClick={() => { + onClick={e => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } + e.stopPropagation(); }}> <FontAwesomeIcon icon={'arrow-right'} /> </div> <div className="presPanel-divider"></div> <Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}> - <div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.gotoDocument(0, this.activeItem)}> + <div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.nextSlide(0)}> <b>1</b> </div> </Tooltip> diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 7bc7ba3ed..7e728306c 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -80,8 +80,6 @@ export class TopBar extends React.Component { * - Taking a snapshot of a dashboard */ @computed get topbarCenter() { - const myDashboards = DocListCast(Doc.MyDashboards.data); - const activeDashboard = Doc.ActiveDashboard; // const dashboardItems = myDashboards.map(board => { // const boardTitle = StrCast(board.title); // console.log(boardTitle); @@ -91,10 +89,10 @@ export class TopBar extends React.Component { // val: board, // }; // }); - return activeDashboard ? ( + return Doc.ActiveDashboard ? ( <div className="topbar-center"> <Button - text={StrCast(activeDashboard.title)} + text={StrCast(Doc.ActiveDashboard.title)} tooltip="Browsing mode for directly navigating to documents" fontSize={FontSize.SECONDARY} size={Size.SMALL} @@ -104,7 +102,7 @@ export class TopBar extends React.Component { borderRadius={5} hoverStyle="none" onClick={(e: React.MouseEvent) => { - const dashView = activeDashboard && DocumentManager.Instance.getDocumentView(activeDashboard); + const dashView = Doc.ActiveDashboard && DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard); ContextMenu.Instance.addItem({ description: 'Open Dashboard View', event: this.navigateToHome, icon: 'edit' }); ContextMenu.Instance.addItem({ description: 'Snapshot Dashboard', @@ -119,9 +117,9 @@ export class TopBar extends React.Component { }} /> <Button - text={GetEffectiveAcl(Doc.GetProto(activeDashboard)) === AclAdmin ? 'Share' : 'View Original'} + text={GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'View Original'} onClick={() => { - SharingManager.Instance.open(undefined, activeDashboard); + SharingManager.Instance.open(undefined, Doc.ActiveDashboard); }} type="outline" fontSize={FontSize.SECONDARY} @@ -183,81 +181,6 @@ export class TopBar extends React.Component { ); } - // render() { - // const activeDashboard = Doc.ActiveDashboard; - // return ( - // //TODO:glr Add support for light / dark mode - // <div style={{ pointerEvents: 'all', background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container"> - // <div className="topbar-inner-container"> - // <div className="topbar-left"> - // {activeDashboard ? ( - // <> - // <div - // className="topbar-button-icon" - // onClick={e => { - // ContextMenu.Instance.addItem({ description: 'Logout', event: () => window.location.assign(Utils.prepend('/logout')), icon: 'edit' }); - // ContextMenu.Instance.displayMenu(e.clientX + 5, e.clientY + 10); - // }}> - // {Doc.CurrentUserEmail} - // </div> - // <div className="topbar-button-icon" onClick={this.navigateToHome}> - // <FontAwesomeIcon icon="home" /> - // </div> - // </> - // ) : null} - // </div> - // <div className="topbar-center"> - // <div className="topbar-title" onClick={() => activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}> - // {activeDashboard ? StrCast(activeDashboard.title) : 'Dash'} - // </div> - // <div - // className="topbar-button-icon" - // onClick={e => { - // const dashView = activeDashboard && DocumentManager.Instance.getDocumentView(activeDashboard); - // ContextMenu.Instance.addItem({ description: 'Open Dashboard View', event: this.navigateToHome, icon: 'edit' }); - // ContextMenu.Instance.addItem({ - // description: 'Snapshot Dashboard', - // event: async () => { - // const batch = UndoManager.StartBatch('snapshot'); - // await DashboardView.snapshotDashboard(); - // batch.end(); - // }, - // icon: 'edit', - // }); - // dashView?.showContextMenu(e.clientX + 20, e.clientY + 30); - // }}> - // <FontAwesomeIcon color="white" size="lg" icon="bars" /> - // </div> - // <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom"> - // <div className="topbar-button-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => (MainView.Instance._exploreMode = !MainView.Instance._exploreMode))}> - // <FontAwesomeIcon color={MainView.Instance._exploreMode ? 'red' : 'white'} icon="eye" size="lg" /> - // </div> - // </Tooltip> - // </div> - // <div className="topbar-right"> - // {Doc.ActiveDashboard ? ( - // <div - // className="topbar-button-icon" - // onClick={() => { - // SharingManager.Instance.open(undefined, activeDashboard); - // }}> - // {GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'view original'} - // </div> - // ) : null} - // <div className="topbar-button-icon" onClick={() => ReportManager.Instance.open()}> - // <MdBugReport /> - // </div> - // <div className="topbar-button-icon" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')}> - // <FontAwesomeIcon icon="question-circle" /> - // </div> - // <div className="topbar-button-icon" onClick={() => SettingsManager.Instance.open()}> - // <FontAwesomeIcon icon="cog" /> - // </div> - // </div> - // </div> - // </div> - // ); - // } render() { return ( //TODO:glr Add support for light / dark mode diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 74a3d8cf2..a3c742f28 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -239,6 +239,8 @@ export class Doc extends RefField { return DocCast(Doc.UserDoc().activeDashboard); } public static set ActiveDashboard(val: Doc | undefined) { + const overlays = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), null); + overlays && (overlays.length = 0); Doc.UserDoc().activeDashboard = val; } public static set ActiveTool(tool: InkTool) { diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 0b6e18743..76cf36d44 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -60,6 +60,18 @@ export default class UploadManager extends ApiManager { return new Promise<void>(resolve => { form.parse(req, async (_err, _fields, files) => { const results: Upload.FileResponse[] = []; + if (_err?.message) { + results.push({ + source: { + size: 0, + path: 'none', + name: 'none', + type: 'none', + toJSON: () => ({ name: 'none', path: '' }), + }, + result: { name: 'failed upload', message: `${_err.message}` }, + }); + } for (const key in files) { const f = files[key]; if (!Array.isArray(f)) { |