diff options
Diffstat (limited to 'src/mobile/MobileInterface.tsx')
-rw-r--r-- | src/mobile/MobileInterface.tsx | 983 |
1 files changed, 742 insertions, 241 deletions
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index a50ec103e..a1719c015 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -1,47 +1,75 @@ -import React = require('react'); +import * as React from "react"; import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEraser, faHighlighter, faLongArrowAltLeft, faMousePointer, faPenNib } from '@fortawesome/free-solid-svg-icons'; +import { + faTasks, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, + faTerminal, faToggleOn, faFile as fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, + faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, + faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, + faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, + faThumbtack, faTree, faTv, faBook, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faHome, faLongArrowAltLeft, faBars, faTh, faChevronLeft +} from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable } from 'mobx'; +import { action, computed, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; +import * as rp from 'request-promise'; +import { Doc, DocListCast } from '../fields/Doc'; +import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; +import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from '../Utils'; import { DocServer } from '../client/DocServer'; -import { Docs } from '../client/documents/Documents'; -import { DocumentManager } from '../client/util/DocumentManager'; -import RichTextMenu from '../client/views/nodes/formattedText/RichTextMenu'; +import { Docs, DocumentOptions } from '../client/documents/Documents'; import { Scripting } from '../client/util/Scripting'; -import { Transform } from '../client/util/Transform'; -import { DocumentDecorations } from '../client/views/DocumentDecorations'; -import GestureOverlay from '../client/views/GestureOverlay'; import { DocumentView } from '../client/views/nodes/DocumentView'; -import { RadialMenu } from '../client/views/nodes/RadialMenu'; -import { PreviewCursor } from '../client/views/PreviewCursor'; -import { Doc, DocListCast, FieldResult } from '../fields/Doc'; -import { Id } from '../fields/FieldSymbols'; +import { Transform } from '../client/util/Transform'; +import "./MobileInterface.scss"; +import "./MobileMenu.scss"; +import "./MobileHome.scss"; +import "./ImageUpload.scss"; +import "./AudioUpload.scss"; +import { DocumentManager } from '../client/util/DocumentManager'; +import SettingsManager from '../client/util/SettingsManager'; +import { Uploader } from "./ImageUpload"; +import { DockedFrameRenderer } from '../client/views/collections/CollectionDockingView'; import { InkTool } from '../fields/InkField'; import { listSpec } from '../fields/Schema'; +import { nullAudio } from '../fields/URLField'; +import GestureOverlay from "../client/views/GestureOverlay"; +import { ScriptField } from "../fields/ScriptField"; +import InkOptionsMenu from "../client/views/collections/collectionFreeForm/InkOptionsMenu"; +import { RadialMenu } from "../client/views/nodes/RadialMenu"; +import { UndoManager, undoBatch } from "../client/util/UndoManager"; +import { MainView } from "../client/views/MainView"; +import { List } from "../fields/List"; +import { AudioUpload } from "./AudioUpload"; import { Cast, FieldValue } from '../fields/Types'; -import { WebField } from "../fields/URLField"; -import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; -import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, returnEmptyFilter } from '../Utils'; -import "./MobileInterface.scss"; import { CollectionView } from '../client/views/collections/CollectionView'; import { InkingStroke } from '../client/views/InkingStroke'; -library.add(faLongArrowAltLeft); +library.add(faTasks, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus, + faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard, + faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt, + faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter, + faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote, + faThumbtack, faTree, faTv, faUndoAlt, faBook, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faHome, faLongArrowAltLeft, faBars, faTh, faChevronLeft); @observer -export default class MobileInterface extends React.Component { +export class MobileInterface extends React.Component { @observable static Instance: MobileInterface; @computed private get userDoc() { return Doc.UserDoc(); } @computed private get mainContainer() { return this.userDoc ? FieldValue(Cast(this.userDoc.activeMobile, Doc)) : CurrentUserUtils.GuestMobile; } - // @observable private currentView: "main" | "ink" | "upload" = "main"; - private mainDoc: any = CurrentUserUtils.setupMobileDoc(this.userDoc); + @observable private mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(this.userDoc); @observable private renderView?: () => JSX.Element; + @observable private sidebarActive: boolean = false; //to toggle sidebar display + @observable private imageUploadActive: boolean = false; //to toggle image upload + @observable private audioUploadActive: boolean = false; + @observable private menuListView: boolean = false; //to switch between menu view (list / icon) + @observable private _ink: boolean = false; //toggle whether ink is being dispalyed - // private inkDoc?: Doc; - public drawingInk: boolean = false; - - // private uploadDoc?: Doc; + public _activeDoc: Doc = this.mainDoc; // doc updated as the active mobile page is updated (initially home menu) + public _homeDoc: Doc = this.mainDoc; // home menu as a document + private _homeMenu: boolean = true; // to determine whether currently at home menu + private _child: Doc | null = null; // currently selected document + private _parents: Array<Doc> = []; // array of parent docs (for pathbar) + private _library: Doc = CurrentUserUtils.setupLibrary(this.userDoc); // to access documents in Dash Web constructor(props: Readonly<{}>) { super(props); @@ -50,298 +78,771 @@ export default class MobileInterface extends React.Component { @action componentDidMount = () => { - library.add(...[faPenNib, faHighlighter, faEraser, faMousePointer]); + Doc.UserDoc().activeMobile = this._homeDoc; + this._homeDoc._viewType === "stacking" ? this.menuListView = true : this.menuListView = false; + Doc.SetSelectedTool(InkTool.None); + this.switchCurrentView((userDoc: Doc) => this._homeDoc); - if (this.userDoc && !this.mainContainer) { - this.userDoc.activeMobile = this.mainDoc; - } + document.removeEventListener("dblclick", this.onReactDoubleClick); + document.addEventListener("dblclick", this.onReactDoubleClick); } @action + componentWillUnmount = () => { + document.removeEventListener('dblclick', this.onReactDoubleClick); + } + + // Prevent zooming in when double tapping the screen + onReactDoubleClick = (e: MouseEvent) => { + e.stopPropagation(); + } + + // Switch the mobile view to the given doc + @action switchCurrentView = (doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => { if (!this.userDoc) return; - this.userDoc.activeMobile = doc(this.userDoc); + Doc.UserDoc().activeMobile = doc(this.userDoc); onSwitch && onSwitch(); this.renderView = renderView; } - onSwitchInking = () => { - Doc.SetSelectedTool(InkTool.Pen); - MobileInterface.Instance.drawingInk = true; + // For toggling the hamburger menu + @action + toggleSidebar = () => this.sidebarActive = !this.sidebarActive - DocServer.Mobile.dispatchOverlayTrigger({ - enableOverlay: true, - width: window.innerWidth, - height: window.innerHeight - }); + /** + * Method called when 'Library' button is pressed on the home screen + */ + switchToLibrary = () => { + this._parents.push(this._activeDoc); + this.switchCurrentView((userDoc: Doc) => this._library); + this._activeDoc = this._library; + this._homeMenu = false; + this.sidebarActive = true; + } + + openWorkspaces = () => { + this._parents.push(this._activeDoc); + this.switchCurrentView((userDoc: Doc) => this._library); + this._activeDoc = this._library; + this._homeMenu = false; } - onSwitchUpload = async () => { - let width = 300; - let height = 300; + /** + * Back method for navigating through items + */ + back = () => { + const header = document.getElementById("header") as HTMLElement; + const doc = Cast(this._parents.pop(), Doc) as Doc; - // get width and height of the collection doc - if (this.mainContainer) { - const data = Cast(this.mainContainer.data, listSpec(Doc)); - if (data) { - const collectionDoc = await data[1]; // this should be the collection doc since the positions should be locked - const docView = DocumentManager.Instance.getDocumentView(collectionDoc); - if (docView) { - width = docView.nativeWidth ? docView.nativeWidth : 300; - height = docView.nativeHeight ? docView.nativeHeight : 300; - } + if (doc === Cast(this._library, Doc) as Doc) { + this._child = null; + this.userDoc.activeMobile = this._library; + } else if (doc === Cast(this._homeDoc, Doc) as Doc) { + this._homeMenu = true; + this._parents = []; + this._activeDoc = this._homeDoc; + this._child = null; + this.switchCurrentView((userDoc: Doc) => this._homeDoc); + } else { + if (doc) { + this._child = doc; + this.switchCurrentView((userDoc: Doc) => doc); + this._homeMenu = false; + header.textContent = String(doc.title); } } - DocServer.Mobile.dispatchOverlayTrigger({ - enableOverlay: true, - width: width, - height: height, - text: "Documents uploaded from mobile will show here", - }); - } - - renderDefaultContent = () => { - if (this.mainContainer) { - return <DocumentView - Document={this.mainContainer} - DataDoc={undefined} - LibraryPath={emptyPath} - addDocument={returnFalse} - addDocTab={returnFalse} - pinToPres={emptyFunction} - rootSelected={returnFalse} - removeDocument={undefined} - onClick={undefined} - ScreenToLocalTransform={Transform.Identity} - ContentScaling={returnOne} - NativeHeight={returnZero} - NativeWidth={returnZero} - PanelWidth={() => window.screen.width} - PanelHeight={() => window.screen.height} - renderDepth={0} - focus={emptyFunction} - backgroundColor={returnEmptyString} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} />; + if (doc) { + this._activeDoc = doc; } - return "hello"; + this._ink = false; } - onBack = (e: React.MouseEvent) => { - this.switchCurrentView((userDoc: Doc) => this.mainDoc); - Doc.SetSelectedTool(InkTool.None); // TODO: switch to previous tool - - DocServer.Mobile.dispatchOverlayTrigger({ - enableOverlay: false, - width: window.innerWidth, - height: window.innerHeight - }); - - // this.inkDoc = undefined; - this.drawingInk = false; - } - - shiftLeft = (e: React.MouseEvent) => { - DocServer.Mobile.dispatchOverlayPositionUpdate({ - dx: -10 - }); - e.preventDefault(); - e.stopPropagation(); + /** + * Return 'Home", which implies returning to 'Home' buttons + */ + returnHome = () => { + if (!this._homeMenu || this.sidebarActive) { + this._homeMenu = true; + this._parents = []; + this._activeDoc = this._homeDoc; + this._child = null; + this.switchCurrentView((userDoc: Doc) => this._homeDoc); + } + if (this.sidebarActive) { + this.toggleSidebar(); + } } - shiftRight = (e: React.MouseEvent) => { - DocServer.Mobile.dispatchOverlayPositionUpdate({ - dx: 10 - }); - e.preventDefault(); - e.stopPropagation(); + /** + * Return to primary Workspace in library (Workspaces Doc) + */ + returnMain = () => { + this._parents = [this._homeDoc]; + this._activeDoc = this._library; + this.switchCurrentView((userDoc: Doc) => this._library); + this._homeMenu = false; + this._child = null; } - panelHeight = () => window.innerHeight; - panelWidth = () => window.innerWidth; - renderInkingContent = () => { - console.log("rendering inking content"); - // TODO: support panning and zooming - // TODO: handle moving of ink strokes + /** + * DocumentView for graphic display of all documents + */ + displayWorkspaces = () => { if (this.mainContainer) { + const backgroundColor = () => "white"; return ( - <div className="mobileInterface"> - <div className="mobileInterface-inkInterfaceButtons"> - <div className="navButtons"> - <button className="mobileInterface-button cancel" onClick={this.onBack} title="Cancel drawing">BACK</button> - </div> - <div className="inkSettingButtons"> - <button className="mobileInterface-button cancel" onClick={this.onBack} title="Cancel drawing"><FontAwesomeIcon icon="long-arrow-alt-left" /></button> - </div> - <div className="navButtons"> - <button className="mobileInterface-button" onClick={this.shiftLeft} title="Shift left">left</button> - <button className="mobileInterface-button" onClick={this.shiftRight} title="Shift right">right</button> - </div> - </div> - <CollectionView + <div style={{ position: "relative", top: '198px', height: `calc(100% - 350px)`, width: "100%", left: "0%" }}> + <DocumentView Document={this.mainContainer} DataDoc={undefined} LibraryPath={emptyPath} - filterAddDocument={returnTrue} - fieldKey={""} - dropAction={"alias"} - bringToFront={emptyFunction} + addDocument={returnFalse} addDocTab={returnFalse} pinToPres={emptyFunction} - PanelWidth={this.panelWidth} - PanelHeight={this.panelHeight} + rootSelected={returnFalse} + removeDocument={undefined} + onClick={undefined} + ScreenToLocalTransform={Transform.Identity} + ContentScaling={returnOne} + PanelWidth={this.returnWidth} + PanelHeight={this.returnHeight} NativeHeight={returnZero} NativeWidth={returnZero} - focus={emptyFunction} - isSelected={returnFalse} - select={emptyFunction} - active={returnFalse} - ContentScaling={returnOne} - whenActiveChanged={returnFalse} - ScreenToLocalTransform={Transform.Identity} renderDepth={0} + focus={emptyFunction} + backgroundColor={backgroundColor} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} docFilters={returnEmptyFilter} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} - rootSelected={returnTrue}> - </CollectionView> + /> </div> ); } } - upload = async (e: React.MouseEvent) => { - if (this.mainContainer) { - const data = Cast(this.mainContainer.data, listSpec(Doc)); - if (data) { - const collectionDoc = await data[1]; // this should be the collection doc since the positions should be locked - const children = DocListCast(collectionDoc.data); - const uploadDoc = children.length === 1 ? children[0] : Docs.Create.StackingDocument(children, { - title: "Mobile Upload Collection", backgroundColor: "white", lockedPosition: true, _width: 300, _height: 300 - }); - if (uploadDoc) { - DocServer.Mobile.dispatchMobileDocumentUpload({ - docId: uploadDoc[Id], - }); - } + /** + * Note: window.innerWidth and window.screen.width compute different values. + * window.screen.width is the display size, however window.innerWidth is the + * display resolution which computes differently. + */ + returnWidth = () => window.innerWidth; //The windows width + returnHeight = () => (window.innerHeight - 300); //Calculating the windows height (-300 to account for topbar) + + /** + * Handles the click functionality in the library panel. + * Navigates to the given doc and updates the sidebar. + * @param doc: doc for which the method is called + */ + @undoBatch + handleClick = async (doc: Doc) => { + const children = DocListCast(doc.data); + if (doc.type !== "collection" && this.sidebarActive) this.openFromSidebar(doc); + else if (doc.type === "collection" && children.length === 0) this.openFromSidebar(doc); + else { + this._parents.push(this._activeDoc); + this._activeDoc = doc; + this.switchCurrentView((userDoc: Doc) => doc); + this._homeMenu = false; + this._child = doc; + } + } + + openFromSidebar = (doc: Doc) => { + this._parents.push(this._activeDoc); + this._activeDoc = doc; + this.switchCurrentView((userDoc: Doc) => doc); + this._homeMenu = false; + this._child = doc; //? + this.toggleSidebar(); + } + + /** + * Handles creation of array which is then rendered in renderPathbar() + */ + createPathname = () => { + const docArray = []; + this._parents.map((doc: Doc, index: any) => { + docArray.push(doc); + }); + docArray.push(this._activeDoc); + return docArray; + } + + // Renders the graphical pathbar + renderPathbar = () => { + const docArray = this.createPathname(); + const items = docArray.map((doc: Doc, index: any) => { + if (index === 0) { + return ( + <> + {this._homeMenu ? + <div className="pathbarItem"> + <div className="pathbarText" + style={{ backgroundColor: "rgb(119, 37, 37)" }} + key={index} + onClick={() => this.handlePathClick(doc, index)}>{doc.title} + </div> + </div> + : + <div className="pathbarItem"> + <div className="pathbarText" + key={index} + onClick={() => this.handlePathClick(doc, index)}>{doc.title} + </div> + </div>} + </>); + + } else if (doc === this._activeDoc) { + return ( + <div className="pathbarItem"> + <FontAwesomeIcon className="pathIcon" icon="angle-right" size="lg" /> + <div className="pathbarText" + style={{ backgroundColor: "rgb(119, 37, 37)" }} + key={index} + onClick={() => this.handlePathClick(doc, index)}>{doc.title} + </div> + </div>); + } else { + return ( + <div className="pathbarItem"> + <FontAwesomeIcon className="pathIcon" icon="angle-right" size="lg" /> + <div className="pathbarText" + key={index} + onClick={() => this.handlePathClick(doc, index)}>{doc.title} + </div> + </div>); } + + }); + if (this._parents.length !== 0) { + return (<div className="pathbar"> + <div className="scrollmenu"> + {items} + </div> + <div className="back" > + <FontAwesomeIcon onClick={this.back} icon={"chevron-left"} color="white" size={"2x"} /> + </div> + <div className="hidePath" /> + </div>); + } else { + return (<div className="pathbar"> + <div className="scrollmenu"> + {items} + </div> + <div className="hidePath" /> + </div>); } - e.stopPropagation(); - e.preventDefault(); } - addWebToCollection = async () => { - let url = "https://en.wikipedia.org/wiki/Hedgehog"; - if (this.mainContainer) { - const data = Cast(this.mainContainer.data, listSpec(Doc)); - if (data) { - const webDoc = await data[0]; - const urlField: FieldResult<WebField> = Cast(webDoc.data, WebField); - url = urlField ? urlField.url.toString() : "https://en.wikipedia.org/wiki/Hedgehog"; + // Handles when user clicks on a document in the pathbar + handlePathClick = (doc: Doc, index: number) => { + if (doc === this._library) { + this._activeDoc = doc; + this._child = null; + this.switchCurrentView((userDoc: Doc) => doc); + this._parents.length = index; + } else if (doc === this._homeDoc) { + this.returnHome(); + } else { + this._activeDoc = doc; + this._child = doc; + this.switchCurrentView((userDoc: Doc) => doc); + this._parents.length = index; + } + } + + // Renders the contents of the menu and sidebar + renderDefaultContent = () => { + if (this._homeMenu) { + return ( + <div> + <div className="navbar"> + <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} /> + <div className="header" id="header">{this._homeDoc.title}</div> + <div className="cover" id="cover" onClick={(e) => this.stop(e)}></div> + <div className="toggle-btn" id="menuButton" onClick={this.toggleSidebar}> + <span></span> + <span></span> + <span></span> + </div> + </div> + {this.renderPathbar()} + </div> + ); + } + let workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; + if (this._child) { + workspaces = this._child; + } + + const buttons = DocListCast(workspaces.data).map((doc: Doc, index: any) => { + if (doc.type !== "ink") { + return ( + <div + className="item" + key={index} + onClick={() => this.handleClick(doc)}> + <div className="item-title"> {doc.title} </div> + <div className="item-type">{doc.type}</div> + <FontAwesomeIcon className="right" icon="angle-right" size="lg" /> + <FontAwesomeIcon className="open" onClick={() => this.openFromSidebar(doc)} icon="external-link-alt" size="lg" /> + </div>); } + }); + + return ( + <div> + <div className="navbar"> + <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} /> + <div className="header" id="header">{this.sidebarActive ? "library" : this._activeDoc.title}</div> + <div className={`toggle-btn ${this.sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}> + <span></span> + <span></span> + <span></span> + </div> + </div> + {this.renderPathbar()} + <div className={`sidebar ${this.sidebarActive ? "active" : ""}`}> + <div className="sidebarButtons"> + {this._child ? + <> + {buttons} + <div + className="item" key="home" + onClick={this.returnMain} + style={{ opacity: 0.7 }}> + <FontAwesomeIcon className="right" icon="angle-double-left" size="lg" /> + <div className="item-type">Return to workspaces</div> + </div> + </> : + <> + {buttons} + <div + className="item" + style={{ opacity: 0.7 }} + onClick={() => this.createNewWorkspace()}> + <FontAwesomeIcon className="right" icon="plus" size="lg" /> + <div className="item-type">Create New Workspace</div> + </div> + </> + } + </div> + </div> + </div> + ); + } + + /** + * Handles the Create New Workspace button in the menu + */ + @action + createNewWorkspace = async (id?: string) => { + const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; + const workspaceCount = DocListCast(workspaces.data).length + 1; + const freeformOptions: DocumentOptions = { + x: 0, + y: 400, + title: "Collection " + workspaceCount, + _LODdisable: true + }; + const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); + const workspaceDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600, path: [Doc.UserDoc().myCatalog as Doc] }], { title: `Workspace ${workspaceCount}` }, id, "row"); + + const toggleTheme = ScriptField.MakeScript(`self.darkScheme = !self.darkScheme`); + const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); + const cloneWorkspace = ScriptField.MakeScript(`cloneWorkspace()`); + workspaceDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneWorkspace!]); + workspaceDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Workspace Layout"]); + + Doc.AddDocToList(workspaces, "data", workspaceDoc); + // bcz: strangely, we need a timeout to prevent exceptions/issues initializing GoldenLayout (the rendering engine for Main Container) + } + + stop = (e: React.MouseEvent) => { + e.stopPropagation(); + } + + // Button for uploading mobile audio + uploadAudioButton = () => { + if (this._activeDoc.type === "audio") { + return <div className="docButton" + title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"} + style={{ backgroundColor: "black", color: "white" }} + // onClick={this.uploadAudio} + > + <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload" + /> + </div>; } - Docs.Create.WebDocument(url, { _width: 300, _height: 300, title: "Mobile Upload Web Doc" }); } - clearUpload = async () => { - if (this.mainContainer) { - const data = Cast(this.mainContainer.data, listSpec(Doc)); - if (data) { - const collectionDoc = await data[1]; - const children = DocListCast(collectionDoc.data); - children.forEach(doc => { - }); - // collectionDoc[data] = new List<Doc>(); + // Button for switching between pen and ink mode + @action + onSwitchInking = () => { + const button = document.getElementById("inkButton") as HTMLElement; + button.style.backgroundColor = this._ink ? "white" : "black"; + button.style.color = this._ink ? "black" : "white"; + + if (!this._ink) { + Doc.SetSelectedTool(InkTool.Pen); + this._ink = true; + } else { + Doc.SetSelectedTool(InkTool.None); + this._ink = false; + } + } + + // The static ink menu that appears at the top + inkMenu = () => { + if (this._activeDoc._viewType === "docking") { + if (this._ink) { + console.log("here"); + return <div className="colorSelector"> + <InkOptionsMenu /> + </div>; + } } } - renderUploadContent() { - if (this.mainContainer) { + + undo = () => { + if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc && this._activeDoc.title !== "WORKSPACES") { + return (<> + <div className="docButton" + style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanUndo() ? "1" : "0.4", }} + id="undoButton" + title="undo" + onClick={(e: React.MouseEvent) => { + UndoManager.Undo(); + e.stopPropagation(); + }}> + <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="undo-alt" /> + </div> + </>); + } + } + + redo = () => { + if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc && this._activeDoc.title !== "WORKSPACES") { + return (<> + <div className="docButton" + style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanRedo() ? "1" : "0.4", }} + id="undoButton" + title="redo" + onClick={(e: React.MouseEvent) => { + UndoManager.Redo(); + e.stopPropagation(); + }}> + <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="redo-alt" /> + </div> + </>); + } + } + + // Button for switching into ink mode + drawInk = () => { + if (this._activeDoc._viewType === "docking") { return ( - <div className="mobileInterface" onDragOver={this.onDragOver}> - <div className="mobileInterface-inkInterfaceButtons"> - <button className="mobileInterface-button cancel" onClick={this.onBack} title="Back">BACK</button> - {/* <button className="mobileInterface-button" onClick={this.clearUpload} title="Clear Upload">CLEAR</button> */} - {/* <button className="mobileInterface-button" onClick={this.addWeb} title="Add Web Doc to Upload Collection"></button> */} - <button className="mobileInterface-button" onClick={this.upload} title="Upload">UPLOAD</button> + <> + <div className="docButton" + id="inkButton" + title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"} + onClick={this.onSwitchInking}> + <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="pen-nib" + /> + </div> + </>); + } + } + + // Mobile doc button for uploading + upload = () => { + if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc) { + return ( + <> + <div className="docButton" + id="uploadButton" + title={"uploadFile"} + onClick={this.toggleUpload}> + <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload" + /> + </div> + </>); + } + } + + // Button to download images on the mobile + downloadDocument = () => { + if (this._activeDoc.type === "image" || this._activeDoc.type === "pdf" || this._activeDoc.type === "video") { + const url = this._activeDoc["data-path"]?.toString(); + return <div className="docButton" + title={"Download Image"} + style={{ backgroundColor: "white", color: "black" }} + onClick={e => { + window.open(url); + console.log(url); + }}> + <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="download" + /> + </div>; + } + } + + // Mobile audio doc + recordAudio = async () => { + // upload to server with known URL + if (this._activeDoc.title !== "mobile audio") { + this._parents.push(this._activeDoc); + } + const audioDoc = Cast(Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100, title: "mobile audio" }), Doc) as Doc; + if (audioDoc) { + this._activeDoc = audioDoc; + this.switchCurrentView((userDoc: Doc) => audioDoc); + this._homeMenu = false; + } + } + + // // Pushing the audio doc onto Dash Web through the right side bar + // uploadAudio = () => { + // const audioRightSidebar = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc; + // const audioDoc = this._activeDoc; + // const data = Cast(audioRightSidebar.data, listSpec(Doc)); + + // if (data) { + // data.push(audioDoc); + // } + // } + + // Button for pinning images to presentation + pinToPresentation = () => { + // Only making button available if it is an image + if (!(this._activeDoc.type === "collection" || this._activeDoc.type === "presentation")) { + const isPinned = this._activeDoc && Doc.isDocPinned(this._activeDoc); + return <div className="docButton" + title={Doc.isDocPinned(this._activeDoc) ? "Unpin from presentation" : "Pin to presentation"} + style={{ backgroundColor: isPinned ? "black" : "white", color: isPinned ? "white" : "black" }} + onClick={e => { + if (isPinned) { + DockedFrameRenderer.UnpinDoc(this._activeDoc); + } + else { + DockedFrameRenderer.PinDoc(this._activeDoc); + } + }}> + <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" + /> + </div>; + } + } + + // Buttons for switching the menu between large and small icons + switchMenuView = () => { + if (this._activeDoc.title === this._homeDoc.title) { + return ( + <div className="homeSwitch"> + <div className={`list ${!this.menuListView ? "active" : ""}`} onClick={this.changeToIconView}> + <FontAwesomeIcon size="sm" icon="th-large" /> + </div> + <div className={`list ${this.menuListView ? "active" : ""}`} onClick={this.changeToListView}> + <FontAwesomeIcon size="sm" icon="bars" /> </div> - <DocumentView - Document={this.mainContainer} - DataDoc={undefined} - LibraryPath={emptyPath} - addDocument={returnFalse} - addDocTab={returnFalse} - pinToPres={emptyFunction} - rootSelected={returnFalse} - removeDocument={undefined} - onClick={undefined} - ScreenToLocalTransform={Transform.Identity} - ContentScaling={returnOne} - NativeHeight={returnZero} - NativeWidth={returnZero} - PanelWidth={() => window.screen.width} - PanelHeight={() => window.screen.height} - renderDepth={0} - focus={emptyFunction} - backgroundColor={returnEmptyString} - parentActive={returnTrue} - whenActiveChanged={emptyFunction} - bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> </div> ); } } + // Logic for switching the menu into the icons + @action + changeToIconView = () => { + if (this._homeDoc._viewType = "stacking") { + this.menuListView = false; + this._homeDoc._viewType = "masonry"; + this._homeDoc.columnWidth = 300; + const menuButtons = DocListCast(this._homeDoc.data); + console.log('hello'); + menuButtons.map((doc: Doc, index: any) => { + console.log(index); + const buttonData = DocListCast(doc.data); + buttonData[1]._nativeWidth = 0.1; + buttonData[1]._width = 0.1; + buttonData[1]._dimMagnitude = 0; + buttonData[1]._opacity = 0; + console.log(buttonData); + console.log(doc._nativeWidth); + doc._nativeWidth = 400; + console.log(doc._nativeWidth); + }); + } + } + + // Logic for switching the menu into the stacking view + @action + changeToListView = () => { + if (this._homeDoc._viewType = "masonry") { + this._homeDoc._viewType = "stacking"; + this.menuListView = true; + const menuButtons = DocListCast(this._homeDoc.data); + console.log('hello'); + menuButtons.map((doc: Doc, index: any) => { + const buttonData = DocListCast(doc.data); + buttonData[1]._nativeWidth = 450; + buttonData[1]._dimMagnitude = 2; + buttonData[1]._opacity = 1; + console.log(doc._nativeWidth); + doc._nativeWidth = 900; + console.log(doc._nativeWidth); + }); + } + } + + // For setting up the presentation document for the home menu + setupDefaultPresentation = () => { + if (this._activeDoc.title !== "Presentation") { + this._parents.push(this._activeDoc); + } + + const presentation = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; + + if (presentation) { + this._activeDoc = presentation; + this.switchCurrentView((userDoc: Doc) => presentation); + this._homeMenu = false; + } + } + + // For toggling image upload pop up + @action + toggleUpload = () => this.imageUploadActive = !this.imageUploadActive + + // For toggling image upload pop up + @action + toggleAudio = () => this.audioUploadActive = !this.audioUploadActive + + @action + toggleUploadInCollection = () => { + const button = document.getElementById("imageButton") as HTMLElement; + button.style.backgroundColor = this.imageUploadActive ? "white" : "black"; + button.style.color = this.imageUploadActive ? "black" : "white"; + + this.imageUploadActive = !this.imageUploadActive; + } + + // For closing the image upload pop up + @action + closeUpload = () => { + this.imageUploadActive = false; + } + + // Returns the image upload pop up + uploadImage = () => { + if (this.imageUploadActive) { + console.log("active"); + } else if (!this.imageUploadActive) { + + } + + let doc; + let toggle; + if (this._homeMenu === false) { + doc = this._activeDoc; + toggle = this.toggleUploadInCollection; + } else { + doc = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc; + toggle = this.toggleUpload; + } + return ( + <div> + <div className="closeUpload" onClick={toggle}> + <FontAwesomeIcon icon="window-close" size={"lg"} /> + </div> + <Uploader Document={doc} /> + </div> + ); + } + + displayRadialMenu = () => { + if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc) { + return <RadialMenu />; + } + } + onDragOver = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); } + uploadImageButton = () => { + if (this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc && this._activeDoc._viewType !== "docking" && this._activeDoc.title !== "WORKSPACES") { + return <div className="docButton" + id="imageButton" + title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"} + onClick={this.toggleUpload}> + <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload" + /> + </div>; + } + } + + switchToMobileUploads = () => { + if (this._activeDoc.title !== "Presentation") { + this._parents.push(this._activeDoc); + } + const mobileUpload = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc; + console.log(mobileUpload.title); + this._activeDoc = mobileUpload; + this.switchCurrentView((userDoc: Doc) => mobileUpload); + this._homeMenu = false; + } + render() { - // const content = this.currentView === "main" ? this.mainContent : - // this.currentView === "ink" ? this.inkContent : - // this.currentView === "upload" ? this.uploadContent : <></>; return ( <div className="mobileInterface-container" onDragOver={this.onDragOver}> - {/* <DocumentDecorations /> - <GestureOverlay> - {this.renderView ? this.renderView() : this.renderDefaultContent()} - </GestureOverlay> */} - - {/* <DictationOverlay /> - <SharingManager /> - <GoogleAuthenticationManager /> */} - <DocumentDecorations /> + <SettingsManager /> + <div className={`image-upload ${this.imageUploadActive ? "active" : ""}`}> + {this.uploadImage()} + </div> + <div className={`audio-upload ${this.audioUploadActive ? "active" : ""}`}> + <AudioUpload /> + </div> + {this.switchMenuView()} + {this.inkMenu()} <GestureOverlay> - {this.renderView ? this.renderView() : this.renderDefaultContent()} + <div className="docButtonContainer"> + {this.pinToPresentation()} + {this.downloadDocument()} + {this.undo()} + {this.drawInk()} + {this.redo()} + {/* {this.upload()} */} + {this.uploadImageButton()} + {/* {this.uploadAudioButton()} */} + </div> + {this.displayWorkspaces()} + {this.renderDefaultContent()} </GestureOverlay> - <PreviewCursor /> - {/* <ContextMenu /> */} - <RadialMenu /> - <RichTextMenu /> - {/* <PDFMenu /> - <MarqueeOptionsMenu /> - <OverlayView /> */} + {this.displayRadialMenu()} </div> ); } } -Scripting.addGlobal(function switchMobileView(doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); }); -Scripting.addGlobal(function onSwitchMobileInking() { return MobileInterface.Instance.onSwitchInking(); }); -Scripting.addGlobal(function renderMobileInking() { return MobileInterface.Instance.renderInkingContent(); }); -Scripting.addGlobal(function onSwitchMobileUpload() { return MobileInterface.Instance.onSwitchUpload(); }); -Scripting.addGlobal(function renderMobileUpload() { return MobileInterface.Instance.renderUploadContent(); }); -Scripting.addGlobal(function addWebToMobileUpload() { return MobileInterface.Instance.addWebToCollection(); }); + +Scripting.addGlobal(function switchMobileView(doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); }, + "changes the active document displayed on the mobile, (doc: any)"); +Scripting.addGlobal(function openMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); }, + "opens the presentation on mobile"); +Scripting.addGlobal(function toggleMobileSidebar() { return MobileInterface.Instance.toggleSidebar(); }); +Scripting.addGlobal(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); }); +Scripting.addGlobal(function openMobileSettings() { return SettingsManager.Instance.open(); }); +Scripting.addGlobal(function openWorkspaces() { return MobileInterface.Instance.openWorkspaces(); }); +Scripting.addGlobal(function uploadImageMobile() { return MobileInterface.Instance.toggleUpload(); }); +Scripting.addGlobal(function switchToMobileUploads() { return MobileInterface.Instance.switchToMobileUploads(); }); |