aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/google_docs/GoogleApiClientUtils.ts40
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx44
-rw-r--r--src/new_fields/RichTextField.ts25
3 files changed, 74 insertions, 35 deletions
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts
index c6c7d7bd4..c1cbba31f 100644
--- a/src/client/apis/google_docs/GoogleApiClientUtils.ts
+++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts
@@ -14,6 +14,11 @@ export namespace GoogleApiClientUtils {
Update = "update"
}
+ export enum WriteMode {
+ Insert,
+ Replace
+ }
+
export type DocumentId = string;
export type Reference = DocumentId | CreateOptions;
export type TextContent = string | string[];
@@ -37,6 +42,7 @@ export namespace GoogleApiClientUtils {
export type ReadOptions = RetrieveOptions & { removeNewlines?: boolean };
export interface WriteOptions {
+ mode: WriteMode;
content: TextContent;
reference: Reference;
index?: number; // if excluded, will compute the last index of the document and append the content there
@@ -158,28 +164,40 @@ export namespace GoogleApiClientUtils {
};
export const write = async (options: WriteOptions): Promise<UpdateResult> => {
+ const requests: docs_v1.Schema$Request[] = [];
const documentId = await Utils.initialize(options.reference);
if (!documentId) {
return undefined;
}
let index = options.index;
- if (!index) {
+ const mode = options.mode;
+ if (!(index && mode === WriteMode.Insert)) {
let schema = await retrieve({ documentId });
if (!schema || !(index = Utils.endOf(schema))) {
return undefined;
}
}
- const text = options.content;
- const updateOptions = {
- documentId,
- requests: [{
- insertText: {
- text: isArray(text) ? text.join("\n") : text,
- location: { index }
+ if (mode === WriteMode.Replace) {
+ requests.push({
+ deleteContentRange: {
+ range: {
+ startIndex: 1,
+ endIndex: index
+ }
}
- }]
- };
- return update(updateOptions);
+ });
+ index = 1;
+ }
+ const text = options.content;
+ requests.push({
+ insertText: {
+ text: isArray(text) ? text.join("\n") : text,
+ location: { index }
+ }
+ });
+ let replies = await update({ documentId, requests });
+ console.log(replies);
+ return replies;
};
}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index 5ba2aa0cf..ad29ce775 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, ToPlainText, FromPlainText } from "../../../new_fields/RichTextField";
+import { RichTextField, ToGoogleDocText, FromGoogleDocText } 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";
@@ -39,6 +39,7 @@ 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";
library.add(faEdit);
library.add(faSmile, faTextHeight, faUpload);
@@ -299,7 +300,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let exportState = await GoogleApiClientUtils.Docs.read({ documentId });
if (exportState) {
let data = Cast(dataDoc.data, RichTextField);
- data && data[FromPlainText](exportState);
+ if (data) {
+ dataDoc.data = new RichTextField(data[FromGoogleDocText](exportState));
+ }
} else {
delete dataDoc[googleDocKey];
}
@@ -629,6 +632,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._undoTyping.end();
this._undoTyping = undefined;
}
+ this.updateGoogleDoc();
}
public _undoTyping?: UndoManager.Batch;
onKeyPress = (e: React.KeyboardEvent) => {
@@ -683,23 +687,33 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
});
ContextMenu.Instance.addItem({ description: "Text Funcs...", subitems: subitems, icon: "text-height" });
if (!(googleDocKey in Doc.GetProto(this.props.Document))) {
- ContextMenu.Instance.addItem({ description: "Export to Google Doc...", event: this.exportToGoogleDoc, icon: "upload" });
+ ContextMenu.Instance.addItem({
+ description: "Export to Google Doc...",
+ event: this.updateGoogleDoc,
+ icon: "upload"
+ });
}
}
- exportToGoogleDoc = () => {
- const dataDoc = Doc.GetProto(this.props.Document);
- const data = Cast(dataDoc.data, RichTextField);
- if (!data) {
- return;
+ updateGoogleDoc = () => {
+ let modes = GoogleApiClientUtils.Docs.WriteMode;
+ let mode = modes.Replace;
+ let reference: Opt<GoogleApiClientUtils.Docs.Reference> = Cast(this.dataDoc[googleDocKey], "string");
+ if (!reference) {
+ mode = modes.Insert;
+ reference = {
+ title: StrCast(this.dataDoc.title),
+ handler: id => this.dataDoc[googleDocKey] = id
+ };
+ }
+ const data = Cast(this.dataDoc.data, RichTextField);
+ if (data) {
+ GoogleApiClientUtils.Docs.write({
+ mode,
+ content: data[ToGoogleDocText](),
+ reference
+ });
}
- GoogleApiClientUtils.Docs.write({
- reference: {
- title: StrCast(dataDoc.title),
- handler: id => dataDoc[googleDocKey] = id
- },
- content: data[ToPlainText]()
- });
}
render() {
diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts
index f0284b1b8..92b19b921 100644
--- a/src/new_fields/RichTextField.ts
+++ b/src/new_fields/RichTextField.ts
@@ -4,14 +4,14 @@ import { Deserializable } from "../client/util/SerializationHelper";
import { Copy, ToScriptString } from "./FieldSymbols";
import { scriptingGlobal } from "../client/util/Scripting";
-export const ToPlainText = Symbol("PlainText");
-export const FromPlainText = Symbol("PlainText");
+export const ToGoogleDocText = Symbol("PlainText");
+export const FromGoogleDocText = Symbol("PlainText");
@scriptingGlobal
@Deserializable("RichTextField")
export class RichTextField extends ObjectField {
@serializable(true)
- Data: string;
+ readonly Data: string;
constructor(data: string) {
super();
@@ -26,24 +26,28 @@ export class RichTextField extends ObjectField {
return `new RichTextField("${this.Data}")`;
}
- [ToPlainText]() {
+ [ToGoogleDocText]() {
let content = JSON.parse(this.Data).doc.content;
let paragraphs = content.filter((item: any) => item.type === "paragraph");
let output = "";
for (let i = 0; i < paragraphs.length; i++) {
let paragraph = paragraphs[i];
+ let addNewLine = i > 0 ? paragraphs[i - 1].content : false;
if (paragraph.content) {
output += paragraph.content.map((block: any) => block.text).join("");
} else {
- output += i > 0 && paragraphs[i - 1].content ? "\n\n" : "\n";
+ output += "\n";
}
+ addNewLine && (output += "\n");
}
return output;
}
- [FromPlainText](plainText: string) {
+ [FromGoogleDocText](plainText: string) {
let elements = plainText.split("\n");
+ !elements[elements.length - 1].length && elements.pop();
let parsed = JSON.parse(this.Data);
+ let blankCount = 0;
parsed.doc.content = elements.map(text => {
let paragraph: any = { type: "paragraph" };
if (text.length) {
@@ -52,15 +56,18 @@ export class RichTextField extends ObjectField {
marks: [],
text
}];
+ } else {
+ blankCount++;
}
return paragraph;
});
+ let selection = plainText.length + 2 * blankCount;
parsed.selection = {
type: "text",
- anchor: plainText.length,
- head: plainText.length
+ anchor: selection,
+ head: selection
};
- this.Data = JSON.stringify(parsed);
+ return JSON.stringify(parsed);
}
} \ No newline at end of file