diff options
author | Sam Wilkins <samwilkins333@gmail.com> | 2019-08-19 22:39:56 -0400 |
---|---|---|
committer | Sam Wilkins <samwilkins333@gmail.com> | 2019-08-19 22:39:56 -0400 |
commit | 00cecb10d80bad8d717cd06ddc4bf156664b9f2d (patch) | |
tree | f68193b3cafd52c19181041a564eb473290fc839 | |
parent | df11d1afdb271b37618ab9e1d362b2f4a1145982 (diff) |
fixed push pull UI
-rw-r--r-- | src/client/apis/google_docs/GoogleApiClientUtils.ts | 3 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 14 | ||||
-rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 122 | ||||
-rw-r--r-- | src/new_fields/RichTextField.ts | 8 |
4 files changed, 81 insertions, 66 deletions
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts index 55b4a76f8..821c52270 100644 --- a/src/client/apis/google_docs/GoogleApiClientUtils.ts +++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts @@ -4,6 +4,9 @@ import { RouteStore } from "../../../server/RouteStore"; import { Opt } from "../../../new_fields/Doc"; import { isArray } from "util"; +export const Pulls = "googleDocsPullCount"; +export const Pushes = "googleDocsPushCount"; + export namespace GoogleApiClientUtils { export namespace Docs { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 80d4ecb9b..797b43add 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -30,6 +30,7 @@ import { ObjectField } from '../../new_fields/ObjectField'; import { MetadataEntryMenu } from './MetadataEntryMenu'; import { ImageBox } from './nodes/ImageBox'; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; +import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -626,7 +627,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (!canPush) return (null); return ( <div className={"linkButtonWrapper"}> - <div title="Push to Google Docs" className="linkButton-linker" onClick={() => thisDoc.pushToGoogleDocsTrigger = !thisDoc.pushToGoogleDocsTrigger}> + <div title="Push to Google Docs" className="linkButton-linker" onClick={() => { + DocumentDecorations.hasPushedHack = false; + thisDoc[Pushes] = NumCast(thisDoc[Pushes]) + 1; + }}> <FontAwesomeIcon className="documentdecorations-icon" icon="arrow-alt-circle-up" size="sm" /> </div> </div> @@ -639,13 +643,19 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (!canPull) return (null); return ( <div className={"linkButtonWrapper"}> - <div title="Pull From Google Docs" className="linkButton-linker" onClick={() => thisDoc.pullFromGoogleDocsTrigger = !thisDoc.pullFromGoogleDocsTrigger}> + <div title="Pull From Google Docs" className="linkButton-linker" onClick={() => { + DocumentDecorations.hasPulledHack = false; + thisDoc[Pulls] = NumCast(thisDoc[Pulls]) + 1; + }}> <FontAwesomeIcon className="documentdecorations-icon" icon="arrow-alt-circle-down" size="sm" /> </div> </div> ); } + public static hasPushedHack = false; + public static hasPulledHack = false; + considerTooltip = () => { let thisDoc = SelectionManager.SelectedDocuments()[0].props.Document; let isTextDoc = thisDoc.data && thisDoc.data instanceof RichTextField; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 406c2c4a6..d2eb71350 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -12,7 +12,7 @@ import { EditorView } from "prosemirror-view"; import { Doc, Opt, DocListCast } from "../../../new_fields/Doc"; import { Id, Copy } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; -import { RichTextField, ToGoogleDocText, FromGoogleDocText } from "../../../new_fields/RichTextField"; +import { RichTextField, ToPlainText, FromPlainText } from "../../../new_fields/RichTextField"; import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema"; import { BoolCast, Cast, NumCast, StrCast, DateCast } from "../../../new_fields/Types"; import { DocServer } from "../../DocServer"; @@ -30,16 +30,14 @@ import { ContextMenu } from "../../views/ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { InkingControl } from "../InkingControl"; -import { Templates } from '../Templates'; import { FieldView, FieldViewProps } from "./FieldView"; import "./FormattedTextBox.scss"; import React = require("react"); -import { For } from 'babel-types'; import { DateField } from '../../../new_fields/DateField'; import { Utils } from '../../../Utils'; import { MainOverlayTextBox } from '../MainOverlayTextBox'; -import { GoogleApiClientUtils } from '../../apis/google_docs/GoogleApiClientUtils'; -import * as diff from "diff"; +import { GoogleApiClientUtils, Pulls, Pushes } from '../../apis/google_docs/GoogleApiClientUtils'; +import { DocumentDecorations } from '../DocumentDecorations'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -84,6 +82,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe private _searchReactionDisposer?: Lambda; private _textReactionDisposer: Opt<IReactionDisposer>; private _proxyReactionDisposer: Opt<IReactionDisposer>; + private pullReactionDisposer: Opt<IReactionDisposer>; + private pushReactionDisposer: Opt<IReactionDisposer>; private dropDisposer?: DragManager.DragDropDisposer; public get CurrentDiv(): HTMLDivElement { return this._ref.current!; } private isGoogleDocsUpdate = false; @@ -345,13 +345,25 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } ); - reaction(() => this.props.Document.pullFromGoogleDocsTrigger, () => { - this.pullFromGoogleDoc(); - }); + this.pullReactionDisposer = reaction( + () => this.props.Document[Pulls], + () => { + if (!DocumentDecorations.hasPulledHack) { + DocumentDecorations.hasPulledHack = true; + this.pullFromGoogleDoc(); + } + } + ); - reaction(() => this.props.Document.pushToGoogleDocsTrigger, () => { - this.pushToGoogleDoc(); - }); + this.pushReactionDisposer = reaction( + () => this.props.Document[Pushes], + () => { + if (!DocumentDecorations.hasPushedHack) { + DocumentDecorations.hasPushedHack = true; + this.pushToGoogleDoc(); + } + } + ); this._textReactionDisposer = reaction( () => this.extensionDoc, @@ -380,13 +392,44 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this.unhighlightSearchTerms(); } }, { fireImmediately: true }); + } - ["pushToGoogleDocsTrigger", "pullFromGoogleDocsTrigger"].map(key => { - let doc = this.props.Document; - if (doc[key] === undefined) { - untracked(() => doc[key] = false); - } - }); + pushToGoogleDoc = () => { + let modes = GoogleApiClientUtils.Docs.WriteMode; + let mode = modes.Replace; + let reference: Opt<GoogleApiClientUtils.Docs.Reference> = Cast(this.dataDoc[googleDocId], "string"); + if (!reference) { + mode = modes.Insert; + reference = { + title: StrCast(this.dataDoc.title), + handler: id => this.dataDoc[googleDocId] = id + }; + } + if (this._editorView) { + let data = Cast(this.dataDoc.data, RichTextField); + let content = data ? data[ToPlainText]() : this._editorView.state.doc.textContent; + GoogleApiClientUtils.Docs.write({ reference, content, mode }); + } + } + + pullFromGoogleDoc = async () => { + let dataDoc = Doc.GetProto(this.props.Document); + let documentId = StrCast(dataDoc[googleDocId]); + if (documentId) { + let exportState = await GoogleApiClientUtils.Docs.read({ documentId }); + UndoManager.RunInBatch(() => { + if (exportState && exportState.body && exportState.title) { + let data = Cast(dataDoc.data, RichTextField); + if (data) { + this.isGoogleDocsUpdate = true; + dataDoc.data = new RichTextField(data[FromPlainText](exportState.body)); + dataDoc.title = exportState.title; + } + } else { + delete dataDoc[googleDocId]; + } + }, Pulls); + } } clipboardTextSerializer = (slice: Slice): string => { @@ -516,6 +559,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._reactionDisposer && this._reactionDisposer(); this._proxyReactionDisposer && this._proxyReactionDisposer(); this._textReactionDisposer && this._textReactionDisposer(); + this.pushReactionDisposer && this.pushReactionDisposer(); + this.pullReactionDisposer && this.pullReactionDisposer(); } onPointerDown = (e: React.PointerEvent): void => { @@ -693,49 +738,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe event: action(() => Doc.GetProto(this.props.Document).autoHeight = !BoolCast(this.props.Document.autoHeight)), icon: "expand-arrows-alt" }); ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems, icon: "text-height" }); - if (!(googleDocId in Doc.GetProto(this.props.Document))) { - ContextMenu.Instance.addItem({ - description: "Export to Google Doc...", - event: this.pushToGoogleDoc, - icon: "upload" - }); - } - } - - pushToGoogleDoc = () => { - let modes = GoogleApiClientUtils.Docs.WriteMode; - let mode = modes.Replace; - let reference: Opt<GoogleApiClientUtils.Docs.Reference> = Cast(this.dataDoc[googleDocId], "string"); - if (!reference) { - mode = modes.Insert; - reference = { - title: StrCast(this.dataDoc.title), - handler: id => this.dataDoc[googleDocId] = id - }; - } - if (this._editorView) { - let data = Cast(this.dataDoc.data, RichTextField); - let content = data ? data[ToGoogleDocText]() : this._editorView.state.doc.textContent; - GoogleApiClientUtils.Docs.write({ reference, content, mode }); - } - } - - pullFromGoogleDoc = async () => { - let dataDoc = Doc.GetProto(this.props.Document); - let documentId = StrCast(dataDoc[googleDocId]); - if (documentId) { - let exportState = await GoogleApiClientUtils.Docs.read({ documentId }); - if (exportState && exportState.body && exportState.title) { - let data = Cast(dataDoc.data, RichTextField); - if (data) { - this.isGoogleDocsUpdate = true; - dataDoc.data = new RichTextField(data[FromGoogleDocText](exportState.body)); - dataDoc.title = exportState.title; - } - } else { - delete dataDoc[googleDocId]; - } - } } diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index ec08293e9..ab58329f9 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -4,8 +4,8 @@ import { Deserializable } from "../client/util/SerializationHelper"; import { Copy, ToScriptString } from "./FieldSymbols"; import { scriptingGlobal } from "../client/util/Scripting"; -export const ToGoogleDocText = Symbol("PlainText"); -export const FromGoogleDocText = Symbol("PlainText"); +export const ToPlainText = Symbol("PlainText"); +export const FromPlainText = Symbol("PlainText"); const delimiter = "\n"; const joiner = ""; @@ -28,7 +28,7 @@ export class RichTextField extends ObjectField { return `new RichTextField("${this.Data}")`; } - [ToGoogleDocText]() { + [ToPlainText]() { // Because we're working with plain text, just concatenate all paragraphs let content = JSON.parse(this.Data).doc.content; let paragraphs = content.filter((item: any) => item.type === "paragraph"); @@ -43,7 +43,7 @@ export class RichTextField extends ObjectField { return textParagraphs.join(joiner).trimEnd(delimiter); } - [FromGoogleDocText](plainText: string) { + [FromPlainText](plainText: string) { // Remap the text, creating blocks split on newlines let elements = plainText.split(delimiter); |