import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from '@material-ui/core'; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, Opt } from "../../fields/Doc"; import { InkField } from '../../fields/InkField'; import { RichTextField } from '../../fields/RichTextField'; import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; import { ImageField } from '../../fields/URLField'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; import { GooglePhotos } from '../apis/google_docs/GooglePhotosClientUtils'; import { Docs, DocUtils } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; import { SelectionManager } from '../util/SelectionManager'; import { undoBatch } from '../util/UndoManager'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionViewType } from './collections/CollectionView'; import { DocumentView } from './nodes/DocumentView'; import { GoogleRef } from "./nodes/formattedText/FormattedTextBox"; import './PropertiesButtons.scss'; import React = require("react"); const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; const cloud: IconProp = "cloud-upload-alt"; const fetch: IconProp = "sync-alt"; enum UtilityButtonState { Default, OpenRight, OpenExternally } @observer export class PropertiesButtons extends React.Component<{}, {}> { private _pullAnimating = false; private _pushAnimating = false; private _pullColorAnimating = false; public static hasPushedHack = false; public static hasPulledHack = false; @observable public static Instance: PropertiesButtons; @observable private openHover = UtilityButtonState.Default; @observable private pushIcon: IconProp = "arrow-alt-circle-up"; @observable private pullIcon: IconProp = "arrow-alt-circle-down"; @observable private pullColor: string = "white"; @observable public isAnimatingFetch = false; @observable public isAnimatingPulse = false; @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc; } @computed get selectedDocumentView() { return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; } @computed get onClick() { return this.selectedDoc?.onClickBehavior ? this.selectedDoc?.onClickBehavior : "nothing"; } public startPullOutcome = action((success: boolean) => { if (!this._pullAnimating) { this._pullAnimating = true; this.pullIcon = success ? "check-circle" : "stop-circle"; setTimeout(() => runInAction(() => { this.pullIcon = "arrow-alt-circle-down"; this._pullAnimating = false; }), 1000); } }); public startPushOutcome = action((success: boolean) => { this.isAnimatingPulse = false; if (!this._pushAnimating) { this._pushAnimating = true; this.pushIcon = success ? "check-circle" : "stop-circle"; setTimeout(() => runInAction(() => { this.pushIcon = "arrow-alt-circle-up"; this._pushAnimating = false; }), 1000); } }); public setPullState = action((unchanged: boolean) => { this.isAnimatingFetch = false; if (!this._pullColorAnimating) { this._pullColorAnimating = true; this.pullColor = unchanged ? "lawngreen" : "red"; setTimeout(this.clearPullColor, 1000); } }); private clearPullColor = action(() => { this.pullColor = "white"; this._pullColorAnimating = false; }); @computed get considerGoogleDocsPush() { const targetDoc = this.selectedDoc; const published = targetDoc && Doc.GetProto(targetDoc)[GoogleRef] !== undefined; const animation = this.isAnimatingPulse ? "shadow-pulse 1s linear infinite" : "none"; return !targetDoc ? (null) : {`${published ? "Push" : "Publish"} to Google Docs`}} placement="top">
{ await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(); !published && runInAction(() => this.isAnimatingPulse = true); PropertiesButtons.hasPushedHack = false; targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1; }}>
Google
; } @computed get considerGoogleDocsPull() { const targetDoc = this.selectedDoc; const dataDoc = targetDoc && Doc.GetProto(targetDoc); const animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none"; const title = (() => { switch (this.openHover) { default: case UtilityButtonState.Default: return `${!dataDoc?.googleDocUnchanged ? "Pull from" : "Fetch"} Google Docs`; case UtilityButtonState.OpenRight: return "Open in Right Split"; case UtilityButtonState.OpenExternally: return "Open in new Browser Tab"; } })(); return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) :
{title}
} placement="top">
{ e.altKey && (this.openHover = UtilityButtonState.OpenExternally); e.shiftKey && (this.openHover = UtilityButtonState.OpenRight); })} onPointerLeave={action(() => this.openHover = UtilityButtonState.Default)} onClick={async e => { const googleDocUrl = `https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`; if (e.shiftKey) { e.preventDefault(); let googleDoc = await Cast(dataDoc.googleDoc, Doc); if (!googleDoc) { const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, useCors: false }; googleDoc = Docs.Create.WebDocument(googleDocUrl, options); dataDoc.googleDoc = googleDoc; } CollectionDockingView.AddSplit(googleDoc, "right"); } else if (e.altKey) { e.preventDefault(); window.open(googleDocUrl); } else { this.clearPullColor(); PropertiesButtons.hasPulledHack = false; targetDoc[Pulls] = NumCast(targetDoc[Pulls]) + 1; dataDoc.googleDocUnchanged && runInAction(() => this.isAnimatingFetch = true); } }}> { switch (this.openHover) { default: case UtilityButtonState.Default: return dataDoc.googleDocUnchanged === false ? (this.pullIcon as any) : fetch; case UtilityButtonState.OpenRight: return "arrow-alt-circle-right"; case UtilityButtonState.OpenExternally: return "share"; } })()} />
Fetch
; } @action makeMask = (inkDoc: Doc) => { inkDoc.isInkMask = !inkDoc.isInkMask; inkDoc._backgroundColor = inkDoc.isInkMask ? "rgba(0,0,0,0.7)" : undefined; inkDoc.mixBlendMode = inkDoc.isInkMask ? "hard-light" : undefined; inkDoc.color = "#9b9b9bff"; inkDoc._stayInCollection = inkDoc.isInkMask ? true : undefined; } propToggleBtn = (label: string, property: string, tooltip: (on?: any) => string, icon: (on: boolean) => string, onClick?: (dv: Opt, doc: Doc, property: string) => void) => { const targetDoc = this.selectedDoc; const onPropToggle = (dv: Opt, doc: Doc, prop: string) => (dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? undefined : true; return !targetDoc ? (null) : {tooltip(targetDoc?.[property])} } placement="top">
{ if (SelectionManager.Views().length) { SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property)); } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property); })} >
{label}
; } @computed get lockButton() { return this.propToggleBtn("Position", "_lockedPosition", on => `${on ? "Unlock" : "Lock"} XY location on freeform view`, on => on ? "unlock" : "lock"); } @computed get panButton() { return this.propToggleBtn("Pan", "_lockedTransform", on => `${on ? "Unlock" : "Lock"} panning of view`, on => on ? "unlock" : "lock"); } @computed get dictationButton() { return this.propToggleBtn("Dictate", "_showAudio", on => `${on ? "Hide" : "Show"} dictation/recording controls`, () => "microphone"); } @computed get maskButton() { return this.propToggleBtn("Mask", "isInkMask", on => on ? "Make plain ink" : "Make highlight mask", () => "paint-brush", (dv, doc) => this.makeMask(dv?.layoutDoc || doc)); } @computed get clustersButton() { return this.propToggleBtn("Clusters", "_useClusters", on => `${on ? "Hide" : "Show"} clusters`, () => "braille"); } @computed get fitContentButton() { return this.propToggleBtn("Fit All", "_fitToBox", on => `${on ? "Don't" : ""} fit content to container visible area`, on => on ? "expand-arrows-alt" : "compress-arrows-alt"); } @computed get fitWidthButton() { return this.propToggleBtn("Fit Wid", "_fitWidth", on => `${on ? "Don't" : ""} fit content to width of container`, on => on ? "arrows-alt-h" : "arrows-alt-h"); } @computed get captionButton() { return this.propToggleBtn("Caption", "_showCaption", on => `${on ? "Hide" : "Show"} caption footer`, on => "closed-captioning", (dv, doc) => (dv?.rootDoc || doc)._showCaption = (dv?.rootDoc || doc)._showCaption === undefined ? "caption" : undefined); } @computed get chromeButton() { return this.propToggleBtn("Controls", "_chromeStatus", on => `${on === "enabled" ? "Hide" : "Show"} editing UI`, on => "edit", (dv, doc) => (dv?.rootDoc || doc)._chromeStatus = (dv?.rootDoc || doc)._chromeStatus === undefined ? "enabled" : undefined); } @computed get titleButton() { return this.propToggleBtn("Title", "_showTitle", on => "Switch between title styles", on => "text-width", (dv, doc) => (dv?.rootDoc || doc)._showTitle = !(dv?.rootDoc || doc)._showTitle ? "title" : (dv?.rootDoc || doc)._showTitle === "title" ? "title:hover" : undefined); } @computed get downloadButton() { const targetDoc = this.selectedDoc; return !targetDoc ? (null) : {"Download Document"}} placement="top">
this.selectedDoc && Doc.Zip(this.selectedDoc)}>
downld
; } @computed get onClickButton() { return !this.selectedDoc ? (null) :
Choose onClick behavior
} placement="top">
e.stopPropagation()} > {}
onclick
; } @undoBatch @action handleOptionChange = (e: any) => { this.selectedDoc && (this.selectedDoc.onClickBehavior = e.target.value); SelectionManager.Views().filter(dv => dv.docView).map(dv => dv.docView!).forEach(docView => { docView.noOnClick(); switch (e.target.value) { case "enterPortal": docView.makeIntoPortal(); break; case "toggleDetail": docView.toggleDetail(); break; case "linkInPlace": docView.toggleFollowLink("inPlace", true, false); break; case "linkOnRight": docView.toggleFollowLink("add:right", false, false); break; } }); } @undoBatch @action editOnClickScript = () => { if (this.selectedDoc) { if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.rootDoc, undefined, "onClick")); else DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, "onClick"); } } @computed get onClickFlyout() { return
{Doc.UserDoc().noviceMode ? (null) :
Edit onClick Script
}
; } @computed get googlePhotosButton() { const targetDoc = this.selectedDoc; return !targetDoc ? (null) :
{"Export to Google Photos"}
} placement="top">
this.selectedDoc && GooglePhotos.Export.CollectionToAlbum({ collection: this.selectedDoc }).then(console.log)}> {}
google
; } render() { if (!this.selectedDoc) return (null); const layoutField = this.selectedDoc[Doc.LayoutFieldKey(this.selectedDoc)]; const isText = layoutField instanceof RichTextField; const isImage = layoutField instanceof ImageField; const isInk = layoutField instanceof InkField; const isCollection = this.selectedDoc.type === DocumentType.COL; const isFreeForm = this.selectedDoc._viewType === CollectionViewType.Freeform; const considerPull = isText && this.considerGoogleDocsPull; const considerPush = isText && this.considerGoogleDocsPush; return
{this.titleButton}
{this.captionButton}
{this.chromeButton}
{this.lockButton}
{this.panButton}
{this.dictationButton}
{this.onClickButton}
{this.considerGoogleDocsPush}
{this.considerGoogleDocsPull}
{this.googlePhotosButton}
{this.clustersButton}
{this.fitContentButton}
{this.fitWidthButton}
{this.maskButton}
; } }