import React = require('react'); import { library } from '@fortawesome/fontawesome-svg-core'; 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 * 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 { 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 { Scripting } from '../client/util/Scripting'; import GestureOverlay from '../client/views/GestureOverlay'; import { InkingControl } from '../client/views/InkingControl'; 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 { RadialMenu } from '../client/views/nodes/RadialMenu'; import { Id } from '../fields/FieldSymbols'; import { WebField, nullAudio } from "../fields/URLField"; import { FieldResult } from "../fields/Doc"; import { AssignAllExtensions } from '../extensions/General/Extensions'; import { listSpec } from '../fields/Schema'; 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 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"; @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 = this.userDoc; private _child: Doc | null = null; private _parents: Array = []; private _menu: Doc = this.mainDoc; private _open: boolean = false; private _library: Doc = Cast(this.userDoc.myWorkspaces, Doc) as Doc; constructor(props: Readonly<{}>) { super(props); MobileInterface.Instance = this; } @action componentDidMount = () => { library.add(...[faPenNib, faHighlighter, faEraser, faMousePointer, faThumbtack]); if (this.userDoc && !this.mainContainer) { this.userDoc.activeMobile = this.mainDoc; } } @action switchCurrentView = (doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => { if (!this.userDoc) return; this.userDoc.activeMobile = doc(this.userDoc); onSwitch && onSwitch(); this.renderView = renderView; } onSwitchInking = () => { InkingControl.Instance.switchTool(InkTool.Pen); MobileInterface.Instance.drawingInk = true; DocServer.Mobile.dispatchOverlayTrigger({ enableOverlay: true, width: window.innerWidth, height: window.innerHeight }); } 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) { 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; } } } DocServer.Mobile.dispatchOverlayTrigger({ enableOverlay: true, width: width, height: height, text: "Documents uploaded from mobile will show here", }); } 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) { const backgroundColor = () => "white"; return (
window.screen.width} PanelHeight={() => window.screen.height} renderDepth={0} focus={emptyFunction} backgroundColor={backgroundColor} parentActive={returnTrue} whenActiveChanged={emptyFunction} bringToFront={emptyFunction} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} />
); } } 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 (
this.handleClick(doc)}>{doc.title}
{doc.type}
); }); if (this._child) { buttons = DocListCast(this._child.data).map((doc: Doc, index: any) => { return (
this.handleClick(doc)}>{doc.title}
{doc.type}
); }); } if (!this._child) { return (
{this.createPathname()}
{this.renderView}
); } else { return (
{this.createPathname()}
); } } 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 ( //
//
//
//
//
//
//
//
//
//
//
//
// ); // } renderActiveCollection = (userDoc: Doc) => { if (this.activeContainer) { const active = Cast(this.activeContainer.data, listSpec(Doc)); if (active) { return (
HELLO!
); } } } onBack = (e: React.MouseEvent) => { this.switchCurrentView((userDoc: Doc) => this.mainDoc); InkingControl.Instance.switchTool(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(); } shiftRight = (e: React.MouseEvent) => { DocServer.Mobile.dispatchOverlayPositionUpdate({ dx: 10 }); e.preventDefault(); e.stopPropagation(); } panelHeight = () => window.innerHeight; panelWidth = () => window.innerWidth; //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 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], }); } } } 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 = Cast(webDoc.data, WebField); url = urlField ? urlField.url.toString() : "https://en.wikipedia.org/wiki/Hedgehog"; } } 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(); } } } onDragOver = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); } render() { // const content = this.currentView === "main" ? this.mainContent : // this.currentView === "ink" ? this.inkContent : // this.currentView === "upload" ? this.uploadContent : <>; return (
{/* {this.renderView ? this.renderView() : this.renderDefaultContent()} */} {/* */} {this.displayWorkspaces()} {/* */} {/* */} {/* */}
{this.renderDefaultContent()}
{/* */} {/* */} {/* */} {/* */}
); } } Scripting.addGlobal(function switchMobileView(doc: (userDoc: Doc) => Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); }); // 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(, document.getElementById('root')); })(); // 1 // renderUploadContent() { // if (this.mainContainer) { // return ( //
//
// // {/* */} // {/* */} // //
// window.screen.width} // PanelHeight={() => window.screen.height} // renderDepth={0} // focus={emptyFunction} // backgroundColor={returnEmptyString} // parentActive={returnTrue} // whenActiveChanged={emptyFunction} // bringToFront={emptyFunction} // ContainingCollectionView={undefined} // ContainingCollectionDoc={undefined} /> //
// ); // } // } // 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 ( //
//
//
// //
//
// //
//
// // //
//
// // //
// ); // } // }