diff options
Diffstat (limited to 'src/mobile/MobileInterface.tsx')
-rw-r--r-- | src/mobile/MobileInterface.tsx | 526 |
1 files changed, 379 insertions, 147 deletions
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 6c2e797d6..1f7244653 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -1,47 +1,56 @@ import React = require('react'); import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEraser, faHighlighter, faLongArrowAltLeft, faMousePointer, faPenNib } from '@fortawesome/free-solid-svg-icons'; +import { faEraser, faHighlighter, faLongArrowAltLeft, faMousePointer, faPenNib, faThumbtack, faHome } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; -import { DocServer } from '../client/DocServer'; +import * as rp from 'request-promise'; import { Docs } from '../client/documents/Documents'; -import { DocumentManager } from '../client/util/DocumentManager'; -import RichTextMenu from '../client/views/nodes/formattedText/RichTextMenu'; -import { Scripting } from '../client/util/Scripting'; +import { DocumentView } from '../client/views/nodes/DocumentView'; +import { emptyPath, emptyFunction, returnFalse, returnOne, returnTrue, returnZero, Utils } from '../Utils'; import { Transform } from '../client/util/Transform'; -import { DocumentDecorations } from '../client/views/DocumentDecorations'; -import GestureOverlay from '../client/views/GestureOverlay'; +import { Scripting } from '../client/util/Scripting'; import { InkingControl } from '../client/views/InkingControl'; -import { DocumentView } from '../client/views/nodes/DocumentView'; -import { RadialMenu } from '../client/views/nodes/RadialMenu'; -import { PreviewCursor } from '../client/views/PreviewCursor'; +import "./MobileInterface.scss"; +import "./MobileMenu.scss"; +import { DocServer } from '../client/DocServer'; +import { DocumentManager } from '../client/util/DocumentManager'; +import SettingsManager from '../client/util/SettingsManager'; +import { Uploader } from "./ImageUpload"; +import { DockedFrameRenderer } from '../client/views/collections/CollectionDockingView'; import { Doc, DocListCast, FieldResult } from '../fields/Doc'; -import { Id } from '../fields/FieldSymbols'; +import { FieldValue, Cast, StrCast } from '../fields/Types'; import { InkTool } from '../fields/InkField'; import { listSpec } from '../fields/Schema'; -import { Cast, FieldValue } from '../fields/Types'; -import { WebField } from "../fields/URLField"; +import { nullAudio, WebField } from '../fields/URLField'; +import { Id } from '../fields/FieldSymbols'; import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; -import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero } from '../Utils'; -import "./MobileInterface.scss"; -import { CollectionView } from '../client/views/collections/CollectionView'; library.add(faLongArrowAltLeft); +library.add(faHome); @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; } + @computed private get activeContainer() { 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.setupMobileDoc(this.userDoc); @observable private renderView?: () => JSX.Element; + @observable private sidebarActive = true; + + public _activeDoc: Doc = this.mainDoc; // private inkDoc?: Doc; public drawingInk: boolean = false; - // private uploadDoc?: Doc; + // private _uploadDoc: Doc = this.userDoc; + private _child: Doc | null = null; + private _parents: Array<Doc> = []; + private _menu: Doc = this.mainDoc; + private _open: boolean = false; + private _library: Doc = Cast(this.userDoc.myWorkspaces, Doc) as Doc; constructor(props: Readonly<{}>) { super(props); @@ -50,7 +59,7 @@ export default class MobileInterface extends React.Component { @action componentDidMount = () => { - library.add(...[faPenNib, faHighlighter, faEraser, faMousePointer]); + library.add(...[faPenNib, faHighlighter, faEraser, faMousePointer, faThumbtack]); if (this.userDoc && !this.mainContainer) { this.userDoc.activeMobile = this.mainDoc; @@ -81,6 +90,7 @@ export default class MobileInterface extends React.Component { onSwitchUpload = async () => { let width = 300; let height = 300; + const res = await rp.get(Utils.prepend("/getUserDocumentId")); // get width and height of the collection doc if (this.mainContainer) { @@ -102,34 +112,243 @@ export default class MobileInterface extends React.Component { }); } - renderDefaultContent = () => { + back = () => { + const doc = Cast(this._parents.pop(), Doc) as Doc; + if (doc === Cast(this._menu, Doc) as Doc) { + this._child = null; + this.userDoc.activeMobile = this.mainDoc; + } else { + if (doc) { + this._child = doc; + this.switchCurrentView((userDoc: Doc) => doc); + } + } + if (doc) { + this._activeDoc = doc; + } + } + + returnHome = () => { + this._parents = []; + this._activeDoc = this._menu; + this.switchCurrentView((userDoc: Doc) => this._menu); + this._child = null; + } + + displayWorkspaces = () => { 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} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} />; + const backgroundColor = () => "white"; + return ( + <div style={{ position: "relative", top: '200px', height: `calc(100% - 250px)`, width: "100%", overflow: "hidden" }}> + <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={backgroundColor} + parentActive={returnTrue} + whenActiveChanged={emptyFunction} + bringToFront={emptyFunction} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + /> + </div> + ); + } + } + + handleClick(doc: Doc) { + const children = DocListCast(doc.data); + if (doc.type !== "collection") { + this._parents.push(this._activeDoc); + this._activeDoc = doc; + this.switchCurrentView((userDoc: Doc) => doc); + this.toggleSidebar(); + } else if (doc.type === "collection" && children.length === 0) { + console.log("This collection has no children"); + } else { + this._parents.push(this._activeDoc); + this._activeDoc = doc; + this.switchCurrentView((userDoc: Doc) => doc); + this._child = doc; + } + + // let sidebar = document.getElementById("sidebar") as HTMLElement; + // sidebar.classList.toggle('active'); + } + + createPathname = () => { + let pathname = ""; + this._parents.map((doc: Doc, index: any) => { + if (doc === this.mainDoc) { + pathname = pathname + doc.title; + } else { + pathname = pathname + " > " + doc.title; + } + }); + if (this._activeDoc === this.mainDoc) { + pathname = pathname + this._activeDoc.title; + } else { + pathname = pathname + " > " + this._activeDoc.title; + } + return pathname; + } + + @action + toggleSidebar = () => this.sidebarActive = !this.sidebarActive + + openLibrary() { + this._activeDoc = this.mainDoc; + this.switchCurrentView(() => this.mainDoc); + this._child = this._library; + } + + renderDefaultContent = () => { + const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; + const buttons = DocListCast(this._child ? this._child.data : workspaces.data).map((doc: Doc, index: any) => { + return ( + <div + className="item" + key={index} + onClick={() => this.handleClick(doc)}>{doc.title} + <div className="type">{doc.type}</div> + <FontAwesomeIcon className="right" icon="angle-right" size="lg" /> + </div>); + }); + return ( + <> + <div className="navbar"> + <div className={"header"}>{this.sidebarActive ? StrCast(this._activeDoc.title) : "Menu"}</div> + <div + className={`toggle-btn ${this.sidebarActive ? "active" : ""}`} + onClick={this.toggleSidebar} + /> + </div> + <div className="pathbar"> + <div className="pathname">{this.createPathname()}</div> + </div> + <div className={`sidebar ${this.sidebarActive ? "active" : ""}`}> + <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} /> + {this._child ? + <> + <div className="back" onClick={this.back}>←</div> + <div>{buttons}</div> + <div className="item" key="home" onClick={this.returnHome}>Home</div> + </> : + <> + {buttons} + {/* <div className="item" key="library" onClick={this.openLibrary}> + Library + </div> */} + <Uploader Document={workspaces} /> + <div className="item" key="audio" onClick={this.recordAudio}>Record Audio</div> + <div className="item" key="presentation" onClick={this.openDefaultPresentation}>Presentation</div> + <div className="item" key="settings" onClick={() => SettingsManager.Instance.open()}>Settings</div> + </> + } + </div> + {this._child ? null : <div>{this.renderView}</div>} + </> + ); + } + + pinToPresentation = () => { + // Only making button available if it is an image + if (this._activeDoc.type === "image") { + const isPinned = this._activeDoc && Doc.isDocPinned(this._activeDoc); + return <div className="pinButton" + 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>; + } + } + + recordAudio = async () => { + // upload to server with known URL + this._parents.push(this._activeDoc); + const audioDoc = Cast(Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100, title: "mobile audio" }), Doc) as Doc; + if (audioDoc) { + console.log("audioClicked: " + audioDoc.title); + this._activeDoc = audioDoc; + this.switchCurrentView((userDoc: Doc) => audioDoc); + this.toggleSidebar(); + } + const audioRightSidebar = Cast(Doc.UserDoc().rightSidebarCollection, Doc) as Doc; + if (audioRightSidebar) { + console.log(audioRightSidebar.title); + const data = Cast(audioRightSidebar.data, listSpec(Doc)); + if (data) { + data.push(audioDoc); + } + } + } + + openDefaultPresentation = () => { + this._parents.push(this._activeDoc); + const presentation = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; + + if (presentation) { + console.log("presentation clicked: " + presentation.title); + this._activeDoc = presentation; + this.switchCurrentView((userDoc: Doc) => presentation); + this.toggleSidebar(); + } + } + + // mobileHome = () => { + // return ( + // <div className="homeContainer"> + // <div className="uploadButton"> + + // </div> + // <div className="presentationButton"> + + // </div> + // <div className="recordAudioButton"> + + // </div> + // <div className="inkButton"> + + // </div> + // <div className="settingsButton"> + + // </div> + // </div> + // ); + // } + + renderActiveCollection = (userDoc: Doc) => { + if (this.activeContainer) { + const active = Cast(this.activeContainer.data, listSpec(Doc)); + if (active) { + return ( + <div className="mobileInterface-background">HELLO!</div> + ); + } } - return "hello"; } onBack = (e: React.MouseEvent) => { @@ -164,60 +383,15 @@ export default class MobileInterface extends React.Component { panelHeight = () => window.innerHeight; panelWidth = () => window.innerWidth; - renderInkingContent = () => { - console.log("rendering inking content"); - // TODO: support panning and zooming - // TODO: handle moving of ink strokes - if (this.mainContainer) { - 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 - Document={this.mainContainer} - DataDoc={undefined} - LibraryPath={emptyPath} - fieldKey={""} - dropAction={"alias"} - bringToFront={emptyFunction} - addDocTab={returnFalse} - pinToPres={emptyFunction} - PanelWidth={this.panelWidth} - PanelHeight={this.panelHeight} - NativeHeight={returnZero} - NativeWidth={returnZero} - focus={emptyFunction} - isSelected={returnFalse} - select={emptyFunction} - active={returnFalse} - ContentScaling={returnOne} - whenActiveChanged={returnFalse} - ScreenToLocalTransform={Transform.Identity} - renderDepth={0} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - rootSelected={returnTrue}> - </CollectionView> - </div> - ); - } - } + //WAS 3 + + //WAS 1 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 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 @@ -260,45 +434,6 @@ export default class MobileInterface extends React.Component { } } - renderUploadContent() { - if (this.mainContainer) { - 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> - <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} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> - </div> - ); - } - } - onDragOver = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); @@ -314,18 +449,22 @@ export default class MobileInterface extends React.Component { <GestureOverlay> {this.renderView ? this.renderView() : this.renderDefaultContent()} </GestureOverlay> */} - + {/* <GestureOverlay> */} + <SettingsManager /> + {this.displayWorkspaces()} + {this.pinToPresentation()} + {/* </GestureOverlay> */} {/* <DictationOverlay /> <SharingManager /> <GoogleAuthenticationManager /> */} - <DocumentDecorations /> - <GestureOverlay> - {this.renderView ? this.renderView() : this.renderDefaultContent()} - </GestureOverlay> - <PreviewCursor /> + {/* <DocumentDecorations /> */} + <div> + {this.renderDefaultContent()} + </div> + {/* <PreviewCursor /> */} {/* <ContextMenu /> */} - <RadialMenu /> - <RichTextMenu /> + {/* <RadialMenu /> + <RichTextMenu /> */} {/* <PDFMenu /> <MarqueeOptionsMenu /> <OverlayView /> */} @@ -335,9 +474,102 @@ export default class MobileInterface extends React.Component { } 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(); }); +// WAS 2 + +// 1 +// renderUploadContent() { +// if (this.mainContainer) { +// 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> +// <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} +// ContainingCollectionView={undefined} +// ContainingCollectionDoc={undefined} /> +// </div> +// ); +// } +// } + +// 2 +// 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(); }); + +// 3 + // renderInkingContent = () => { + // console.log("rendering inking content"); + // // TODO: support panning and zooming + // // TODO: handle moving of ink strokes + // if (this.mainContainer) { + // 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 + // Document={this.mainContainer} + // DataDoc={undefined} + // LibraryPath={emptyPath} + // fieldKey={""} + // dropAction={"alias"} + // bringToFront={emptyFunction} + // addDocTab={returnFalse} + // pinToPres={emptyFunction} + // PanelWidth={this.panelWidth} + // PanelHeight={this.panelHeight} + // NativeHeight={returnZero} + // NativeWidth={returnZero} + // focus={emptyFunction} + // isSelected={returnFalse} + // select={emptyFunction} + // active={returnFalse} + // ContentScaling={returnOne} + // whenActiveChanged={returnFalse} + // ScreenToLocalTransform={Transform.Identity} + // renderDepth={0} + // ContainingCollectionView={undefined} + // ContainingCollectionDoc={undefined} + // rootSelected={returnTrue}> + // </CollectionView> + // </div> + // ); + // } + // } |