diff options
-rw-r--r-- | deploy/mobile/image.html | 11 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/.DS_Store | bin | 6148 -> 8196 bytes | |||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 4 | ||||
-rw-r--r-- | src/client/views/nodes/FieldView.tsx | 6 | ||||
-rw-r--r-- | src/mobile/ImageUpload.scss | 55 | ||||
-rw-r--r-- | src/mobile/ImageUpload.tsx | 67 | ||||
-rw-r--r-- | src/mobile/MobileInterface.scss | 9 | ||||
-rw-r--r-- | src/mobile/MobileInterface.tsx | 600 | ||||
-rw-r--r-- | src/mobile/MobileMenu.scss | 240 | ||||
-rw-r--r-- | webpack.config.js | 2 |
11 files changed, 778 insertions, 218 deletions
diff --git a/deploy/mobile/image.html b/deploy/mobile/image.html index 6424d2a60..cca6f763b 100644 --- a/deploy/mobile/image.html +++ b/deploy/mobile/image.html @@ -1,15 +1,16 @@ <html> <head> - <title>Test view</title> + <title>Dash Mobile</title> <link href="https://fonts.googleapis.com/css?family=Fjalla+One|Hind+Siliguri:300" rel="stylesheet"> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" + integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> + <script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.3.1/typescript.min.js"></script> </head> <body> - <div id="root"> - <p>Capture Image: <input type="file" accept="image/*" id="capture"> - </div> - <script src="../imageUpload.js"></script> + <div id="root"></div> + <script src="../mobileInterface.js"></script> </body> </html>
\ No newline at end of file diff --git a/package.json b/package.json index 8bf1e6b74..ca82d523d 100644 --- a/package.json +++ b/package.json @@ -228,4 +228,4 @@ "xoauth2": "^1.2.0", "xregexp": "^4.3.0" } -}
\ No newline at end of file +} diff --git a/src/.DS_Store b/src/.DS_Store Binary files differindex 5b35884bd..299b902c6 100644 --- a/src/.DS_Store +++ b/src/.DS_Store diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 496099557..377f908e9 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -444,6 +444,10 @@ export class CurrentUserUtils { }); } + static setupMobileMenu(userDoc: Doc) { + return CurrentUserUtils.setupWorkspaces(userDoc); + } + static setupMobileInkingDoc(userDoc: Doc) { return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white" }); } diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index e9dc43bd8..0bb1401aa 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -91,9 +91,9 @@ export class FieldView extends React.Component<FieldViewProps> { else if (field instanceof VideoField) { return <VideoBox {...this.props} />; } - else if (field instanceof AudioField) { - return <AudioBox {...this.props} />; - } else if (field instanceof DateField) { + // else if (field instanceof AudioField) { + // return <AudioBox {...this.props} />; + else if (field instanceof DateField) { return <p>{field.date.toLocaleString()}</p>; } else if (field instanceof Doc) { diff --git a/src/mobile/ImageUpload.scss b/src/mobile/ImageUpload.scss index eea69b81c..715924cba 100644 --- a/src/mobile/ImageUpload.scss +++ b/src/mobile/ImageUpload.scss @@ -1,12 +1,26 @@ @import "../client/views/globalCssVariables.scss"; .imgupload_cont { - display: flex; - justify-content: center; - flex-direction: column; - align-items: center; - width: 100vw; - height: 100vh; + // display: flex; + // justify-content: center; + // flex-direction: column; + // align-items: center; + // width: 100vw; + // height: 100vh; + + .upload_label { + font-weight: normal !important; + width: 100%; + padding: 13px 12px; + border-bottom: 1px solid rgba(200, 200, 200, 0.7); + font-family: Arial, Helvetica, sans-serif; + font-style: normal; + font-weight: normal; + user-select: none; + font-size: 35px; + text-transform: uppercase; + color: black; + } .button_file { text-align: center; @@ -21,14 +35,27 @@ display: none; } - .upload_label, + // .upload_label, + // .upload_button { + // background: $dark-color; + // font-size: 500%; + // font-family: $sans-serif; + // text-align: center; + // padding: 5vh; + // margin-bottom: 20px; + // color: white; + // } + .upload_button { - background: $dark-color; - font-size: 500%; - font-family: $sans-serif; - text-align: center; - padding: 5vh; - margin-bottom: 20px; - color: white; + width: 100%; + padding: 13px 12px; + border-bottom: 1px solid rgba(200, 200, 200, 0.7); + font-family: Arial, Helvetica, sans-serif; + font-style: normal; + font-weight: normal; + user-select: none; + font-size: 35px; + text-transform: uppercase; + color: black; } }
\ No newline at end of file diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index b15042f9f..2552e2112 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -4,16 +4,19 @@ import { Docs } from '../client/documents/Documents'; import "./ImageUpload.scss"; import React = require('react'); import { DocServer } from '../client/DocServer'; -import { Opt, Doc } from '../fields/Doc'; +import { Opt, Doc, DocListCast } from '../fields/Doc'; import { Cast } from '../fields/Types'; import { listSpec } from '../fields/Schema'; import { List } from '../fields/List'; import { observer } from 'mobx-react'; import { observable } from 'mobx'; import { Utils } from '../Utils'; -import MobileInterface from './MobileInterface'; -import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; -import { resolvedPorts } from '../client/views/Main'; +import { Networking } from '../client/Network'; +import { MobileDocumentUploadContent } from '../server/Message'; + +export interface ImageUploadProps { + Document: Doc; +} // const onPointerDown = (e: React.TouchEvent) => { // let imgInput = document.getElementById("input_image_file"); @@ -24,7 +27,7 @@ import { resolvedPorts } from '../client/views/Main'; const inputRef = React.createRef<HTMLInputElement>(); @observer -class Uploader extends React.Component { +export class Uploader extends React.Component<ImageUploadProps> { @observable error: string = ""; @observable status: string = ""; @@ -39,20 +42,15 @@ class Uploader extends React.Component { if (files && files.length !== 0) { console.log(files[0]); const name = files[0].name; - const formData = new FormData(); - formData.append("file", files[0]); - - const upload = window.location.origin + "/uploadFormData"; + const res = await Networking.UploadFilesToServer(files[0]); this.status = "uploading image"; - console.log("uploading image", formData); - const res = await fetch(upload, { - method: 'POST', - body: formData - }); this.status = "upload image, getting json"; - const json = await res.json(); - json.map(async (file: any) => { - const path = window.location.origin + file; + + res.map(async ({ result }) => { + if (result instanceof Error) { + return; + } + const path = Utils.prepend(result.accessPaths.agnostic.client); const doc = Docs.Create.ImageDocument(path, { _nativeWidth: 200, _width: 200, title: name }); this.status = "getting user document"; @@ -75,12 +73,10 @@ class Uploader extends React.Component { pending.data = new List([doc]); } this.status = "finished"; + console.log("hi"); } - }); - - // console.log(window.location.origin + file[0]) - //imgPrev.setAttribute("src", window.location.origin + files[0].name) + }); } } } catch (error) { @@ -91,12 +87,12 @@ class Uploader extends React.Component { render() { return ( <div className="imgupload_cont"> - <label htmlFor="input_image_file" className="upload_label">Choose an Image</label> + <label htmlFor="input_image_file" className="upload_label" onClick={this.onClick}>Upload Image</label> <input type="file" accept="image/*" className="input_file" id="input_image_file" ref={inputRef}></input> - <button onClick={this.onClick} className="upload_button">Upload</button> + {/* <div onClick={this.onClick} className="upload_button">Upload</div> */} <img id="img_preview" src=""></img> <p>{this.status}</p> - <p>{this.error}</p> + {/* <p>{this.error}</p> */} </div> ); } @@ -104,25 +100,4 @@ class Uploader extends React.Component { } -// DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, "image upload"); -(async () => { - const info = await CurrentUserUtils.loadCurrentUser(); - DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, info.email + "mobile"); - await Docs.Prototypes.initialize(); - if (info.id !== "__guest__") { - // a guest will not have an id registered - await CurrentUserUtils.loadUserDocument(info); - } - document.getElementById('root')!.addEventListener('wheel', event => { - if (event.ctrlKey) { - event.preventDefault(); - } - }, true); - ReactDOM.render(( - // <Uploader /> - <MobileInterface /> - ), - document.getElementById('root') - ); -} -)();
\ No newline at end of file +// DocServer.init(window.location.protocol, window.location.hostname, 4321, "image upload");
\ No newline at end of file diff --git a/src/mobile/MobileInterface.scss b/src/mobile/MobileInterface.scss index 4d86e208f..f75e60a37 100644 --- a/src/mobile/MobileInterface.scss +++ b/src/mobile/MobileInterface.scss @@ -16,4 +16,13 @@ height: 100%; position: relative; touch-action: none; + width: 100%; +} + +.mobileInterface-background { + height: 100%; + width: 100%; + position: relative; + touch-action: none; + background-color: pink; }
\ No newline at end of file diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 6c2e797d6..9b28d066c 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -1,47 +1,68 @@ 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 ReactDOM from "react-dom"; +import * as rp from 'request-promise'; +import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; +import { FieldValue, Cast, StrCast } from '../fields/Types'; +import { Doc, DocListCast, Opt } from '../fields/Doc'; 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 { CollectionView } from '../client/views/collections/CollectionView'; +import { DocumentView } from '../client/views/nodes/DocumentView'; +import { emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, returnZero, Utils } from '../Utils'; import { Transform } from '../client/util/Transform'; -import { DocumentDecorations } from '../client/views/DocumentDecorations'; +import { Scripting } from '../client/util/Scripting'; import GestureOverlay from '../client/views/GestureOverlay'; import { InkingControl } from '../client/views/InkingControl'; -import { DocumentView } from '../client/views/nodes/DocumentView'; -import { RadialMenu } from '../client/views/nodes/RadialMenu'; +import { InkTool } from '../fields/InkField'; +import "./MobileInterface.scss"; +import "./MobileMenu.scss"; +import { DocServer } from '../client/DocServer'; +import { DocumentDecorations } from '../client/views/DocumentDecorations'; import { PreviewCursor } from '../client/views/PreviewCursor'; -import { Doc, DocListCast, FieldResult } from '../fields/Doc'; +import { RadialMenu } from '../client/views/nodes/RadialMenu'; import { Id } from '../fields/FieldSymbols'; -import { InkTool } from '../fields/InkField'; +import { WebField, nullAudio } from "../fields/URLField"; +import { FieldResult } from "../fields/Doc"; +import { AssignAllExtensions } from '../extensions/General/Extensions'; import { listSpec } from '../fields/Schema'; -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 } from '../Utils'; -import "./MobileInterface.scss"; -import { CollectionView } from '../client/views/collections/CollectionView'; +import { DocumentManager } from '../client/util/DocumentManager'; +import RichTextMenu from '../client/views/nodes/formattedText/RichTextMenu'; +import { MainView } from '../client/views/MainView'; +import SettingsManager from '../client/util/SettingsManager'; +import { Uploader } from "./ImageUpload"; +import { Upload } from '../server/SharedMediaTypes'; +import { createTypePredicateNodeWithModifier } from 'typescript'; +import { AudioBox } from '../client/views/nodes/AudioBox'; +import { List } from '../fields/List'; 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.setupMobileMenu(this.userDoc); @observable private renderView?: () => JSX.Element; + 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 +71,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 +102,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 +124,286 @@ export default class MobileInterface extends React.Component { }); } - renderDefaultContent = () => { + toggleSidebar = () => { + console.log("clicked"); + let menuButton = document.getElementById("menuButton") as HTMLElement; + menuButton.classList.toggle('active'); + + let sidebar = document.getElementById("sidebar") as HTMLElement; + sidebar.classList.toggle('active'); + + let header = document.getElementById("header") as HTMLElement; + + if (!sidebar.classList.contains('active')) { + header.textContent = String(this._activeDoc.title); + } else { + header.textContent = "menu"; + } + } + + back = () => { + let 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: "80%", overflow: "hidden", left: "10%" }}> + <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) { + let 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; + } + + openLibrary() { + this._activeDoc = this.mainDoc; + this.switchCurrentView((userDoc: Doc) => this.mainDoc); + this._child = this._library; + } + + renderDefaultContent = () => { + const workspaces = Cast(this.userDoc.myWorkspaces, Doc) as Doc; + let buttons = DocListCast(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>); + }); + + if (this._child) { + buttons = DocListCast(this._child.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>); + }); + } + + if (!this._child) { + return ( + <div> + <div className="navbar"> + <div className="header" id="header">MENU</div> + <div className="toggle-btn" id="menuButton" onClick={this.toggleSidebar}> + <span></span> + <span></span> + <span></span> + </div> + </div> + <div className="pathbar"> + <div className="pathname"> + {this.createPathname()} + </div> + </div> + <div className="sidebar" id="sidebar"> + <div> + <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} /> + {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> + </div> + <div> + {this.renderView} + </div> + </div> + ); + } + else { + return ( + <div> + <div className="navbar"> + <div className="header" id="header">menu</div> + <div className="toggle-btn" id="menuButton" onClick={this.toggleSidebar}> + <span></span> + <span></span> + <span></span> + </div> + </div> + <div className="pathbar"> + <div className="pathname"> + {this.createPathname()} + </div> + </div> + <div className="sidebar" id="sidebar"> + <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} /> + <div className="back" onClick={this.back}> + ← + </div> + <div> + {buttons} + </div> + <div className="item" key="home" onClick={this.returnHome}> + Home + </div> + </div> + </div> + ); + } + } + + recordAudio = async () => { + // upload to server with known URL + + 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 = () => { + 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 +438,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 @@ -259,46 +488,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 +503,21 @@ export default class MobileInterface extends React.Component { <GestureOverlay> {this.renderView ? this.renderView() : this.renderDefaultContent()} </GestureOverlay> */} - + {/* <GestureOverlay> */} + <SettingsManager /> + {this.displayWorkspaces()} + {/* </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 +527,121 @@ 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 + +AssignAllExtensions(); + +(async () => { + const info = await CurrentUserUtils.loadCurrentUser(); + DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + " (mobile)"); + await Docs.Prototypes.initialize(); + if (info.id !== "__guest__") { + // a guest will not have an id registered + await CurrentUserUtils.loadUserDocument(info); + } + document.getElementById('root')!.addEventListener('wheel', event => { + if (event.ctrlKey) { + event.preventDefault(); + } + }, true); + ReactDOM.render(<MobileInterface />, document.getElementById('root')); +})(); + + +// 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> + // ); + // } + // }
\ No newline at end of file diff --git a/src/mobile/MobileMenu.scss b/src/mobile/MobileMenu.scss new file mode 100644 index 000000000..250340e36 --- /dev/null +++ b/src/mobile/MobileMenu.scss @@ -0,0 +1,240 @@ +$navbar-height: 120px; +$pathbar-height: 50px; + +* { + margin: 0px; + padding: 0px; + box-sizing: border-box; + font-family: "Open Sans"; +} + +body { + overflow: hidden; +} + +.navbar { + position: fixed; + top: 0px; + left: 0px; + width: 100vw; + height: $navbar-height; + background-color: whitesmoke; + border-bottom: 5px solid black; +} + +.navbar .toggle-btn { + position: absolute; + right: 20px; + top: 30px; + height: 70px; + width: 70px; + transition: all 300ms ease-in-out 200ms; +} + +.navbar .header { + position: absolute; + top: 50%; + top: calc(9px + 50%); + right: 50%; + transform: translate(50%, -50%); + font-size: 40; + user-select: none; + text-transform: uppercase; + font-family: Arial, Helvetica, sans-serif; +} + +.navbar .toggle-btn span { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 70%; + height: 4px; + background: black; + transition: all 200ms ease; +} + +.navbar .toggle-btn span:nth-child(1) { + transition: top 200ms ease-in-out; + top: 30%; +} + +.navbar .toggle-btn span:nth-child(3) { + transition: top 200ms ease-in-out; + top: 70%; +} + +.navbar .toggle-btn.active { + transition: transform 200ms ease-in-out 200ms; + transform: rotate(135deg); +} + +.navbar .toggle-btn.active span:nth-child(1) { + top: 50%; +} + +.navbar .toggle-btn.active span:nth-child(2) { + transform: translate(-50%, -50%) rotate(90deg); +} + +.navbar .toggle-btn.active span:nth-child(3) { + top: 50%; +} + +.sidebar { + position: absolute; + top: 200px; + opacity: 0; + right: -100%; + width: 100%; + height: calc(100% - (200px)); + z-index: 5; + background-color: whitesmoke; + transition: all 400ms ease 50ms; + padding: 20px; + // border-right: 5px solid black; +} + +.sidebar .item { + width: 100%; + padding: 13px 12px; + border-bottom: 1px solid rgba(200, 200, 200, 0.7); + font-family: Arial, Helvetica, sans-serif; + font-style: normal; + font-weight: normal; + user-select: none; + font-size: 35px; + text-transform: uppercase; + color: black; +} + +.sidebar .home { + position: absolute; + top: -135px; + right: calc(50% + 80px); + transform: translate(0%, -50%); + font-size: 40; + user-select: none; + text-transform: uppercase; + font-family: Arial, Helvetica, sans-serif; + z-index: 200; +} + +.type { + display: inline; + text-transform: lowercase; + margin-left: 20px; + font-size: 35px; + font-style: italic; + color: rgb(28, 28, 28); +} + +.right { + margin-left: 20px; + z-index: 200; +} + +.left { + width: 100%; + height: 100%; +} + +.sidebar .logout { + width: 100%; + padding: 13px 12px; + border-bottom: 1px solid rgba(200, 200, 200, 0.7); + font-family: Arial, Helvetica, sans-serif; + font-style: normal; + font-weight: normal; + user-select: none; + font-size: 30px; + text-transform: uppercase; + color: black; +} + +.sidebar .settings { + width: 100%; + padding: 13px 12px; + border-bottom: 1px solid rgba(200, 200, 200, 0.7); + font-family: Arial, Helvetica, sans-serif; + font-style: normal; + font-weight: normal; + user-select: none; + font-size: 30px; + text-transform: uppercase; + color: black; +} + + +.sidebar.active { + right: 0%; + opacity: 1; +} + +.back { + position: absolute; + top: -140px; + left: 50px; + transform: translate(0%, -50%); + color: black; + font-size: 60; + user-select: none; + text-transform: uppercase; + z-index: 100; + font-family: Arial, Helvetica, sans-serif; +} + + +.pathbar { + position: absolute; + top: 118px; + background: #1a1a1a; + z-index: 20; + border-radius: 0px; + width: 100%; + height: 80px; + transition: all 400ms ease 50ms; +} + +.pathname { + position: relative; + font-size: 25; + top: 50%; + width: 90%; + left: 3%; + color: whitesmoke; + transform: translate(0%, -50%); + z-index: 20; + font-family: sans-serif; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + direction: rtl; + text-align: left; + text-transform: uppercase; +} + +.homeContainer { + position: relative; + top: 200px; + height: calc(100% - 250px); + width: 90%; + overflow: scroll; + left: 5%; + background-color: lightpink; +} + +.pinButton { + position: relative; + width: 100px; + height: 100px; + font-size: 90px; + text-align: center; + left: 50%; + transform: translate(-50%, 0); + border-style: solid; + border-radius: 50px; + border-width: medium; + background-color: pink; + z-index: 100; +}
\ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index bd1d3b5a9..274f14069 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -43,7 +43,7 @@ module.exports = { repl: ["./src/debug/Repl.tsx", 'webpack-hot-middleware/client?reload=true'], test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'], inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'], - imageUpload: ["./src/mobile/ImageUpload.tsx", 'webpack-hot-middleware/client?reload=true'], + mobileInterface: ["./src/mobile/MobileInterface.tsx", 'webpack-hot-middleware/client?reload=true'], }, optimization: { noEmitOnErrors: true |